From 5d381a47d1ff648fff4d9f83615cf0cb92cbf510 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 1 Feb 2011 16:28:36 +0000 Subject: Beginning of channel.credit support. Tests pass, except for drain=true. --- src/rabbit_amqqueue_process.erl | 3 ++- src/rabbit_channel.erl | 54 ++++++++++++++++++++++++++--------------- src/rabbit_limiter.erl | 46 +++++++++++++++++------------------ 3 files changed, 60 insertions(+), 43 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 3418c663..b74b9034 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1075,7 +1075,8 @@ handle_cast({limit, ChPid, LimiterPid}, State) -> true -> ok end, - NewLimited = Limited andalso LimiterPid =/= undefined, + NewLimited = Limited andalso LimiterPid =/= undefined + andalso rabbit_limiter:is_blocked(LimiterPid), C#cr{limiter_pid = LimiterPid, is_limit_active = NewLimited} end)); diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index a82e5eff..eb634cca 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -462,6 +462,7 @@ check_name(Kind, NameBin = <<"amq.", _/binary>>) -> check_name(_Kind, NameBin) -> NameBin. +%% TODO port this queue_blocked(QPid, State = #ch{blocking = Blocking}) -> case dict:find(QPid, Blocking) of error -> State; @@ -1003,31 +1004,46 @@ handle_method(#'confirm.select'{nowait = NoWait}, _, State) -> return_ok(State#ch{confirm_enabled = true}, NoWait, #'confirm.select_ok'{}); -handle_method(#'channel.flow'{active = true}, _, - State = #ch{limiter_pid = LimiterPid}) -> - LimiterPid1 = case rabbit_limiter:unblock(LimiterPid) of - ok -> LimiterPid; - stopped -> unlimit_queues(State) - end, - {reply, #'channel.flow_ok'{active = true}, - State#ch{limiter_pid = LimiterPid1}}; +handle_method(#'channel.flow'{active = true}, Content, State) -> + {noreply, State1 = #ch{writer_pid = WriterPid}} = + handle_method(#'channel.credit'{credit = -1, drain = true}, + Content, State), + ok = rabbit_writer:send_command(WriterPid, + #'channel.flow_ok'{active = true}), + {noreply, State1}; -handle_method(#'channel.flow'{active = false}, _, - State = #ch{limiter_pid = LimiterPid, +handle_method(#'channel.flow'{active = false}, Content, State) -> + {noreply, State1 = #ch{writer_pid = WriterPid}} = + handle_method(#'channel.credit'{credit = 0, drain = true}, + Content, State), + ok = rabbit_writer:send_command(WriterPid, + #'channel.flow_ok'{active = false}), + {noreply, State1}; + +handle_method(#'channel.credit'{credit = Credit, drain = Drain}, _, + State = #ch{limiter_pid = LimiterPid, consumer_mapping = Consumers}) -> LimiterPid1 = case LimiterPid of undefined -> start_limiter(State); Other -> Other end, - State1 = State#ch{limiter_pid = LimiterPid1}, - ok = rabbit_limiter:block(LimiterPid1), - case consumer_queues(Consumers) of - [] -> {reply, #'channel.flow_ok'{active = false}, State1}; - QPids -> Queues = [{QPid, erlang:monitor(process, QPid)} || - QPid <- QPids], - ok = rabbit_amqqueue:flush_all(QPids, self()), - {noreply, State1#ch{blocking = dict:from_list(Queues)}} - end; + LimiterPid2 = + case rabbit_limiter:set_credit(LimiterPid1, Credit, Drain) of + ok -> limit_queues(LimiterPid1, State), + LimiterPid1; + stopped -> unlimit_queues(State) + end, + State1 = State#ch{limiter_pid = LimiterPid2}, + {noreply, State1}; + + %% TODO port this bit + %% case consumer_queues(Consumers) of + %% [] -> {reply, #'channel.flow_ok'{active = false}, State1}; + %% QPids -> Queues = [{QPid, erlang:monitor(process, QPid)} || + %% QPid <- QPids], + %% ok = rabbit_amqqueue:flush_all(QPids, self()), + %% {noreply, State1#ch{blocking = dict:from_list(Queues)}} + %% end; handle_method(_MethodRecord, _Content, _State) -> rabbit_misc:protocol_error( diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index 86ea7282..bf9cf583 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -22,7 +22,7 @@ handle_info/2, prioritise_call/3]). -export([start_link/2]). -export([limit/2, can_send/3, ack/2, register/2, unregister/2]). --export([get_limit/1, block/1, unblock/1, is_blocked/1]). +-export([get_limit/1, set_credit/3, is_blocked/1]). %%---------------------------------------------------------------------------- @@ -38,8 +38,8 @@ -spec(register/2 :: (maybe_pid(), pid()) -> 'ok'). -spec(unregister/2 :: (maybe_pid(), pid()) -> 'ok'). -spec(get_limit/1 :: (maybe_pid()) -> non_neg_integer()). --spec(block/1 :: (maybe_pid()) -> 'ok'). --spec(unblock/1 :: (maybe_pid()) -> 'ok' | 'stopped'). +%% -spec(block/1 :: (maybe_pid()) -> 'ok'). +%% -spec(unblock/1 :: (maybe_pid()) -> 'ok' | 'stopped'). -spec(is_blocked/1 :: (maybe_pid()) -> boolean()). -endif. @@ -48,7 +48,8 @@ -record(lim, {prefetch_count = 0, ch_pid, - blocked = false, + credit = unlimited, + drain = false, queues = dict:new(), % QPid -> {MonitorRef, Notify} volume = 0}). %% 'Notify' is a boolean that indicates whether a queue should be @@ -95,15 +96,12 @@ get_limit(Pid) -> fun () -> 0 end, fun () -> gen_server2:call(Pid, get_limit, infinity) end). -block(undefined) -> +set_credit(undefined, _, _) -> ok; -block(LimiterPid) -> - gen_server2:call(LimiterPid, block, infinity). - -unblock(undefined) -> - ok; -unblock(LimiterPid) -> - gen_server2:call(LimiterPid, unblock, infinity). +set_credit(LimiterPid, -1, Drain) -> + gen_server2:call(LimiterPid, {set_credit, unlimited, Drain}, infinity); +set_credit(LimiterPid, Credit, Drain) -> + gen_server2:call(LimiterPid, {set_credit, Credit, Drain}, infinity). is_blocked(undefined) -> false; @@ -121,14 +119,18 @@ prioritise_call(get_limit, _From, _State) -> 9; prioritise_call(_Msg, _From, _State) -> 0. handle_call({can_send, _QPid, _AckRequired}, _From, - State = #lim{blocked = true}) -> + State = #lim{credit = 0}) -> {reply, false, State}; handle_call({can_send, QPid, AckRequired}, _From, - State = #lim{volume = Volume}) -> + State = #lim{volume = Volume, credit = Credit}) -> case limit_reached(State) of true -> {reply, false, limit_queue(QPid, State)}; false -> {reply, true, State#lim{volume = if AckRequired -> Volume + 1; true -> Volume + end, + credit = case Credit of + unlimited -> unlimited; + _ -> Credit - 1 end}} end; @@ -141,11 +143,8 @@ handle_call({limit, PrefetchCount}, _From, State) -> {stop, State1} -> {stop, normal, stopped, State1} end; -handle_call(block, _From, State) -> - {reply, ok, State#lim{blocked = true}}; - -handle_call(unblock, _From, State) -> - case maybe_notify(State, State#lim{blocked = false}) of +handle_call({set_credit, Credit, Drain}, _From, State) -> + case maybe_notify(State, State#lim{credit = Credit, drain = Drain}) of {cont, State1} -> {reply, ok, State1}; {stop, State1} -> {stop, normal, stopped, State1} end; @@ -183,9 +182,9 @@ maybe_notify(OldState, NewState) -> case (limit_reached(OldState) orelse blocked(OldState)) andalso not (limit_reached(NewState) orelse blocked(NewState)) of true -> NewState1 = notify_queues(NewState), - {case NewState1#lim.prefetch_count of - 0 -> stop; - _ -> cont + {case {NewState1#lim.prefetch_count, NewState1#lim.credit} of + {0, unlimited} -> stop; + _ -> cont end, NewState1}; false -> {cont, NewState} end. @@ -193,7 +192,8 @@ maybe_notify(OldState, NewState) -> limit_reached(#lim{prefetch_count = Limit, volume = Volume}) -> Limit =/= 0 andalso Volume >= Limit. -blocked(#lim{blocked = Blocked}) -> Blocked. +blocked(#lim{credit = 0}) -> true; +blocked(_) -> false. remember_queue(QPid, State = #lim{queues = Queues}) -> case dict:is_key(QPid, Queues) of -- cgit v1.2.1 From 80bf3ebfe759c7164aa44a1e15a346b4139b1eb6 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 1 Feb 2011 16:53:28 +0000 Subject: Support drain (probably in a rather inefficient way). --- src/rabbit_amqqueue_process.erl | 7 +++++-- src/rabbit_limiter.erl | 32 +++++++++++++++++--------------- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index b74b9034..9f497f3d 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -335,7 +335,9 @@ ch_record_state_transition(OldCR, NewCR) -> deliver_msgs_to_consumers(Funs = {PredFun, DeliverFun}, FunAcc, State = #q{q = #amqqueue{name = QName}, active_consumers = ActiveConsumers, - blocked_consumers = BlockedConsumers}) -> + blocked_consumers = BlockedConsumers, + backing_queue = BQ, + backing_queue_state = BQS}) -> case queue:out(ActiveConsumers) of {{value, QEntry = {ChPid, #consumer{tag = ConsumerTag, ack_required = AckRequired}}}, @@ -345,7 +347,8 @@ deliver_msgs_to_consumers(Funs = {PredFun, DeliverFun}, FunAcc, acktags = ChAckTags} = ch_record(ChPid), IsMsgReady = PredFun(FunAcc, State), case (IsMsgReady andalso - rabbit_limiter:can_send( LimiterPid, self(), AckRequired )) of + rabbit_limiter:can_send( LimiterPid, self(), AckRequired, + BQ:len(BQS) )) of true -> {{Message, IsDelivered, AckTag}, FunAcc1, State1} = DeliverFun(AckRequired, FunAcc, State), diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index bf9cf583..cd3ac9c5 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -21,7 +21,7 @@ -export([init/1, terminate/2, code_change/3, handle_call/3, handle_cast/2, handle_info/2, prioritise_call/3]). -export([start_link/2]). --export([limit/2, can_send/3, ack/2, register/2, unregister/2]). +-export([limit/2, can_send/4, ack/2, register/2, unregister/2]). -export([get_limit/1, set_credit/3, is_blocked/1]). %%---------------------------------------------------------------------------- @@ -33,7 +33,7 @@ -spec(start_link/2 :: (pid(), non_neg_integer()) -> rabbit_types:ok_pid_or_error()). -spec(limit/2 :: (maybe_pid(), non_neg_integer()) -> 'ok' | 'stopped'). --spec(can_send/3 :: (maybe_pid(), pid(), boolean()) -> boolean()). +-spec(can_send/4 :: (maybe_pid(), pid(), boolean(), non_neg_integer()) -> boolean()). -spec(ack/2 :: (maybe_pid(), non_neg_integer()) -> 'ok'). -spec(register/2 :: (maybe_pid(), pid()) -> 'ok'). -spec(unregister/2 :: (maybe_pid(), pid()) -> 'ok'). @@ -70,12 +70,12 @@ limit(LimiterPid, PrefetchCount) -> %% Ask the limiter whether the queue can deliver a message without %% breaching a limit -can_send(undefined, _QPid, _AckRequired) -> +can_send(undefined, _QPid, _AckRequired, _Len) -> true; -can_send(LimiterPid, QPid, AckRequired) -> +can_send(LimiterPid, QPid, AckRequired, Len) -> rabbit_misc:with_exit_handler( fun () -> true end, - fun () -> gen_server2:call(LimiterPid, {can_send, QPid, AckRequired}, + fun () -> gen_server2:call(LimiterPid, {can_send, QPid, AckRequired, Len}, infinity) end). %% Let the limiter know that the channel has received some acks from a @@ -118,20 +118,22 @@ init([ChPid, UnackedMsgCount]) -> prioritise_call(get_limit, _From, _State) -> 9; prioritise_call(_Msg, _From, _State) -> 0. -handle_call({can_send, _QPid, _AckRequired}, _From, +handle_call({can_send, _QPid, _AckRequired, _Len}, _From, State = #lim{credit = 0}) -> {reply, false, State}; -handle_call({can_send, QPid, AckRequired}, _From, - State = #lim{volume = Volume, credit = Credit}) -> +handle_call({can_send, QPid, AckRequired, Len}, _From, + State = #lim{volume = Volume, credit = Credit, drain = Drain}) -> case limit_reached(State) of true -> {reply, false, limit_queue(QPid, State)}; - false -> {reply, true, State#lim{volume = if AckRequired -> Volume + 1; - true -> Volume - end, - credit = case Credit of - unlimited -> unlimited; - _ -> Credit - 1 - end}} + false -> {reply, true, + State#lim{volume = if AckRequired -> Volume + 1; + true -> Volume + end, + credit = case {Credit, Len, Drain} of + {unlimited, _, _} -> unlimited; + {_, 1, true} -> 0; + {_, _, _} -> Credit - 1 + end}} end; handle_call(get_limit, _From, State = #lim{prefetch_count = PrefetchCount}) -> -- cgit v1.2.1 From c44c0fb9bb2adcbb9082efdcd18f4444e8a4d203 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 2 Feb 2011 12:04:07 +0000 Subject: Credit needs to be per ctag. --- src/rabbit_amqqueue_process.erl | 2 +- src/rabbit_channel.erl | 57 +++++++++------- src/rabbit_limiter.erl | 142 ++++++++++++++++++++++++++-------------- 3 files changed, 127 insertions(+), 74 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 9f497f3d..9fda12cd 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -348,7 +348,7 @@ deliver_msgs_to_consumers(Funs = {PredFun, DeliverFun}, FunAcc, IsMsgReady = PredFun(FunAcc, State), case (IsMsgReady andalso rabbit_limiter:can_send( LimiterPid, self(), AckRequired, - BQ:len(BQS) )) of + ConsumerTag, BQ:len(BQS) )) of true -> {{Message, IsDelivered, AckTag}, FunAcc1, State1} = DeliverFun(AckRequired, FunAcc, State), diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index eb634cca..bac106f9 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -1004,23 +1004,34 @@ handle_method(#'confirm.select'{nowait = NoWait}, _, State) -> return_ok(State#ch{confirm_enabled = true}, NoWait, #'confirm.select_ok'{}); -handle_method(#'channel.flow'{active = true}, Content, State) -> - {noreply, State1 = #ch{writer_pid = WriterPid}} = - handle_method(#'channel.credit'{credit = -1, drain = true}, - Content, State), - ok = rabbit_writer:send_command(WriterPid, - #'channel.flow_ok'{active = true}), - {noreply, State1}; +handle_method(#'channel.flow'{active = true}, _, + State = #ch{limiter_pid = LimiterPid}) -> + LimiterPid1 = case rabbit_limiter:unblock(LimiterPid) of + ok -> LimiterPid; + stopped -> unlimit_queues(State) + end, + {reply, #'channel.flow_ok'{active = true}, + State#ch{limiter_pid = LimiterPid1}}; -handle_method(#'channel.flow'{active = false}, Content, State) -> - {noreply, State1 = #ch{writer_pid = WriterPid}} = - handle_method(#'channel.credit'{credit = 0, drain = true}, - Content, State), - ok = rabbit_writer:send_command(WriterPid, - #'channel.flow_ok'{active = false}), - {noreply, State1}; +handle_method(#'channel.flow'{active = false}, _, + State = #ch{limiter_pid = LimiterPid, + consumer_mapping = Consumers}) -> + LimiterPid1 = case LimiterPid of + undefined -> start_limiter(State); + Other -> Other + end, + State1 = State#ch{limiter_pid = LimiterPid1}, + ok = rabbit_limiter:block(LimiterPid1), + case consumer_queues(Consumers) of + [] -> {reply, #'channel.flow_ok'{active = false}, State1}; + QPids -> Queues = [{QPid, erlang:monitor(process, QPid)} || + QPid <- QPids], + ok = rabbit_amqqueue:flush_all(QPids, self()), + {noreply, State1#ch{blocking = dict:from_list(Queues)}} + end; -handle_method(#'channel.credit'{credit = Credit, drain = Drain}, _, +handle_method(#'basic.credit'{consumer_tag = CTag, credit = Credit, + drain = Drain}, _, State = #ch{limiter_pid = LimiterPid, consumer_mapping = Consumers}) -> LimiterPid1 = case LimiterPid of @@ -1028,7 +1039,7 @@ handle_method(#'channel.credit'{credit = Credit, drain = Drain}, _, Other -> Other end, LimiterPid2 = - case rabbit_limiter:set_credit(LimiterPid1, Credit, Drain) of + case rabbit_limiter:set_credit(LimiterPid1, CTag, Credit, Drain) of ok -> limit_queues(LimiterPid1, State), LimiterPid1; stopped -> unlimit_queues(State) @@ -1036,7 +1047,7 @@ handle_method(#'channel.credit'{credit = Credit, drain = Drain}, _, State1 = State#ch{limiter_pid = LimiterPid2}, {noreply, State1}; - %% TODO port this bit + %% TODO port this bit ? %% case consumer_queues(Consumers) of %% [] -> {reply, #'channel.flow_ok'{active = false}, State1}; %% QPids -> Queues = [{QPid, erlang:monitor(process, QPid)} || @@ -1237,12 +1248,12 @@ consumer_queues(Consumers) -> notify_limiter(undefined, _Acked) -> ok; notify_limiter(LimiterPid, Acked) -> - case rabbit_misc:queue_fold(fun ({_, none, _}, Acc) -> Acc; - ({_, _, _}, Acc) -> Acc + 1 - end, 0, Acked) of - 0 -> ok; - Count -> rabbit_limiter:ack(LimiterPid, Count) - end. + %% TODO this could be faster, group the acks + rabbit_misc:queue_fold( + fun ({_, none, _}, Acc) -> Acc; + ({_, CTag, _}, Acc) -> rabbit_limiter:ack(LimiterPid, CTag), + Acc + end, ok, Acked). is_message_persistent(Content) -> case rabbit_basic:is_message_persistent(Content) of diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index cd3ac9c5..efe6023b 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -21,8 +21,8 @@ -export([init/1, terminate/2, code_change/3, handle_call/3, handle_cast/2, handle_info/2, prioritise_call/3]). -export([start_link/2]). --export([limit/2, can_send/4, ack/2, register/2, unregister/2]). --export([get_limit/1, set_credit/3, is_blocked/1]). +-export([limit/2, can_send/5, ack/2, register/2, unregister/2]). +-export([get_limit/1, block/1, unblock/1, set_credit/4, is_blocked/1]). %%---------------------------------------------------------------------------- @@ -33,13 +33,13 @@ -spec(start_link/2 :: (pid(), non_neg_integer()) -> rabbit_types:ok_pid_or_error()). -spec(limit/2 :: (maybe_pid(), non_neg_integer()) -> 'ok' | 'stopped'). --spec(can_send/4 :: (maybe_pid(), pid(), boolean(), non_neg_integer()) -> boolean()). --spec(ack/2 :: (maybe_pid(), non_neg_integer()) -> 'ok'). +-spec(can_send/5 :: (maybe_pid(), pid(), boolean(), binary(), non_neg_integer()) -> boolean()). +-spec(ack/2 :: (maybe_pid(), binary()) -> 'ok'). -spec(register/2 :: (maybe_pid(), pid()) -> 'ok'). -spec(unregister/2 :: (maybe_pid(), pid()) -> 'ok'). -spec(get_limit/1 :: (maybe_pid()) -> non_neg_integer()). -%% -spec(block/1 :: (maybe_pid()) -> 'ok'). -%% -spec(unblock/1 :: (maybe_pid()) -> 'ok' | 'stopped'). +-spec(block/1 :: (maybe_pid()) -> 'ok'). +-spec(unblock/1 :: (maybe_pid()) -> 'ok' | 'stopped'). -spec(is_blocked/1 :: (maybe_pid()) -> boolean()). -endif. @@ -48,10 +48,13 @@ -record(lim, {prefetch_count = 0, ch_pid, - credit = unlimited, - drain = false, + blocked = false, + credits = dict:new(), queues = dict:new(), % QPid -> {MonitorRef, Notify} volume = 0}). + +-record(credit, {credit = 0, drain = false}). + %% 'Notify' is a boolean that indicates whether a queue should be %% notified of a change in the limit or volume that may allow it to %% deliver more messages via the limiter's channel. @@ -70,18 +73,19 @@ limit(LimiterPid, PrefetchCount) -> %% Ask the limiter whether the queue can deliver a message without %% breaching a limit -can_send(undefined, _QPid, _AckRequired, _Len) -> +can_send(undefined, _QPid, _AckRequired, _CTag, _Len) -> true; -can_send(LimiterPid, QPid, AckRequired, Len) -> +can_send(LimiterPid, QPid, AckRequired, CTag, Len) -> rabbit_misc:with_exit_handler( fun () -> true end, - fun () -> gen_server2:call(LimiterPid, {can_send, QPid, AckRequired, Len}, + fun () -> gen_server2:call(LimiterPid, + {can_send, QPid, AckRequired, CTag, Len}, infinity) end). %% Let the limiter know that the channel has received some acks from a %% consumer -ack(undefined, _Count) -> ok; -ack(LimiterPid, Count) -> gen_server2:cast(LimiterPid, {ack, Count}). +ack(undefined, _CTag) -> ok; +ack(LimiterPid, CTag) -> gen_server2:cast(LimiterPid, {ack, CTag}). register(undefined, _QPid) -> ok; register(LimiterPid, QPid) -> gen_server2:cast(LimiterPid, {register, QPid}). @@ -96,12 +100,20 @@ get_limit(Pid) -> fun () -> 0 end, fun () -> gen_server2:call(Pid, get_limit, infinity) end). -set_credit(undefined, _, _) -> +block(undefined) -> + ok; +block(LimiterPid) -> + gen_server2:call(LimiterPid, block, infinity). + +unblock(undefined) -> ok; -set_credit(LimiterPid, -1, Drain) -> - gen_server2:call(LimiterPid, {set_credit, unlimited, Drain}, infinity); -set_credit(LimiterPid, Credit, Drain) -> - gen_server2:call(LimiterPid, {set_credit, Credit, Drain}, infinity). +unblock(LimiterPid) -> + gen_server2:call(LimiterPid, unblock, infinity). + +set_credit(undefined, _, _, _) -> + ok; +set_credit(LimiterPid, CTag, Credit, Drain) -> + gen_server2:call(LimiterPid, {set_credit, CTag, Credit, Drain}, infinity). is_blocked(undefined) -> false; @@ -118,47 +130,47 @@ init([ChPid, UnackedMsgCount]) -> prioritise_call(get_limit, _From, _State) -> 9; prioritise_call(_Msg, _From, _State) -> 0. -handle_call({can_send, _QPid, _AckRequired, _Len}, _From, - State = #lim{credit = 0}) -> +handle_call({can_send, _QPid, _AckRequired, _CTag, _Len}, _From, + State = #lim{blocked = true}) -> {reply, false, State}; -handle_call({can_send, QPid, AckRequired, Len}, _From, - State = #lim{volume = Volume, credit = Credit, drain = Drain}) -> - case limit_reached(State) of +handle_call({can_send, QPid, AckRequired, CTag, Len}, _From, + State = #lim{volume = Volume}) -> + case limit_reached(CTag, State) of true -> {reply, false, limit_queue(QPid, State)}; false -> {reply, true, - State#lim{volume = if AckRequired -> Volume + 1; - true -> Volume - end, - credit = case {Credit, Len, Drain} of - {unlimited, _, _} -> unlimited; - {_, 1, true} -> 0; - {_, _, _} -> Credit - 1 - end}} + decr_credit(CTag, Len, + State#lim{volume = if AckRequired -> Volume + 1; + true -> Volume + end})} end; handle_call(get_limit, _From, State = #lim{prefetch_count = PrefetchCount}) -> {reply, PrefetchCount, State}; handle_call({limit, PrefetchCount}, _From, State) -> - case maybe_notify(State, State#lim{prefetch_count = PrefetchCount}) of + case maybe_notify(irrelevant, + State, State#lim{prefetch_count = PrefetchCount}) of {cont, State1} -> {reply, ok, State1}; {stop, State1} -> {stop, normal, stopped, State1} end; -handle_call({set_credit, Credit, Drain}, _From, State) -> - case maybe_notify(State, State#lim{credit = Credit, drain = Drain}) of - {cont, State1} -> {reply, ok, State1}; - {stop, State1} -> {stop, normal, stopped, State1} - end; +handle_call(block, _From, State) -> + {reply, ok, State#lim{blocked = true}}; + +handle_call(unblock, _From, State) -> + maybe_notify_reply(irrelevant, State, State#lim{blocked = false}); + +handle_call({set_credit, CTag, Credit, Drain}, _From, State) -> + maybe_notify_reply(CTag, State, update_credit(CTag, Credit, Drain, State)); handle_call(is_blocked, _From, State) -> {reply, blocked(State), State}. -handle_cast({ack, Count}, State = #lim{volume = Volume}) -> +handle_cast({ack, CTag}, State = #lim{volume = Volume}) -> NewVolume = if Volume == 0 -> 0; - true -> Volume - Count + true -> Volume - 1 end, - {cont, State1} = maybe_notify(State, State#lim{volume = NewVolume}), + {cont, State1} = maybe_notify(CTag, State, State#lim{volume = NewVolume}), {noreply, State1}; handle_cast({register, QPid}, State) -> @@ -180,22 +192,52 @@ code_change(_, State, _) -> %% Internal plumbing %%---------------------------------------------------------------------------- -maybe_notify(OldState, NewState) -> - case (limit_reached(OldState) orelse blocked(OldState)) andalso - not (limit_reached(NewState) orelse blocked(NewState)) of +maybe_notify_reply(CTag, OldState, NewState) -> + case maybe_notify(CTag, OldState, NewState) of + {cont, State} -> {reply, ok, State}; + {stop, State} -> {stop, normal, stopped, State} + end. + +maybe_notify(CTag, OldState, NewState) -> + case (limit_reached(CTag, OldState) orelse blocked(OldState)) andalso + not (limit_reached(CTag, NewState) orelse blocked(NewState)) of true -> NewState1 = notify_queues(NewState), - {case {NewState1#lim.prefetch_count, NewState1#lim.credit} of - {0, unlimited} -> stop; - _ -> cont + {case {NewState1#lim.prefetch_count, + dict:size(NewState1#lim.credits)} of + {0, 0} -> stop; + _ -> cont end, NewState1}; false -> {cont, NewState} end. -limit_reached(#lim{prefetch_count = Limit, volume = Volume}) -> - Limit =/= 0 andalso Volume >= Limit. +limit_reached(CTag, #lim{prefetch_count = Limit, volume = Volume, + credits = Credits}) -> + case dict:find(CTag, Credits) of + {ok, #credit{ credit = 0 }} -> true; + _ -> false + end orelse (Limit =/= 0 andalso Volume >= Limit). + +decr_credit(CTag, Len, State = #lim{ credits = Credits } ) -> + case dict:find(CTag, Credits) of + {ok, #credit{ credit = Credit, drain = Drain }} -> + NewCredit = case {Len, Drain} of + {1, true} -> 0; + {_, _} -> Credit - 1 + end, + update_credit(CTag, NewCredit, Drain, State); + error -> + State + end. + +update_credit(CTag, -1, _Drain, State = #lim{credits = Credits}) -> + State#lim{credits = dict:erase(CTag, Credits)}; + +update_credit(CTag, Credit, Drain, State = #lim{credits = Credits}) -> + State#lim{credits = dict:store(CTag, + #credit{credit = Credit, drain = Drain}, + Credits)}. -blocked(#lim{credit = 0}) -> true; -blocked(_) -> false. +blocked(#lim{blocked = Blocked}) -> Blocked. remember_queue(QPid, State = #lim{queues = Queues}) -> case dict:is_key(QPid, Queues) of -- cgit v1.2.1 From b5861c7c239af97813adac731b58049ad66fd9d6 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 2 Feb 2011 12:40:13 +0000 Subject: Return a basic.credit-state after receiving a basic.credit. --- src/rabbit_channel.erl | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index bac106f9..a0624101 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -1034,6 +1034,19 @@ handle_method(#'basic.credit'{consumer_tag = CTag, credit = Credit, drain = Drain}, _, State = #ch{limiter_pid = LimiterPid, consumer_mapping = Consumers}) -> + %% We get Available first because it's likely that as soon as we set + %% the credit msgs will get consumed and it'll be out of date. Why do we + %% want that? Because at least then it's consistent with the credit value + %% we return. And Available is always going to be racy. + Available = case dict:find(CTag, Consumers) of + {ok, QName} -> + case rabbit_amqqueue:with( + QName, fun (Q) -> rabbit_amqqueue:stat(Q) end) of + {ok, Len, _} -> Len; + _ -> -1 + end; + error -> -1 + end, LimiterPid1 = case LimiterPid of undefined -> start_limiter(State); Other -> Other @@ -1045,7 +1058,10 @@ handle_method(#'basic.credit'{consumer_tag = CTag, credit = Credit, stopped -> unlimit_queues(State) end, State1 = State#ch{limiter_pid = LimiterPid2}, - {noreply, State1}; + return_ok(State1, false, #'basic.credit_state'{consumer_tag = CTag, + credit = Credit, + available = Available, + drain = Drain}); %% TODO port this bit ? %% case consumer_queues(Consumers) of -- cgit v1.2.1 From 988d8dfe9c3bd91b30861062aaaa274400ce8453 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 2 Feb 2011 17:36:48 +0000 Subject: Send a credit-state when magical draining happens. --- src/rabbit_channel.erl | 2 +- src/rabbit_limiter.erl | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index a0624101..c4b9daf7 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -462,7 +462,7 @@ check_name(Kind, NameBin = <<"amq.", _/binary>>) -> check_name(_Kind, NameBin) -> NameBin. -%% TODO port this +%% TODO port this(?) queue_blocked(QPid, State = #ch{blocking = Blocking}) -> case dict:find(QPid, Blocking) of error -> State; diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index efe6023b..4a05050f 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -15,6 +15,7 @@ %% -module(rabbit_limiter). +-include("rabbit_framing.hrl"). -behaviour(gen_server2). @@ -217,18 +218,27 @@ limit_reached(CTag, #lim{prefetch_count = Limit, volume = Volume, _ -> false end orelse (Limit =/= 0 andalso Volume >= Limit). -decr_credit(CTag, Len, State = #lim{ credits = Credits } ) -> +decr_credit(CTag, Len, State = #lim{ credits = Credits, ch_pid = ChPid } ) -> case dict:find(CTag, Credits) of {ok, #credit{ credit = Credit, drain = Drain }} -> - NewCredit = case {Len, Drain} of - {1, true} -> 0; - {_, _} -> Credit - 1 + NewCredit = case {Credit, Len, Drain} of + {1, _, _} -> 0; %% Usual reduction to 0 + {_, 1, true} -> send_drained(ChPid, CTag), + 0; %% Magic reduction to 0 + {_, _, _} -> Credit - 1 end, update_credit(CTag, NewCredit, Drain, State); error -> State end. +send_drained(ChPid, CTag) -> + rabbit_channel:send_command(ChPid, + #'basic.credit_state'{consumer_tag = CTag, + credit = 0, + available = 0, + drain = true}). + update_credit(CTag, -1, _Drain, State = #lim{credits = Credits}) -> State#lim{credits = dict:erase(CTag, Credits)}; -- cgit v1.2.1 From 34fe529ddb4d61ac74b6976e3b79e5b3e4928230 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 3 Feb 2011 13:38:11 +0000 Subject: Change mind again on how this should work - have a synchronous reply method, and an async method for spontaneous notification. --- src/rabbit_channel.erl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index c4b9daf7..139dfd47 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -1058,10 +1058,10 @@ handle_method(#'basic.credit'{consumer_tag = CTag, credit = Credit, stopped -> unlimit_queues(State) end, State1 = State#ch{limiter_pid = LimiterPid2}, - return_ok(State1, false, #'basic.credit_state'{consumer_tag = CTag, - credit = Credit, - available = Available, - drain = Drain}); + return_ok(State1, false, #'basic.credit_ok'{consumer_tag = CTag, + credit = Credit, + available = Available, + drain = Drain}); %% TODO port this bit ? %% case consumer_queues(Consumers) of -- cgit v1.2.1 From 8e1b3d290e6b4d0b20f5eaf51a56195c80da0889 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 3 Feb 2011 15:49:12 +0000 Subject: Remove redundant fields from credit-ok. --- src/rabbit_channel.erl | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 139dfd47..f76026d2 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -1058,10 +1058,7 @@ handle_method(#'basic.credit'{consumer_tag = CTag, credit = Credit, stopped -> unlimit_queues(State) end, State1 = State#ch{limiter_pid = LimiterPid2}, - return_ok(State1, false, #'basic.credit_ok'{consumer_tag = CTag, - credit = Credit, - available = Available, - drain = Drain}); + return_ok(State1, false, #'basic.credit_ok'{available = Available}); %% TODO port this bit ? %% case consumer_queues(Consumers) of -- cgit v1.2.1 From 4c8c7fd13676cdf3cb02b15acd4a811bb5b04a14 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Wed, 9 Feb 2011 16:27:42 +0000 Subject: Use a base for basic.credit, to account for messages that have been delivered but not received by the client --- src/rabbit_channel.erl | 6 ++++-- src/rabbit_limiter.erl | 58 +++++++++++++++++++++++++++++++------------------- 2 files changed, 40 insertions(+), 24 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index f76026d2..5b264cc6 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -1030,7 +1030,9 @@ handle_method(#'channel.flow'{active = false}, _, {noreply, State1#ch{blocking = dict:from_list(Queues)}} end; -handle_method(#'basic.credit'{consumer_tag = CTag, credit = Credit, +handle_method(#'basic.credit'{consumer_tag = CTag, + credit = Credit, + count = Count, drain = Drain}, _, State = #ch{limiter_pid = LimiterPid, consumer_mapping = Consumers}) -> @@ -1052,7 +1054,7 @@ handle_method(#'basic.credit'{consumer_tag = CTag, credit = Credit, Other -> Other end, LimiterPid2 = - case rabbit_limiter:set_credit(LimiterPid1, CTag, Credit, Drain) of + case rabbit_limiter:set_credit(LimiterPid1, CTag, Credit, Count, Drain) of ok -> limit_queues(LimiterPid1, State), LimiterPid1; stopped -> unlimit_queues(State) diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index 4a05050f..a9c0406f 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -23,7 +23,7 @@ handle_info/2, prioritise_call/3]). -export([start_link/2]). -export([limit/2, can_send/5, ack/2, register/2, unregister/2]). --export([get_limit/1, block/1, unblock/1, set_credit/4, is_blocked/1]). +-export([get_limit/1, block/1, unblock/1, set_credit/5, is_blocked/1]). %%---------------------------------------------------------------------------- @@ -54,7 +54,7 @@ queues = dict:new(), % QPid -> {MonitorRef, Notify} volume = 0}). --record(credit, {credit = 0, drain = false}). +-record(credit, {count = 0, credit = 0, drain = false}). %% 'Notify' is a boolean that indicates whether a queue should be %% notified of a change in the limit or volume that may allow it to @@ -111,10 +111,11 @@ unblock(undefined) -> unblock(LimiterPid) -> gen_server2:call(LimiterPid, unblock, infinity). -set_credit(undefined, _, _, _) -> +set_credit(undefined, _, _, _, _) -> ok; -set_credit(LimiterPid, CTag, Credit, Drain) -> - gen_server2:call(LimiterPid, {set_credit, CTag, Credit, Drain}, infinity). +set_credit(LimiterPid, CTag, Credit, Count, Drain) -> + io:format("Set credit for ~p: credit ~p, count ~p, drain ~p~n", [CTag, Credit, Count, Drain]), + gen_server2:call(LimiterPid, {set_credit, CTag, Credit, Count, Drain}, infinity). is_blocked(undefined) -> false; @@ -161,8 +162,8 @@ handle_call(block, _From, State) -> handle_call(unblock, _From, State) -> maybe_notify_reply(irrelevant, State, State#lim{blocked = false}); -handle_call({set_credit, CTag, Credit, Drain}, _From, State) -> - maybe_notify_reply(CTag, State, update_credit(CTag, Credit, Drain, State)); +handle_call({set_credit, CTag, Credit, Count, Drain}, _From, State) -> + maybe_notify_reply(CTag, State, update_credit(CTag, Credit, Count, Drain, State)); handle_call(is_blocked, _From, State) -> {reply, blocked(State), State}. @@ -218,34 +219,47 @@ limit_reached(CTag, #lim{prefetch_count = Limit, volume = Volume, _ -> false end orelse (Limit =/= 0 andalso Volume >= Limit). -decr_credit(CTag, Len, State = #lim{ credits = Credits, ch_pid = ChPid } ) -> +decr_credit(CTag, Len, State = #lim{ credits = Credits, + ch_pid = ChPid } ) -> case dict:find(CTag, Credits) of - {ok, #credit{ credit = Credit, drain = Drain }} -> - NewCredit = case {Credit, Len, Drain} of - {1, _, _} -> 0; %% Usual reduction to 0 - {_, 1, true} -> send_drained(ChPid, CTag), - 0; %% Magic reduction to 0 - {_, _, _} -> Credit - 1 - end, - update_credit(CTag, NewCredit, Drain, State); + {ok, #credit{ credit = Credit, count = Count, drain = Drain }} -> + {NewCredit, NewCount} = + case {Credit, Len, Drain} of + {1, _, _} -> {0, Count + 1}; %% Usual reduction to 0 + {_, 1, true} -> + NewCount0 = Count + (Credit - 1), + send_drained(ChPid, CTag, NewCount0), + {0, NewCount0}; %% Magic reduction to 0 + {_, _, _} -> {Credit - 1, Count + 1} + end, + update_credit(CTag, NewCredit, NewCount, Drain, State); error -> State end. -send_drained(ChPid, CTag) -> +send_drained(ChPid, CTag, Count) -> rabbit_channel:send_command(ChPid, #'basic.credit_state'{consumer_tag = CTag, credit = 0, + count = Count, available = 0, drain = true}). -update_credit(CTag, -1, _Drain, State = #lim{credits = Credits}) -> +update_credit(CTag, -1, _Count, _Drain, State = #lim{credits = Credits}) -> State#lim{credits = dict:erase(CTag, Credits)}; -update_credit(CTag, Credit, Drain, State = #lim{credits = Credits}) -> - State#lim{credits = dict:store(CTag, - #credit{credit = Credit, drain = Drain}, - Credits)}. +%% Edge case: if the queue has nothing in it, and drain is set, we want to +%% send a credit. +update_credit(CTag, Credit, Count, Drain, State = #lim{credits = Credits}) -> + New = case dict:find(CTag, Credits) of + #credit{ count = OldCount, + credit = OldCredit } = Old -> + Old#credit{ credit = erlang:max( + 0, OldCount + OldCredit - Count), + count = Count, drain = Drain }; + _ -> #credit{ count = Count, credit = Credit, drain = Drain } + end, + State#lim{credits = dict:store(CTag, New, Credits)}. blocked(#lim{blocked = Blocked}) -> Blocked. -- cgit v1.2.1 From 0e9717aaf780e7cc37ff07da68d6c880a32950e4 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 9 Feb 2011 17:03:54 +0000 Subject: Remove io:format --- src/rabbit_limiter.erl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index a9c0406f..6798f7e5 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -114,7 +114,6 @@ unblock(LimiterPid) -> set_credit(undefined, _, _, _, _) -> ok; set_credit(LimiterPid, CTag, Credit, Count, Drain) -> - io:format("Set credit for ~p: credit ~p, count ~p, drain ~p~n", [CTag, Credit, Count, Drain]), gen_server2:call(LimiterPid, {set_credit, CTag, Credit, Count, Drain}, infinity). is_blocked(undefined) -> -- cgit v1.2.1 From c05eadffe1a8a9141e669bd169b611adb0097f2f Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Wed, 9 Feb 2011 17:52:18 +0000 Subject: Separate calculating the credit from updating the state, and do it properly. --- src/rabbit_limiter.erl | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index 6798f7e5..a9ecf2e0 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -162,7 +162,7 @@ handle_call(unblock, _From, State) -> maybe_notify_reply(irrelevant, State, State#lim{blocked = false}); handle_call({set_credit, CTag, Credit, Count, Drain}, _From, State) -> - maybe_notify_reply(CTag, State, update_credit(CTag, Credit, Count, Drain, State)); + maybe_notify_reply(CTag, State, reset_credit(CTag, Credit, Count, Drain, State)); handle_call(is_blocked, _From, State) -> {reply, blocked(State), State}. @@ -244,21 +244,31 @@ send_drained(ChPid, CTag, Count) -> available = 0, drain = true}). +%% Assert the credit state. The count may not match ours, in which +%% case we must rebase the credit. +%% TODO Edge case: if the queue has nothing in it, and drain is set, +%% we want to send a basic.credit back. +reset_credit(CTag, Credit0, Count0, Drain, State = #lim{credits = Credits}) -> + Count = + case dict:find(CTag, Credits) of + {ok, #credit{ count = LocalCount }} -> + LocalCount; + _ -> Count0 + end, + %% Our credit may have been reduced while messages are + %% in flight, so we bottom out at 0. + Credit = erlang:max(0, Count0 + Credit0 - Count), + update_credit(CTag, Credit, Count, Drain, State). + +%% Store the credit update_credit(CTag, -1, _Count, _Drain, State = #lim{credits = Credits}) -> State#lim{credits = dict:erase(CTag, Credits)}; -%% Edge case: if the queue has nothing in it, and drain is set, we want to -%% send a credit. update_credit(CTag, Credit, Count, Drain, State = #lim{credits = Credits}) -> - New = case dict:find(CTag, Credits) of - #credit{ count = OldCount, - credit = OldCredit } = Old -> - Old#credit{ credit = erlang:max( - 0, OldCount + OldCredit - Count), - count = Count, drain = Drain }; - _ -> #credit{ count = Count, credit = Credit, drain = Drain } - end, - State#lim{credits = dict:store(CTag, New, Credits)}. + State#lim{credits = dict:store(CTag, + #credit{credit = Credit, + count = Count, + drain = Drain}, Credits)}. blocked(#lim{blocked = Blocked}) -> Blocked. -- cgit v1.2.1 From 2970f6af9cc43ce2b9b44a3b116cb1dfe6b51493 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Fri, 11 Feb 2011 13:54:29 +0000 Subject: Use serial number arithmetic for credit --- src/rabbit_limiter.erl | 15 +++++++++------ src/rabbit_misc.erl | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/rabbit_tests.erl | 26 ++++++++++++++++++++++++- 3 files changed, 85 insertions(+), 7 deletions(-) diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index a9ecf2e0..50cd2aaa 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -25,6 +25,8 @@ -export([limit/2, can_send/5, ack/2, register/2, unregister/2]). -export([get_limit/1, block/1, unblock/1, set_credit/5, is_blocked/1]). +-import(rabbit_misc, [serial_add/2, serial_diff/2]). + %%---------------------------------------------------------------------------- -ifdef(use_specs). @@ -224,12 +226,13 @@ decr_credit(CTag, Len, State = #lim{ credits = Credits, {ok, #credit{ credit = Credit, count = Count, drain = Drain }} -> {NewCredit, NewCount} = case {Credit, Len, Drain} of - {1, _, _} -> {0, Count + 1}; %% Usual reduction to 0 + {1, _, _} -> {0, serial_add(Count, 1)}; {_, 1, true} -> - NewCount0 = Count + (Credit - 1), + %% Drain, so advance til credit = 0 + NewCount0 = serial_add(Count, (Credit - 1)), send_drained(ChPid, CTag, NewCount0), {0, NewCount0}; %% Magic reduction to 0 - {_, _, _} -> {Credit - 1, Count + 1} + {_, _, _} -> {Credit - 1, serial_add(Count, 1)} end, update_credit(CTag, NewCredit, NewCount, Drain, State); error -> @@ -255,9 +258,9 @@ reset_credit(CTag, Credit0, Count0, Drain, State = #lim{credits = Credits}) -> LocalCount; _ -> Count0 end, - %% Our credit may have been reduced while messages are - %% in flight, so we bottom out at 0. - Credit = erlang:max(0, Count0 + Credit0 - Count), + %% Our credit may have been reduced while messages are in flight, + %% so we bottom out at 0. + Credit = erlang:max(0, serial_diff(serial_add(Count0, Credit0), Count)), update_credit(CTag, Credit, Count, Drain, State). %% Store the credit diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 3a4fb024..aef8c22a 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -56,12 +56,14 @@ -export([lock_file/1]). -export([const_ok/1, const/1]). -export([ntoa/1, ntoab/1]). +-export([serial_add/2, serial_compare/2, serial_diff/2]). %%---------------------------------------------------------------------------- -ifdef(use_specs). -export_type([resource_name/0, thunk/1, const/1]). +-export_type([serial_number/0]). -type(ok_or_error() :: rabbit_types:ok_or_error(any())). -type(thunk(T) :: fun(() -> T)). @@ -75,6 +77,8 @@ fun ((atom(), [term()]) -> [{digraph:vertex(), digraph_label()}])). -type(graph_edge_fun() :: fun ((atom(), [term()]) -> [{digraph:vertex(), digraph:vertex()}])). +-type(serial_number() :: non_neg_integer()). +-type(serial_compare_result() :: 'equal' | 'less' | 'greater'). -spec(method_record_type/1 :: (rabbit_framing:amqp_method_record()) -> rabbit_framing:amqp_method_name()). @@ -194,6 +198,12 @@ -spec(const/1 :: (A) -> const(A)). -spec(ntoa/1 :: (inet:ip_address()) -> string()). -spec(ntoab/1 :: (inet:ip_address()) -> string()). +-spec(serial_add/2 :: (serial_number(), non_neg_integer()) -> + serial_number()). +-spec(serial_compare/2 :: (serial_number(), serial_number()) -> + serial_compare_result()). +-spec(serial_diff/2 :: (serial_number(), serial_number()) -> + integer()). -endif. @@ -849,3 +859,44 @@ ntoab(IP) -> 0 -> Str; _ -> "[" ++ Str ++ "]" end. + +%% Serial arithmetic for unsigned ints. +%% http://www.faqs.org/rfcs/rfc1982.html +%% SERIAL_BITS = 32 + +%% 2 ^ SERIAL_BITS +-define(SERIAL_MAX, 16#100000000). +%% 2 ^ (SERIAL_BITS - 1) - 1 +-define(SERIAL_MAX_ADDEND, 16#7fffffff). + +serial_add(S, N) when N =< ?SERIAL_MAX_ADDEND -> + (S + N) rem ?SERIAL_MAX; +serial_add(S, N) -> + exit({out_of_bound_serial_addition, S, N}). + +serial_compare(A, B) -> + if A =:= B -> + equal; + (A < B andalso B - A < ?SERIAL_MAX_ADDEND) orelse + (A > B andalso A - B > ?SERIAL_MAX_ADDEND) -> + less; + (A < B andalso B - A > ?SERIAL_MAX_ADDEND) orelse + (A > B andalso B - A < ?SERIAL_MAX_ADDEND) -> + greater; + true -> exit({indeterminate_serial_comparison, A, B}) + end. + +-define(SERIAL_DIFF_BOUND, 16#80000000). + +serial_diff(A, B) -> + Diff = A - B, + if Diff > (?SERIAL_DIFF_BOUND) -> + %% B is actually greater than A + - (?SERIAL_MAX - Diff); + Diff < - (?SERIAL_DIFF_BOUND) -> + ?SERIAL_MAX + Diff; + Diff < ?SERIAL_DIFF_BOUND andalso Diff > -?SERIAL_DIFF_BOUND -> + Diff; + true -> + exit({indeterminate_serial_diff, A, B}) + end. diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 49b09508..925649b8 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -18,7 +18,7 @@ -compile([export_all]). --export([all_tests/0, test_parsing/0]). +-export([all_tests/0, test_parsing/0, test_serial_arithmetic/0]). -include("rabbit.hrl"). -include("rabbit_framing.hrl"). @@ -46,6 +46,7 @@ all_tests() -> passed = test_parsing(), passed = test_content_framing(), passed = test_content_transcoding(), + passed = test_serial_arithmetic(), passed = test_topic_matching(), passed = test_log_management(), passed = test_app_management(), @@ -580,6 +581,29 @@ sequence_with_content(Sequence) -> rabbit_framing_amqp_0_9_1), Sequence). +test_serial_arithmetic() -> + 1 = rabbit_misc:serial_add(0, 1), + 16#7fffffff = rabbit_misc:serial_add(0, 16#7fffffff), + 0 = rabbit_misc:serial_add(16#ffffffff, 1), + %% Cannot add more than 2 ^ 31 - 1 + case catch rabbit_misc:serial_add(200, 16#80000000) of + {'EXIT', {out_of_bound_serial_addition, _, _}} -> ok; + _ -> exit(fail_out_of_bound_serial_addition) + end, + + 1 = rabbit_misc:serial_diff(1, 0), + 2 = rabbit_misc:serial_diff(1, 16#ffffffff), + -2 = rabbit_misc:serial_diff(16#ffffffff, 1), + case catch rabbit_misc:serial_diff(0, 16#80000000) of + {'EXIT', {indeterminate_serial_diff, _, _}} -> ok; + _ -> exit(fail_indeterminate_serial_difference) + end, + case catch rabbit_misc:serial_diff(16#ffffffff, 16#7fffffff) of + {'EXIT', {indeterminate_serial_diff, _, _}} -> ok; + _ -> exit(fail_indeterminate_serial_difference) + end, + passed. + test_topic_match(P, R) -> test_topic_match(P, R, true). -- cgit v1.2.1 From 30ae7c070be46821bac4bc1df6fe1da4ad569da5 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 25 Jul 2011 11:20:23 +0100 Subject: ...and unbreak. --- src/rabbit_channel.erl | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 2f6cccd4..f441adc8 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -1111,13 +1111,11 @@ handle_method(#'basic.credit'{consumer_tag = CTag, %% want that? Because at least then it's consistent with the credit value %% we return. And Available is always going to be racy. Available = case dict:find(CTag, Consumers) of - {ok, QName} -> - case rabbit_amqqueue:with( - QName, fun (Q) -> rabbit_amqqueue:stat(Q) end) of - {ok, Len, _} -> Len; - _ -> -1 - end; - error -> -1 + {ok, {Q, _}} -> case rabbit_amqqueue:stat(Q) of + {ok, Len, _} -> Len; + _ -> -1 + end; + error -> -1 %% TODO these -1s smell very iffy! end, LimiterPid1 = case LimiterPid of undefined -> start_limiter(State); -- cgit v1.2.1 From 767fc91fecc0ac98e9d6dd87257f4af18f138dfb Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 25 Jul 2011 11:27:42 +0100 Subject: Face down, nine-edge first. --- src/rabbit_channel.erl | 3 ++- src/rabbit_limiter.erl | 10 +++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index f441adc8..0610af65 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -1122,7 +1122,8 @@ handle_method(#'basic.credit'{consumer_tag = CTag, Other -> Other end, LimiterPid2 = - case rabbit_limiter:set_credit(LimiterPid1, CTag, Credit, Count, Drain) of + case rabbit_limiter:set_credit( + LimiterPid1, CTag, Credit, Count, Drain) of ok -> limit_queues(LimiterPid1, State), LimiterPid1; stopped -> unlimit_queues(State) diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index 7729a10a..81f973e4 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -36,7 +36,9 @@ -spec(start_link/2 :: (pid(), non_neg_integer()) -> rabbit_types:ok_pid_or_error()). -spec(limit/2 :: (maybe_pid(), non_neg_integer()) -> 'ok' | 'stopped'). --spec(can_send/5 :: (maybe_pid(), pid(), boolean(), binary(), non_neg_integer()) -> boolean()). +-spec(can_send/5 :: + (maybe_pid(), pid(), boolean(), binary(), non_neg_integer()) + -> boolean()). -spec(ack/2 :: (maybe_pid(), binary()) -> 'ok'). -spec(register/2 :: (maybe_pid(), pid()) -> 'ok'). -spec(unregister/2 :: (maybe_pid(), pid()) -> 'ok'). @@ -116,7 +118,8 @@ unblock(LimiterPid) -> set_credit(undefined, _, _, _, _) -> ok; set_credit(LimiterPid, CTag, Credit, Count, Drain) -> - gen_server2:call(LimiterPid, {set_credit, CTag, Credit, Count, Drain}, infinity). + gen_server2:call( + LimiterPid, {set_credit, CTag, Credit, Count, Drain}, infinity). is_blocked(undefined) -> false; @@ -164,7 +167,8 @@ handle_call(unblock, _From, State) -> maybe_notify_reply(irrelevant, State, State#lim{blocked = false}); handle_call({set_credit, CTag, Credit, Count, Drain}, _From, State) -> - maybe_notify_reply(CTag, State, reset_credit(CTag, Credit, Count, Drain, State)); + maybe_notify_reply(CTag, State, + reset_credit(CTag, Credit, Count, Drain, State)); handle_call(is_blocked, _From, State) -> {reply, blocked(State), State}. -- cgit v1.2.1 From 5a4b32e1ccf37570973097086d4a07b6958207d1 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 25 Jul 2011 11:38:18 +0100 Subject: Remove some dead TODOs from when this was very young. --- src/rabbit_channel.erl | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 0610af65..7f0f0002 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -501,7 +501,6 @@ check_name(Kind, NameBin = <<"amq.", _/binary>>) -> check_name(_Kind, NameBin) -> NameBin. -%% TODO port this(?) queue_blocked(QPid, State = #ch{blocking = Blocking}) -> case dict:find(QPid, Blocking) of error -> State; @@ -1131,15 +1130,6 @@ handle_method(#'basic.credit'{consumer_tag = CTag, State1 = State#ch{limiter_pid = LimiterPid2}, return_ok(State1, false, #'basic.credit_ok'{available = Available}); - %% TODO port this bit ? - %% case consumer_queues(Consumers) of - %% [] -> {reply, #'channel.flow_ok'{active = false}, State1}; - %% QPids -> Queues = [{QPid, erlang:monitor(process, QPid)} || - %% QPid <- QPids], - %% ok = rabbit_amqqueue:flush_all(QPids, self()), - %% {noreply, State1#ch{blocking = dict:from_list(Queues)}} - %% end; - handle_method(_MethodRecord, _Content, _State) -> rabbit_misc:protocol_error( command_invalid, "unimplemented method", []). -- cgit v1.2.1 From e29f550382dea13acce9d7c5e503ef0a8d67f722 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Wed, 24 Aug 2011 15:59:49 +0100 Subject: Rectify some poor conflict resolution choices. --- src/rabbit_amqqueue_process.erl | 9 +++++++-- src/rabbit_limiter.erl | 13 +++++++------ 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 2ca3c572..8333b753 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -369,7 +369,9 @@ ch_record_state_transition(OldCR, NewCR) -> deliver_msgs_to_consumers(Funs = {PredFun, DeliverFun}, FunAcc, State = #q{q = #amqqueue{name = QName}, active_consumers = ActiveConsumers, - blocked_consumers = BlockedConsumers}) -> + blocked_consumers = BlockedConsumers, + backing_queue = BQ, + backing_queue_state = BQS}) -> case queue:out(ActiveConsumers) of {{value, QEntry = {ChPid, #consumer{tag = ConsumerTag, ack_required = AckRequired}}}, @@ -379,7 +381,9 @@ deliver_msgs_to_consumers(Funs = {PredFun, DeliverFun}, FunAcc, acktags = ChAckTags} = ch_record(ChPid), IsMsgReady = PredFun(FunAcc, State), case (IsMsgReady andalso - rabbit_limiter:can_send(Limiter, self(), AckRequired)) of + rabbit_limiter:can_send(Limiter, self(), + AckRequired, ConsumerTag, + BQ:len(BQS))) of true -> {{Message, IsDelivered, AckTag}, FunAcc1, State1} = DeliverFun(AckRequired, FunAcc, State), @@ -1117,6 +1121,7 @@ handle_cast({limit, ChPid, Limiter}, State) -> andalso rabbit_limiter:is_blocked(Limiter), C#cr{limiter = Limiter, is_limit_active = Limited} end)); + handle_cast({flush, ChPid}, State) -> ok = rabbit_channel:flushed(ChPid, self()), noreply(State); diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index c219eaec..5bc20636 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -24,7 +24,7 @@ -export([start_link/0, make_token/0, make_token/1, is_enabled/1, enable/2, disable/1]). --export([limit/2, can_send/3, ack/2, register/2, unregister/2]). +-export([limit/2, can_send/5, ack/2, register/2, unregister/2]). -export([get_limit/1, block/1, unblock/1, is_blocked/1]). -export([set_credit/5]). @@ -47,7 +47,7 @@ -spec(enable/2 :: (token(), non_neg_integer()) -> token()). -spec(disable/1 :: (token()) -> token()). -spec(limit/2 :: (token(), non_neg_integer()) -> 'ok' | {'disabled', token()}). --spec(can_send/3 :: (token(), pid(), boolean()) -> boolean()). +-spec(can_send/3 :: (token(), pid(), boolean(), ) -> boolean()). -spec(ack/2 :: (token(), non_neg_integer()) -> 'ok'). -spec(register/2 :: (token(), pid()) -> 'ok'). -spec(unregister/2 :: (token(), pid()) -> 'ok'). @@ -94,18 +94,19 @@ limit(Limiter, PrefetchCount) -> %% breaching a limit. Note that we don't use maybe_call here in order %% to avoid always going through with_exit_handler/2, even when the %% limiter is disabled. -can_send(#token{pid = Pid, enabled = true}, QPid, AckRequired) -> +can_send(#token{pid = Pid, enabled = true}, QPid, AckRequired, CTag, Len) -> rabbit_misc:with_exit_handler( fun () -> true end, fun () -> - gen_server2:call(Pid, {can_send, QPid, AckRequired}, infinity) + gen_server2:call(Pid, {can_send, QPid, AckRequired, CTag, Len}, + infinity) end); -can_send(_, _, _) -> +can_send(_, _, _, _, _) -> true. %% Let the limiter know that the channel has received some acks from a %% consumer -ack(Limiter, Count) -> maybe_cast(Limiter, {ack, Count}). +ack(Limiter, CTag) -> maybe_cast(Limiter, {ack, CTag}). register(Limiter, QPid) -> maybe_cast(Limiter, {register, QPid}). -- cgit v1.2.1 From 955e20aea7d75e6be6e10db0ad05c58a64fe70ea Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Wed, 24 Aug 2011 16:32:57 +0100 Subject: And correct specs --- src/rabbit_limiter.erl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index 5bc20636..f102c3b9 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -47,7 +47,8 @@ -spec(enable/2 :: (token(), non_neg_integer()) -> token()). -spec(disable/1 :: (token()) -> token()). -spec(limit/2 :: (token(), non_neg_integer()) -> 'ok' | {'disabled', token()}). --spec(can_send/3 :: (token(), pid(), boolean(), ) -> boolean()). +-spec(can_send/5 :: (token(), pid(), boolean(), + rabbit_types:ctag(), non_neg_integer()) -> boolean()). -spec(ack/2 :: (token(), non_neg_integer()) -> 'ok'). -spec(register/2 :: (token(), pid()) -> 'ok'). -spec(unregister/2 :: (token(), pid()) -> 'ok'). @@ -55,7 +56,9 @@ -spec(block/1 :: (token()) -> 'ok'). -spec(unblock/1 :: (token()) -> 'ok' | {'disabled', token()}). -spec(is_blocked/1 :: (token()) -> boolean()). -%% Missing: set_credit +-spec(set_credit/5 :: (token(), rabbit_types:ctag(), + non_neg_integer(), + non_neg_integer(), boolean()) -> 'ok'). -endif. -- cgit v1.2.1 From b47c6de0463a1196705c0e4839f279c2fd9ea9e1 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 28 Mar 2012 17:42:07 +0100 Subject: Rough sketch of exchange decorator concept. --- src/rabbit_exchange.erl | 12 ++++++++-- src/rabbit_exchange_decorator.erl | 46 +++++++++++++++++++++++++++++++++++++++ src/rabbit_registry.erl | 7 +++--- 3 files changed, 60 insertions(+), 5 deletions(-) create mode 100644 src/rabbit_exchange_decorator.erl diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index 83e28c44..853c3549 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -101,7 +101,15 @@ recover() -> [XName || #exchange{name = XName} <- Xs]. callback(#exchange{type = XType}, Fun, Args) -> - apply(type_to_module(XType), Fun, Args). + callback(type_to_module(XType), Fun, Args); + +callback(Module, Fun, Args) -> + %% TODO cache this? + %% TODO what about serialising events? + %% TODO what about sharing the scratch space? + Decorators = rabbit_registry:lookup_all(exchange_decorator), + [ok = apply(M, Fun, Args) || {_, M} <- Decorators], + apply(Module, Fun, Args). declare(XName, Type, Durable, AutoDelete, Internal, Args) -> X = #exchange{name = XName, @@ -129,7 +137,7 @@ declare(XName, Type, Durable, AutoDelete, Internal, Args) -> end end, fun ({new, Exchange}, Tx) -> - ok = XT:create(map_create_tx(Tx), Exchange), + ok = callback(XT, create, [map_create_tx(Tx), Exchange]), rabbit_event:notify_if(not Tx, exchange_created, info(Exchange)), Exchange; ({existing, Exchange}, _Tx) -> diff --git a/src/rabbit_exchange_decorator.erl b/src/rabbit_exchange_decorator.erl new file mode 100644 index 00000000..362ec309 --- /dev/null +++ b/src/rabbit_exchange_decorator.erl @@ -0,0 +1,46 @@ +%% 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 VMware, Inc. +%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% + +-module(rabbit_exchange_decorator). + +-export([behaviour_info/1]). + +behaviour_info(callbacks) -> + [ + {description, 0}, + + %% Should Rabbit ensure that all binding events that are + %% delivered to an individual exchange can be serialised? (they + %% might still be delivered out of order, but there'll be a + %% serial number). + {serialise_events, 0}, + + {route, 2}, + + %% called after declaration and recovery + {create, 2}, + + %% called after exchange (auto)deletion. + {delete, 3}, + + %% called after a binding has been added or recovered + {add_binding, 3}, + + %% called after bindings have been deleted. + {remove_bindings, 3} + ]; +behaviour_info(_Other) -> + undefined. diff --git a/src/rabbit_registry.erl b/src/rabbit_registry.erl index 46c93503..33e3135a 100644 --- a/src/rabbit_registry.erl +++ b/src/rabbit_registry.erl @@ -95,9 +95,10 @@ sanity_check_module(ClassModule, Module) -> true -> ok end. -class_module(exchange) -> rabbit_exchange_type; -class_module(auth_mechanism) -> rabbit_auth_mechanism; -class_module(runtime_parameter) -> rabbit_runtime_parameter. +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. %%--------------------------------------------------------------------------- -- cgit v1.2.1 From 119431ae9862d7a62292e25a17c0c96b3b0aeab1 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 24 Apr 2012 17:50:00 +0100 Subject: Rework event serialisation thing so that decorators get a say too. It's a bit fiddly since we have to ensure that just because a decorator wants serials, doesn't mean everything else has to get them... --- src/rabbit_binding.erl | 11 ++++--- src/rabbit_exchange.erl | 62 ++++++++++++++++++++++++++------------- src/rabbit_exchange_decorator.erl | 2 +- src/rabbit_tests.erl | 4 +-- 4 files changed, 49 insertions(+), 30 deletions(-) diff --git a/src/rabbit_binding.erl b/src/rabbit_binding.erl index bb44797e..f0ea514d 100644 --- a/src/rabbit_binding.erl +++ b/src/rabbit_binding.erl @@ -173,13 +173,11 @@ add(Src, Dst, B) -> mnesia:read({rabbit_durable_route, B}) =:= []) of true -> ok = sync_route(#route{binding = B}, SrcDurable, DstDurable, fun mnesia:write/3), - ok = rabbit_exchange:callback( - Src, add_binding, [transaction, Src, B]), + x_callback(transaction, Src, add_binding, B), Serial = rabbit_exchange:serial(Src), fun () -> - ok = rabbit_exchange:callback( - Src, add_binding, [Serial, Src, B]), - ok = rabbit_event:notify(binding_created, info(B)) + x_callback(Serial, Src, add_binding, B), + ok = rabbit_event:notify(binding_created, info(B)) end; false -> rabbit_misc:const({error, binding_not_found}) end. @@ -487,4 +485,5 @@ process_deletions(Deletions) -> del_notify(Bs) -> [rabbit_event:notify(binding_deleted, info(B)) || B <- Bs]. -x_callback(Arg, X, F, Bs) -> ok = rabbit_exchange:callback(X, F, [Arg, X, Bs]). +x_callback(Serial, X, F, Bs) -> + ok = rabbit_exchange:callback(X, F, Serial, [X, Bs]). diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index 6c82bc7b..7291da8d 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -18,7 +18,7 @@ -include("rabbit.hrl"). -include("rabbit_framing.hrl"). --export([recover/0, callback/3, declare/6, +-export([recover/0, callback/4, declare/6, assert_equivalence/6, assert_args_equivalence/2, check_type/1, lookup/1, lookup_or_die/1, list/1, update_scratch/2, info_keys/0, info/1, info/2, info_all/1, info_all/2, @@ -37,7 +37,9 @@ -type(fun_name() :: atom()). -spec(recover/0 :: () -> [name()]). --spec(callback/3:: (rabbit_types:exchange(), fun_name(), [any()]) -> 'ok'). +-spec(callback/4:: + (rabbit_types:exchange(), fun_name(), non_neg_integer() | atom(), + [any()]) -> 'ok'). -spec(declare/6 :: (name(), type(), boolean(), boolean(), boolean(), rabbit_framing:amqp_table()) @@ -76,7 +78,8 @@ -spec(maybe_auto_delete/1:: (rabbit_types:exchange()) -> 'not_deleted' | {'deleted', rabbit_binding:deletions()}). --spec(serial/1 :: (rabbit_types:exchange()) -> 'none' | pos_integer()). +-spec(serial/1 :: (rabbit_types:exchange()) -> + fun(() -> 'none' | pos_integer())). -spec(peek_serial/1 :: (name()) -> pos_integer() | 'undefined'). -endif. @@ -95,21 +98,44 @@ recover() -> true -> store(X); false -> ok end, - rabbit_exchange:callback(X, create, [map_create_tx(Tx), X]) + callback(X, create, map_create_tx(Tx), [X]) end, rabbit_durable_exchange), [XName || #exchange{name = XName} <- Xs]. -callback(#exchange{type = XType}, Fun, Args) -> - callback(type_to_module(XType), Fun, Args); - -callback(Module, Fun, Args) -> +callback(X = #exchange{type = XType}, Fun, Serial0, Args) -> %% TODO cache this? - %% TODO what about serialising events? %% TODO what about sharing the scratch space? - Decorators = rabbit_registry:lookup_all(exchange_decorator), - [ok = apply(M, Fun, Args) || {_, M} <- Decorators], - apply(Module, Fun, Args). + Serial = fun (Bool) -> + case Serial0 of + _ when is_atom(Serial0) -> Serial0; + _ -> Serial0(Bool) + end + end, + [ok = apply(M, Fun, [Serial(M:serialise_events(X)) | Args]) + || M <- decorators()], + Module = type_to_module(XType), + apply(Module, Fun, [Serial(Module:serialise_events()) | Args]). + +serialise_events(X = #exchange{type = Type}) -> + case [Serialise || M <- decorators(), + Serialise <- [M:serialise_events(X)], + Serialise == true] of + [] -> (type_to_module(Type)):serialise_events(); + _ -> true + end. + +serial(#exchange{name = XName} = X) -> + Serial = case serialise_events(X) of + true -> next_serial(XName); + false -> none + end, + fun (true) -> Serial; + (false) -> none + end. + +decorators() -> + [M || {_, M} <- rabbit_registry:lookup_all(exchange_decorator)]. declare(XName, Type, Durable, AutoDelete, Internal, Args) -> X = #exchange{name = XName, @@ -137,7 +163,7 @@ declare(XName, Type, Durable, AutoDelete, Internal, Args) -> end end, fun ({new, Exchange}, Tx) -> - ok = callback(XT, create, [map_create_tx(Tx), Exchange]), + ok = callback(X, create, map_create_tx(Tx), [Exchange]), rabbit_event:notify_if(not Tx, exchange_created, info(Exchange)), Exchange; ({existing, Exchange}, _Tx) -> @@ -149,9 +175,9 @@ declare(XName, Type, Durable, AutoDelete, Internal, Args) -> map_create_tx(true) -> transaction; map_create_tx(false) -> none. -store(X = #exchange{name = Name, type = Type}) -> +store(X = #exchange{name = Name}) -> ok = mnesia:write(rabbit_exchange, X, write), - case (type_to_module(Type)):serialise_events() of + case serialise_events(X) of true -> S = #exchange_serial{name = Name, next = 1}, ok = mnesia:write(rabbit_exchange_serial, S, write); false -> ok @@ -349,12 +375,6 @@ unconditional_delete(X = #exchange{name = XName}) -> Bindings = rabbit_binding:remove_for_source(XName), {deleted, X, Bindings, rabbit_binding:remove_for_destination(XName)}. -serial(#exchange{name = XName, type = Type}) -> - case (type_to_module(Type)):serialise_events() of - true -> next_serial(XName); - false -> none - end. - next_serial(XName) -> [#exchange_serial{next = Serial}] = mnesia:read(rabbit_exchange_serial, XName, write), diff --git a/src/rabbit_exchange_decorator.erl b/src/rabbit_exchange_decorator.erl index 362ec309..4fa87485 100644 --- a/src/rabbit_exchange_decorator.erl +++ b/src/rabbit_exchange_decorator.erl @@ -26,7 +26,7 @@ behaviour_info(callbacks) -> %% delivered to an individual exchange can be serialised? (they %% might still be delivered out of order, but there'll be a %% serial number). - {serialise_events, 0}, + {serialise_events, 1}, {route, 2}, diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 35bf1012..a502fa95 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -617,8 +617,8 @@ test_topic_matching() -> exchange_op_callback(X, Fun, Args) -> rabbit_misc:execute_mnesia_transaction( - fun () -> rabbit_exchange:callback(X, Fun, [transaction, X] ++ Args) end), - rabbit_exchange:callback(X, Fun, [none, X] ++ Args). + fun () -> rabbit_exchange:callback(X, Fun, transaction, [X] ++ Args) end), + rabbit_exchange:callback(X, Fun, none, [X] ++ Args). test_topic_expect_match(X, List) -> lists:foreach( -- cgit v1.2.1 From 9072f224cf0460760d776d5c5d343b8961d4a8da Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 24 Apr 2012 18:06:52 +0100 Subject: Add an abstraction for reading the scratch --- src/rabbit_exchange.erl | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index 7291da8d..ac2f2610 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -20,7 +20,7 @@ -export([recover/0, callback/4, declare/6, assert_equivalence/6, assert_args_equivalence/2, check_type/1, - lookup/1, lookup_or_die/1, list/1, update_scratch/2, + lookup/1, lookup_or_die/1, list/1, lookup_scratch/1, update_scratch/2, info_keys/0, info/1, info/2, info_all/1, info_all/2, route/2, delete/2]). %% these must be run inside a mnesia tx @@ -60,6 +60,9 @@ (name()) -> rabbit_types:exchange() | rabbit_types:channel_exit()). -spec(list/1 :: (rabbit_types:vhost()) -> [rabbit_types:exchange()]). +-spec(lookup_scratch/1 :: (name()) -> + rabbit_types:ok(term()) | + rabbit_types:error('not_found')). -spec(update_scratch/2 :: (name(), fun((any()) -> any())) -> 'ok'). -spec(info_keys/0 :: () -> rabbit_types:info_keys()). -spec(info/1 :: (rabbit_types:exchange()) -> rabbit_types:infos()). @@ -234,6 +237,12 @@ list(VHostPath) -> rabbit_exchange, #exchange{name = rabbit_misc:r(VHostPath, exchange), _ = '_'}). +lookup_scratch(Name) -> + case lookup(Name) of + {ok, #exchange{scratch = Scratch}} -> {ok, Scratch}; + {error, not_found} -> {error, not_found} + end. + update_scratch(Name, Fun) -> rabbit_misc:execute_mnesia_transaction( fun() -> -- cgit v1.2.1 From 224f89907292fba453992a49b968837c28ec2bb2 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 26 Apr 2012 11:44:12 +0100 Subject: Share the exchange scratch space --- include/rabbit.hrl | 2 +- src/rabbit_exchange.erl | 35 +++++++++++++++++++++++++---------- src/rabbit_upgrade_functions.erl | 18 ++++++++++++++++++ 3 files changed, 44 insertions(+), 11 deletions(-) diff --git a/include/rabbit.hrl b/include/rabbit.hrl index 5c73c8b8..8de31490 100644 --- a/include/rabbit.hrl +++ b/include/rabbit.hrl @@ -43,7 +43,7 @@ -record(resource, {virtual_host, kind, name}). -record(exchange, {name, type, durable, auto_delete, internal, arguments, - scratch}). + scratches}). -record(exchange_serial, {name, next}). -record(amqqueue, {name, durable, auto_delete, exclusive_owner = none, diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index ac2f2610..7455c797 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -20,7 +20,7 @@ -export([recover/0, callback/4, declare/6, assert_equivalence/6, assert_args_equivalence/2, check_type/1, - lookup/1, lookup_or_die/1, list/1, lookup_scratch/1, update_scratch/2, + lookup/1, lookup_or_die/1, list/1, lookup_scratch/2, update_scratch/3, info_keys/0, info/1, info/2, info_all/1, info_all/2, route/2, delete/2]). %% these must be run inside a mnesia tx @@ -60,10 +60,10 @@ (name()) -> rabbit_types:exchange() | rabbit_types:channel_exit()). -spec(list/1 :: (rabbit_types:vhost()) -> [rabbit_types:exchange()]). --spec(lookup_scratch/1 :: (name()) -> +-spec(lookup_scratch/2 :: (name(), atom()) -> rabbit_types:ok(term()) | rabbit_types:error('not_found')). --spec(update_scratch/2 :: (name(), fun((any()) -> any())) -> 'ok'). +-spec(update_scratch/3 :: (name(), atom(), fun((any()) -> any())) -> 'ok'). -spec(info_keys/0 :: () -> rabbit_types:info_keys()). -spec(info/1 :: (rabbit_types:exchange()) -> rabbit_types:infos()). -spec(info/2 :: @@ -108,7 +108,6 @@ recover() -> callback(X = #exchange{type = XType}, Fun, Serial0, Args) -> %% TODO cache this? - %% TODO what about sharing the scratch space? Serial = fun (Bool) -> case Serial0 of _ when is_atom(Serial0) -> Serial0; @@ -237,18 +236,34 @@ list(VHostPath) -> rabbit_exchange, #exchange{name = rabbit_misc:r(VHostPath, exchange), _ = '_'}). -lookup_scratch(Name) -> +lookup_scratch(Name, App) -> case lookup(Name) of - {ok, #exchange{scratch = Scratch}} -> {ok, Scratch}; - {error, not_found} -> {error, not_found} + {ok, #exchange{scratches = undefined}} -> + {error, not_found}; + {ok, #exchange{scratches = Scratches}} -> + case orddict:find(App, Scratches) of + {ok, Value} -> {ok, Value}; + error -> {error, not_found} + end; + {error, not_found} -> + {error, not_found} end. -update_scratch(Name, Fun) -> +update_scratch(Name, App, Fun) -> rabbit_misc:execute_mnesia_transaction( fun() -> case mnesia:wread({rabbit_exchange, Name}) of - [X = #exchange{durable = Durable, scratch = Scratch}] -> - X1 = X#exchange{scratch = Fun(Scratch)}, + [X = #exchange{durable = Durable, scratches = Scratches0}] -> + Scratches1 = case Scratches0 of + undefined -> orddict:new(); + _ -> Scratches0 + end, + Scratch = case orddict:find(App, Scratches1) of + {ok, S} -> S; + error -> undefined + end, + Scratches2 = orddict:store(App, Fun(Scratch), Scratches1), + X1 = X#exchange{scratches = Scratches2}, ok = mnesia:write(rabbit_exchange, X1, write), case Durable of true -> ok = mnesia:write(rabbit_durable_exchange, diff --git a/src/rabbit_upgrade_functions.erl b/src/rabbit_upgrade_functions.erl index 485ccc5f..87e560e8 100644 --- a/src/rabbit_upgrade_functions.erl +++ b/src/rabbit_upgrade_functions.erl @@ -37,6 +37,7 @@ -rabbit_upgrade({mirrored_supervisor, mnesia, []}). -rabbit_upgrade({topic_trie_node, mnesia, []}). -rabbit_upgrade({runtime_parameters, mnesia, []}). +-rabbit_upgrade({exchange_scratches, mnesia, [exchange_scratch]}). %% ------------------------------------------------------------------- @@ -193,6 +194,23 @@ runtime_parameters() -> {attributes, [key, value]}, {disc_copies, [node()]}]). +exchange_scratches() -> + ok = exchange_scratches(rabbit_exchange), + ok = exchange_scratches(rabbit_durable_exchange). + +exchange_scratches(Table) -> + transform( + Table, + fun ({exchange, Name, Type = <<"x-federation">>, Dur, AutoDel, Int, Args, + Scratch}) -> + Scratches = orddict:store(federation, Scratch, orddict:new()), + {exchange, Name, Type, Dur, AutoDel, Int, Args, Scratches}; + %% We assert here that nothing else uses the scratch mechanism ATM + ({exchange, Name, Type, Dur, AutoDel, Int, Args, undefined}) -> + {exchange, Name, Type, Dur, AutoDel, Int, Args, undefined} + end, + [name, type, durable, auto_delete, internal, arguments, scratches]). + %%-------------------------------------------------------------------- transform(TableName, Fun, FieldList) -> -- cgit v1.2.1 From 5cce31b9ccf503f250a2671b7221de49a341a884 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Mon, 30 Apr 2012 15:54:49 +0100 Subject: Fix typo in debian control file --- packaging/debs/Debian/debian/control | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/debs/Debian/debian/control b/packaging/debs/Debian/debian/control index e935acf5..943ed48f 100644 --- a/packaging/debs/Debian/debian/control +++ b/packaging/debs/Debian/debian/control @@ -2,7 +2,7 @@ Source: rabbitmq-server Section: net Priority: extra Maintainer: RabbitMQ Team -Uploader: Emile Joubert +Uploaders: Emile Joubert DM-Upload-Allowed: yes Build-Depends: cdbs, debhelper (>= 5), erlang-dev, python-simplejson, xmlto, xsltproc, erlang-nox (>= 1:12.b.3), erlang-src (>= 1:12.b.3), unzip, zip Standards-Version: 3.8.0 -- cgit v1.2.1 From d1bd4cff93ca8aa7642f2d44f68e8c2c38fb1fb5 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Thu, 3 May 2012 18:51:57 +0100 Subject: add rabbit:start_cold/0 without relying on release/boot-script --- scripts/rabbitmq-server | 3 +++ src/rabbit.erl | 71 ++++++++++++++++++++++++++++++++++++++----------- src/rabbit_misc.erl | 22 ++++++++++++++- src/rabbit_plugins.erl | 54 ++++++++++++++++++++++++++++++++----- 4 files changed, 127 insertions(+), 23 deletions(-) diff --git a/scripts/rabbitmq-server b/scripts/rabbitmq-server index 0a5a4640..3b31e83b 100755 --- a/scripts/rabbitmq-server +++ b/scripts/rabbitmq-server @@ -112,6 +112,9 @@ exec erl \ -sasl sasl_error_logger false \ -rabbit error_logger '{file,"'${RABBITMQ_LOGS}'"}' \ -rabbit sasl_error_logger '{file,"'${RABBITMQ_SASL_LOGS}'"}' \ + -rabbit enabled_plugins_file "$RABBITMQ_ENABLED_PLUGINS_FILE" \ + -rabbit plugins_dir "$RABBITMQ_PLUGINS_DIR" \ + -rabbit plugins_expand_dir "$RABBITMQ_PLUGINS_EXPAND_DIR" \ -os_mon start_cpu_sup false \ -os_mon start_disksup false \ -os_mon start_memsup false \ diff --git a/src/rabbit.erl b/src/rabbit.erl index b1f786a0..7f2845b6 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -18,8 +18,8 @@ -behaviour(application). --export([maybe_hipe_compile/0, prepare/0, start/0, stop/0, stop_and_halt/0, - status/0, is_running/0, is_running/1, environment/0, +-export([maybe_hipe_compile/0, prepare/0, start/0, start_cold/0, stop/0, + stop_and_halt/0, status/0, is_running/0, is_running/1, environment/0, rotate_logs/1, force_event_refresh/0]). -export([start/2, stop/1]). @@ -217,6 +217,7 @@ -spec(maybe_hipe_compile/0 :: () -> 'ok'). -spec(prepare/0 :: () -> 'ok'). -spec(start/0 :: () -> 'ok'). +-spec(start_cold/0 :: () -> 'ok'). -spec(stop/0 :: () -> 'ok'). -spec(stop_and_halt/0 :: () -> no_return()). -spec(status/0 :: @@ -284,22 +285,35 @@ split0([], Ls) -> Ls; split0([I | Is], [L | Ls]) -> split0(Is, Ls ++ [[I | L]]). prepare() -> + %% this ends up looking at the rabbit app's env, so it + %% needs to be loaded, but during the tests, it may end up + %% getting loaded twice, so guard against that + case application:load(rabbit) of + ok -> ok; + {error, {already_loaded, rabbit}} -> ok + end, ok = ensure_working_log_handlers(), ok = rabbit_upgrade:maybe_upgrade_mnesia(). start() -> + start_it(fun() -> + ok = prepare(), + ok = rabbit_misc:start_applications(application_load_order()) + end). + +start_cold() -> + start_it(fun() -> + ok = prepare(), + Plugins = determine_required_plugins(), + ToBeLoaded = ?APPS ++ Plugins, + StartupApps = application_load_order(ToBeLoaded, ToBeLoaded), + ok = rabbit_misc:start_applications(StartupApps) + end). + +start_it(StartFun) -> try - %% prepare/1 ends up looking at the rabbit app's env, so it - %% needs to be loaded, but during the tests, it may end up - %% getting loaded twice, so guard against that - case application:load(rabbit) of - ok -> ok; - {error, {already_loaded, rabbit}} -> ok - end, - ok = prepare(), - ok = rabbit_misc:start_applications(application_load_order()) + StartFun() after - %%give the error loggers some time to catch up timer:sleep(100) end. @@ -394,13 +408,17 @@ stop(_State) -> application_load_order() -> ok = load_applications(), + LoadedApps = application:loaded_applications(), + application_load_order(LoadedApps, ?APPS). + +application_load_order(LoadedApps, RootApps) -> {ok, G} = rabbit_misc:build_acyclic_graph( fun (App, _Deps) -> [{App, App}] end, fun (App, Deps) -> [{Dep, App} || Dep <- Deps] end, - [{App, app_dependencies(App)} || - {App, _Desc, _Vsn} <- application:loaded_applications()]), + [{App, app_dependencies(App)} || App <- LoadedApps]), true = digraph:del_vertices( - G, digraph:vertices(G) -- digraph_utils:reachable(?APPS, G)), + G, digraph:vertices(G) -- + digraph_utils:reachable(RootApps, G)), Result = digraph_utils:topsort(G), true = digraph:delete(G), Result. @@ -408,6 +426,7 @@ application_load_order() -> load_applications() -> load_applications(queue:from_list(?APPS), sets:new()). +%% FIXME: should we move this into rabbit_misc? load_applications(Worklist, Loaded) -> case queue:out(Worklist) of {empty, _WorkList} -> @@ -568,6 +587,28 @@ insert_default_data() -> %%--------------------------------------------------------------------------- %% logging +determine_required_plugins() -> + {ok, PluginDir} = application:get_env(rabbit, plugins_dir), + {ok, ExpandDir} = application:get_env(rabbit, plugins_expand_dir), + {ok, EnabledPluginsFile} = application:get_env(rabbit, + enabled_plugins_file), + rabbit_plugins:prepare_plugins(EnabledPluginsFile, PluginDir, ExpandDir), + find_plugins(ExpandDir). + +find_plugins(PluginDir) -> + [prepare_dir_plugin(PluginName) || + PluginName <- filelib:wildcard(PluginDir ++ "/*/ebin/*.app")]. + +prepare_dir_plugin(PluginAppDescFn) -> + %% Add the plugin ebin directory to the load path + PluginEBinDirN = filename:dirname(PluginAppDescFn), + code:add_path(PluginEBinDirN), + + %% We want the second-last token + NameTokens = string:tokens(PluginAppDescFn,"/."), + PluginNameString = lists:nth(length(NameTokens) - 1, NameTokens), + list_to_atom(PluginNameString). + ensure_working_log_handlers() -> Handlers = gen_event:which_handlers(error_logger), ok = ensure_working_log_handler(error_logger_tty_h, diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 706de835..28c61a2e 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -19,7 +19,7 @@ -include("rabbit_framing.hrl"). -export([method_record_type/1, polite_pause/0, polite_pause/1]). --export([die/1, frame_error/2, amqp_error/4, +-export([die/1, frame_error/2, amqp_error/4, terminate/1, terminate/2, protocol_error/3, protocol_error/4, protocol_error/1]). -export([not_found/1, assert_args_equivalence/4]). -export([dirty_read/1]). @@ -87,6 +87,11 @@ -spec(polite_pause/1 :: (non_neg_integer()) -> 'done'). -spec(die/1 :: (rabbit_framing:amqp_exception()) -> channel_or_connection_exit()). + +%% TODO: figure out what the return types should actually be for these... +-spec(terminate/1 :: (integer()) -> any()). +-spec(terminate/2 :: (string(), integer()) -> any()). + -spec(frame_error/2 :: (rabbit_framing:amqp_method_name(), binary()) -> rabbit_types:connection_exit()). -spec(amqp_error/4 :: @@ -212,6 +217,8 @@ -endif. +-define(ERROR_CODE, 1). + %%---------------------------------------------------------------------------- method_record_type(Record) -> @@ -387,6 +394,19 @@ report_coverage_percentage(File, Cov, NotCov, Mod) -> confirm_to_sender(Pid, MsgSeqNos) -> gen_server2:cast(Pid, {confirm, MsgSeqNos, self()}). +terminate(Fmt, Args) -> + io:format("ERROR: " ++ Fmt ++ "~n", Args), + terminate(?ERROR_CODE). + +terminate(Status) -> + case os:type() of + {unix, _} -> halt(Status); + {win32, _} -> init:stop(Status), + receive + after infinity -> ok + end + end. + throw_on_error(E, Thunk) -> case Thunk() of {error, Reason} -> throw({E, Reason}); diff --git a/src/rabbit_plugins.erl b/src/rabbit_plugins.erl index 2a93c8f2..3ce0d3b1 100644 --- a/src/rabbit_plugins.erl +++ b/src/rabbit_plugins.erl @@ -17,8 +17,7 @@ -module(rabbit_plugins). -include("rabbit.hrl"). --export([start/0, stop/0, find_plugins/1, read_enabled_plugins/1, - lookup_plugins/2, calculate_required_plugins/2, plugin_names/1]). +-export([start/0, stop/0, prepare_plugins/3]). -define(VERBOSE_OPT, "-v"). -define(MINIMAL_OPT, "-m"). @@ -84,12 +83,32 @@ start() -> stop() -> ok. -print_error(Format, Args) -> - rabbit_misc:format_stderr("Error: " ++ Format ++ "~n", Args). +prepare_plugins(EnabledPluginsFile, PluginsDistDir, DestDir) -> + AllPlugins = rabbit_plugins:find_plugins(PluginsDistDir), + Enabled = rabbit_plugins:read_enabled_plugins(EnabledPluginsFile), + ToUnpack = rabbit_plugins:calculate_required_plugins(Enabled, AllPlugins), + ToUnpackPlugins = lookup_plugins(ToUnpack, AllPlugins), -usage() -> - io:format("~s", [rabbit_plugins_usage:usage()]), - rabbit_misc:quit(1). + Missing = Enabled -- rabbit_plugins:plugin_names(ToUnpackPlugins), + case Missing of + [] -> ok; + _ -> io:format("Warning: the following enabled plugins were " + "not found: ~p~n", [Missing]) + end, + + %% Eliminate the contents of the destination directory + case delete_recursively(DestDir) of + ok -> ok; + {error, E} -> rabbit_misc:terminate("Could not delete dir ~s (~p)", + [DestDir, E]) + end, + case filelib:ensure_dir(DestDir ++ "/") of + ok -> ok; + {error, E2} -> rabbit_misc:terminate("Could not create dir ~s (~p)", + [DestDir, E2]) + end, + + [prepare_plugin(Plugin, DestDir) || Plugin <- ToUnpackPlugins]. %%---------------------------------------------------------------------------- @@ -155,6 +174,27 @@ action(disable, ToDisable0, _Opts, PluginsFile, PluginsDir) -> %%---------------------------------------------------------------------------- +print_error(Format, Args) -> + rabbit_misc:format_stderr("Error: " ++ Format ++ "~n", Args). + +usage() -> + io:format("~s", [rabbit_plugins_usage:usage()]), + rabbit_misc:quit(1). + +delete_recursively(Fn) -> + case rabbit_file:recursive_delete([Fn]) of + ok -> ok; + {error, {Path, E}} -> {error, {cannot_delete, Path, E}}; + Error -> Error + end. + +prepare_plugin(#plugin{type = ez, location = Location}, PluginDestDir) -> + zip:unzip(Location, [{cwd, PluginDestDir}]); +prepare_plugin(#plugin{type = dir, name = Name, location = Location}, + PluginsDestDir) -> + rabbit_file:recursive_copy(Location, + filename:join([PluginsDestDir, Name])). + %% Get the #plugin{}s ready to be enabled. find_plugins(PluginsDir) -> EZs = [{ez, EZ} || EZ <- filelib:wildcard("*.ez", PluginsDir)], -- cgit v1.2.1 From 51859ff64a9fbb75d78c4c606e8cdb9b06ccc8f1 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Fri, 4 May 2012 11:15:40 +0100 Subject: rabbit_prelaunch only checks for duplicate node names --- src/rabbit_prelaunch.erl | 214 ++--------------------------------------------- 1 file changed, 5 insertions(+), 209 deletions(-) diff --git a/src/rabbit_prelaunch.erl b/src/rabbit_prelaunch.erl index 162d44f1..d58f54b9 100644 --- a/src/rabbit_prelaunch.erl +++ b/src/rabbit_prelaunch.erl @@ -31,212 +31,21 @@ -spec(start/0 :: () -> no_return()). -spec(stop/0 :: () -> 'ok'). -%% Shut dialyzer up --spec(terminate/1 :: (string()) -> no_return()). --spec(terminate/2 :: (string(), [any()]) -> no_return()). -endif. %%---------------------------------------------------------------------------- start() -> - io:format("Activating RabbitMQ plugins ...~n"), - - %% Determine our various directories - [EnabledPluginsFile, PluginsDistDir, UnpackedPluginDir, NodeStr] = - init:get_plain_arguments(), - RootName = UnpackedPluginDir ++ "/rabbit", - - prepare_plugins(EnabledPluginsFile, PluginsDistDir, UnpackedPluginDir), - - %% Build a list of required apps based on the fixed set, and any plugins - PluginApps = find_plugins(UnpackedPluginDir), - RequiredApps = ?BaseApps ++ PluginApps, - - %% Build the entire set of dependencies - this will load the - %% applications along the way - AllApps = case catch sets:to_list(expand_dependencies(RequiredApps)) of - {failed_to_load_app, App, Err} -> - terminate("failed to load application ~s:~n~p", - [App, Err]); - AppList -> - AppList - end, - AppVersions = [determine_version(App) || App <- AllApps], - RabbitVersion = proplists:get_value(rabbit, AppVersions), - - %% Build the overall release descriptor - RDesc = {release, - {"rabbit", RabbitVersion}, - {erts, erlang:system_info(version)}, - AppVersions}, - - %% Write it out to $RABBITMQ_PLUGINS_EXPAND_DIR/rabbit.rel - rabbit_file:write_file(RootName ++ ".rel", io_lib:format("~p.~n", [RDesc])), - - %% We exclude mochiweb due to its optional use of fdsrv. - XRefExclude = [mochiweb], - - %% Compile the script - ScriptFile = RootName ++ ".script", - case systools:make_script(RootName, [local, silent, - {exref, AllApps -- XRefExclude}]) of - {ok, Module, Warnings} -> - %% This gets lots of spurious no-source warnings when we - %% have .ez files, so we want to supress them to prevent - %% hiding real issues. On Ubuntu, we also get warnings - %% about kernel/stdlib sources being out of date, which we - %% also ignore for the same reason. - WarningStr = Module:format_warning( - [W || W <- Warnings, - case W of - {warning, {source_not_found, _}} -> false; - {warning, {obj_out_of_date, {_,_,WApp,_,_}}} - when WApp == mnesia; - WApp == stdlib; - WApp == kernel; - WApp == sasl; - WApp == crypto; - WApp == os_mon -> false; - _ -> true - end]), - case length(WarningStr) of - 0 -> ok; - _ -> S = string:copies("*", 80), - io:format("~n~s~n~s~s~n~n", [S, WarningStr, S]) - end, - ok; - {error, Module, Error} -> - terminate("generation of boot script file ~s failed:~n~s", - [ScriptFile, Module:format_error(Error)]) - end, - - case post_process_script(ScriptFile) of - ok -> ok; - {error, Reason} -> - terminate("post processing of boot script file ~s failed:~n~w", - [ScriptFile, Reason]) - end, - case systools:script2boot(RootName) of - ok -> ok; - error -> terminate("failed to compile boot script file ~s", - [ScriptFile]) - end, - io:format("~w plugins activated:~n", [length(PluginApps)]), - [io:format("* ~s-~s~n", [App, proplists:get_value(App, AppVersions)]) - || App <- PluginApps], - io:nl(), - + [NodeStr] = init:get_plain_arguments(), ok = duplicate_node_check(NodeStr), - - terminate(0), + rabbit_misc:terminate(0), ok. stop() -> ok. -determine_version(App) -> - application:load(App), - {ok, Vsn} = application:get_key(App, vsn), - {App, Vsn}. - -delete_recursively(Fn) -> - case rabbit_file:recursive_delete([Fn]) of - ok -> ok; - {error, {Path, E}} -> {error, {cannot_delete, Path, E}}; - Error -> Error - end. - -prepare_plugins(EnabledPluginsFile, PluginsDistDir, DestDir) -> - AllPlugins = rabbit_plugins:find_plugins(PluginsDistDir), - Enabled = rabbit_plugins:read_enabled_plugins(EnabledPluginsFile), - ToUnpack = rabbit_plugins:calculate_required_plugins(Enabled, AllPlugins), - ToUnpackPlugins = rabbit_plugins:lookup_plugins(ToUnpack, AllPlugins), - - Missing = Enabled -- rabbit_plugins:plugin_names(ToUnpackPlugins), - case Missing of - [] -> ok; - _ -> io:format("Warning: the following enabled plugins were " - "not found: ~p~n", [Missing]) - end, - - %% Eliminate the contents of the destination directory - case delete_recursively(DestDir) of - ok -> ok; - {error, E} -> terminate("Could not delete dir ~s (~p)", [DestDir, E]) - end, - case filelib:ensure_dir(DestDir ++ "/") of - ok -> ok; - {error, E2} -> terminate("Could not create dir ~s (~p)", [DestDir, E2]) - end, - - [prepare_plugin(Plugin, DestDir) || Plugin <- ToUnpackPlugins]. - -prepare_plugin(#plugin{type = ez, location = Location}, PluginDestDir) -> - zip:unzip(Location, [{cwd, PluginDestDir}]); -prepare_plugin(#plugin{type = dir, name = Name, location = Location}, - PluginsDestDir) -> - rabbit_file:recursive_copy(Location, - filename:join([PluginsDestDir, Name])). - -find_plugins(PluginDir) -> - [prepare_dir_plugin(PluginName) || - PluginName <- filelib:wildcard(PluginDir ++ "/*/ebin/*.app")]. - -prepare_dir_plugin(PluginAppDescFn) -> - %% Add the plugin ebin directory to the load path - PluginEBinDirN = filename:dirname(PluginAppDescFn), - code:add_path(PluginEBinDirN), - - %% We want the second-last token - NameTokens = string:tokens(PluginAppDescFn,"/."), - PluginNameString = lists:nth(length(NameTokens) - 1, NameTokens), - list_to_atom(PluginNameString). - -expand_dependencies(Pending) -> - expand_dependencies(sets:new(), Pending). -expand_dependencies(Current, []) -> - Current; -expand_dependencies(Current, [Next|Rest]) -> - case sets:is_element(Next, Current) of - true -> - expand_dependencies(Current, Rest); - false -> - case application:load(Next) of - ok -> - ok; - {error, {already_loaded, _}} -> - ok; - {error, Reason} -> - throw({failed_to_load_app, Next, Reason}) - end, - {ok, Required} = application:get_key(Next, applications), - Unique = [A || A <- Required, not(sets:is_element(A, Current))], - expand_dependencies(sets:add_element(Next, Current), Rest ++ Unique) - end. - -post_process_script(ScriptFile) -> - case file:consult(ScriptFile) of - {ok, [{script, Name, Entries}]} -> - NewEntries = lists:flatmap(fun process_entry/1, Entries), - case file:open(ScriptFile, [write]) of - {ok, Fd} -> - io:format(Fd, "%% script generated at ~w ~w~n~p.~n", - [date(), time(), {script, Name, NewEntries}]), - file:close(Fd), - ok; - {error, OReason} -> - {error, {failed_to_open_script_file_for_writing, OReason}} - end; - {error, Reason} -> - {error, {failed_to_load_script, Reason}} - end. - -process_entry(Entry = {apply,{application,start_boot,[mnesia,permanent]}}) -> - [{apply,{rabbit,maybe_hipe_compile,[]}}, - {apply,{rabbit,prepare,[]}}, Entry]; -process_entry(Entry) -> - [Entry]. +%%---------------------------------- %% Check whether a node with the same name is already running duplicate_node_check([]) -> @@ -252,11 +61,11 @@ duplicate_node_check(NodeStr) -> "already running on ~p~n", [NodeName, NodeHost]), io:format(rabbit_nodes:diagnostics([Node]) ++ "~n"), - terminate(?ERROR_CODE); + rabbit_misc:terminate(?ERROR_CODE); false -> ok end; {error, EpmdReason} -> - terminate("epmd error for host ~p: ~p (~s)~n", + rabbit_misc:terminate("epmd error for host ~p: ~p (~s)~n", [NodeHost, EpmdReason, case EpmdReason of address -> "unable to establish tcp connection"; @@ -264,16 +73,3 @@ duplicate_node_check(NodeStr) -> _ -> inet:format_error(EpmdReason) end]) end. - -terminate(Fmt, Args) -> - io:format("ERROR: " ++ Fmt ++ "~n", Args), - terminate(?ERROR_CODE). - -terminate(Status) -> - case os:type() of - {unix, _} -> halt(Status); - {win32, _} -> init:stop(Status), - receive - after infinity -> ok - end - end. -- cgit v1.2.1 From 0b615bd03f7ac72f98dec53535f9bc37c8325610 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Fri, 4 May 2012 11:18:53 +0100 Subject: start and start_cold both defer to rabbit_misc to calculate application load order --- src/rabbit.erl | 33 ++++++++++++++++----------------- src/rabbit_misc.erl | 24 +++++++++++++++++++++++- src/rabbit_plugins.erl | 8 ++++---- 3 files changed, 43 insertions(+), 22 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 7f2845b6..13afdc68 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -305,9 +305,21 @@ start_cold() -> start_it(fun() -> ok = prepare(), Plugins = determine_required_plugins(), - ToBeLoaded = ?APPS ++ Plugins, - StartupApps = application_load_order(ToBeLoaded, ToBeLoaded), - ok = rabbit_misc:start_applications(StartupApps) + ToBeLoaded = Plugins ++ ?APPS, + + io:format("~n" + "Activating RabbitMQ plugins ...~n"), + + load_applications(queue:from_list(ToBeLoaded), sets:new()), + StartupApps = + rabbit_misc:calculate_app_dependency_ordering(ToBeLoaded), + ok = rabbit_misc:start_applications(StartupApps), + + io:format("~w plugins activated:~n", [length(Plugins)]), + [io:format("* ~s-~s~n", [AppName, + element(2, application:get_key(AppName, vsn))]) + || AppName <- Plugins], + io:nl() end). start_it(StartFun) -> @@ -408,20 +420,7 @@ stop(_State) -> application_load_order() -> ok = load_applications(), - LoadedApps = application:loaded_applications(), - application_load_order(LoadedApps, ?APPS). - -application_load_order(LoadedApps, RootApps) -> - {ok, G} = rabbit_misc:build_acyclic_graph( - fun (App, _Deps) -> [{App, App}] end, - fun (App, Deps) -> [{Dep, App} || Dep <- Deps] end, - [{App, app_dependencies(App)} || App <- LoadedApps]), - true = digraph:del_vertices( - G, digraph:vertices(G) -- - digraph_utils:reachable(RootApps, G)), - Result = digraph_utils:topsort(G), - true = digraph:delete(G), - Result. + rabbit_misc:calculate_app_dependency_ordering(?APPS). load_applications() -> load_applications(queue:from_list(?APPS), sets:new()). diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 28c61a2e..2fffc2cd 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -42,7 +42,8 @@ -export([dirty_read_all/1, dirty_foreach_key/2, dirty_dump_log/1]). -export([format/2, format_many/1, format_stderr/2]). -export([with_local_io/1, local_info_msg/2]). --export([start_applications/1, stop_applications/1]). +-export([calculate_app_dependency_ordering/1, + start_applications/1, stop_applications/1]). -export([unfold/2, ceil/1, queue_fold/3]). -export([sort_field_table/1]). -export([pid_to_string/1, string_to_pid/1]). @@ -169,6 +170,7 @@ -spec(with_local_io/1 :: (fun (() -> A)) -> A). -spec(local_info_msg/2 :: (string(), [any()]) -> 'ok'). -spec(start_applications/1 :: ([atom()]) -> 'ok'). +-spec(calculate_app_dependency_ordering/1 :: ([atom()]) -> 'ok'). -spec(stop_applications/1 :: ([atom()]) -> 'ok'). -spec(unfold/2 :: (fun ((A) -> ({'true', B, A} | 'false')), A) -> {[B], A}). -spec(ceil/1 :: (number()) -> integer()). @@ -637,6 +639,26 @@ stop_applications(Apps) -> cannot_stop_application, Apps). +calculate_app_dependency_ordering(RootApps) -> + {ok, G} = build_acyclic_graph( + fun (App, _Deps) -> [{App, App}] end, + fun (App, Deps) -> [{Dep, App} || Dep <- Deps] end, + [{App, app_dependencies(App)} || + {App, _Desc, _Vsn} <- application:loaded_applications()]), + try + true = digraph:del_vertices(G, digraph:vertices(G) -- + digraph_utils:reachable(RootApps, G)), + digraph_utils:topsort(G) + after + true = digraph:delete(G) + end. + +app_dependencies(App) -> + case application:get_key(App, applications) of + undefined -> []; + {ok, Lst} -> Lst + end. + unfold(Fun, Init) -> unfold(Fun, [], Init). diff --git a/src/rabbit_plugins.erl b/src/rabbit_plugins.erl index 3ce0d3b1..0a5f8d51 100644 --- a/src/rabbit_plugins.erl +++ b/src/rabbit_plugins.erl @@ -84,12 +84,12 @@ stop() -> ok. prepare_plugins(EnabledPluginsFile, PluginsDistDir, DestDir) -> - AllPlugins = rabbit_plugins:find_plugins(PluginsDistDir), - Enabled = rabbit_plugins:read_enabled_plugins(EnabledPluginsFile), - ToUnpack = rabbit_plugins:calculate_required_plugins(Enabled, AllPlugins), + AllPlugins = find_plugins(PluginsDistDir), + Enabled = read_enabled_plugins(EnabledPluginsFile), + ToUnpack = calculate_required_plugins(Enabled, AllPlugins), ToUnpackPlugins = lookup_plugins(ToUnpack, AllPlugins), - Missing = Enabled -- rabbit_plugins:plugin_names(ToUnpackPlugins), + Missing = Enabled -- plugin_names(ToUnpackPlugins), case Missing of [] -> ok; _ -> io:format("Warning: the following enabled plugins were " -- cgit v1.2.1 From ae28e4e11c70481f97ceae65decd060084762c8a Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Fri, 4 May 2012 11:19:32 +0100 Subject: rabbitmq-server uses start_cold --- scripts/rabbitmq-server | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/scripts/rabbitmq-server b/scripts/rabbitmq-server index 3b31e83b..4dd81a84 100755 --- a/scripts/rabbitmq-server +++ b/scripts/rabbitmq-server @@ -77,14 +77,15 @@ if [ "x" = "x$RABBITMQ_NODE_ONLY" ]; then -hidden \ -s rabbit_prelaunch \ -sname rabbitmqprelaunch$$ \ - -extra "$RABBITMQ_ENABLED_PLUGINS_FILE" "$RABBITMQ_PLUGINS_DIR" "${RABBITMQ_PLUGINS_EXPAND_DIR}" "${RABBITMQ_NODENAME}" + -extra "${RABBITMQ_NODENAME}" then - RABBITMQ_BOOT_FILE="${RABBITMQ_PLUGINS_EXPAND_DIR}/rabbit" - RABBITMQ_EBIN_PATH="" + RABBITMQ_BOOT_FILE=start_sasl + RABBITMQ_EBIN_PATH="-pa ${RABBITMQ_EBIN_ROOT}" else exit 1 fi else + # TODO: does this branch actually make any sense now??? RABBITMQ_BOOT_FILE=start_sasl RABBITMQ_EBIN_PATH="-pa ${RABBITMQ_EBIN_ROOT}" fi @@ -104,6 +105,7 @@ exec erl \ ${RABBITMQ_START_RABBIT} \ -sname ${RABBITMQ_NODENAME} \ -boot ${RABBITMQ_BOOT_FILE} \ + -s rabbit start_cold \ ${RABBITMQ_CONFIG_ARG} \ +W w \ ${RABBITMQ_SERVER_ERL_ARGS} \ @@ -112,9 +114,9 @@ exec erl \ -sasl sasl_error_logger false \ -rabbit error_logger '{file,"'${RABBITMQ_LOGS}'"}' \ -rabbit sasl_error_logger '{file,"'${RABBITMQ_SASL_LOGS}'"}' \ - -rabbit enabled_plugins_file "$RABBITMQ_ENABLED_PLUGINS_FILE" \ - -rabbit plugins_dir "$RABBITMQ_PLUGINS_DIR" \ - -rabbit plugins_expand_dir "$RABBITMQ_PLUGINS_EXPAND_DIR" \ + -rabbit enabled_plugins_file "\"$RABBITMQ_ENABLED_PLUGINS_FILE\"" \ + -rabbit plugins_dir "\"$RABBITMQ_PLUGINS_DIR\"" \ + -rabbit plugins_expand_dir "\"$RABBITMQ_PLUGINS_EXPAND_DIR\"" \ -os_mon start_cpu_sup false \ -os_mon start_disksup false \ -os_mon start_memsup false \ -- cgit v1.2.1 From 29eef735d36b4c72e590e891c5ed24838402ee0c Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Fri, 4 May 2012 12:04:43 +0100 Subject: plugin environment bootstrap and introspection code moved to rabbit_plugins --- src/rabbit.erl | 43 +++++++++++++------------------------------ src/rabbit_plugins.erl | 36 ++++++++++++++++++++++++++++++------ 2 files changed, 43 insertions(+), 36 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 13afdc68..87526aae 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -298,28 +298,26 @@ prepare() -> start() -> start_it(fun() -> ok = prepare(), - ok = rabbit_misc:start_applications(application_load_order()) + ok = rabbit_misc:start_applications(application_load_order()), + ok = print_plugin_info(rabbit_plugins:active_plugins()) end). start_cold() -> start_it(fun() -> ok = prepare(), - Plugins = determine_required_plugins(), + Plugins = rabbit_plugins:bootstrap_envinronment(), ToBeLoaded = Plugins ++ ?APPS, io:format("~n" "Activating RabbitMQ plugins ...~n"), + io:format("Plugins: ~p~n", [Plugins]), load_applications(queue:from_list(ToBeLoaded), sets:new()), StartupApps = rabbit_misc:calculate_app_dependency_ordering(ToBeLoaded), ok = rabbit_misc:start_applications(StartupApps), - - io:format("~w plugins activated:~n", [length(Plugins)]), - [io:format("* ~s-~s~n", [AppName, - element(2, application:get_key(AppName, vsn))]) - || AppName <- Plugins], - io:nl() + + ok = print_plugin_info(Plugins) end). start_it(StartFun) -> @@ -586,28 +584,6 @@ insert_default_data() -> %%--------------------------------------------------------------------------- %% logging -determine_required_plugins() -> - {ok, PluginDir} = application:get_env(rabbit, plugins_dir), - {ok, ExpandDir} = application:get_env(rabbit, plugins_expand_dir), - {ok, EnabledPluginsFile} = application:get_env(rabbit, - enabled_plugins_file), - rabbit_plugins:prepare_plugins(EnabledPluginsFile, PluginDir, ExpandDir), - find_plugins(ExpandDir). - -find_plugins(PluginDir) -> - [prepare_dir_plugin(PluginName) || - PluginName <- filelib:wildcard(PluginDir ++ "/*/ebin/*.app")]. - -prepare_dir_plugin(PluginAppDescFn) -> - %% Add the plugin ebin directory to the load path - PluginEBinDirN = filename:dirname(PluginAppDescFn), - code:add_path(PluginEBinDirN), - - %% We want the second-last token - NameTokens = string:tokens(PluginAppDescFn,"/."), - PluginNameString = lists:nth(length(NameTokens) - 1, NameTokens), - list_to_atom(PluginNameString). - ensure_working_log_handlers() -> Handlers = gen_event:which_handlers(error_logger), ok = ensure_working_log_handler(error_logger_tty_h, @@ -690,6 +666,13 @@ force_event_refresh() -> %%--------------------------------------------------------------------------- %% misc +print_plugin_info(Plugins) -> + io:format("~w plugins activated:~n", [length(Plugins)]), + [io:format("* ~s-~s~n", [AppName, + element(2, application:get_key(AppName, vsn))]) + || AppName <- Plugins], + io:nl(). + erts_version_check() -> FoundVer = erlang:system_info(version), case rabbit_misc:version_compare(?ERTS_MINIMUM, FoundVer, lte) of diff --git a/src/rabbit_plugins.erl b/src/rabbit_plugins.erl index 0a5f8d51..0ab0d3aa 100644 --- a/src/rabbit_plugins.erl +++ b/src/rabbit_plugins.erl @@ -17,7 +17,7 @@ -module(rabbit_plugins). -include("rabbit.hrl"). --export([start/0, stop/0, prepare_plugins/3]). +-export([start/0, stop/0, bootstrap_envinronment/0, active_plugins/0]). -define(VERBOSE_OPT, "-v"). -define(MINIMAL_OPT, "-m"). @@ -30,11 +30,8 @@ -spec(start/0 :: () -> no_return()). -spec(stop/0 :: () -> 'ok'). --spec(find_plugins/1 :: (file:filename()) -> [#plugin{}]). --spec(read_enabled_plugins/1 :: (file:filename()) -> [atom()]). --spec(lookup_plugins/2 :: ([atom()], [#plugin{}]) -> [#plugin{}]). --spec(calculate_required_plugins/2 :: ([atom()], [#plugin{}]) -> [atom()]). --spec(plugin_names/1 :: ([#plugin{}]) -> [atom()]). +-spec(bootstrap_envinronment/0 :: () -> [atom()]). +-spec(active_plugins/0 :: () -> [atom()]). -endif. @@ -83,6 +80,23 @@ start() -> stop() -> ok. +bootstrap_envinronment() -> + {ok, PluginDir} = application:get_env(rabbit, plugins_dir), + {ok, ExpandDir} = application:get_env(rabbit, plugins_expand_dir), + {ok, EnabledPluginsFile} = application:get_env(rabbit, + enabled_plugins_file), + prepare_plugins(EnabledPluginsFile, PluginDir, ExpandDir), + [prepare_dir_plugin(PluginName) || + PluginName <- filelib:wildcard(ExpandDir ++ "/*/ebin/*.app")]. + +active_plugins() -> + {ok, ExpandDir} = application:get_env(rabbit, plugins_expand_dir), + InstalledPlugins = [ P#plugin.name || P <- find_plugins(ExpandDir) ], + [App || {App, _, _} <- application:which_applications(), + lists:member(App, InstalledPlugins)]. + +%%---------------------------------------------------------------------------- + prepare_plugins(EnabledPluginsFile, PluginsDistDir, DestDir) -> AllPlugins = find_plugins(PluginsDistDir), Enabled = read_enabled_plugins(EnabledPluginsFile), @@ -110,6 +124,16 @@ prepare_plugins(EnabledPluginsFile, PluginsDistDir, DestDir) -> [prepare_plugin(Plugin, DestDir) || Plugin <- ToUnpackPlugins]. +prepare_dir_plugin(PluginAppDescFn) -> + %% Add the plugin ebin directory to the load path + PluginEBinDirN = filename:dirname(PluginAppDescFn), + code:add_path(PluginEBinDirN), + + %% We want the second-last token + NameTokens = string:tokens(PluginAppDescFn,"/."), + PluginNameString = lists:nth(length(NameTokens) - 1, NameTokens), + list_to_atom(PluginNameString). + %%---------------------------------------------------------------------------- action(list, [], Opts, PluginsFile, PluginsDir) -> -- cgit v1.2.1 From ed2e74690c0a02567a48b1eaf0ef41b0d87f6e97 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Fri, 4 May 2012 12:28:27 +0100 Subject: move application lifecycle (loading) into one place - rabbit_misc --- src/rabbit.erl | 37 +++---------------------------------- src/rabbit_misc.erl | 38 +++++++++++++++++++++++++++++++------- 2 files changed, 34 insertions(+), 41 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 87526aae..588cc390 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -308,11 +308,9 @@ start_cold() -> Plugins = rabbit_plugins:bootstrap_envinronment(), ToBeLoaded = Plugins ++ ?APPS, - io:format("~n" - "Activating RabbitMQ plugins ...~n"), - io:format("Plugins: ~p~n", [Plugins]), + io:format("~nActivating RabbitMQ plugins ...~n"), - load_applications(queue:from_list(ToBeLoaded), sets:new()), + ok = rabbit_misc:load_applications(ToBeLoaded), StartupApps = rabbit_misc:calculate_app_dependency_ordering(ToBeLoaded), ok = rabbit_misc:start_applications(StartupApps), @@ -417,38 +415,9 @@ stop(_State) -> %% application life cycle application_load_order() -> - ok = load_applications(), + ok = rabbit_misc:load_applications(?APPS), rabbit_misc:calculate_app_dependency_ordering(?APPS). -load_applications() -> - load_applications(queue:from_list(?APPS), sets:new()). - -%% FIXME: should we move this into rabbit_misc? -load_applications(Worklist, Loaded) -> - case queue:out(Worklist) of - {empty, _WorkList} -> - ok; - {{value, App}, Worklist1} -> - case sets:is_element(App, Loaded) of - true -> load_applications(Worklist1, Loaded); - false -> case application:load(App) of - ok -> ok; - {error, {already_loaded, App}} -> ok; - Error -> throw(Error) - end, - load_applications( - queue:join(Worklist1, - queue:from_list(app_dependencies(App))), - sets:add_element(App, Loaded)) - end - end. - -app_dependencies(App) -> - case application:get_key(App, applications) of - undefined -> []; - {ok, Lst} -> Lst - end. - %%--------------------------------------------------------------------------- %% boot step logic diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 2fffc2cd..87865ebc 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -42,7 +42,7 @@ -export([dirty_read_all/1, dirty_foreach_key/2, dirty_dump_log/1]). -export([format/2, format_many/1, format_stderr/2]). -export([with_local_io/1, local_info_msg/2]). --export([calculate_app_dependency_ordering/1, +-export([calculate_app_dependency_ordering/1, load_applications/1, start_applications/1, stop_applications/1]). -export([unfold/2, ceil/1, queue_fold/3]). -export([sort_field_table/1]). @@ -170,6 +170,7 @@ -spec(with_local_io/1 :: (fun (() -> A)) -> A). -spec(local_info_msg/2 :: (string(), [any()]) -> 'ok'). -spec(start_applications/1 :: ([atom()]) -> 'ok'). +-spec(load_applications/1 :: ([atom()]) -> 'ok'). -spec(calculate_app_dependency_ordering/1 :: ([atom()]) -> 'ok'). -spec(stop_applications/1 :: ([atom()]) -> 'ok'). -spec(unfold/2 :: (fun ((A) -> ({'true', B, A} | 'false')), A) -> {[B], A}). @@ -623,6 +624,35 @@ manage_applications(Iterate, Do, Undo, SkipError, ErrorTag, Apps) -> end, [], Apps), ok. +load_applications(Apps) -> + load_applications(queue:from_list(Apps), sets:new()), + ok. + +load_applications(Worklist, Loaded) -> + case queue:out(Worklist) of + {empty, _WorkList} -> + ok; + {{value, App}, Worklist1} -> + case sets:is_element(App, Loaded) of + true -> load_applications(Worklist1, Loaded); + false -> case application:load(App) of + ok -> ok; + {error, {already_loaded, App}} -> ok; + Error -> throw(Error) + end, + load_applications( + queue:join(Worklist1, + queue:from_list(app_dependencies(App))), + sets:add_element(App, Loaded)) + end + end. + +app_dependencies(App) -> + case application:get_key(App, applications) of + undefined -> []; + {ok, Lst} -> Lst + end. + start_applications(Apps) -> manage_applications(fun lists:foldl/3, fun application:start/1, @@ -653,12 +683,6 @@ calculate_app_dependency_ordering(RootApps) -> true = digraph:delete(G) end. -app_dependencies(App) -> - case application:get_key(App, applications) of - undefined -> []; - {ok, Lst} -> Lst - end. - unfold(Fun, Init) -> unfold(Fun, [], Init). -- cgit v1.2.1 From 7ac5a4750af268ecdd71168854aad726695ce9a2 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Fri, 4 May 2012 13:31:25 +0100 Subject: ensure that all dependent applications are stopped when shutting down the broker without halting the emulator --- src/rabbit.erl | 10 +++++++--- src/rabbit_misc.erl | 19 ++++++++++++++----- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 588cc390..6425730c 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -298,7 +298,7 @@ prepare() -> start() -> start_it(fun() -> ok = prepare(), - ok = rabbit_misc:start_applications(application_load_order()), + ok = rabbit_misc:start_applications(app_startup_order()), ok = print_plugin_info(rabbit_plugins:active_plugins()) end). @@ -327,7 +327,7 @@ start_it(StartFun) -> stop() -> rabbit_log:info("Stopping Rabbit~n"), - ok = rabbit_misc:stop_applications(application_load_order()). + ok = rabbit_misc:stop_applications(app_shutdown_order()). stop_and_halt() -> try @@ -414,10 +414,14 @@ stop(_State) -> %%--------------------------------------------------------------------------- %% application life cycle -application_load_order() -> +app_startup_order() -> ok = rabbit_misc:load_applications(?APPS), rabbit_misc:calculate_app_dependency_ordering(?APPS). +app_shutdown_order() -> + Apps = ?APPS ++ rabbit_plugins:active_plugins(), + rabbit_misc:calculate_app_dependency_ordering(Apps, true). + %%--------------------------------------------------------------------------- %% boot step logic diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 87865ebc..6371c24c 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -42,8 +42,9 @@ -export([dirty_read_all/1, dirty_foreach_key/2, dirty_dump_log/1]). -export([format/2, format_many/1, format_stderr/2]). -export([with_local_io/1, local_info_msg/2]). --export([calculate_app_dependency_ordering/1, load_applications/1, - start_applications/1, stop_applications/1]). +-export([calculate_app_dependency_ordering/1, + calculate_app_dependency_ordering/2]). +-export([load_applications/1, start_applications/1, stop_applications/1]). -export([unfold/2, ceil/1, queue_fold/3]). -export([sort_field_table/1]). -export([pid_to_string/1, string_to_pid/1]). @@ -171,7 +172,9 @@ -spec(local_info_msg/2 :: (string(), [any()]) -> 'ok'). -spec(start_applications/1 :: ([atom()]) -> 'ok'). -spec(load_applications/1 :: ([atom()]) -> 'ok'). --spec(calculate_app_dependency_ordering/1 :: ([atom()]) -> 'ok'). +-spec(calculate_app_dependency_ordering/1 :: ([atom()]) -> [digraph:vertex()]). +-spec(calculate_app_dependency_ordering/2 :: ([atom()], + boolean()) -> [digraph:vertex()]). -spec(stop_applications/1 :: ([atom()]) -> 'ok'). -spec(unfold/2 :: (fun ((A) -> ({'true', B, A} | 'false')), A) -> {[B], A}). -spec(ceil/1 :: (number()) -> integer()). @@ -670,14 +673,20 @@ stop_applications(Apps) -> Apps). calculate_app_dependency_ordering(RootApps) -> + calculate_app_dependency_ordering(RootApps, false). + +calculate_app_dependency_ordering(RootApps, StripUnreachable) -> {ok, G} = build_acyclic_graph( fun (App, _Deps) -> [{App, App}] end, fun (App, Deps) -> [{Dep, App} || Dep <- Deps] end, [{App, app_dependencies(App)} || {App, _Desc, _Vsn} <- application:loaded_applications()]), try - true = digraph:del_vertices(G, digraph:vertices(G) -- - digraph_utils:reachable(RootApps, G)), + case StripUnreachable of + true -> digraph:del_vertices(G, digraph:vertices(G) -- + digraph_utils:reachable(RootApps, G)); + false -> ok + end, digraph_utils:topsort(G) after true = digraph:delete(G) -- cgit v1.2.1 From d409bee9b24e8f5971bba6df415fa076c498d9d3 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Fri, 4 May 2012 13:59:45 +0100 Subject: attempt to make rabbitmq-server.bat behave like it's unix cousin --- scripts/rabbitmq-server.bat | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/scripts/rabbitmq-server.bat b/scripts/rabbitmq-server.bat index ca49a5d8..ae3cb629 100755 --- a/scripts/rabbitmq-server.bat +++ b/scripts/rabbitmq-server.bat @@ -94,15 +94,7 @@ set RABBITMQ_EBIN_ROOT=!TDP0!..\ebin -noinput -hidden ^ -s rabbit_prelaunch ^ -sname rabbitmqprelaunch!RANDOM! ^ --extra "!RABBITMQ_ENABLED_PLUGINS_FILE:\=/!" ^ - "!RABBITMQ_PLUGINS_DIR:\=/!" ^ - "!RABBITMQ_PLUGINS_EXPAND_DIR:\=/!" ^ - "!RABBITMQ_NODENAME!" - -set RABBITMQ_BOOT_FILE=!RABBITMQ_PLUGINS_EXPAND_DIR!\rabbit -if ERRORLEVEL 1 ( - exit /B 1 -) +-extra "!RABBITMQ_NODENAME!" set RABBITMQ_EBIN_PATH= @@ -126,7 +118,8 @@ if not "!RABBITMQ_NODE_IP_ADDRESS!"=="" ( "!ERLANG_HOME!\bin\erl.exe" ^ !RABBITMQ_EBIN_PATH! ^ -noinput ^ --boot "!RABBITMQ_BOOT_FILE!" ^ +-boot start_sasl ^ +-s rabbit start_cold ^ !RABBITMQ_CONFIG_ARG! ^ -sname !RABBITMQ_NODENAME! ^ +W w ^ @@ -139,6 +132,9 @@ if not "!RABBITMQ_NODE_IP_ADDRESS!"=="" ( -sasl sasl_error_logger false ^ -rabbit error_logger {file,\""!LOGS:\=/!"\"} ^ -rabbit sasl_error_logger {file,\""!SASL_LOGS:\=/!"\"} ^ +-rabbit enabled_plugins_file \""!RABBITMQ_ENABLED_PLUGINS_FILE:\=/!"\" ^ +-rabbit plugins_dir \""!RABBITMQ_PLUGINS_DIR:\=/!"\" ^ +-rabbit plugins_expand_dir \""!RABBITMQ_PLUGINS_EXPAND_DIR:\=/!"\" ^ -os_mon start_cpu_sup false ^ -os_mon start_disksup false ^ -os_mon start_memsup false ^ -- cgit v1.2.1 From 3fce3ee273299686a0e562377472b99c8d6c2728 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Fri, 4 May 2012 16:27:56 +0100 Subject: remove the fun argument in `rabbit_mnesia:init_db/3'. --- src/rabbit_mnesia.erl | 27 +++++++++++---------------- src/rabbit_upgrade.erl | 6 +++--- 2 files changed, 14 insertions(+), 19 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 7e9346f9..35dfc313 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -18,7 +18,7 @@ -module(rabbit_mnesia). -export([ensure_mnesia_dir/0, dir/0, status/0, init/0, is_db_empty/0, - cluster/1, force_cluster/1, reset/0, force_reset/0, init_db/3, + cluster/1, force_cluster/1, reset/0, force_reset/0, init_db/2, is_clustered/0, running_clustered_nodes/0, all_clustered_nodes/0, empty_ram_only_tables/0, copy_db/1, wait_for_tables/1, create_cluster_nodes_config/1, read_cluster_nodes_config/0, @@ -46,7 +46,7 @@ -spec(dir/0 :: () -> file:filename()). -spec(ensure_mnesia_dir/0 :: () -> 'ok'). -spec(init/0 :: () -> 'ok'). --spec(init_db/3 :: ([node()], boolean(), rabbit_misc:thunk('ok')) -> 'ok'). +-spec(init_db/2 :: ([node()], boolean()) -> 'ok'). -spec(is_db_empty/0 :: () -> boolean()). -spec(cluster/1 :: ([node()]) -> 'ok'). -spec(force_cluster/1 :: ([node()]) -> 'ok'). @@ -505,23 +505,11 @@ delete_previously_running_nodes() -> FileName, Reason}}) end. -init_db(ClusterNodes, Force) -> - init_db( - ClusterNodes, Force, - fun () -> - case rabbit_upgrade:maybe_upgrade_local() of - ok -> ok; - %% If we're just starting up a new node we won't have a - %% version - starting_from_scratch -> ok = rabbit_version:record_desired() - end - end). - %% Take a cluster node config and create the right kind of node - a %% standalone disk node, or disk or ram node connected to the %% specified cluster nodes. If Force is false, don't allow %% connections to offline nodes. -init_db(ClusterNodes, Force, SecondaryPostMnesiaFun) -> +init_db(ClusterNodes, Force) -> UClusterNodes = lists:usort(ClusterNodes), ProperClusterNodes = UClusterNodes -- [node()], case mnesia:change_config(extra_db_nodes, ProperClusterNodes) of @@ -559,7 +547,14 @@ init_db(ClusterNodes, Force, SecondaryPostMnesiaFun) -> ok = create_local_table_copy(schema, CopyTypeAlt), ok = create_local_table_copies(CopyType), - ok = SecondaryPostMnesiaFun(), + ok = case rabbit_upgrade:maybe_upgrade_local() of + ok -> ok; + %% If we're just starting up a new node we won't + %% have a version + starting_from_scratch -> + rabbit_version:record_desired() + end, + %% We've taken down mnesia, so ram nodes will need %% to re-sync case is_disc_node() of diff --git a/src/rabbit_upgrade.erl b/src/rabbit_upgrade.erl index e1a7bcae..d5fa0d69 100644 --- a/src/rabbit_upgrade.erl +++ b/src/rabbit_upgrade.erl @@ -66,11 +66,11 @@ %% into the boot process by prelaunch before the mnesia application is %% started. By the time Mnesia is started the upgrades have happened %% (on the primary), or Mnesia has been reset (on the secondary) and -%% rabbit_mnesia:init_db/3 can then make the node rejoin the cluster +%% rabbit_mnesia:init_db/2 can then make the node rejoin the cluster %% in the normal way. %% %% The non-mnesia upgrades are then triggered by -%% rabbit_mnesia:init_db/3. Of course, it's possible for a given +%% rabbit_mnesia:init_db/2. Of course, it's possible for a given %% upgrade process to only require Mnesia upgrades, or only require %% non-Mnesia upgrades. In the latter case no Mnesia resets and %% reclusterings occur. @@ -230,7 +230,7 @@ secondary_upgrade(AllNodes) -> false -> AllNodes -- [node()] end, rabbit_misc:ensure_ok(mnesia:start(), cannot_start_mnesia), - ok = rabbit_mnesia:init_db(ClusterNodes, true, fun () -> ok end), + ok = rabbit_mnesia:init_db(ClusterNodes, true), ok = rabbit_version:record_desired_for_scope(mnesia), ok. -- cgit v1.2.1 From 2a37146f9958d131ff76379977b9f2b6f4e32398 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Fri, 4 May 2012 16:41:17 +0100 Subject: re-added a boolean parameter to init_db to prevent upgrading --- src/rabbit_mnesia.erl | 24 ++++++++++++++++-------- src/rabbit_upgrade.erl | 6 +++--- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 35dfc313..2a04dfe2 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -46,7 +46,7 @@ -spec(dir/0 :: () -> file:filename()). -spec(ensure_mnesia_dir/0 :: () -> 'ok'). -spec(init/0 :: () -> 'ok'). --spec(init_db/2 :: ([node()], boolean()) -> 'ok'). +-spec(init_db/3 :: ([node()], boolean(), boolean()) -> 'ok'). -spec(is_db_empty/0 :: () -> boolean()). -spec(cluster/1 :: ([node()]) -> 'ok'). -spec(force_cluster/1 :: ([node()]) -> 'ok'). @@ -505,11 +505,13 @@ delete_previously_running_nodes() -> FileName, Reason}}) end. +init_db(ClusterNodes, Force) -> init_db(ClusterNodes, Force, true). + %% Take a cluster node config and create the right kind of node - a %% standalone disk node, or disk or ram node connected to the %% specified cluster nodes. If Force is false, don't allow %% connections to offline nodes. -init_db(ClusterNodes, Force) -> +init_db(ClusterNodes, Force, Upgrade) -> UClusterNodes = lists:usort(ClusterNodes), ProperClusterNodes = UClusterNodes -- [node()], case mnesia:change_config(extra_db_nodes, ProperClusterNodes) of @@ -547,12 +549,18 @@ init_db(ClusterNodes, Force) -> ok = create_local_table_copy(schema, CopyTypeAlt), ok = create_local_table_copies(CopyType), - ok = case rabbit_upgrade:maybe_upgrade_local() of - ok -> ok; - %% If we're just starting up a new node we won't - %% have a version - starting_from_scratch -> - rabbit_version:record_desired() + ok = case Upgrade of + true -> + case rabbit_upgrade:maybe_upgrade_local() of + ok -> + ok; + %% If we're just starting up a new node we + %% won't have a version + starting_from_scratch -> + rabbit_version:record_desired() + end; + false -> + ok end, %% We've taken down mnesia, so ram nodes will need diff --git a/src/rabbit_upgrade.erl b/src/rabbit_upgrade.erl index d5fa0d69..25efb91a 100644 --- a/src/rabbit_upgrade.erl +++ b/src/rabbit_upgrade.erl @@ -66,11 +66,11 @@ %% into the boot process by prelaunch before the mnesia application is %% started. By the time Mnesia is started the upgrades have happened %% (on the primary), or Mnesia has been reset (on the secondary) and -%% rabbit_mnesia:init_db/2 can then make the node rejoin the cluster +%% rabbit_mnesia:init_db/3 can then make the node rejoin the cluster %% in the normal way. %% %% The non-mnesia upgrades are then triggered by -%% rabbit_mnesia:init_db/2. Of course, it's possible for a given +%% rabbit_mnesia:init_db/3. Of course, it's possible for a given %% upgrade process to only require Mnesia upgrades, or only require %% non-Mnesia upgrades. In the latter case no Mnesia resets and %% reclusterings occur. @@ -230,7 +230,7 @@ secondary_upgrade(AllNodes) -> false -> AllNodes -- [node()] end, rabbit_misc:ensure_ok(mnesia:start(), cannot_start_mnesia), - ok = rabbit_mnesia:init_db(ClusterNodes, true), + ok = rabbit_mnesia:init_db(ClusterNodes, true, false), ok = rabbit_version:record_desired_for_scope(mnesia), ok. -- cgit v1.2.1 From 094b023bc2e957ea4d28b64c1e370fd30020217b Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Fri, 4 May 2012 16:46:35 +0100 Subject: forgot to change the arity of init_db in the export --- src/rabbit_mnesia.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 2a04dfe2..a120b9a3 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -18,7 +18,7 @@ -module(rabbit_mnesia). -export([ensure_mnesia_dir/0, dir/0, status/0, init/0, is_db_empty/0, - cluster/1, force_cluster/1, reset/0, force_reset/0, init_db/2, + cluster/1, force_cluster/1, reset/0, force_reset/0, init_db/3, is_clustered/0, running_clustered_nodes/0, all_clustered_nodes/0, empty_ram_only_tables/0, copy_db/1, wait_for_tables/1, create_cluster_nodes_config/1, read_cluster_nodes_config/0, -- cgit v1.2.1 From cd154815d147287a1e436d827be5b421fac237b8 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Fri, 4 May 2012 16:56:01 +0100 Subject: fix windows batch file code path handling during cold starts --- scripts/rabbitmq-server.bat | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/rabbitmq-server.bat b/scripts/rabbitmq-server.bat index ae3cb629..0967024a 100755 --- a/scripts/rabbitmq-server.bat +++ b/scripts/rabbitmq-server.bat @@ -96,7 +96,7 @@ set RABBITMQ_EBIN_ROOT=!TDP0!..\ebin -sname rabbitmqprelaunch!RANDOM! ^ -extra "!RABBITMQ_NODENAME!" -set RABBITMQ_EBIN_PATH= +set RABBITMQ_EBIN_PATH="-pa !RABBITMQ_EBIN_ROOT!" if "!RABBITMQ_CONFIG_FILE!"=="" ( set RABBITMQ_CONFIG_FILE=!RABBITMQ_BASE!\rabbitmq @@ -116,9 +116,9 @@ if not "!RABBITMQ_NODE_IP_ADDRESS!"=="" ( ) "!ERLANG_HOME!\bin\erl.exe" ^ -!RABBITMQ_EBIN_PATH! ^ +-pa "!RABBITMQ_EBIN_ROOT!" ^ -noinput ^ --boot start_sasl ^ +-boot start_sasl ^ -s rabbit start_cold ^ !RABBITMQ_CONFIG_ARG! ^ -sname !RABBITMQ_NODENAME! ^ -- cgit v1.2.1 From 9e8f332fe6f4f3737f5d347af0205771995c0c98 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Fri, 4 May 2012 17:08:05 +0100 Subject: apply startup changes to windows service batch utility --- scripts/rabbitmq-service.bat | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/scripts/rabbitmq-service.bat b/scripts/rabbitmq-service.bat index 9e274840..f49ec266 100755 --- a/scripts/rabbitmq-service.bat +++ b/scripts/rabbitmq-service.bat @@ -157,22 +157,6 @@ if "!RABBITMQ_ENABLED_PLUGINS_FILE!"=="" ( set RABBITMQ_PLUGINS_DIR=!TDP0!..\plugins set RABBITMQ_EBIN_ROOT=!TDP0!..\ebin -"!ERLANG_HOME!\bin\erl.exe" ^ --pa "!RABBITMQ_EBIN_ROOT!" ^ --noinput -hidden ^ --s rabbit_prelaunch ^ --extra "!RABBITMQ_ENABLED_PLUGINS_FILE:\=/!" ^ - "!RABBITMQ_PLUGINS_DIR:\=/!" ^ - "!RABBITMQ_PLUGINS_EXPAND_DIR:\=/!" ^ - "" - -set RABBITMQ_BOOT_FILE=!RABBITMQ_PLUGINS_EXPAND_DIR!\rabbit -if ERRORLEVEL 1 ( - exit /B 1 -) - -set RABBITMQ_EBIN_PATH= - if "!RABBITMQ_CONFIG_FILE!"=="" ( set RABBITMQ_CONFIG_FILE=!RABBITMQ_BASE!\rabbitmq ) @@ -191,8 +175,9 @@ if not "!RABBITMQ_NODE_IP_ADDRESS!"=="" ( ) set ERLANG_SERVICE_ARGUMENTS= ^ -!RABBITMQ_EBIN_PATH! ^ --boot "!RABBITMQ_BOOT_FILE!" ^ +-pa "!RABBITMQ_EBIN_ROOT!" ^ +-boot start_sasl ^ +-s rabbit start_cold ^ !RABBITMQ_CONFIG_ARG! ^ +W w ^ +A30 ^ @@ -204,6 +189,9 @@ set ERLANG_SERVICE_ARGUMENTS= ^ -sasl sasl_error_logger false ^ -rabbit error_logger {file,\""!LOGS:\=/!"\"} ^ -rabbit sasl_error_logger {file,\""!SASL_LOGS:\=/!"\"} ^ +-rabbit enabled_plugins_file \""!RABBITMQ_ENABLED_PLUGINS_FILE:\=/!"\" ^ +-rabbit plugins_dir \""!RABBITMQ_PLUGINS_DIR:\=/!"\" ^ +-rabbit plugins_expand_dir \""!RABBITMQ_PLUGINS_EXPAND_DIR:\=/!"\" ^ -os_mon start_cpu_sup false ^ -os_mon start_disksup false ^ -os_mon start_memsup false ^ -- cgit v1.2.1 From 928c90a11037f6742f5e5680c7f36a2614bdb68f Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Fri, 4 May 2012 17:42:00 +0100 Subject: do not overwrite RABBITMQ_PLUGINS_DIR on windows --- scripts/rabbitmq-plugins.bat | 4 +++- scripts/rabbitmq-server.bat | 5 ++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/scripts/rabbitmq-plugins.bat b/scripts/rabbitmq-plugins.bat index 66a900a1..3d7a2d99 100755 --- a/scripts/rabbitmq-plugins.bat +++ b/scripts/rabbitmq-plugins.bat @@ -43,7 +43,9 @@ if "!RABBITMQ_ENABLED_PLUGINS_FILE!"=="" ( set RABBITMQ_ENABLED_PLUGINS_FILE=!RABBITMQ_BASE!\enabled_plugins ) -set RABBITMQ_PLUGINS_DIR=!TDP0!..\plugins +if "!RABBITMQ_PLUGINS_DIR!"=="" ( + set RABBITMQ_PLUGINS_DIR=!TDP0!..\plugins +) "!ERLANG_HOME!\bin\erl.exe" -pa "!TDP0!..\ebin" -noinput -hidden -sname rabbitmq-plugins!RANDOM! -s rabbit_plugins -enabled_plugins_file "!RABBITMQ_ENABLED_PLUGINS_FILE!" -plugins_dist_dir "!RABBITMQ_PLUGINS_DIR:\=/!" -extra !STAR! diff --git a/scripts/rabbitmq-server.bat b/scripts/rabbitmq-server.bat index ca49a5d8..0123ed24 100755 --- a/scripts/rabbitmq-server.bat +++ b/scripts/rabbitmq-server.bat @@ -86,7 +86,10 @@ if "!RABBITMQ_ENABLED_PLUGINS_FILE!"=="" ( set RABBITMQ_ENABLED_PLUGINS_FILE=!RABBITMQ_BASE!\enabled_plugins ) -set RABBITMQ_PLUGINS_DIR=!TDP0!..\plugins +if "!RABBITMQ_PLUGINS_DIR!"=="" ( + set RABBITMQ_PLUGINS_DIR=!TDP0!..\plugins +) + set RABBITMQ_EBIN_ROOT=!TDP0!..\ebin "!ERLANG_HOME!\bin\erl.exe" ^ -- cgit v1.2.1 From 5154482b74b9374f2fede4d63ecee938f2198ccc Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Tue, 8 May 2012 09:22:18 +0100 Subject: revert deleted comment explaining why we're sleeping for 100ms on startup --- src/rabbit.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rabbit.erl b/src/rabbit.erl index 6425730c..0d00706b 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -322,6 +322,7 @@ start_it(StartFun) -> try StartFun() after + %% give the error loggers some time to catch up timer:sleep(100) end. -- cgit v1.2.1 From e33890fad17bb07f0739713d806f96b01ccc031d Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Tue, 8 May 2012 09:31:49 +0100 Subject: rename start_cold to boot --- scripts/rabbitmq-server | 2 +- scripts/rabbitmq-server.bat | 2 +- scripts/rabbitmq-service.bat | 2 +- src/rabbit.erl | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts/rabbitmq-server b/scripts/rabbitmq-server index 4dd81a84..e041d425 100755 --- a/scripts/rabbitmq-server +++ b/scripts/rabbitmq-server @@ -105,7 +105,7 @@ exec erl \ ${RABBITMQ_START_RABBIT} \ -sname ${RABBITMQ_NODENAME} \ -boot ${RABBITMQ_BOOT_FILE} \ - -s rabbit start_cold \ + -s rabbit boot \ ${RABBITMQ_CONFIG_ARG} \ +W w \ ${RABBITMQ_SERVER_ERL_ARGS} \ diff --git a/scripts/rabbitmq-server.bat b/scripts/rabbitmq-server.bat index 0967024a..e594522d 100755 --- a/scripts/rabbitmq-server.bat +++ b/scripts/rabbitmq-server.bat @@ -119,7 +119,7 @@ if not "!RABBITMQ_NODE_IP_ADDRESS!"=="" ( -pa "!RABBITMQ_EBIN_ROOT!" ^ -noinput ^ -boot start_sasl ^ --s rabbit start_cold ^ +-s rabbit boot ^ !RABBITMQ_CONFIG_ARG! ^ -sname !RABBITMQ_NODENAME! ^ +W w ^ diff --git a/scripts/rabbitmq-service.bat b/scripts/rabbitmq-service.bat index f49ec266..849bedcf 100755 --- a/scripts/rabbitmq-service.bat +++ b/scripts/rabbitmq-service.bat @@ -177,7 +177,7 @@ if not "!RABBITMQ_NODE_IP_ADDRESS!"=="" ( set ERLANG_SERVICE_ARGUMENTS= ^ -pa "!RABBITMQ_EBIN_ROOT!" ^ -boot start_sasl ^ --s rabbit start_cold ^ +-s rabbit boot ^ !RABBITMQ_CONFIG_ARG! ^ +W w ^ +A30 ^ diff --git a/src/rabbit.erl b/src/rabbit.erl index 0d00706b..b7e640ff 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -18,7 +18,7 @@ -behaviour(application). --export([maybe_hipe_compile/0, prepare/0, start/0, start_cold/0, stop/0, +-export([maybe_hipe_compile/0, prepare/0, start/0, boot/0, stop/0, stop_and_halt/0, status/0, is_running/0, is_running/1, environment/0, rotate_logs/1, force_event_refresh/0]). @@ -217,7 +217,7 @@ -spec(maybe_hipe_compile/0 :: () -> 'ok'). -spec(prepare/0 :: () -> 'ok'). -spec(start/0 :: () -> 'ok'). --spec(start_cold/0 :: () -> 'ok'). +-spec(boot/0 :: () -> 'ok'). -spec(stop/0 :: () -> 'ok'). -spec(stop_and_halt/0 :: () -> no_return()). -spec(status/0 :: @@ -302,7 +302,7 @@ start() -> ok = print_plugin_info(rabbit_plugins:active_plugins()) end). -start_cold() -> +boot() -> start_it(fun() -> ok = prepare(), Plugins = rabbit_plugins:bootstrap_envinronment(), -- cgit v1.2.1 From 77e9767b031e2afd7f20dbdc937401051bb9487d Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Wed, 9 May 2012 10:57:36 +0100 Subject: migrate application handling code to app_utils --- src/app_utils.erl | 109 ++++++++++++++++++++++++++++++++++++++++++++++ src/rabbit.erl | 16 +++---- src/rabbit_misc.erl | 86 ------------------------------------ src/rabbit_networking.erl | 2 +- 4 files changed, 118 insertions(+), 95 deletions(-) create mode 100644 src/app_utils.erl diff --git a/src/app_utils.erl b/src/app_utils.erl new file mode 100644 index 00000000..dfbf7106 --- /dev/null +++ b/src/app_utils.erl @@ -0,0 +1,109 @@ +%% 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 VMware, Inc. +%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% +-module(app_utils). + +-export([load_applications/1, start_applications/1, + stop_applications/1, app_dependency_order/2]). + +-ifdef(use_specs). + +-spec start_applications([atom()]) -> 'ok'. +-spec load_applications([atom()]) -> 'ok'. +-spec stop_applications([atom()]) -> 'ok'. +-spec app_dependency_order([atom()], boolean()) -> [digraph:vertex()]. + +-endif. + +%%--------------------------------------------------------------------------- +%% Public API + +load_applications(Apps) -> + load_applications(queue:from_list(Apps), sets:new()), + ok. + +start_applications(Apps) -> + manage_applications(fun lists:foldl/3, + fun application:start/1, + fun application:stop/1, + already_started, + cannot_start_application, + Apps). + +stop_applications(Apps) -> + manage_applications(fun lists:foldr/3, + fun application:stop/1, + fun application:start/1, + not_started, + cannot_stop_application, + Apps). + +app_dependency_order(RootApps, StripUnreachable) -> + {ok, G} = rabbit_misc:build_acyclic_graph( + fun (App, _Deps) -> [{App, App}] end, + fun (App, Deps) -> [{Dep, App} || Dep <- Deps] end, + [{App, app_dependencies(App)} || + {App, _Desc, _Vsn} <- application:loaded_applications()]), + try + case StripUnreachable of + true -> digraph:del_vertices(G, digraph:vertices(G) -- + digraph_utils:reachable(RootApps, G)); + false -> ok + end, + digraph_utils:topsort(G) + after + true = digraph:delete(G) + end. + +%%--------------------------------------------------------------------------- +%% Private API + +load_applications(Worklist, Loaded) -> + case queue:out(Worklist) of + {empty, _WorkList} -> + ok; + {{value, App}, Worklist1} -> + case sets:is_element(App, Loaded) of + true -> load_applications(Worklist1, Loaded); + false -> case application:load(App) of + ok -> ok; + {error, {already_loaded, App}} -> ok; + Error -> throw(Error) + end, + load_applications( + queue:join(Worklist1, + queue:from_list(app_dependencies(App))), + sets:add_element(App, Loaded)) + end + end. + +app_dependencies(App) -> + case application:get_key(App, applications) of + undefined -> []; + {ok, Lst} -> Lst + end. + +manage_applications(Iterate, Do, Undo, SkipError, ErrorTag, Apps) -> + Iterate(fun (App, Acc) -> + case Do(App) of + ok -> [App | Acc]; + {error, {SkipError, _}} -> Acc; + {error, Reason} -> + lists:foreach(Undo, Acc), + throw({error, {ErrorTag, App, Reason}}) + end + end, [], Apps), + ok. + diff --git a/src/rabbit.erl b/src/rabbit.erl index b7e640ff..ee0f82a1 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -298,7 +298,7 @@ prepare() -> start() -> start_it(fun() -> ok = prepare(), - ok = rabbit_misc:start_applications(app_startup_order()), + ok = app_utils:start_applications(app_startup_order()), ok = print_plugin_info(rabbit_plugins:active_plugins()) end). @@ -310,10 +310,10 @@ boot() -> io:format("~nActivating RabbitMQ plugins ...~n"), - ok = rabbit_misc:load_applications(ToBeLoaded), + ok = app_utils:load_applications(ToBeLoaded), StartupApps = - rabbit_misc:calculate_app_dependency_ordering(ToBeLoaded), - ok = rabbit_misc:start_applications(StartupApps), + app_utils:app_dependency_order(ToBeLoaded, false), + ok = app_utils:start_applications(StartupApps), ok = print_plugin_info(Plugins) end). @@ -328,7 +328,7 @@ start_it(StartFun) -> stop() -> rabbit_log:info("Stopping Rabbit~n"), - ok = rabbit_misc:stop_applications(app_shutdown_order()). + ok = app_utils:stop_applications(app_shutdown_order()). stop_and_halt() -> try @@ -416,12 +416,12 @@ stop(_State) -> %% application life cycle app_startup_order() -> - ok = rabbit_misc:load_applications(?APPS), - rabbit_misc:calculate_app_dependency_ordering(?APPS). + ok = app_utils:load_applications(?APPS), + app_utils:app_dependency_order(?APPS, false). app_shutdown_order() -> Apps = ?APPS ++ rabbit_plugins:active_plugins(), - rabbit_misc:calculate_app_dependency_ordering(Apps, true). + app_utils:app_dependency_order(Apps, true). %%--------------------------------------------------------------------------- %% boot step logic diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 6371c24c..955d769c 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -42,9 +42,6 @@ -export([dirty_read_all/1, dirty_foreach_key/2, dirty_dump_log/1]). -export([format/2, format_many/1, format_stderr/2]). -export([with_local_io/1, local_info_msg/2]). --export([calculate_app_dependency_ordering/1, - calculate_app_dependency_ordering/2]). --export([load_applications/1, start_applications/1, stop_applications/1]). -export([unfold/2, ceil/1, queue_fold/3]). -export([sort_field_table/1]). -export([pid_to_string/1, string_to_pid/1]). @@ -170,12 +167,6 @@ -spec(format_stderr/2 :: (string(), [any()]) -> 'ok'). -spec(with_local_io/1 :: (fun (() -> A)) -> A). -spec(local_info_msg/2 :: (string(), [any()]) -> 'ok'). --spec(start_applications/1 :: ([atom()]) -> 'ok'). --spec(load_applications/1 :: ([atom()]) -> 'ok'). --spec(calculate_app_dependency_ordering/1 :: ([atom()]) -> [digraph:vertex()]). --spec(calculate_app_dependency_ordering/2 :: ([atom()], - boolean()) -> [digraph:vertex()]). --spec(stop_applications/1 :: ([atom()]) -> 'ok'). -spec(unfold/2 :: (fun ((A) -> ({'true', B, A} | 'false')), A) -> {[B], A}). -spec(ceil/1 :: (number()) -> integer()). -spec(queue_fold/3 :: (fun ((any(), B) -> B), B, queue()) -> B). @@ -615,83 +606,6 @@ with_local_io(Fun) -> local_info_msg(Format, Args) -> with_local_io(fun () -> error_logger:info_msg(Format, Args) end). -manage_applications(Iterate, Do, Undo, SkipError, ErrorTag, Apps) -> - Iterate(fun (App, Acc) -> - case Do(App) of - ok -> [App | Acc]; - {error, {SkipError, _}} -> Acc; - {error, Reason} -> - lists:foreach(Undo, Acc), - throw({error, {ErrorTag, App, Reason}}) - end - end, [], Apps), - ok. - -load_applications(Apps) -> - load_applications(queue:from_list(Apps), sets:new()), - ok. - -load_applications(Worklist, Loaded) -> - case queue:out(Worklist) of - {empty, _WorkList} -> - ok; - {{value, App}, Worklist1} -> - case sets:is_element(App, Loaded) of - true -> load_applications(Worklist1, Loaded); - false -> case application:load(App) of - ok -> ok; - {error, {already_loaded, App}} -> ok; - Error -> throw(Error) - end, - load_applications( - queue:join(Worklist1, - queue:from_list(app_dependencies(App))), - sets:add_element(App, Loaded)) - end - end. - -app_dependencies(App) -> - case application:get_key(App, applications) of - undefined -> []; - {ok, Lst} -> Lst - end. - -start_applications(Apps) -> - manage_applications(fun lists:foldl/3, - fun application:start/1, - fun application:stop/1, - already_started, - cannot_start_application, - Apps). - -stop_applications(Apps) -> - manage_applications(fun lists:foldr/3, - fun application:stop/1, - fun application:start/1, - not_started, - cannot_stop_application, - Apps). - -calculate_app_dependency_ordering(RootApps) -> - calculate_app_dependency_ordering(RootApps, false). - -calculate_app_dependency_ordering(RootApps, StripUnreachable) -> - {ok, G} = build_acyclic_graph( - fun (App, _Deps) -> [{App, App}] end, - fun (App, Deps) -> [{Dep, App} || Dep <- Deps] end, - [{App, app_dependencies(App)} || - {App, _Desc, _Vsn} <- application:loaded_applications()]), - try - case StripUnreachable of - true -> digraph:del_vertices(G, digraph:vertices(G) -- - digraph_utils:reachable(RootApps, G)); - false -> ok - end, - digraph_utils:topsort(G) - after - true = digraph:delete(G) - end. - unfold(Fun, Init) -> unfold(Fun, [], Init). diff --git a/src/rabbit_networking.erl b/src/rabbit_networking.erl index f0c75d23..4562608a 100644 --- a/src/rabbit_networking.erl +++ b/src/rabbit_networking.erl @@ -147,7 +147,7 @@ start() -> ok. ensure_ssl() -> - ok = rabbit_misc:start_applications([crypto, public_key, ssl]), + ok = app_utils:start_applications([crypto, public_key, ssl]), {ok, SslOptsConfig} = application:get_env(rabbit, ssl_options), % unknown_ca errors are silently ignored prior to R14B unless we -- cgit v1.2.1 From 4a0d06700d0565e614fde99d0442bd146a9a51fd Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Wed, 9 May 2012 12:15:41 +0100 Subject: rabbitmqctl uses rabbit:await_startup/0 --- src/app_utils.erl | 16 ++++++++++++++-- src/rabbit.erl | 8 ++++++-- src/rabbit_control.erl | 7 +++---- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/app_utils.erl b/src/app_utils.erl index dfbf7106..4bef83a5 100644 --- a/src/app_utils.erl +++ b/src/app_utils.erl @@ -16,13 +16,15 @@ -module(app_utils). -export([load_applications/1, start_applications/1, - stop_applications/1, app_dependency_order/2]). + stop_applications/1, app_dependency_order/2, + wait_for_applications/1]). -ifdef(use_specs). --spec start_applications([atom()]) -> 'ok'. -spec load_applications([atom()]) -> 'ok'. +-spec start_applications([atom()]) -> 'ok'. -spec stop_applications([atom()]) -> 'ok'. +-spec wait_for_applications([atom()]) -> 'ok'. -spec app_dependency_order([atom()], boolean()) -> [digraph:vertex()]. -endif. @@ -50,6 +52,9 @@ stop_applications(Apps) -> cannot_stop_application, Apps). +wait_for_applications(Apps) -> + [wait_for_application(App) || App <- Apps], ok. + app_dependency_order(RootApps, StripUnreachable) -> {ok, G} = rabbit_misc:build_acyclic_graph( fun (App, _Deps) -> [{App, App}] end, @@ -70,6 +75,13 @@ app_dependency_order(RootApps, StripUnreachable) -> %%--------------------------------------------------------------------------- %% Private API +wait_for_application(Application) -> + case lists:keymember(Application, 1, application:which_applications()) of + true -> ok; + false -> timer:sleep(1000), + wait_for_application(Application) + end. + load_applications(Worklist, Loaded) -> case queue:out(Worklist) of {empty, _WorkList} -> diff --git a/src/rabbit.erl b/src/rabbit.erl index ee0f82a1..1d54d0b6 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -19,8 +19,8 @@ -behaviour(application). -export([maybe_hipe_compile/0, prepare/0, start/0, boot/0, stop/0, - stop_and_halt/0, status/0, is_running/0, is_running/1, environment/0, - rotate_logs/1, force_event_refresh/0]). + stop_and_halt/0, await_startup/0, status/0, is_running/0, + is_running/1, environment/0, rotate_logs/1, force_event_refresh/0]). -export([start/2, stop/1]). @@ -220,6 +220,7 @@ -spec(boot/0 :: () -> 'ok'). -spec(stop/0 :: () -> 'ok'). -spec(stop_and_halt/0 :: () -> no_return()). +-spec(await_startup/0 :: () -> 'ok'). -spec(status/0 :: () -> [{pid, integer()} | {running_applications, [{atom(), string(), string()}]} | @@ -339,6 +340,9 @@ stop_and_halt() -> end, ok. +await_startup() -> + app_utils:wait_for_applications(app_startup_order()). + status() -> S1 = [{pid, list_to_integer(os:getpid())}, {running_applications, application:which_applications(infinity)}, diff --git a/src/rabbit_control.erl b/src/rabbit_control.erl index 9b317cee..d9b2ae97 100644 --- a/src/rabbit_control.erl +++ b/src/rabbit_control.erl @@ -407,10 +407,9 @@ wait_for_application(Node, PidFile, Application, Inform) -> wait_for_application(Node, Pid, Application) -> case process_up(Pid) of - true -> case rabbit_nodes:is_running(Node, Application) of - true -> ok; - false -> timer:sleep(?EXTERNAL_CHECK_INTERVAL), - wait_for_application(Node, Pid, Application) + true -> case rpc:call(Node, rabbit, await_startup, []) of + {badrpc, _} -> {error, node_not_responding}; + ok -> ok end; false -> {error, process_not_running} end. -- cgit v1.2.1 From 4fb3b1ce40b9fdea8c1d56391bbed0c7a1d23618 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Wed, 9 May 2012 17:46:43 +0100 Subject: dumping schema table on changes in ram nodes --- src/rabbit_mnesia.erl | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index a120b9a3..08e91e27 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -97,6 +97,7 @@ status() -> init() -> ensure_mnesia_running(), ensure_mnesia_dir(), + monitor_schema(), Nodes = read_cluster_nodes_config(), ok = init_db(Nodes, should_be_disc_node(Nodes)), %% We intuitively expect the global name server to be synced when @@ -830,3 +831,21 @@ start_mnesia() -> stop_mnesia() -> stopped = mnesia:stop(), ensure_mnesia_not_running(). + +handle_schema_events() -> + receive + {mnesia_table_event, _Event} -> + case is_disc_node() of + true -> ok; + false -> mnesia:dump_tables([schema]) + end, + handle_schema_events(); + _ -> + exit(non_event) + end. + +monitor_schema() -> + spawn(fun () -> mnesia:subscribe({table, schema, simple}), + handle_schema_events() + end), + ok. -- cgit v1.2.1 From 4b4e35e1b3cd5f3eff9e385db1f9d7b6ffa4240b Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Thu, 10 May 2012 14:42:12 +0100 Subject: remove the subscription to the schema table Since dump_tables won't work with the schema table (and works different to what I had understood anyways) I don't think it's worth it to subscribe to the table, and it's probably better to just modify the config file through rabbit_node_monitor. --- src/rabbit_mnesia.erl | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 08e91e27..a120b9a3 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -97,7 +97,6 @@ status() -> init() -> ensure_mnesia_running(), ensure_mnesia_dir(), - monitor_schema(), Nodes = read_cluster_nodes_config(), ok = init_db(Nodes, should_be_disc_node(Nodes)), %% We intuitively expect the global name server to be synced when @@ -831,21 +830,3 @@ start_mnesia() -> stop_mnesia() -> stopped = mnesia:stop(), ensure_mnesia_not_running(). - -handle_schema_events() -> - receive - {mnesia_table_event, _Event} -> - case is_disc_node() of - true -> ok; - false -> mnesia:dump_tables([schema]) - end, - handle_schema_events(); - _ -> - exit(non_event) - end. - -monitor_schema() -> - spawn(fun () -> mnesia:subscribe({table, schema, simple}), - handle_schema_events() - end), - ok. -- cgit v1.2.1 From ca8a42634b9643f0964e58300818ef006978a7b7 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Thu, 10 May 2012 14:43:00 +0100 Subject: remote TODO and extraneous branches from rabbit-server --- scripts/rabbitmq-server | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/scripts/rabbitmq-server b/scripts/rabbitmq-server index e041d425..57f37ff9 100755 --- a/scripts/rabbitmq-server +++ b/scripts/rabbitmq-server @@ -70,25 +70,20 @@ case "$(uname -s)" in esac RABBITMQ_EBIN_ROOT="${RABBITMQ_HOME}/ebin" -if [ "x" = "x$RABBITMQ_NODE_ONLY" ]; then - if erl \ +if erl \ -pa "$RABBITMQ_EBIN_ROOT" \ -noinput \ -hidden \ -s rabbit_prelaunch \ -sname rabbitmqprelaunch$$ \ -extra "${RABBITMQ_NODENAME}" - then +then RABBITMQ_BOOT_FILE=start_sasl RABBITMQ_EBIN_PATH="-pa ${RABBITMQ_EBIN_ROOT}" - else - exit 1 - fi else - # TODO: does this branch actually make any sense now??? - RABBITMQ_BOOT_FILE=start_sasl - RABBITMQ_EBIN_PATH="-pa ${RABBITMQ_EBIN_ROOT}" + exit 1 fi + RABBITMQ_CONFIG_ARG= [ -f "${RABBITMQ_CONFIG_FILE}.config" ] && RABBITMQ_CONFIG_ARG="-config ${RABBITMQ_CONFIG_FILE}" -- cgit v1.2.1 From 7c3dc522e43d91eb60b9ab280da32e7d43407e9b Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Thu, 10 May 2012 15:46:12 +0100 Subject: removed `rabbit_mnesia:force_cluster' I'll also have to remove the `Force' parameter from `init_db', fixing bug 24815. --- src/rabbit_control.erl | 6 ------ src/rabbit_mnesia.erl | 30 +++++++----------------------- 2 files changed, 7 insertions(+), 29 deletions(-) diff --git a/src/rabbit_control.erl b/src/rabbit_control.erl index 2dea2a2f..74581e15 100644 --- a/src/rabbit_control.erl +++ b/src/rabbit_control.erl @@ -186,12 +186,6 @@ action(cluster, Node, ClusterNodeSs, _Opts, Inform) -> [Node, ClusterNodes]), rpc_call(Node, rabbit_mnesia, cluster, [ClusterNodes]); -action(force_cluster, Node, ClusterNodeSs, _Opts, Inform) -> - ClusterNodes = lists:map(fun list_to_atom/1, ClusterNodeSs), - Inform("Forcefully clustering node ~p with ~p (ignoring offline nodes)", - [Node, ClusterNodes]), - rpc_call(Node, rabbit_mnesia, force_cluster, [ClusterNodes]); - action(wait, Node, [PidFile], _Opts, Inform) -> Inform("Waiting for ~p", [Node]), wait_for_application(Node, PidFile, rabbit, Inform); diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index a120b9a3..f8533b04 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -18,8 +18,8 @@ -module(rabbit_mnesia). -export([ensure_mnesia_dir/0, dir/0, status/0, init/0, is_db_empty/0, - cluster/1, force_cluster/1, reset/0, force_reset/0, init_db/3, - is_clustered/0, running_clustered_nodes/0, all_clustered_nodes/0, + cluster/1, reset/0, force_reset/0, init_db/3, is_clustered/0, + running_clustered_nodes/0, all_clustered_nodes/0, empty_ram_only_tables/0, copy_db/1, wait_for_tables/1, create_cluster_nodes_config/1, read_cluster_nodes_config/0, record_running_nodes/0, read_previously_running_nodes/0, @@ -49,8 +49,6 @@ -spec(init_db/3 :: ([node()], boolean(), boolean()) -> 'ok'). -spec(is_db_empty/0 :: () -> boolean()). -spec(cluster/1 :: ([node()]) -> 'ok'). --spec(force_cluster/1 :: ([node()]) -> 'ok'). --spec(cluster/2 :: ([node()], boolean()) -> 'ok'). -spec(reset/0 :: () -> 'ok'). -spec(force_reset/0 :: () -> 'ok'). -spec(is_clustered/0 :: () -> boolean()). @@ -110,26 +108,17 @@ is_db_empty() -> lists:all(fun (Tab) -> mnesia:dirty_first(Tab) == '$end_of_table' end, table_names()). -cluster(ClusterNodes) -> - cluster(ClusterNodes, false). -force_cluster(ClusterNodes) -> - cluster(ClusterNodes, true). - %% Alter which disk nodes this node is clustered with. This can be a %% subset of all the disk nodes in the cluster but can (and should) %% include the node itself if it is to be a disk rather than a ram %% node. If Force is false, only connections to online nodes are %% allowed. -cluster(ClusterNodes, Force) -> - rabbit_misc:local_info_msg("Clustering with ~p~s~n", - [ClusterNodes, if Force -> " forcefully"; - true -> "" - end]), +cluster(ClusterNodes) -> + rabbit_misc:local_info_msg("Clustering with ~p~s~n", [ClusterNodes]), ensure_mnesia_not_running(), ensure_mnesia_dir(), - case not Force andalso is_clustered() andalso - is_only_disc_node(node(), false) andalso + case is_clustered() andalso is_only_disc_node(node(), false) andalso not should_be_disc_node(ClusterNodes) of true -> log_both("last running disc node leaving cluster"); @@ -164,17 +153,12 @@ cluster(ClusterNodes, Force) -> %% before we can join it. But, since we don't know if we're in a %% cluster or not, we just pre-emptively leave it before joining. ProperClusterNodes = ClusterNodes -- [node()], - try - ok = leave_cluster(ProperClusterNodes, ProperClusterNodes) - catch - {error, {no_running_cluster_nodes, _, _}} when Force -> - ok - end, + ok = leave_cluster(ProperClusterNodes, ProperClusterNodes), %% Join the cluster start_mnesia(), try - ok = init_db(ClusterNodes, Force), + ok = init_db(ClusterNodes, false), ok = create_cluster_nodes_config(ClusterNodes) after stop_mnesia() -- cgit v1.2.1 From 75cbb7186da34808999658c766b52e274a5d03e2 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Fri, 11 May 2012 14:20:31 +0100 Subject: remove the bug 24919 stuff --- src/rabbit_control.erl | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/rabbit_control.erl b/src/rabbit_control.erl index 96758151..74581e15 100644 --- a/src/rabbit_control.erl +++ b/src/rabbit_control.erl @@ -39,12 +39,6 @@ {"Permissions", rabbit_auth_backend_internal, list_vhost_permissions, vhost_perms_info_keys}]). --define(OPTS_COMMANDS, - [{?VHOST_OPT, [set_permissions, clear_permissions, list_permissions, - list_user_permissions, list_queues, list_bindings, - list_connections, list_channels, list_consumers, - trace_on, trace_off]}]). - %%---------------------------------------------------------------------------- -ifdef(use_specs). @@ -90,15 +84,6 @@ start() -> [string:join([atom_to_list(Command) | Args], " ")]) end, - lists:foreach(fun ({Opt, Commands}) -> - case {proplists:lookup(Opt, Opts), - lists:member(Command, Commands)} of - {{_, _}, false} -> PrintInvalidCommandError(), - usage(); - _ -> ok - end - end, ?OPTS_COMMANDS), - %% The reason we don't use a try/catch here is that rpc:call turns %% thrown errors into normal return values case catch action(Command, Node, Args, Opts, Inform) of -- cgit v1.2.1 From 39e73c465ce66311aa90a6ded05843a4a20a2bc9 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Fri, 11 May 2012 17:35:34 +0100 Subject: added `join_cluster', deprecated `cluster, changed the structure of the node config --- src/rabbit_control.erl | 14 ++++++-- src/rabbit_mnesia.erl | 87 +++++++++++++++++++++++++++++++++----------------- src/rabbit_upgrade.erl | 4 +-- 3 files changed, 72 insertions(+), 33 deletions(-) diff --git a/src/rabbit_control.erl b/src/rabbit_control.erl index 74581e15..1bab7518 100644 --- a/src/rabbit_control.erl +++ b/src/rabbit_control.erl @@ -25,6 +25,7 @@ -define(QUIET_OPT, "-q"). -define(NODE_OPT, "-n"). -define(VHOST_OPT, "-p"). +-define(RAM_OPT, "--ram"). -define(GLOBAL_QUERIES, [{"Connections", rabbit_networking, connection_info_all, @@ -60,7 +61,8 @@ start() -> {[Command0 | Args], Opts} = case rabbit_misc:get_options([{flag, ?QUIET_OPT}, {option, ?NODE_OPT, NodeStr}, - {option, ?VHOST_OPT, "/"}], + {option, ?VHOST_OPT, "/"}, + {flag, ?RAM_OPT}], init:get_plain_arguments()) of {[], _Opts} -> usage(); CmdArgsAndOpts -> CmdArgsAndOpts @@ -181,10 +183,18 @@ action(force_reset, Node, [], _Opts, Inform) -> call(Node, {rabbit_mnesia, force_reset, []}); action(cluster, Node, ClusterNodeSs, _Opts, Inform) -> + io:format("'cluster' is deprecated, please us 'join_cluster'.~n"), ClusterNodes = lists:map(fun list_to_atom/1, ClusterNodeSs), + DiscNode = rabbit_mnesia:should_be_disc_node_legacy(ClusterNodes), Inform("Clustering node ~p with ~p", [Node, ClusterNodes]), - rpc_call(Node, rabbit_mnesia, cluster, [ClusterNodes]); + rpc_call(Node, rabbit_mnesia, cluster, [{ClusterNodes, DiscNode}]); + +action(join_cluster, Node, ClusterNodeSs, Opts, Inform) -> + ClusterNodes = lists:map(fun list_to_atom/1, ClusterNodeSs), + DiscNode = not proplists:get_bool(?RAM_OPT, Opts), + Inform("Clustering node ~p with ~p", [Node, ClusterNodes]), + rpc_call(Node, rabbit_mnesia, cluster, [{ClusterNodes, DiscNode}]); action(wait, Node, [PidFile], _Opts, Inform) -> Inform("Waiting for ~p", [Node]), diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index f8533b04..0587daa3 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -24,7 +24,7 @@ create_cluster_nodes_config/1, read_cluster_nodes_config/0, record_running_nodes/0, read_previously_running_nodes/0, running_nodes_filename/0, is_disc_node/0, on_node_down/1, - on_node_up/1]). + on_node_up/1, should_be_disc_node_legacy/1]). -export([table_names/0]). @@ -38,15 +38,16 @@ -ifdef(use_specs). --export_type([node_type/0]). +-export_type([node_type/0, node_config/0]). -type(node_type() :: disc_only | disc | ram | unknown). +-type(node_config() :: {[node()], boolean()}). -spec(status/0 :: () -> [{'nodes', [{node_type(), [node()]}]} | {'running_nodes', [node()]}]). -spec(dir/0 :: () -> file:filename()). -spec(ensure_mnesia_dir/0 :: () -> 'ok'). -spec(init/0 :: () -> 'ok'). --spec(init_db/3 :: ([node()], boolean(), boolean()) -> 'ok'). +-spec(init_db/3 :: (node_config(), boolean(), boolean()) -> 'ok'). -spec(is_db_empty/0 :: () -> boolean()). -spec(cluster/1 :: ([node()]) -> 'ok'). -spec(reset/0 :: () -> 'ok'). @@ -58,14 +59,15 @@ -spec(create_tables/0 :: () -> 'ok'). -spec(copy_db/1 :: (file:filename()) -> rabbit_types:ok_or_error(any())). -spec(wait_for_tables/1 :: ([atom()]) -> 'ok'). --spec(create_cluster_nodes_config/1 :: ([node()]) -> 'ok'). --spec(read_cluster_nodes_config/0 :: () -> [node()]). +-spec(create_cluster_nodes_config/1 :: (node_config()) -> 'ok'). +-spec(read_cluster_nodes_config/0 :: () -> node_config()). -spec(record_running_nodes/0 :: () -> 'ok'). -spec(read_previously_running_nodes/0 :: () -> [node()]). -spec(running_nodes_filename/0 :: () -> file:filename()). -spec(is_disc_node/0 :: () -> boolean()). -spec(on_node_up/1 :: (node()) -> 'ok'). -spec(on_node_down/1 :: (node()) -> 'ok'). +-spec(should_be_disc_node_legacy/1 :: ([node()]) -> boolean()). -spec(table_names/0 :: () -> [atom()]). @@ -95,8 +97,8 @@ status() -> init() -> ensure_mnesia_running(), ensure_mnesia_dir(), - Nodes = read_cluster_nodes_config(), - ok = init_db(Nodes, should_be_disc_node(Nodes)), + Config = {_, DiscNode} = read_cluster_nodes_config(), + ok = init_db(Config, DiscNode), %% We intuitively expect the global name server to be synced when %% Mnesia is up. In fact that's not guaranteed to be the case - let's %% make it so. @@ -113,20 +115,29 @@ is_db_empty() -> %% include the node itself if it is to be a disk rather than a ram %% node. If Force is false, only connections to online nodes are %% allowed. -cluster(ClusterNodes) -> +cluster({DiscoveryNodes, DiscNode}) -> + case is_only_disc_node(node(), false) andalso not DiscNode of + true -> throw({error, + {standalone_ram_node, + "You can't cluster a node if it's the only " + "disc node in its existing cluster."}}); + _ -> ok + end, + + %% TODO: Emit some warning/error if this node is in DiscoveryNodes + ProperDiscoveryNodes = DiscoveryNodes -- [node()], + ClusterNodes = case discover_cluster(ProperDiscoveryNodes) of + {ok, ClusterNodes0} -> ClusterNodes0; + {error, Reason} -> throw({error, Reason}) + end, + Config = {ClusterNodes, DiscNode}, + rabbit_misc:local_info_msg("Clustering with ~p~s~n", [ClusterNodes]), ensure_mnesia_not_running(), ensure_mnesia_dir(), - case is_clustered() andalso is_only_disc_node(node(), false) andalso - not should_be_disc_node(ClusterNodes) - of - true -> log_both("last running disc node leaving cluster"); - _ -> ok - end, - %% Wipe mnesia if we're changing type from disc to ram - case {is_disc_node(), should_be_disc_node(ClusterNodes)} of + case {is_disc_node(), DiscNode} of {true, false} -> rabbit_misc:with_local_io( fun () -> error_logger:warning_msg( "changing node type; wiping " @@ -158,8 +169,8 @@ cluster(ClusterNodes) -> %% Join the cluster start_mnesia(), try - ok = init_db(ClusterNodes, false), - ok = create_cluster_nodes_config(ClusterNodes) + ok = init_db(Config, false), + ok = create_cluster_nodes_config(Config) after stop_mnesia() end, @@ -182,6 +193,11 @@ all_clustered_nodes() -> running_clustered_nodes() -> mnesia:system_info(running_db_nodes). +running_clustered_disc_nodes() -> + RunningSet = sets:from_list(running_clustered_nodes()), + DiscSet = sets:from_list(nodes_of_type(disc_copies)), + sets:to_list(sets:intersection(RunningSet, DiscSet)). + empty_ram_only_tables() -> Node = node(), lists:foreach( @@ -193,6 +209,14 @@ empty_ram_only_tables() -> end, table_names()), ok. +discover_cluster([]) -> + {error, cannot_discover_cluster}; +discover_cluster([Node | Nodes]) -> + case rpc:call(Node, rabbit_mnesia, all_clustered_nodes, []) of + {badrpc, _Reason} -> discover_cluster(Nodes); + Res -> {ok, Res} + end. + %%-------------------------------------------------------------------- nodes_of_type(Type) -> @@ -429,9 +453,9 @@ check_tables(Fun) -> cluster_nodes_config_filename() -> dir() ++ "/cluster_nodes.config". -create_cluster_nodes_config(ClusterNodes) -> +create_cluster_nodes_config(Config) -> FileName = cluster_nodes_config_filename(), - case rabbit_file:write_term_file(FileName, [ClusterNodes]) of + case rabbit_file:write_term_file(FileName, [Config]) of ok -> ok; {error, Reason} -> throw({error, {cannot_create_cluster_nodes_config, @@ -439,12 +463,17 @@ create_cluster_nodes_config(ClusterNodes) -> end. read_cluster_nodes_config() -> + Convert = fun (Config = {_, _}) -> Config; + (ClusterNodes) -> + rabbit_log:warning("reading legacy node config"), + {ClusterNodes, should_be_disc_node_legacy(ClusterNodes)} + end, FileName = cluster_nodes_config_filename(), case rabbit_file:read_term_file(FileName) of - {ok, [ClusterNodes]} -> ClusterNodes; + {ok, [Config]} -> Convert(Config); {error, enoent} -> - {ok, ClusterNodes} = application:get_env(rabbit, cluster_nodes), - ClusterNodes; + {ok, Config} = application:get_env(rabbit, cluster_nodes), + Convert(Config); {error, Reason} -> throw({error, {cannot_read_cluster_nodes_config, FileName, Reason}}) @@ -495,7 +524,7 @@ init_db(ClusterNodes, Force) -> init_db(ClusterNodes, Force, true). %% standalone disk node, or disk or ram node connected to the %% specified cluster nodes. If Force is false, don't allow %% connections to offline nodes. -init_db(ClusterNodes, Force, Upgrade) -> +init_db({ClusterNodes, WantDiscNode}, Force, Upgrade) -> UClusterNodes = lists:usort(ClusterNodes), ProperClusterNodes = UClusterNodes -- [node()], case mnesia:change_config(extra_db_nodes, ProperClusterNodes) of @@ -504,7 +533,6 @@ init_db(ClusterNodes, Force, Upgrade) -> "Mnesia could not connect to any disc nodes."}}); {ok, Nodes} -> WasDiscNode = is_disc_node(), - WantDiscNode = should_be_disc_node(ClusterNodes), %% We create a new db (on disk, or in ram) in the first %% two cases and attempt to upgrade the in the other two case {Nodes, WasDiscNode, WantDiscNode} of @@ -607,7 +635,10 @@ create_schema(Type) -> is_disc_node() -> mnesia:system_info(use_dir). -should_be_disc_node(ClusterNodes) -> +%% We're not using this anymore - the config stores whether the node is disc or +%% not - but we still need it when reading old-style configs and in +%% `rabbit_control'. +should_be_disc_node_legacy(ClusterNodes) -> ClusterNodes == [] orelse lists:member(node(), ClusterNodes). move_db() -> @@ -793,9 +824,7 @@ on_node_down(Node) -> end. is_only_disc_node(Node, _MnesiaRunning = true) -> - RunningSet = sets:from_list(running_clustered_nodes()), - DiscSet = sets:from_list(nodes_of_type(disc_copies)), - [Node] =:= sets:to_list(sets:intersection(RunningSet, DiscSet)); + [Node] =:= running_clustered_disc_nodes(); is_only_disc_node(Node, false) -> start_mnesia(), Res = is_only_disc_node(Node, true), diff --git a/src/rabbit_upgrade.erl b/src/rabbit_upgrade.erl index 25efb91a..27312990 100644 --- a/src/rabbit_upgrade.erl +++ b/src/rabbit_upgrade.erl @@ -123,8 +123,8 @@ remove_backup() -> maybe_upgrade_mnesia() -> %% rabbit_mnesia:all_clustered_nodes/0 will return [] at this point %% if we are a RAM node since Mnesia has not started yet. - AllNodes = lists:usort(rabbit_mnesia:all_clustered_nodes() ++ - rabbit_mnesia:read_cluster_nodes_config()), + {ClusterNodes, _DiscNode} = rabbit_mnesia:read_cluster_nodes_config(), + AllNodes = lists:usort(rabbit_mnesia:all_clustered_nodes() ++ ClusterNodes), case rabbit_version:upgrades_required(mnesia) of {error, starting_from_scratch} -> ok; -- cgit v1.2.1 From e1b02aeefcbb9f6c7ac0c73a7b7e917ff4bde66a Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Fri, 11 May 2012 18:33:14 +0100 Subject: reseting when clustering, `reset' now disconnects correctly Also, I check that we're not joining a cluster we're already in. --- src/rabbit_control.erl | 4 +-- src/rabbit_mnesia.erl | 90 ++++++++++++++++++++++---------------------------- 2 files changed, 42 insertions(+), 52 deletions(-) diff --git a/src/rabbit_control.erl b/src/rabbit_control.erl index 1bab7518..357073c1 100644 --- a/src/rabbit_control.erl +++ b/src/rabbit_control.erl @@ -188,13 +188,13 @@ action(cluster, Node, ClusterNodeSs, _Opts, Inform) -> DiscNode = rabbit_mnesia:should_be_disc_node_legacy(ClusterNodes), Inform("Clustering node ~p with ~p", [Node, ClusterNodes]), - rpc_call(Node, rabbit_mnesia, cluster, [{ClusterNodes, DiscNode}]); + rpc_call(Node, rabbit_mnesia, join_cluster, [{ClusterNodes, DiscNode}]); action(join_cluster, Node, ClusterNodeSs, Opts, Inform) -> ClusterNodes = lists:map(fun list_to_atom/1, ClusterNodeSs), DiscNode = not proplists:get_bool(?RAM_OPT, Opts), Inform("Clustering node ~p with ~p", [Node, ClusterNodes]), - rpc_call(Node, rabbit_mnesia, cluster, [{ClusterNodes, DiscNode}]); + rpc_call(Node, rabbit_mnesia, join_cluster, [{ClusterNodes, DiscNode}]); action(wait, Node, [PidFile], _Opts, Inform) -> Inform("Waiting for ~p", [Node]), diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 0587daa3..78529d3b 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -18,13 +18,13 @@ -module(rabbit_mnesia). -export([ensure_mnesia_dir/0, dir/0, status/0, init/0, is_db_empty/0, - cluster/1, reset/0, force_reset/0, init_db/3, is_clustered/0, + join_cluster/1, reset/0, force_reset/0, init_db/3, is_clustered/0, running_clustered_nodes/0, all_clustered_nodes/0, empty_ram_only_tables/0, copy_db/1, wait_for_tables/1, create_cluster_nodes_config/1, read_cluster_nodes_config/0, record_running_nodes/0, read_previously_running_nodes/0, - running_nodes_filename/0, is_disc_node/0, on_node_down/1, - on_node_up/1, should_be_disc_node_legacy/1]). + running_nodes_filename/0, is_disc_node/0, on_node_down/1, on_node_up/1, + should_be_disc_node_legacy/1]). -export([table_names/0]). @@ -49,7 +49,7 @@ -spec(init/0 :: () -> 'ok'). -spec(init_db/3 :: (node_config(), boolean(), boolean()) -> 'ok'). -spec(is_db_empty/0 :: () -> boolean()). --spec(cluster/1 :: ([node()]) -> 'ok'). +-spec(join_cluster/1 :: ([node()]) -> 'ok'). -spec(reset/0 :: () -> 'ok'). -spec(force_reset/0 :: () -> 'ok'). -spec(is_clustered/0 :: () -> boolean()). @@ -115,7 +115,10 @@ is_db_empty() -> %% include the node itself if it is to be a disk rather than a ram %% node. If Force is false, only connections to online nodes are %% allowed. -cluster({DiscoveryNodes, DiscNode}) -> +join_cluster({DiscoveryNodes, DiscNode}) -> + ensure_mnesia_not_running(), + ensure_mnesia_dir(), + case is_only_disc_node(node(), false) andalso not DiscNode of true -> throw({error, {standalone_ram_node, @@ -124,47 +127,28 @@ cluster({DiscoveryNodes, DiscNode}) -> _ -> ok end, - %% TODO: Emit some warning/error if this node is in DiscoveryNodes ProperDiscoveryNodes = DiscoveryNodes -- [node()], ClusterNodes = case discover_cluster(ProperDiscoveryNodes) of {ok, ClusterNodes0} -> ClusterNodes0; {error, Reason} -> throw({error, Reason}) end, - Config = {ClusterNodes, DiscNode}, + + case lists:member(node(), ClusterNodes) of + true -> throw({error, {already_clustered, + "You are already clustered with the nodes you " + "have selected."}}); + false -> ok + end, - rabbit_misc:local_info_msg("Clustering with ~p~s~n", [ClusterNodes]), - ensure_mnesia_not_running(), - ensure_mnesia_dir(), + %% reset the node. this simplifies things and it will be needed in this case + %% - we're joining a new cluster with new nodes which are not in synch with + %% the current node. I also lifts the burden of reseting the node from the + %% user. + reset(false), - %% Wipe mnesia if we're changing type from disc to ram - case {is_disc_node(), DiscNode} of - {true, false} -> rabbit_misc:with_local_io( - fun () -> error_logger:warning_msg( - "changing node type; wiping " - "mnesia...~n~n") - end), - rabbit_misc:ensure_ok(mnesia:delete_schema([node()]), - cannot_delete_schema); - _ -> ok - end, + rabbit_misc:local_info_msg("Clustering with ~p~s~n", [ClusterNodes]), - %% Pre-emptively leave the cluster - %% - %% We're trying to handle the following two cases: - %% 1. We have a two-node cluster, where both nodes are disc nodes. - %% One node is re-clustered as a ram node. When it tries to - %% re-join the cluster, but before it has time to update its - %% tables definitions, the other node will order it to re-create - %% its disc tables. So, we need to leave the cluster before we - %% can join it again. - %% 2. We have a two-node cluster, where both nodes are disc nodes. - %% One node is forcefully reset (so, the other node thinks its - %% still a part of the cluster). The reset node is re-clustered - %% as a ram node. Same as above, we need to leave the cluster - %% before we can join it. But, since we don't know if we're in a - %% cluster or not, we just pre-emptively leave it before joining. - ProperClusterNodes = ClusterNodes -- [node()], - ok = leave_cluster(ProperClusterNodes, ProperClusterNodes), + Config = {ClusterNodes, DiscNode}, %% Join the cluster start_mnesia(), @@ -751,20 +735,26 @@ reset(Force) -> case not Force andalso is_clustered() andalso is_only_disc_node(node(), false) of - true -> log_both("no other disc nodes running"); + true -> throw({error, {standalone_ram_node, + "You can't reset a node if it's the only disc " + "node in a cluster. Please convert another node" + " of the cluster to a disc node first."}}); false -> ok end, Node = node(), - Nodes = all_clustered_nodes() -- [Node], case Force of - true -> ok; + true -> + %% mnesia is down, so all_clustered_nodes() will return [node()], so + %% it's useless to try to disconnect from cluster. + []; false -> ensure_mnesia_dir(), start_mnesia(), + Nodes = all_clustered_nodes() -- [Node], RunningNodes = try - %% Force=true here so that reset still works when clustered - %% with a node which is down + %% Force=true here so that reset still works when + %% clustered with a node which is down ok = init_db(read_cluster_nodes_config(), true), running_clustered_nodes() -- [Node] after @@ -772,17 +762,17 @@ reset(Force) -> end, leave_cluster(Nodes, RunningNodes), rabbit_misc:ensure_ok(mnesia:delete_schema([Node]), - cannot_delete_schema) + cannot_delete_schema), + %% We need to make sure that we don't end up in a distributed Erlang + %% system with nodes while not being in an Mnesia cluster with + %% them. We don't handle that well. + [erlang:disconnect_node(N) || N <- Nodes], + ok = delete_cluster_nodes_config() end, - %% We need to make sure that we don't end up in a distributed - %% Erlang system with nodes while not being in an Mnesia cluster - %% with them. We don't handle that well. - [erlang:disconnect_node(N) || N <- Nodes], - ok = delete_cluster_nodes_config(), %% remove persisted messages and any other garbage we find ok = rabbit_file:recursive_delete(filelib:wildcard(dir() ++ "/*")), ok. - + leave_cluster([], _) -> ok; leave_cluster(Nodes, RunningNodes) -> %% find at least one running cluster node and instruct it to -- cgit v1.2.1 From c87507b5a8d2e9d37bddbb6f40bb7e09ceaf3f11 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 14 May 2012 15:28:45 +0100 Subject: "Activating RabbitMQ plugins ..." is no longer particularly true or useful to report here. And clean up some trailing spaces. --- src/rabbit.erl | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 1d54d0b6..669b4053 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -308,14 +308,9 @@ boot() -> ok = prepare(), Plugins = rabbit_plugins:bootstrap_envinronment(), ToBeLoaded = Plugins ++ ?APPS, - - io:format("~nActivating RabbitMQ plugins ...~n"), - ok = app_utils:load_applications(ToBeLoaded), - StartupApps = - app_utils:app_dependency_order(ToBeLoaded, false), + StartupApps = app_utils:app_dependency_order(ToBeLoaded, false), ok = app_utils:start_applications(StartupApps), - ok = print_plugin_info(Plugins) end). -- cgit v1.2.1 From c67ca33aaf86f4f424e29b1a9e98255a43424069 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 14 May 2012 16:15:03 +0100 Subject: Make the list of plugins look a bit more like the boot steps. --- src/rabbit.erl | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 669b4053..f735fbea 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -640,11 +640,14 @@ force_event_refresh() -> %% misc print_plugin_info(Plugins) -> - io:format("~w plugins activated:~n", [length(Plugins)]), - [io:format("* ~s-~s~n", [AppName, - element(2, application:get_key(AppName, vsn))]) - || AppName <- Plugins], - io:nl(). + io:format("~n-- plugins running~n"), + [print_plugin_info(AppName, element(2, application:get_key(AppName, vsn))) + || AppName <- Plugins], + ok. + +print_plugin_info(Plugin, Vsn) -> + Len = 76 - length(Vsn), + io:format("~-" ++ integer_to_list(Len) ++ "s ~s~n", [Plugin, Vsn]). erts_version_check() -> FoundVer = erlang:system_info(version), -- cgit v1.2.1 From cf273fd3fdcee5564b748e77f39322626237894a Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Mon, 14 May 2012 16:57:28 +0100 Subject: change function to find out about cluster nodes See comment above `check_mnesia_running/1' in `rabbit_mnesia'. In short, the function to find out about all/running nodes in the cluster do not work if mnesia is down and the node is a ram node, while we assumed that they would always work. Moreover, the functions to get the cluster disc nodes require mnesia to be up. --- src/rabbit.erl | 3 +- src/rabbit_control.erl | 8 ++- src/rabbit_mnesia.erl | 184 ++++++++++++++++++++++++++++++++++++------------- src/rabbit_upgrade.erl | 10 +-- 4 files changed, 150 insertions(+), 55 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index ea9731b6..bff7af97 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -513,8 +513,7 @@ boot_step_error({error, {timeout_waiting_for_tables, _}}, _Stacktrace) -> case rabbit_mnesia:read_previously_running_nodes() of [] -> {"Timeout contacting cluster nodes. Since RabbitMQ was" " shut down forcefully~nit cannot determine which nodes" - " are timing out. Details on all nodes will~nfollow.~n", - rabbit_mnesia:all_clustered_nodes() -- [node()]}; + " are timing out.~n"}; Ns -> {rabbit_misc:format( "Timeout contacting cluster nodes: ~p.~n", [Ns]), Ns} diff --git a/src/rabbit_control.erl b/src/rabbit_control.erl index 357073c1..dc51b764 100644 --- a/src/rabbit_control.erl +++ b/src/rabbit_control.erl @@ -379,8 +379,14 @@ action(list_parameters, Node, Args = [], _Opts, Inform) -> action(report, Node, _Args, _Opts, Inform) -> io:format("Reporting server status on ~p~n~n", [erlang:universaltime()]), + Nodes = + case unsafe_rpc(Node, rabbit_mnesia, running_clustered_nodes_safe, []) + of + {ok, Res} -> Res; + {error, Reason} -> throw({error, Reason}) + end, [begin ok = action(Action, N, [], [], Inform), io:nl() end || - N <- unsafe_rpc(Node, rabbit_mnesia, running_clustered_nodes, []), + N <- Nodes, Action <- [status, cluster_status, environment]], VHosts = unsafe_rpc(Node, rabbit_vhost, list, []), [print_report(Node, Q) || Q <- ?GLOBAL_QUERIES], diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 78529d3b..324a6df4 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -19,7 +19,8 @@ -export([ensure_mnesia_dir/0, dir/0, status/0, init/0, is_db_empty/0, join_cluster/1, reset/0, force_reset/0, init_db/3, is_clustered/0, - running_clustered_nodes/0, all_clustered_nodes/0, + running_clustered_nodes/0, running_clustered_nodes_safe/0, + all_clustered_nodes/0, all_clustered_nodes_safe/0, empty_ram_only_tables/0, copy_db/1, wait_for_tables/1, create_cluster_nodes_config/1, read_cluster_nodes_config/0, record_running_nodes/0, read_previously_running_nodes/0, @@ -28,6 +29,9 @@ -export([table_names/0]). +%% Used internally in rpc calls, see `discover_nodes/1' +-export([all_clustered_and_disc_nodes/0]). + %% create_tables/0 exported for helping embed RabbitMQ in or alongside %% other mnesia-using Erlang applications, such as ejabberd -export([create_tables/0]). @@ -49,12 +53,15 @@ -spec(init/0 :: () -> 'ok'). -spec(init_db/3 :: (node_config(), boolean(), boolean()) -> 'ok'). -spec(is_db_empty/0 :: () -> boolean()). --spec(join_cluster/1 :: ([node()]) -> 'ok'). +-spec(join_cluster/1 :: ({[node()], boolean()}) -> 'ok'). -spec(reset/0 :: () -> 'ok'). -spec(force_reset/0 :: () -> 'ok'). -spec(is_clustered/0 :: () -> boolean()). -spec(running_clustered_nodes/0 :: () -> [node()]). -spec(all_clustered_nodes/0 :: () -> [node()]). +-spec(running_clustered_nodes_safe/0 :: () -> {'ok', [node()]} | + {'error', any()}). +-spec(all_clustered_nodes_safe/0 :: () -> {'ok', [node()]} | {'error', any()}). -spec(empty_ram_only_tables/0 :: () -> 'ok'). -spec(create_tables/0 :: () -> 'ok'). -spec(copy_db/1 :: (file:filename()) -> rabbit_types:ok_or_error(any())). @@ -76,6 +83,10 @@ %%---------------------------------------------------------------------------- status() -> + RamNode = {error, + {stopped_ram_node, + "This is ram node which is not running, and thus " + "information about the cluster can't be retrieved."}}, [{nodes, case mnesia:system_info(is_running) of yes -> [{Key, Nodes} || {Key, CopyType} <- [{disc_only, disc_only_copies}, @@ -85,13 +96,14 @@ status() -> Nodes = nodes_of_type(CopyType), Nodes =/= [] end]; - no -> case all_clustered_nodes() of - [] -> []; - Nodes -> [{unknown, Nodes}] + no -> case all_clustered_nodes_safe() of + {ok, Nodes} -> [{unknown, Nodes}]; + {error, _Reason} -> exit(RamNode) end; Reason when Reason =:= starting; Reason =:= stopping -> exit({rabbit_busy, try_again_later}) end}, + %% If we reached this point running_clustered_nodes() is safe {running_nodes, running_clustered_nodes()}]. init() -> @@ -116,10 +128,7 @@ is_db_empty() -> %% node. If Force is false, only connections to online nodes are %% allowed. join_cluster({DiscoveryNodes, DiscNode}) -> - ensure_mnesia_not_running(), - ensure_mnesia_dir(), - - case is_only_disc_node(node(), false) andalso not DiscNode of + case is_disc_and_clustered() andalso is_only_disc_node(node(), false) of true -> throw({error, {standalone_ram_node, "You can't cluster a node if it's the only " @@ -127,11 +136,14 @@ join_cluster({DiscoveryNodes, DiscNode}) -> _ -> ok end, + ensure_mnesia_not_running(), + ensure_mnesia_dir(), + ProperDiscoveryNodes = DiscoveryNodes -- [node()], - ClusterNodes = case discover_cluster(ProperDiscoveryNodes) of - {ok, ClusterNodes0} -> ClusterNodes0; - {error, Reason} -> throw({error, Reason}) - end, + {ClusterNodes, DiscNodes} = case discover_cluster(ProperDiscoveryNodes) of + {ok, Res} -> Res; + {error, Reason} -> throw({error, Reason}) + end, case lists:member(node(), ClusterNodes) of true -> throw({error, {already_clustered, @@ -148,7 +160,7 @@ join_cluster({DiscoveryNodes, DiscNode}) -> rabbit_misc:local_info_msg("Clustering with ~p~s~n", [ClusterNodes]), - Config = {ClusterNodes, DiscNode}, + Config = {DiscNodes, DiscNode}, %% Join the cluster start_mnesia(), @@ -167,20 +179,85 @@ join_cluster({DiscoveryNodes, DiscNode}) -> reset() -> reset(false). force_reset() -> reset(true). +%% This function will fail if mnesia is not running and the node is a ram node. is_clustered() -> RunningNodes = running_clustered_nodes(), [node()] /= RunningNodes andalso [] /= RunningNodes. +%% This function exists since we often want to check if the node is clustered +%% only if the node is a disc node as well, and so we can call `is_clustered/0' +%% safely. +is_disc_and_clustered() -> + is_disc_node() andalso is_clustered(). + +%% The situations with functions that retrieve the nodes in the cluster is +%% messy. +%% +%% * If we want to get all the nodes or the running nodes, we can do that +%% while mnesia is offline *if* the node is a disc node. If the node is ram, +%% the result will always be [node()]. +%% `all_clustered_nodes/0' and `running_clustered_nodes/0' will fail if +%% these conditions are not met. `all_clustered_nodes_safe/0' and +%% `running_clustered_nodes_safe/0' won't, but can return an error. +%% +%% * If we want to get the cluster disc nodes (running or not), we need to +%% start mnesia in any case. All the functions related to disc nodes are +%% "safe", in the sense that they should never crash and return either the +%% nodes or an error (much like the _safe function for all the nodes). + +check_mnesia_running(Fun) -> + case mnesia:system_info(is_running) of + yes -> {ok, Fun()}; + no -> {error, {mnesia_not_running, node()}} + end. + +check_disc_or_mnesia_running(Fun) -> + case is_disc_node() of + true -> + {ok, Fun()}; + false -> + case check_mnesia_running(Fun) of + {ok, Res} -> {ok, Res}; + {error, _Reason} -> {error, + {mnesia_not_running, + "Mnesia is not running and this node is " + "a ram node", node()}} + end + end. + +all_clustered_nodes_safe() -> + check_disc_or_mnesia_running(fun () -> mnesia:system_info(db_nodes) end). + all_clustered_nodes() -> - mnesia:system_info(db_nodes). + {ok, Nodes} = all_clustered_nodes_safe(), + Nodes. + +all_clustered_disc_nodes() -> + check_mnesia_running(fun () -> nodes_of_type(disc_copies) end). + +running_clustered_nodes_safe() -> + check_disc_or_mnesia_running( + fun () -> mnesia:system_info(running_db_nodes) end). running_clustered_nodes() -> - mnesia:system_info(running_db_nodes). + {ok, Nodes} = running_clustered_nodes_safe(), + Nodes. running_clustered_disc_nodes() -> - RunningSet = sets:from_list(running_clustered_nodes()), - DiscSet = sets:from_list(nodes_of_type(disc_copies)), - sets:to_list(sets:intersection(RunningSet, DiscSet)). + check_mnesia_running( + fun () -> + Running = running_clustered_nodes(), + {ok, Disc} = all_clustered_disc_nodes(), + sets:to_list(sets:intersection(sets:from_list(Running), + sets:from_list(Disc))) + end). + +all_clustered_and_disc_nodes() -> + check_mnesia_running( + fun () -> + {ok, DiscNodes} = all_clustered_disc_nodes(), + {all_clustered_nodes(), DiscNodes} + end). empty_ram_only_tables() -> Node = node(), @@ -194,11 +271,13 @@ empty_ram_only_tables() -> ok. discover_cluster([]) -> - {error, cannot_discover_cluster}; + {error, {cannot_discover_cluster, + "The cluster nodes provided are either offline or not running."}}; discover_cluster([Node | Nodes]) -> - case rpc:call(Node, rabbit_mnesia, all_clustered_nodes, []) of + case rpc:call(Node, rabbit_mnesia, all_clustered_and_disc_nodes, []) of {badrpc, _Reason} -> discover_cluster(Nodes); - Res -> {ok, Res} + {error, _Reason} -> discover_cluster(Nodes); + {ok, Res} -> {ok, Res} end. %%-------------------------------------------------------------------- @@ -449,7 +528,7 @@ create_cluster_nodes_config(Config) -> read_cluster_nodes_config() -> Convert = fun (Config = {_, _}) -> Config; (ClusterNodes) -> - rabbit_log:warning("reading legacy node config"), + log_both("reading legacy node config"), {ClusterNodes, should_be_disc_node_legacy(ClusterNodes)} end, FileName = cluster_nodes_config_filename(), @@ -728,11 +807,12 @@ wait_for_tables(TableNames) -> end. reset(Force) -> - rabbit_misc:local_info_msg("Resetting Rabbit~s~n", [if Force -> " forcefully"; - true -> "" - end]), + rabbit_misc:local_info_msg("Resetting Rabbit~s~n", + [if Force -> " forcefully"; + true -> "" + end]), ensure_mnesia_not_running(), - case not Force andalso is_clustered() andalso + case not Force andalso is_disc_and_clustered() andalso is_only_disc_node(node(), false) of true -> throw({error, {standalone_ram_node, @@ -742,32 +822,39 @@ reset(Force) -> false -> ok end, Node = node(), - case Force of - true -> - %% mnesia is down, so all_clustered_nodes() will return [node()], so - %% it's useless to try to disconnect from cluster. - []; - false -> - ensure_mnesia_dir(), - start_mnesia(), - Nodes = all_clustered_nodes() -- [Node], - RunningNodes = - try - %% Force=true here so that reset still works when - %% clustered with a node which is down - ok = init_db(read_cluster_nodes_config(), true), - running_clustered_nodes() -- [Node] - after - stop_mnesia() - end, - leave_cluster(Nodes, RunningNodes), + MaybeNodes = + case Force of + true -> + all_clustered_nodes_safe(); + false -> + ensure_mnesia_dir(), + start_mnesia(), + Nodes0 = all_clustered_nodes(), + RunningNodes = + try + %% Force=true here so that reset still works when + %% clustered with a node which is down + ok = init_db(read_cluster_nodes_config(), true), + running_clustered_nodes() -- [Node] + after + stop_mnesia() + end, + leave_cluster(Nodes0, RunningNodes), rabbit_misc:ensure_ok(mnesia:delete_schema([Node]), cannot_delete_schema), + {ok, Nodes0} + end, + case MaybeNodes of + {ok, Nodes} -> %% We need to make sure that we don't end up in a distributed Erlang %% system with nodes while not being in an Mnesia cluster with %% them. We don't handle that well. [erlang:disconnect_node(N) || N <- Nodes], - ok = delete_cluster_nodes_config() + ok = delete_cluster_nodes_config(); + {error, _Reason} -> + log_both("Since this ram node is being force reseted, " + "the node hasn't been disconnected from the " + "cluster correctly, bad things might happen.") end, %% remove persisted messages and any other garbage we find ok = rabbit_file:recursive_delete(filelib:wildcard(dir() ++ "/*")), @@ -814,7 +901,8 @@ on_node_down(Node) -> end. is_only_disc_node(Node, _MnesiaRunning = true) -> - [Node] =:= running_clustered_disc_nodes(); + {ok, Nodes} = running_clustered_disc_nodes(), + [Node] =:= Nodes; is_only_disc_node(Node, false) -> start_mnesia(), Res = is_only_disc_node(Node, true), diff --git a/src/rabbit_upgrade.erl b/src/rabbit_upgrade.erl index 27312990..0b3413f1 100644 --- a/src/rabbit_upgrade.erl +++ b/src/rabbit_upgrade.erl @@ -121,10 +121,12 @@ remove_backup() -> info("upgrades: Mnesia backup removed~n", []). maybe_upgrade_mnesia() -> - %% rabbit_mnesia:all_clustered_nodes/0 will return [] at this point - %% if we are a RAM node since Mnesia has not started yet. - {ClusterNodes, _DiscNode} = rabbit_mnesia:read_cluster_nodes_config(), - AllNodes = lists:usort(rabbit_mnesia:all_clustered_nodes() ++ ClusterNodes), + {ClusterNodes1, _DiscNode} = rabbit_mnesia:read_cluster_nodes_config(), + ClusterNodes2 = case rabbit_mnesia:all_clustered_nodes() of + {ok, Res} -> Res; + {error, _Reason} -> [] + end, + AllNodes = lists:usort(ClusterNodes1 ++ ClusterNodes2), case rabbit_version:upgrades_required(mnesia) of {error, starting_from_scratch} -> ok; -- cgit v1.2.1 From da7008581513307955db6c65f0fca7b657129b15 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Mon, 14 May 2012 18:08:29 +0100 Subject: fix leave_cluster, update rabbit_app.in --- ebin/rabbit_app.in | 2 +- src/rabbit_mnesia.erl | 58 +++++++++++++++++++++++++++----------------------- src/rabbit_upgrade.erl | 2 +- 3 files changed, 33 insertions(+), 29 deletions(-) diff --git a/ebin/rabbit_app.in b/ebin/rabbit_app.in index b7d14f20..d4a3a118 100644 --- a/ebin/rabbit_app.in +++ b/ebin/rabbit_app.in @@ -30,7 +30,7 @@ {default_user_tags, [administrator]}, {default_vhost, <<"/">>}, {default_permissions, [<<".*">>, <<".*">>, <<".*">>]}, - {cluster_nodes, []}, + {cluster_nodes, {[], true}}, {server_properties, []}, {collect_statistics, none}, {collect_statistics_interval, 5000}, diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 324a6df4..45131536 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -835,14 +835,14 @@ reset(Force) -> %% Force=true here so that reset still works when %% clustered with a node which is down ok = init_db(read_cluster_nodes_config(), true), - running_clustered_nodes() -- [Node] + running_clustered_nodes() after stop_mnesia() end, - leave_cluster(Nodes0, RunningNodes), - rabbit_misc:ensure_ok(mnesia:delete_schema([Node]), - cannot_delete_schema), - {ok, Nodes0} + leave_cluster(Nodes0, RunningNodes), + rabbit_misc:ensure_ok(mnesia:delete_schema([Node]), + cannot_delete_schema), + {ok, Nodes0} end, case MaybeNodes of {ok, Nodes} -> @@ -859,29 +859,33 @@ reset(Force) -> %% remove persisted messages and any other garbage we find ok = rabbit_file:recursive_delete(filelib:wildcard(dir() ++ "/*")), ok. - + leave_cluster([], _) -> ok; -leave_cluster(Nodes, RunningNodes) -> - %% find at least one running cluster node and instruct it to - %% remove our schema copy which will in turn result in our node - %% being removed as a cluster node from the schema, with that - %% change being propagated to all nodes - case lists:any( - fun (Node) -> - case rpc:call(Node, mnesia, del_table_copy, - [schema, node()]) of - {atomic, ok} -> true; - {badrpc, nodedown} -> false; - {aborted, {node_not_running, _}} -> false; - {aborted, Reason} -> - throw({error, {failed_to_leave_cluster, - Nodes, RunningNodes, Reason}}) - end - end, - RunningNodes) of - true -> ok; - false -> throw({error, {no_running_cluster_nodes, - Nodes, RunningNodes}}) +leave_cluster(Nodes, RunningNodes0) -> + case RunningNodes0 -- [node()] of + [] -> ok; + RunningNodes -> + %% find at least one running cluster node and instruct it to remove + %% our schema copy which will in turn result in our node being + %% removed as a cluster node from the schema, with that change being + %% propagated to all nodes + case lists:any( + fun (Node) -> + case rpc:call(Node, mnesia, del_table_copy, + [schema, node()]) of + {atomic, ok} -> true; + {badrpc, nodedown} -> false; + {aborted, {node_not_running, _}} -> false; + {aborted, Reason} -> + throw({error, {failed_to_leave_cluster, + Nodes, RunningNodes, Reason}}) + end + end, + RunningNodes) of + true -> ok; + false -> throw({error, {no_running_cluster_nodes, + Nodes, RunningNodes}}) + end end. wait_for(Condition) -> diff --git a/src/rabbit_upgrade.erl b/src/rabbit_upgrade.erl index 0b3413f1..f20c2308 100644 --- a/src/rabbit_upgrade.erl +++ b/src/rabbit_upgrade.erl @@ -122,7 +122,7 @@ remove_backup() -> maybe_upgrade_mnesia() -> {ClusterNodes1, _DiscNode} = rabbit_mnesia:read_cluster_nodes_config(), - ClusterNodes2 = case rabbit_mnesia:all_clustered_nodes() of + ClusterNodes2 = case rabbit_mnesia:all_clustered_nodes_safe() of {ok, Res} -> Res; {error, _Reason} -> [] end, -- cgit v1.2.1 From 3975186c7ff7023a08f6e40a570d9f7549aa4ec2 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Tue, 15 May 2012 16:20:30 +0100 Subject: rabbit_control waits as long as process is alive --- scripts/rabbitmq-server | 25 +++++++++++-------------- scripts/rabbitmq-server.bat | 14 ++++++++------ src/rabbit_control.erl | 6 ++++-- 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/scripts/rabbitmq-server b/scripts/rabbitmq-server index 57f37ff9..ba18766c 100755 --- a/scripts/rabbitmq-server +++ b/scripts/rabbitmq-server @@ -70,18 +70,15 @@ case "$(uname -s)" in esac RABBITMQ_EBIN_ROOT="${RABBITMQ_HOME}/ebin" -if erl \ - -pa "$RABBITMQ_EBIN_ROOT" \ - -noinput \ - -hidden \ - -s rabbit_prelaunch \ - -sname rabbitmqprelaunch$$ \ - -extra "${RABBITMQ_NODENAME}" -then - RABBITMQ_BOOT_FILE=start_sasl - RABBITMQ_EBIN_PATH="-pa ${RABBITMQ_EBIN_ROOT}" -else - exit 1 +if ! `erl \ + -pa "$RABBITMQ_EBIN_ROOT" \ + -noinput \ + -hidden \ + -s rabbit_prelaunch \ + -sname rabbitmqprelaunch$$ \ + -extra "${RABBITMQ_NODENAME}"`; + then + exit 1; fi RABBITMQ_CONFIG_ARG= @@ -96,10 +93,10 @@ RABBITMQ_LISTEN_ARG= set -f exec erl \ - ${RABBITMQ_EBIN_PATH} \ + -pa ${RABBITMQ_EBIN_ROOT} \ ${RABBITMQ_START_RABBIT} \ -sname ${RABBITMQ_NODENAME} \ - -boot ${RABBITMQ_BOOT_FILE} \ + -boot start_sasl \ -s rabbit boot \ ${RABBITMQ_CONFIG_ARG} \ +W w \ diff --git a/scripts/rabbitmq-server.bat b/scripts/rabbitmq-server.bat index e594522d..58f085af 100755 --- a/scripts/rabbitmq-server.bat +++ b/scripts/rabbitmq-server.bat @@ -89,12 +89,14 @@ if "!RABBITMQ_ENABLED_PLUGINS_FILE!"=="" ( set RABBITMQ_PLUGINS_DIR=!TDP0!..\plugins set RABBITMQ_EBIN_ROOT=!TDP0!..\ebin -"!ERLANG_HOME!\bin\erl.exe" ^ --pa "!RABBITMQ_EBIN_ROOT!" ^ --noinput -hidden ^ --s rabbit_prelaunch ^ --sname rabbitmqprelaunch!RANDOM! ^ --extra "!RABBITMQ_NODENAME!" +if not "!ERLANG_HOME!\bin\erl.exe" ^ + -pa "!RABBITMQ_EBIN_ROOT!" ^ + -noinput -hidden ^ + -s rabbit_prelaunch ^ + -sname rabbitmqprelaunch!RANDOM! ^ + -extra "!RABBITMQ_NODENAME!" ( + exit /B +) set RABBITMQ_EBIN_PATH="-pa !RABBITMQ_EBIN_ROOT!" diff --git a/src/rabbit_control.erl b/src/rabbit_control.erl index d9b2ae97..59df82b3 100644 --- a/src/rabbit_control.erl +++ b/src/rabbit_control.erl @@ -408,8 +408,10 @@ wait_for_application(Node, PidFile, Application, Inform) -> wait_for_application(Node, Pid, Application) -> case process_up(Pid) of true -> case rpc:call(Node, rabbit, await_startup, []) of - {badrpc, _} -> {error, node_not_responding}; - ok -> ok + ok -> ok; + Other -> timer:sleep(?EXTERNAL_CHECK_INTERVAL), + wait_for_application(Node, Pid, + Application) end; false -> {error, process_not_running} end. -- cgit v1.2.1 From 294e9f7061363bf6c0d4a2db1aee8b37bbd3269e Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Tue, 15 May 2012 16:40:12 +0100 Subject: preserve original 'wait on any app' functionality --- src/rabbit_control.erl | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/rabbit_control.erl b/src/rabbit_control.erl index 59df82b3..0e9e034a 100644 --- a/src/rabbit_control.erl +++ b/src/rabbit_control.erl @@ -406,13 +406,20 @@ wait_for_application(Node, PidFile, Application, Inform) -> wait_for_application(Node, Pid, Application). wait_for_application(Node, Pid, Application) -> + while_process_is_alive(Node, Pid, + fun() -> rabbit_nodes:is_running(Node, Application) end). + +wait_for_startup(Node, Pid) -> + while_process_is_alive(Node, Pid, + fun() -> rpc:call(Node, rabbit, await_startup, []) end). + +while_process_is_alive(Node, Pid, Activity) -> case process_up(Pid) of - true -> case rpc:call(Node, rabbit, await_startup, []) of + true -> case Activity(Node) of ok -> ok; Other -> timer:sleep(?EXTERNAL_CHECK_INTERVAL), - wait_for_application(Node, Pid, - Application) - end; + while_process_is_alive(Node, Pid, Activity) + end; false -> {error, process_not_running} end. -- cgit v1.2.1 From c0da1632ca9332d682014585ec0747685d55d21a Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Tue, 15 May 2012 16:44:20 +0100 Subject: remove vestigial ERROR_CODE define and dangling TODO from rabbit_misc --- src/rabbit_misc.erl | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 955d769c..ae20ea83 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -87,7 +87,6 @@ -spec(die/1 :: (rabbit_framing:amqp_exception()) -> channel_or_connection_exit()). -%% TODO: figure out what the return types should actually be for these... -spec(terminate/1 :: (integer()) -> any()). -spec(terminate/2 :: (string(), integer()) -> any()). @@ -214,8 +213,6 @@ -endif. --define(ERROR_CODE, 1). - %%---------------------------------------------------------------------------- method_record_type(Record) -> -- cgit v1.2.1 From e3bcf88ed268998626ec5aff913b5b58e748c5a9 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Tue, 15 May 2012 16:53:50 +0100 Subject: remove ref to old define; document terminate/1 vs quit/1 --- src/rabbit_misc.erl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index ae20ea83..cc1417e9 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -390,8 +390,11 @@ confirm_to_sender(Pid, MsgSeqNos) -> terminate(Fmt, Args) -> io:format("ERROR: " ++ Fmt ++ "~n", Args), - terminate(?ERROR_CODE). + terminate(1). +%% like quit/1, uses a slower shutdown on windows +%% (required to flush stdout), however terminate/1 also blocks +%% indefinitely until the flush has completed. terminate(Status) -> case os:type() of {unix, _} -> halt(Status); -- cgit v1.2.1 From 770ce1320cb73faac9921ef833dca71bdac8f10d Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Tue, 15 May 2012 18:04:32 +0100 Subject: store more info about the cluster on disc, check other nodes before clustering Now the `cluster_nodes.config; doesn't store a "config" anymore, but all the information we need about the cluster nodes. This file is updated whenever a new node comes up. Moreover, this file should be *always* present, and it's set up in `rabbit:prepare/0'. Now that we have this file, the various functions regarding the status of the cluster (`all_clustered_nodes/0', `running_clustered_nodes/0;, etc.) can "work" even when mnesia is down (and when the rode is a ram node). Given the assumption that the status file is up to date, with the status of the cluster (before the node went down if the node is down) a lot of operations become easier. I'd like a review at this point, the best thing is probably to diff directly with default since a lot of previous commits are not relevant anymore. The most noticeable changes compared to default: * `join_cluster' works differently. The nodes provided will be used to "discover" the cluster, and are not used to determine whether the node should be ram or not. The node will be a disc node by default, and can be initialized as RAM with the `--ram' flag. The old `cluster' command is preserved, adapted to work with the new functions while preserving similar semantics (we decide on whether the node is disc or not by looking at the list of nodes provided). * `force_cluster' has been removed. * The `join_cluster' operation will fail if: - The node is currently the only disc node of its cluster - We can't connect to any of the nodes provided - The node is currently already clustered with the cluster of the nodes provided * On default restart RAM nodes try to connect to the nodes that were first given by the user, and are not aware of the changes that might have occurred in the cluster when they were online. Since the cluster status is kept updated on disk now, the RAM node will be aware of changes in the cluster when restarted. * Before starting up mnesia, the node contacts the nodes it thinks it's clustered with, and if the nodes are not clustered with the node anymore the startup procedure fail. We fail only when we know for sure that something is wrong - e.g. it won't fail it it doesn't find any online node Things to do: * Implement `uncluster'/`leave_cluster' to kick out a node from a cluster from another node - this is easy. * Implement something like `change_node_type', since given how `join_cluster' works it is not possible right now. * Rewrite the tests regarding to clustering. * Think hard about what can go wrong regarding the cluster status file and the relevant functions. The stuff in `rabbit_upgrade' is particularly worrying, and I need to make sure that things will work when upgrading rabbitmq, by reading old file or upgrading them. * Split `init_db/4' in various functions. We have much stronger assumptions now, for example we should never need to reset or wipe the schema in there. In general it's an ugly function, expecially the optional upgrade part * Probably something else... --- src/rabbit.erl | 10 +- src/rabbit_control.erl | 9 +- src/rabbit_mnesia.erl | 431 +++++++++++++++++++++++++------------------------ src/rabbit_upgrade.erl | 29 +--- 4 files changed, 239 insertions(+), 240 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index bff7af97..eff2fac2 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -285,6 +285,9 @@ split0([I | Is], [L | Ls]) -> split0(Is, Ls ++ [[I | L]]). prepare() -> ok = ensure_working_log_handlers(), + ok = rabbit_mnesia:ensure_mnesia_dir(), + ok = rabbit_mnesia:initialize_cluster_nodes_status(), + ok = rabbit_mnesia:check_cluster_consistency(), ok = rabbit_upgrade:maybe_upgrade_mnesia(). start() -> @@ -380,7 +383,7 @@ start(normal, []) -> end. stop(_State) -> - ok = rabbit_mnesia:record_running_nodes(), + ok = rabbit_mnesia:update_cluster_nodes_status(), terminated_ok = error_logger:delete_report_handler(rabbit_error_logger), ok = rabbit_alarm:stop(), ok = case rabbit_mnesia:is_clustered() of @@ -509,11 +512,12 @@ sort_boot_steps(UnsortedSteps) -> end. boot_step_error({error, {timeout_waiting_for_tables, _}}, _Stacktrace) -> + {AllNodes, _, _} = rabbit_mnesia:read_cluster_nodes_status(), {Err, Nodes} = - case rabbit_mnesia:read_previously_running_nodes() of + case AllNodes -- [node()] of [] -> {"Timeout contacting cluster nodes. Since RabbitMQ was" " shut down forcefully~nit cannot determine which nodes" - " are timing out.~n"}; + " are timing out.~n", []}; Ns -> {rabbit_misc:format( "Timeout contacting cluster nodes: ~p.~n", [Ns]), Ns} diff --git a/src/rabbit_control.erl b/src/rabbit_control.erl index dc51b764..478155a1 100644 --- a/src/rabbit_control.erl +++ b/src/rabbit_control.erl @@ -185,16 +185,15 @@ action(force_reset, Node, [], _Opts, Inform) -> action(cluster, Node, ClusterNodeSs, _Opts, Inform) -> io:format("'cluster' is deprecated, please us 'join_cluster'.~n"), ClusterNodes = lists:map(fun list_to_atom/1, ClusterNodeSs), - DiscNode = rabbit_mnesia:should_be_disc_node_legacy(ClusterNodes), - Inform("Clustering node ~p with ~p", - [Node, ClusterNodes]), - rpc_call(Node, rabbit_mnesia, join_cluster, [{ClusterNodes, DiscNode}]); + DiscNode = rabbit_mnesia:should_be_disc_node(ClusterNodes), + Inform("Clustering node ~p with ~p", [Node, ClusterNodes]), + rpc_call(Node, rabbit_mnesia, join_cluster, [ClusterNodes, DiscNode]); action(join_cluster, Node, ClusterNodeSs, Opts, Inform) -> ClusterNodes = lists:map(fun list_to_atom/1, ClusterNodeSs), DiscNode = not proplists:get_bool(?RAM_OPT, Opts), Inform("Clustering node ~p with ~p", [Node, ClusterNodes]), - rpc_call(Node, rabbit_mnesia, join_cluster, [{ClusterNodes, DiscNode}]); + rpc_call(Node, rabbit_mnesia, join_cluster, [ClusterNodes, DiscNode]); action(wait, Node, [PidFile], _Opts, Inform) -> Inform("Waiting for ~p", [Node]), diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 45131536..8427ae2a 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -18,19 +18,18 @@ -module(rabbit_mnesia). -export([ensure_mnesia_dir/0, dir/0, status/0, init/0, is_db_empty/0, - join_cluster/1, reset/0, force_reset/0, init_db/3, is_clustered/0, - running_clustered_nodes/0, running_clustered_nodes_safe/0, - all_clustered_nodes/0, all_clustered_nodes_safe/0, - empty_ram_only_tables/0, copy_db/1, wait_for_tables/1, - create_cluster_nodes_config/1, read_cluster_nodes_config/0, - record_running_nodes/0, read_previously_running_nodes/0, - running_nodes_filename/0, is_disc_node/0, on_node_down/1, on_node_up/1, - should_be_disc_node_legacy/1]). + join_cluster/2, check_cluster_consistency/0, reset/0, force_reset/0, + init_db/4, is_clustered/0, running_clustered_nodes/0, + all_clustered_nodes/0, empty_ram_only_tables/0, copy_db/1, + wait_for_tables/1, initialize_cluster_nodes_status/0, + write_cluster_nodes_status/1, read_cluster_nodes_status/0, + update_cluster_nodes_status/0, is_disc_node/0, on_node_down/1, + on_node_up/1, should_be_disc_node/1]). -export([table_names/0]). %% Used internally in rpc calls, see `discover_nodes/1' --export([all_clustered_and_disc_nodes/0]). +-export([cluster_status_if_running/0]). %% create_tables/0 exported for helping embed RabbitMQ in or alongside %% other mnesia-using Erlang applications, such as ejabberd @@ -42,39 +41,35 @@ -ifdef(use_specs). --export_type([node_type/0, node_config/0]). +-export_type([node_type/0, node_status/0]). -type(node_type() :: disc_only | disc | ram | unknown). --type(node_config() :: {[node()], boolean()}). +-type(node_status() :: {[node()], [node()], [node()]}). -spec(status/0 :: () -> [{'nodes', [{node_type(), [node()]}]} | {'running_nodes', [node()]}]). -spec(dir/0 :: () -> file:filename()). -spec(ensure_mnesia_dir/0 :: () -> 'ok'). -spec(init/0 :: () -> 'ok'). --spec(init_db/3 :: (node_config(), boolean(), boolean()) -> 'ok'). +-spec(init_db/4 :: ([node()], boolean(), boolean(), boolean()) -> 'ok'). -spec(is_db_empty/0 :: () -> boolean()). --spec(join_cluster/1 :: ({[node()], boolean()}) -> 'ok'). +-spec(join_cluster/2 :: ([node()], boolean()) -> 'ok'). -spec(reset/0 :: () -> 'ok'). -spec(force_reset/0 :: () -> 'ok'). -spec(is_clustered/0 :: () -> boolean()). -spec(running_clustered_nodes/0 :: () -> [node()]). -spec(all_clustered_nodes/0 :: () -> [node()]). --spec(running_clustered_nodes_safe/0 :: () -> {'ok', [node()]} | - {'error', any()}). --spec(all_clustered_nodes_safe/0 :: () -> {'ok', [node()]} | {'error', any()}). -spec(empty_ram_only_tables/0 :: () -> 'ok'). -spec(create_tables/0 :: () -> 'ok'). -spec(copy_db/1 :: (file:filename()) -> rabbit_types:ok_or_error(any())). -spec(wait_for_tables/1 :: ([atom()]) -> 'ok'). --spec(create_cluster_nodes_config/1 :: (node_config()) -> 'ok'). --spec(read_cluster_nodes_config/0 :: () -> node_config()). --spec(record_running_nodes/0 :: () -> 'ok'). --spec(read_previously_running_nodes/0 :: () -> [node()]). --spec(running_nodes_filename/0 :: () -> file:filename()). +-spec(write_cluster_nodes_status/1 :: (node_status()) -> 'ok'). +-spec(read_cluster_nodes_status/0 :: () -> node_status()). +-spec(initialize_cluster_nodes_status/0 :: () -> 'ok'). +-spec(update_cluster_nodes_status/0 :: () -> 'ok'). -spec(is_disc_node/0 :: () -> boolean()). -spec(on_node_up/1 :: (node()) -> 'ok'). -spec(on_node_down/1 :: (node()) -> 'ok'). --spec(should_be_disc_node_legacy/1 :: ([node()]) -> boolean()). +-spec(should_be_disc_node/1 :: ([node()]) -> boolean()). -spec(table_names/0 :: () -> [atom()]). @@ -83,10 +78,6 @@ %%---------------------------------------------------------------------------- status() -> - RamNode = {error, - {stopped_ram_node, - "This is ram node which is not running, and thus " - "information about the cluster can't be retrieved."}}, [{nodes, case mnesia:system_info(is_running) of yes -> [{Key, Nodes} || {Key, CopyType} <- [{disc_only, disc_only_copies}, @@ -96,39 +87,42 @@ status() -> Nodes = nodes_of_type(CopyType), Nodes =/= [] end]; - no -> case all_clustered_nodes_safe() of - {ok, Nodes} -> [{unknown, Nodes}]; - {error, _Reason} -> exit(RamNode) - end; + no -> [{unknown, all_clustered_nodes()}]; Reason when Reason =:= starting; Reason =:= stopping -> exit({rabbit_busy, try_again_later}) end}, - %% If we reached this point running_clustered_nodes() is safe {running_nodes, running_clustered_nodes()}]. init() -> ensure_mnesia_running(), ensure_mnesia_dir(), - Config = {_, DiscNode} = read_cluster_nodes_config(), - ok = init_db(Config, DiscNode), + {DiscNodes, WantDiscNode} = read_cluster_nodes_config(), + ok = init_db(DiscNodes, WantDiscNode, WantDiscNode), %% We intuitively expect the global name server to be synced when %% Mnesia is up. In fact that's not guaranteed to be the case - let's %% make it so. ok = global:sync(), - ok = delete_previously_running_nodes(), ok. is_db_empty() -> lists:all(fun (Tab) -> mnesia:dirty_first(Tab) == '$end_of_table' end, table_names()). -%% Alter which disk nodes this node is clustered with. This can be a -%% subset of all the disk nodes in the cluster but can (and should) -%% include the node itself if it is to be a disk rather than a ram -%% node. If Force is false, only connections to online nodes are -%% allowed. -join_cluster({DiscoveryNodes, DiscNode}) -> - case is_disc_and_clustered() andalso is_only_disc_node(node(), false) of +%% Make the node join a cluster. The node will be reset automatically before we +%% actually cluster it. The nodes provided will be used to find out about the +%% nodes in the cluster. +%% This function will fail if: +%% +%% * The node is currently the only disc node of its cluster +%% * We can't connect to any of the nodes provided +%% * The node is currently already clustered with the cluster of the nodes +%% provided +%% +%% Note that we make no attempt to verify that the nodes provided are all in the +%% same cluster, we simply pick the first online node and we cluster to its +%% cluster. +join_cluster(DiscoveryNodes, WantDiscNode) -> + case is_disc_and_clustered() andalso is_only_disc_node(node()) of true -> throw({error, {standalone_ram_node, "You can't cluster a node if it's the only " @@ -140,10 +134,11 @@ join_cluster({DiscoveryNodes, DiscNode}) -> ensure_mnesia_dir(), ProperDiscoveryNodes = DiscoveryNodes -- [node()], - {ClusterNodes, DiscNodes} = case discover_cluster(ProperDiscoveryNodes) of - {ok, Res} -> Res; - {error, Reason} -> throw({error, Reason}) - end, + {ClusterNodes, DiscNodes, _} = + case discover_cluster(ProperDiscoveryNodes) of + {ok, Res} -> Res; + {error, Reason} -> throw({error, Reason}) + end, case lists:member(node(), ClusterNodes) of true -> throw({error, {already_clustered, @@ -160,13 +155,10 @@ join_cluster({DiscoveryNodes, DiscNode}) -> rabbit_misc:local_info_msg("Clustering with ~p~s~n", [ClusterNodes]), - Config = {DiscNodes, DiscNode}, - %% Join the cluster start_mnesia(), try - ok = init_db(Config, false), - ok = create_cluster_nodes_config(Config) + ok = init_db(DiscNodes, WantDiscNode, false) after stop_mnesia() end, @@ -179,14 +171,10 @@ join_cluster({DiscoveryNodes, DiscNode}) -> reset() -> reset(false). force_reset() -> reset(true). -%% This function will fail if mnesia is not running and the node is a ram node. is_clustered() -> RunningNodes = running_clustered_nodes(), [node()] /= RunningNodes andalso [] /= RunningNodes. -%% This function exists since we often want to check if the node is clustered -%% only if the node is a disc node as well, and so we can call `is_clustered/0' -%% safely. is_disc_and_clustered() -> is_disc_node() andalso is_clustered(). @@ -196,67 +184,84 @@ is_disc_and_clustered() -> %% * If we want to get all the nodes or the running nodes, we can do that %% while mnesia is offline *if* the node is a disc node. If the node is ram, %% the result will always be [node()]. -%% `all_clustered_nodes/0' and `running_clustered_nodes/0' will fail if -%% these conditions are not met. `all_clustered_nodes_safe/0' and -%% `running_clustered_nodes_safe/0' won't, but can return an error. -%% %% * If we want to get the cluster disc nodes (running or not), we need to -%% start mnesia in any case. All the functions related to disc nodes are -%% "safe", in the sense that they should never crash and return either the -%% nodes or an error (much like the _safe function for all the nodes). +%% start mnesia in any case. +%% +%% In the following functions we try to get the data from mnesia when we can, +%% otherwise we fall back to the cluster status file. check_mnesia_running(Fun) -> case mnesia:system_info(is_running) of yes -> {ok, Fun()}; - no -> {error, {mnesia_not_running, node()}} + no -> error end. check_disc_or_mnesia_running(Fun) -> case is_disc_node() of - true -> - {ok, Fun()}; - false -> - case check_mnesia_running(Fun) of - {ok, Res} -> {ok, Res}; - {error, _Reason} -> {error, - {mnesia_not_running, - "Mnesia is not running and this node is " - "a ram node", node()}} - end + true -> {ok, Fun()}; + false -> case check_mnesia_running(Fun) of + {ok, Res} -> {ok, Res}; + error -> error + end end. -all_clustered_nodes_safe() -> - check_disc_or_mnesia_running(fun () -> mnesia:system_info(db_nodes) end). +check_or_cluster_status(Fun, Check) -> + case Check(Fun) of + {ok, Res} -> {ok, Res}; + error -> {status, read_cluster_nodes_status()} + end. all_clustered_nodes() -> - {ok, Nodes} = all_clustered_nodes_safe(), - Nodes. + case check_or_cluster_status( + fun () -> mnesia:system_info(db_nodes) end, + fun check_disc_or_mnesia_running/1) + of + {ok, Nodes} -> Nodes; + {status, {Nodes, _, _}} -> Nodes + end. all_clustered_disc_nodes() -> - check_mnesia_running(fun () -> nodes_of_type(disc_copies) end). - -running_clustered_nodes_safe() -> - check_disc_or_mnesia_running( - fun () -> mnesia:system_info(running_db_nodes) end). + case check_or_cluster_status( + fun () -> nodes_of_type(disc_copies) end, + fun check_mnesia_running/1) + of + {ok, Nodes} -> Nodes; + {status, {_, Nodes, _}} -> Nodes + end. running_clustered_nodes() -> - {ok, Nodes} = running_clustered_nodes_safe(), - Nodes. + case check_or_cluster_status( + fun () -> mnesia:system_info(running_db_nodes) end, + fun check_disc_or_mnesia_running/1) + of + {ok, Nodes} -> Nodes; + {status, {_, _, Nodes}} -> Nodes + end. running_clustered_disc_nodes() -> - check_mnesia_running( - fun () -> - Running = running_clustered_nodes(), - {ok, Disc} = all_clustered_disc_nodes(), - sets:to_list(sets:intersection(sets:from_list(Running), - sets:from_list(Disc))) - end). + {DiscNodes, RunningNodes} = + case check_or_cluster_status( + fun () -> + {all_clustered_disc_nodes(), running_clustered_nodes()} + end, + fun check_mnesia_running/1) + of + {ok, Nodes} -> + Nodes; + {status, {_, DiscNodes0, RunningNodes0}} -> + {DiscNodes0, RunningNodes0} + end, + sets:to_list(sets:intersection(sets:from_list(DiscNodes), + sets:from_list(RunningNodes))). -all_clustered_and_disc_nodes() -> +%% This function is a bit different, we want it to return correctly only when +%% the node is actually online. This is because when we discover the nodes we +%% want online, "working" nodes only. +cluster_status_if_running() -> check_mnesia_running( fun () -> - {ok, DiscNodes} = all_clustered_disc_nodes(), - {all_clustered_nodes(), DiscNodes} + {mnesia:system_info(db_nodes), nodes_of_type(disc_copies), + mnesia:system_info(running_db_nodes)} end). empty_ram_only_tables() -> @@ -274,12 +279,38 @@ discover_cluster([]) -> {error, {cannot_discover_cluster, "The cluster nodes provided are either offline or not running."}}; discover_cluster([Node | Nodes]) -> - case rpc:call(Node, rabbit_mnesia, all_clustered_and_disc_nodes, []) of + case rpc:call(Node, rabbit_mnesia, cluster_status_if_running, []) of {badrpc, _Reason} -> discover_cluster(Nodes); - {error, _Reason} -> discover_cluster(Nodes); + error -> discover_cluster(Nodes); {ok, Res} -> {ok, Res} end. +%% This does not guarantee us much, but it avoids some situations that will +%% definitely end in disaster (a node starting and trying to merge its schema +%% to another node which is not clustered with it). +check_cluster_consistency() -> + ThisNode = node(), + lists:foreach( + fun(Node) -> + case rpc:call(Node, rabbit_mnesia, cluster_status_if_running, []) + of + {badrpc, _Reason} -> ok; + error -> ok; + {ok, {AllNodes, _, _}} -> + case lists:member(ThisNode, AllNodes) of + true -> + ok; + false -> + throw({error, + {inconsistent_cluster, + rabbit_misc:format( + "Node ~p thinks it's clustered with " + "node ~p, but ~p disagrees", + [ThisNode, Node, Node])}}) + end + end + end, rabbit_mnesia:all_clustered_nodes()). + %%-------------------------------------------------------------------- nodes_of_type(Type) -> @@ -504,90 +535,86 @@ check_tables(Fun) -> Errors -> {error, Errors} end. -%% The cluster node config file contains some or all of the disk nodes -%% that are members of the cluster this node is / should be a part of. +%% The cluster node status file contains all we need to know about the cluster: +%% +%% * All the clustered nodes +%% * The disc nodes +%% * The running nodes. %% -%% If the file is absent, the list is empty, or only contains the -%% current node, then the current node is a standalone (disk) -%% node. Otherwise it is a node that is part of a cluster as either a -%% disk node, if it appears in the cluster node config, or ram node if -%% it doesn't. +%% If the current node is a disc node it will be included in the disc nodes +%% list. +%% +%% We strive to keep the file up to date and we rely on this assumption in +%% various situations. Obviously when mnesia is offline the information we have +%% will be outdated, but it can't be otherwise. -cluster_nodes_config_filename() -> +cluster_nodes_status_filename() -> dir() ++ "/cluster_nodes.config". -create_cluster_nodes_config(Config) -> - FileName = cluster_nodes_config_filename(), - case rabbit_file:write_term_file(FileName, [Config]) of +%% Creates a status file with the default data (one disc node), only if an +%% existing cluster does not exist. +initialize_cluster_nodes_status() -> + try read_cluster_nodes_status() of + _ -> ok + catch + throw:{error, {cannot_read_cluster_nodes_status, _, enoent}} -> + write_cluster_nodes_status({[node()], [node()], [node()]}) + end. + +write_cluster_nodes_status(Status) -> + FileName = cluster_nodes_status_filename(), + case rabbit_file:write_term_file(FileName, [Status]) of ok -> ok; {error, Reason} -> - throw({error, {cannot_create_cluster_nodes_config, + throw({error, {cannot_write_cluster_nodes_status, FileName, Reason}}) end. -read_cluster_nodes_config() -> - Convert = fun (Config = {_, _}) -> Config; - (ClusterNodes) -> - log_both("reading legacy node config"), - {ClusterNodes, should_be_disc_node_legacy(ClusterNodes)} - end, - FileName = cluster_nodes_config_filename(), +read_cluster_nodes_status() -> + FileName = cluster_nodes_status_filename(), case rabbit_file:read_term_file(FileName) of - {ok, [Config]} -> Convert(Config); - {error, enoent} -> - {ok, Config} = application:get_env(rabbit, cluster_nodes), - Convert(Config); + {ok, [{_, _, _} = Status]} -> Status; {error, Reason} -> - throw({error, {cannot_read_cluster_nodes_config, - FileName, Reason}}) + throw({error, {cannot_read_cluster_nodes_status, FileName, Reason}}) end. -delete_cluster_nodes_config() -> - FileName = cluster_nodes_config_filename(), +reset_cluster_nodes_status() -> + FileName = cluster_nodes_status_filename(), case file:delete(FileName) of ok -> ok; {error, enoent} -> ok; {error, Reason} -> - throw({error, {cannot_delete_cluster_nodes_config, + throw({error, {cannot_delete_cluster_nodes_status, FileName, Reason}}) - end. - -running_nodes_filename() -> - filename:join(dir(), "nodes_running_at_shutdown"). - -record_running_nodes() -> - FileName = running_nodes_filename(), - Nodes = running_clustered_nodes() -- [node()], - %% Don't check the result: we're shutting down anyway and this is - %% a best-effort-basis. - rabbit_file:write_term_file(FileName, [Nodes]), - ok. + end, + write_cluster_nodes_status({[node()], [node()], [node()]}). -read_previously_running_nodes() -> - FileName = running_nodes_filename(), - case rabbit_file:read_term_file(FileName) of - {ok, [Nodes]} -> Nodes; - {error, enoent} -> []; - {error, Reason} -> throw({error, {cannot_read_previous_nodes_file, - FileName, Reason}}) - end. +%% To update the cluster status when mnesia is running. +update_cluster_nodes_status() -> + {ok, Status} = cluster_status_if_running(), + write_cluster_nodes_status(Status). -delete_previously_running_nodes() -> - FileName = running_nodes_filename(), - case file:delete(FileName) of - ok -> ok; - {error, enoent} -> ok; - {error, Reason} -> throw({error, {cannot_delete_previous_nodes_file, - FileName, Reason}}) +%% The cluster config contains the nodes that the node should try to contact to +%% form a cluster, and whether the node should be a disc node. When starting the +%% database, if the nodes in the cluster status are the initial ones, we try to +%% read the cluster config. +read_cluster_nodes_config() -> + {AllNodes, DiscNodes, _} = read_cluster_nodes_status(), + Node = node(), + case AllNodes of + [Node] -> {ok, Config} = application:get_env(rabbit, cluster_nodes), + Config; + _ -> {AllNodes, should_be_disc_node(DiscNodes)} end. -init_db(ClusterNodes, Force) -> init_db(ClusterNodes, Force, true). +init_db(ClusterNodes, WantDiscNode, Force) -> + init_db(ClusterNodes, WantDiscNode, Force, true). %% Take a cluster node config and create the right kind of node - a %% standalone disk node, or disk or ram node connected to the %% specified cluster nodes. If Force is false, don't allow %% connections to offline nodes. -init_db({ClusterNodes, WantDiscNode}, Force, Upgrade) -> +init_db(ClusterNodes, WantDiscNode, Force, Upgrade) -> UClusterNodes = lists:usort(ClusterNodes), ProperClusterNodes = UClusterNodes -- [node()], case mnesia:change_config(extra_db_nodes, ProperClusterNodes) of @@ -615,15 +642,17 @@ init_db({ClusterNodes, WantDiscNode}, Force, Upgrade) -> %% Subsequent node in cluster, catch up ensure_version_ok( rpc:call(AnotherNode, rabbit_version, recorded, [])), - {CopyType, CopyTypeAlt} = - case WantDiscNode of - true -> {disc, disc_copies}; - false -> {ram, ram_copies} - end, + {CopyType, CopyTypeAlt} = case WantDiscNode of + true -> {disc, disc_copies}; + false -> {ram, ram_copies} + end, ok = wait_for_replicated_tables(), ok = create_local_table_copy(schema, CopyTypeAlt), ok = create_local_table_copies(CopyType), + %% Write the status now that mnesia is running and clustered + update_cluster_nodes_status(), + ok = case Upgrade of true -> case rabbit_upgrade:maybe_upgrade_local() of @@ -698,11 +727,8 @@ create_schema(Type) -> is_disc_node() -> mnesia:system_info(use_dir). -%% We're not using this anymore - the config stores whether the node is disc or -%% not - but we still need it when reading old-style configs and in -%% `rabbit_control'. -should_be_disc_node_legacy(ClusterNodes) -> - ClusterNodes == [] orelse lists:member(node(), ClusterNodes). +should_be_disc_node(DiscNodes) -> + DiscNodes == [] orelse lists:member(node(), DiscNodes). move_db() -> stop_mnesia(), @@ -813,7 +839,7 @@ reset(Force) -> end]), ensure_mnesia_not_running(), case not Force andalso is_disc_and_clustered() andalso - is_only_disc_node(node(), false) + is_only_disc_node(node()) of true -> throw({error, {standalone_ram_node, "You can't reset a node if it's the only disc " @@ -822,40 +848,34 @@ reset(Force) -> false -> ok end, Node = node(), - MaybeNodes = - case Force of - true -> - all_clustered_nodes_safe(); - false -> - ensure_mnesia_dir(), - start_mnesia(), - Nodes0 = all_clustered_nodes(), - RunningNodes = - try - %% Force=true here so that reset still works when - %% clustered with a node which is down - ok = init_db(read_cluster_nodes_config(), true), - running_clustered_nodes() - after - stop_mnesia() - end, - leave_cluster(Nodes0, RunningNodes), - rabbit_misc:ensure_ok(mnesia:delete_schema([Node]), - cannot_delete_schema), - {ok, Nodes0} - end, - case MaybeNodes of - {ok, Nodes} -> - %% We need to make sure that we don't end up in a distributed Erlang - %% system with nodes while not being in an Mnesia cluster with - %% them. We don't handle that well. - [erlang:disconnect_node(N) || N <- Nodes], - ok = delete_cluster_nodes_config(); - {error, _Reason} -> - log_both("Since this ram node is being force reseted, " - "the node hasn't been disconnected from the " - "cluster correctly, bad things might happen.") + Nodes = all_clustered_nodes(), + case Force of + true -> + ok; + false -> + ensure_mnesia_dir(), + start_mnesia(), + %% Reconnecting so that we will get an up to date RunningNodes + RunningNodes = + try + %% Force=true here so that reset still works when clustered + %% with a node which is down + {_, DiscNodes, _} = read_cluster_nodes_status(), + ok = init_db(DiscNodes, should_be_disc_node(DiscNodes), + true), + running_clustered_nodes() + after + stop_mnesia() + end, + leave_cluster(Nodes, RunningNodes), + rabbit_misc:ensure_ok(mnesia:delete_schema([Node]), + cannot_delete_schema) end, + %% We need to make sure that we don't end up in a distributed Erlang system + %% with nodes while not being in an Mnesia cluster with them. We don't + %% handle that well. + [erlang:disconnect_node(N) || N <- Nodes], + ok = reset_cluster_nodes_status(), %% remove persisted messages and any other garbage we find ok = rabbit_file:recursive_delete(filelib:wildcard(dir() ++ "/*")), ok. @@ -893,30 +913,21 @@ wait_for(Condition) -> timer:sleep(1000). on_node_up(Node) -> - case is_only_disc_node(Node, true) of + update_cluster_nodes_status(), + case is_only_disc_node(Node) of true -> rabbit_log:info("cluster contains disc nodes again~n"); false -> ok end. on_node_down(Node) -> - case is_only_disc_node(Node, true) of + case is_only_disc_node(Node) of true -> rabbit_log:info("only running disc node went down~n"); false -> ok end. -is_only_disc_node(Node, _MnesiaRunning = true) -> - {ok, Nodes} = running_clustered_disc_nodes(), - [Node] =:= Nodes; -is_only_disc_node(Node, false) -> - start_mnesia(), - Res = is_only_disc_node(Node, true), - stop_mnesia(), - Res. - -log_both(Warning) -> - io:format("Warning: ~s~n", [Warning]), - rabbit_misc:with_local_io( - fun () -> error_logger:warning_msg("~s~n", [Warning]) end). +is_only_disc_node(Node) -> + Nodes = running_clustered_disc_nodes(), + [Node] =:= Nodes. start_mnesia() -> rabbit_misc:ensure_ok(mnesia:start(), cannot_start_mnesia), diff --git a/src/rabbit_upgrade.erl b/src/rabbit_upgrade.erl index f20c2308..8c95e9dd 100644 --- a/src/rabbit_upgrade.erl +++ b/src/rabbit_upgrade.erl @@ -121,12 +121,7 @@ remove_backup() -> info("upgrades: Mnesia backup removed~n", []). maybe_upgrade_mnesia() -> - {ClusterNodes1, _DiscNode} = rabbit_mnesia:read_cluster_nodes_config(), - ClusterNodes2 = case rabbit_mnesia:all_clustered_nodes_safe() of - {ok, Res} -> Res; - {error, _Reason} -> [] - end, - AllNodes = lists:usort(ClusterNodes1 ++ ClusterNodes2), + AllNodes = rabbit_mnesia:all_clustered_nodes(), case rabbit_version:upgrades_required(mnesia) of {error, starting_from_scratch} -> ok; @@ -152,19 +147,16 @@ maybe_upgrade_mnesia() -> upgrade_mode(AllNodes) -> case nodes_running(AllNodes) of [] -> - AfterUs = rabbit_mnesia:read_previously_running_nodes(), - case {is_disc_node_legacy(), AfterUs} of + case {is_disc_node_legacy(), AllNodes} of {true, []} -> primary; {true, _} -> - Filename = rabbit_mnesia:running_nodes_filename(), + %% TODO: Here I'm assuming that the various cluster status + %% files are consistent with each other, I think I can + %% provide a solution if they're not... die("Cluster upgrade needed but other disc nodes shut " "down after this one.~nPlease first start the last " - "disc node to shut down.~n~nNote: if several disc " - "nodes were shut down simultaneously they may " - "all~nshow this message. In which case, remove " - "the lock file on one of them and~nstart that node. " - "The lock file on this node is:~n~n ~s ", [Filename]); + "disc node to shut down.~n", []); {false, _} -> die("Cluster upgrade needed but this is a ram node.~n" "Please first start the last disc node to shut down.", @@ -224,15 +216,8 @@ secondary_upgrade(AllNodes) -> IsDiscNode = is_disc_node_legacy(), rabbit_misc:ensure_ok(mnesia:delete_schema([node()]), cannot_delete_schema), - %% Note that we cluster with all nodes, rather than all disc nodes - %% (as we can't know all disc nodes at this point). This is safe as - %% we're not writing the cluster config, just setting up Mnesia. - ClusterNodes = case IsDiscNode of - true -> AllNodes; - false -> AllNodes -- [node()] - end, rabbit_misc:ensure_ok(mnesia:start(), cannot_start_mnesia), - ok = rabbit_mnesia:init_db(ClusterNodes, true, false), + ok = rabbit_mnesia:init_db(AllNodes, IsDiscNode, true, false), ok = rabbit_version:record_desired_for_scope(mnesia), ok. -- cgit v1.2.1 From c0ae7544adfa9ee332da7b2117c28ec764b5ec46 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Wed, 16 May 2012 09:19:12 +0100 Subject: rename bootstrap_environment to prepare_plugins; fix while_process_is_alive activity call --- src/rabbit.erl | 2 +- src/rabbit_control.erl | 6 +++--- src/rabbit_plugins.erl | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index f735fbea..31296058 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -306,7 +306,7 @@ start() -> boot() -> start_it(fun() -> ok = prepare(), - Plugins = rabbit_plugins:bootstrap_envinronment(), + Plugins = rabbit_plugins:prepare_plugins(), ToBeLoaded = Plugins ++ ?APPS, ok = app_utils:load_applications(ToBeLoaded), StartupApps = app_utils:app_dependency_order(ToBeLoaded, false), diff --git a/src/rabbit_control.erl b/src/rabbit_control.erl index 0e9e034a..6dc8d445 100644 --- a/src/rabbit_control.erl +++ b/src/rabbit_control.erl @@ -411,12 +411,12 @@ wait_for_application(Node, Pid, Application) -> wait_for_startup(Node, Pid) -> while_process_is_alive(Node, Pid, - fun() -> rpc:call(Node, rabbit, await_startup, []) end). + fun() -> rpc:call(Node, rabbit, await_startup, []) =:= ok end). while_process_is_alive(Node, Pid, Activity) -> case process_up(Pid) of - true -> case Activity(Node) of - ok -> ok; + true -> case Activity() of + true -> ok; Other -> timer:sleep(?EXTERNAL_CHECK_INTERVAL), while_process_is_alive(Node, Pid, Activity) end; diff --git a/src/rabbit_plugins.erl b/src/rabbit_plugins.erl index 0ab0d3aa..8a8e7052 100644 --- a/src/rabbit_plugins.erl +++ b/src/rabbit_plugins.erl @@ -17,7 +17,7 @@ -module(rabbit_plugins). -include("rabbit.hrl"). --export([start/0, stop/0, bootstrap_envinronment/0, active_plugins/0]). +-export([start/0, stop/0, prepare_plugins/0, active_plugins/0]). -define(VERBOSE_OPT, "-v"). -define(MINIMAL_OPT, "-m"). @@ -30,7 +30,7 @@ -spec(start/0 :: () -> no_return()). -spec(stop/0 :: () -> 'ok'). --spec(bootstrap_envinronment/0 :: () -> [atom()]). +-spec(prepare_plugins/0 :: () -> [atom()]). -spec(active_plugins/0 :: () -> [atom()]). -endif. @@ -80,13 +80,13 @@ start() -> stop() -> ok. -bootstrap_envinronment() -> +prepare_plugins() -> {ok, PluginDir} = application:get_env(rabbit, plugins_dir), {ok, ExpandDir} = application:get_env(rabbit, plugins_expand_dir), {ok, EnabledPluginsFile} = application:get_env(rabbit, enabled_plugins_file), prepare_plugins(EnabledPluginsFile, PluginDir, ExpandDir), - [prepare_dir_plugin(PluginName) || + [prepare_dir_plugin(PluginName) || PluginName <- filelib:wildcard(ExpandDir ++ "/*/ebin/*.app")]. active_plugins() -> -- cgit v1.2.1 From 09b1ff83a1cef206551b2308b0d9eece7bed106b Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Wed, 16 May 2012 12:02:03 +0100 Subject: split rabbit_plugins into client and server side modules --- scripts/rabbitmq-plugins | 2 +- scripts/rabbitmq-plugins.bat | 2 +- src/rabbit_plugins.erl | 344 ++++++++----------------------------------- src/rabbit_plugins_main.erl | 271 ++++++++++++++++++++++++++++++++++ 4 files changed, 334 insertions(+), 285 deletions(-) create mode 100644 src/rabbit_plugins_main.erl diff --git a/scripts/rabbitmq-plugins b/scripts/rabbitmq-plugins index 14a18d57..97c74791 100755 --- a/scripts/rabbitmq-plugins +++ b/scripts/rabbitmq-plugins @@ -31,7 +31,7 @@ exec erl \ -noinput \ -hidden \ -sname rabbitmq-plugins$$ \ - -s rabbit_plugins \ + -s rabbit_plugins_main \ -enabled_plugins_file "$RABBITMQ_ENABLED_PLUGINS_FILE" \ -plugins_dist_dir "$RABBITMQ_PLUGINS_DIR" \ -extra "$@" diff --git a/scripts/rabbitmq-plugins.bat b/scripts/rabbitmq-plugins.bat index 66a900a1..bc198393 100755 --- a/scripts/rabbitmq-plugins.bat +++ b/scripts/rabbitmq-plugins.bat @@ -45,7 +45,7 @@ if "!RABBITMQ_ENABLED_PLUGINS_FILE!"=="" ( set RABBITMQ_PLUGINS_DIR=!TDP0!..\plugins -"!ERLANG_HOME!\bin\erl.exe" -pa "!TDP0!..\ebin" -noinput -hidden -sname rabbitmq-plugins!RANDOM! -s rabbit_plugins -enabled_plugins_file "!RABBITMQ_ENABLED_PLUGINS_FILE!" -plugins_dist_dir "!RABBITMQ_PLUGINS_DIR:\=/!" -extra !STAR! +"!ERLANG_HOME!\bin\erl.exe" -pa "!TDP0!..\ebin" -noinput -hidden -sname rabbitmq-plugins!RANDOM! -s rabbit_plugins_main -enabled_plugins_file "!RABBITMQ_ENABLED_PLUGINS_FILE!" -plugins_dist_dir "!RABBITMQ_PLUGINS_DIR:\=/!" -extra !STAR! endlocal endlocal diff --git a/src/rabbit_plugins.erl b/src/rabbit_plugins.erl index 8a8e7052..06773cdb 100644 --- a/src/rabbit_plugins.erl +++ b/src/rabbit_plugins.erl @@ -17,69 +17,27 @@ -module(rabbit_plugins). -include("rabbit.hrl"). --export([start/0, stop/0, prepare_plugins/0, active_plugins/0]). - --define(VERBOSE_OPT, "-v"). --define(MINIMAL_OPT, "-m"). --define(ENABLED_OPT, "-E"). --define(ENABLED_ALL_OPT, "-e"). +-export([prepare_plugins/0, active_plugins/0, read_enabled_plugins/1, + find_plugins/1, calculate_plugin_dependencies/3]). %%---------------------------------------------------------------------------- -ifdef(use_specs). --spec(start/0 :: () -> no_return()). --spec(stop/0 :: () -> 'ok'). -spec(prepare_plugins/0 :: () -> [atom()]). -spec(active_plugins/0 :: () -> [atom()]). +-spec(find_plugins/1 :: (string()) -> [#plugin{}]). +-spec(read_enabled_plugins/1 :: (file:filename()) -> [atom()]). +-spec(calculate_plugin_dependencies/3 :: + (boolean(), [atom()], [#plugin{}]) -> [atom()]). -endif. %%---------------------------------------------------------------------------- -start() -> - {ok, [[PluginsFile|_]|_]} = - init:get_argument(enabled_plugins_file), - {ok, [[PluginsDir|_]|_]} = init:get_argument(plugins_dist_dir), - {[Command0 | Args], Opts} = - case rabbit_misc:get_options([{flag, ?VERBOSE_OPT}, - {flag, ?MINIMAL_OPT}, - {flag, ?ENABLED_OPT}, - {flag, ?ENABLED_ALL_OPT}], - init:get_plain_arguments()) of - {[], _Opts} -> usage(); - CmdArgsAndOpts -> CmdArgsAndOpts - end, - Command = list_to_atom(Command0), - PrintInvalidCommandError = - fun () -> - print_error("invalid command '~s'", - [string:join([atom_to_list(Command) | Args], " ")]) - end, - - case catch action(Command, Args, Opts, PluginsFile, PluginsDir) of - ok -> - rabbit_misc:quit(0); - {'EXIT', {function_clause, [{?MODULE, action, _} | _]}} -> - PrintInvalidCommandError(), - usage(); - {'EXIT', {function_clause, [{?MODULE, action, _, _} | _]}} -> - PrintInvalidCommandError(), - usage(); - {error, Reason} -> - print_error("~p", [Reason]), - rabbit_misc:quit(2); - {error_string, Reason} -> - print_error("~s", [Reason]), - rabbit_misc:quit(2); - Other -> - print_error("~p", [Other]), - rabbit_misc:quit(2) - end. - -stop() -> - ok. - +%% +%% @doc Prepares the file system and installs all enabled plugins. +%% prepare_plugins() -> {ok, PluginDir} = application:get_env(rabbit, plugins_dir), {ok, ExpandDir} = application:get_env(rabbit, plugins_expand_dir), @@ -89,18 +47,69 @@ prepare_plugins() -> [prepare_dir_plugin(PluginName) || PluginName <- filelib:wildcard(ExpandDir ++ "/*/ebin/*.app")]. +%% @doc Lists the plugins which are currently running. active_plugins() -> {ok, ExpandDir} = application:get_env(rabbit, plugins_expand_dir), InstalledPlugins = [ P#plugin.name || P <- find_plugins(ExpandDir) ], [App || {App, _, _} <- application:which_applications(), lists:member(App, InstalledPlugins)]. +%% @doc Get the list of plugins which are ready to be enabled. +find_plugins(PluginsDir) -> + EZs = [{ez, EZ} || EZ <- filelib:wildcard("*.ez", PluginsDir)], + FreeApps = [{app, App} || + App <- filelib:wildcard("*/ebin/*.app", PluginsDir)], + {Plugins, Problems} = + lists:foldl(fun ({error, EZ, Reason}, {Plugins1, Problems1}) -> + {Plugins1, [{EZ, Reason} | Problems1]}; + (Plugin = #plugin{}, {Plugins1, Problems1}) -> + {[Plugin|Plugins1], Problems1} + end, {[], []}, + [get_plugin_info(PluginsDir, Plug) || + Plug <- EZs ++ FreeApps]), + case Problems of + [] -> ok; + _ -> io:format("Warning: Problem reading some plugins: ~p~n", + [Problems]) + end, + Plugins. + +%% @doc Read the list of enabled plugins from the supplied term file. +read_enabled_plugins(PluginsFile) -> + case rabbit_file:read_term_file(PluginsFile) of + {ok, [Plugins]} -> Plugins; + {ok, []} -> []; + {ok, [_|_]} -> throw({error, {malformed_enabled_plugins_file, + PluginsFile}}); + {error, enoent} -> []; + {error, Reason} -> throw({error, {cannot_read_enabled_plugins_file, + PluginsFile, Reason}}) + end. + +%% +%% @doc Calculate the dependency graph from Sources. +%% When Reverse =:= true the bottom/leaf level applications are returned in +%% the resulting list, otherwise they're skipped. +%% +calculate_plugin_dependencies(Reverse, Sources, AllPlugins) -> + {ok, G} = rabbit_misc:build_acyclic_graph( + fun (App, _Deps) -> [{App, App}] end, + fun (App, Deps) -> [{App, Dep} || Dep <- Deps] end, + [{Name, Deps} + || #plugin{name = Name, dependencies = Deps} <- AllPlugins]), + Dests = case Reverse of + false -> digraph_utils:reachable(Sources, G); + true -> digraph_utils:reaching(Sources, G) + end, + true = digraph:delete(G), + Dests. + %%---------------------------------------------------------------------------- prepare_plugins(EnabledPluginsFile, PluginsDistDir, DestDir) -> AllPlugins = find_plugins(PluginsDistDir), Enabled = read_enabled_plugins(EnabledPluginsFile), - ToUnpack = calculate_required_plugins(Enabled, AllPlugins), + ToUnpack = calculate_plugin_dependencies(false, Enabled, AllPlugins), ToUnpackPlugins = lookup_plugins(ToUnpack, AllPlugins), Missing = Enabled -- plugin_names(ToUnpackPlugins), @@ -136,75 +145,6 @@ prepare_dir_plugin(PluginAppDescFn) -> %%---------------------------------------------------------------------------- -action(list, [], Opts, PluginsFile, PluginsDir) -> - action(list, [".*"], Opts, PluginsFile, PluginsDir); -action(list, [Pat], Opts, PluginsFile, PluginsDir) -> - format_plugins(Pat, Opts, PluginsFile, PluginsDir); - -action(enable, ToEnable0, _Opts, PluginsFile, PluginsDir) -> - case ToEnable0 of - [] -> throw({error_string, "Not enough arguments for 'enable'"}); - _ -> ok - end, - AllPlugins = find_plugins(PluginsDir), - Enabled = read_enabled_plugins(PluginsFile), - ImplicitlyEnabled = calculate_required_plugins(Enabled, AllPlugins), - ToEnable = [list_to_atom(Name) || Name <- ToEnable0], - Missing = ToEnable -- plugin_names(AllPlugins), - case Missing of - [] -> ok; - _ -> throw({error_string, - fmt_list("The following plugins could not be found:", - Missing)}) - end, - NewEnabled = lists:usort(Enabled ++ ToEnable), - write_enabled_plugins(PluginsFile, NewEnabled), - NewImplicitlyEnabled = calculate_required_plugins(NewEnabled, AllPlugins), - maybe_warn_mochiweb(NewImplicitlyEnabled), - case NewEnabled -- ImplicitlyEnabled of - [] -> io:format("Plugin configuration unchanged.~n"); - _ -> print_list("The following plugins have been enabled:", - NewImplicitlyEnabled -- ImplicitlyEnabled), - report_change() - end; - -action(disable, ToDisable0, _Opts, PluginsFile, PluginsDir) -> - case ToDisable0 of - [] -> throw({error_string, "Not enough arguments for 'disable'"}); - _ -> ok - end, - ToDisable = [list_to_atom(Name) || Name <- ToDisable0], - Enabled = read_enabled_plugins(PluginsFile), - AllPlugins = find_plugins(PluginsDir), - Missing = ToDisable -- plugin_names(AllPlugins), - case Missing of - [] -> ok; - _ -> print_list("Warning: the following plugins could not be found:", - Missing) - end, - ToDisableDeps = calculate_dependencies(true, ToDisable, AllPlugins), - NewEnabled = Enabled -- ToDisableDeps, - case length(Enabled) =:= length(NewEnabled) of - true -> io:format("Plugin configuration unchanged.~n"); - false -> ImplicitlyEnabled = - calculate_required_plugins(Enabled, AllPlugins), - NewImplicitlyEnabled = - calculate_required_plugins(NewEnabled, AllPlugins), - print_list("The following plugins have been disabled:", - ImplicitlyEnabled -- NewImplicitlyEnabled), - write_enabled_plugins(PluginsFile, NewEnabled), - report_change() - end. - -%%---------------------------------------------------------------------------- - -print_error(Format, Args) -> - rabbit_misc:format_stderr("Error: " ++ Format ++ "~n", Args). - -usage() -> - io:format("~s", [rabbit_plugins_usage:usage()]), - rabbit_misc:quit(1). - delete_recursively(Fn) -> case rabbit_file:recursive_delete([Fn]) of ok -> ok; @@ -219,26 +159,6 @@ prepare_plugin(#plugin{type = dir, name = Name, location = Location}, rabbit_file:recursive_copy(Location, filename:join([PluginsDestDir, Name])). -%% Get the #plugin{}s ready to be enabled. -find_plugins(PluginsDir) -> - EZs = [{ez, EZ} || EZ <- filelib:wildcard("*.ez", PluginsDir)], - FreeApps = [{app, App} || - App <- filelib:wildcard("*/ebin/*.app", PluginsDir)], - {Plugins, Problems} = - lists:foldl(fun ({error, EZ, Reason}, {Plugins1, Problems1}) -> - {Plugins1, [{EZ, Reason} | Problems1]}; - (Plugin = #plugin{}, {Plugins1, Problems1}) -> - {[Plugin|Plugins1], Problems1} - end, {[], []}, - [get_plugin_info(PluginsDir, Plug) || - Plug <- EZs ++ FreeApps]), - case Problems of - [] -> ok; - _ -> io:format("Warning: Problem reading some plugins: ~p~n", - [Problems]) - end, - Plugins. - %% Get the #plugin{} from an .ez. get_plugin_info(Base, {ez, EZ0}) -> EZ = filename:join([Base, EZ0]), @@ -298,82 +218,6 @@ parse_binary(Bin) -> Err -> {error, {invalid_app, Err}} end. -%% Pretty print a list of plugins. -format_plugins(Pattern, Opts, PluginsFile, PluginsDir) -> - Verbose = proplists:get_bool(?VERBOSE_OPT, Opts), - Minimal = proplists:get_bool(?MINIMAL_OPT, Opts), - Format = case {Verbose, Minimal} of - {false, false} -> normal; - {true, false} -> verbose; - {false, true} -> minimal; - {true, true} -> throw({error_string, - "Cannot specify -m and -v together"}) - end, - OnlyEnabled = proplists:get_bool(?ENABLED_OPT, Opts), - OnlyEnabledAll = proplists:get_bool(?ENABLED_ALL_OPT, Opts), - - AvailablePlugins = find_plugins(PluginsDir), - EnabledExplicitly = read_enabled_plugins(PluginsFile), - EnabledImplicitly = - calculate_required_plugins(EnabledExplicitly, AvailablePlugins) -- - EnabledExplicitly, - {ok, RE} = re:compile(Pattern), - Plugins = [ Plugin || - Plugin = #plugin{name = Name} <- AvailablePlugins, - re:run(atom_to_list(Name), RE, [{capture, none}]) =:= match, - if OnlyEnabled -> lists:member(Name, EnabledExplicitly); - true -> true - end, - if OnlyEnabledAll -> - lists:member(Name, EnabledImplicitly) or - lists:member(Name, EnabledExplicitly); - true -> - true - end], - Plugins1 = usort_plugins(Plugins), - MaxWidth = lists:max([length(atom_to_list(Name)) || - #plugin{name = Name} <- Plugins1] ++ [0]), - [format_plugin(P, EnabledExplicitly, EnabledImplicitly, Format, - MaxWidth) || P <- Plugins1], - ok. - -format_plugin(#plugin{name = Name, version = Version, - description = Description, dependencies = Deps}, - EnabledExplicitly, EnabledImplicitly, Format, MaxWidth) -> - Glyph = case {lists:member(Name, EnabledExplicitly), - lists:member(Name, EnabledImplicitly)} of - {true, false} -> "[E]"; - {false, true} -> "[e]"; - _ -> "[ ]" - end, - case Format of - minimal -> io:format("~s~n", [Name]); - normal -> io:format("~s ~-" ++ integer_to_list(MaxWidth) ++ - "w ~s~n", [Glyph, Name, Version]); - verbose -> io:format("~s ~w~n", [Glyph, Name]), - io:format(" Version: \t~s~n", [Version]), - case Deps of - [] -> ok; - _ -> io:format(" Dependencies:\t~p~n", [Deps]) - end, - io:format(" Description:\t~s~n", [Description]), - io:format("~n") - end. - -print_list(Header, Plugins) -> - io:format(fmt_list(Header, Plugins)). - -fmt_list(Header, Plugins) -> - lists:flatten( - [Header, $\n, [io_lib:format(" ~s~n", [P]) || P <- Plugins]]). - -usort_plugins(Plugins) -> - lists:usort(fun plugins_cmp/2, Plugins). - -plugins_cmp(#plugin{name = N1, version = V1}, - #plugin{name = N2, version = V2}) -> - {N1, V1} =< {N2, V2}. - %% Filter out applications that can be loaded *right now*. filter_applications(Applications) -> [Application || Application <- Applications, @@ -397,71 +241,5 @@ plugin_names(Plugins) -> lookup_plugins(Names, AllPlugins) -> [P || P = #plugin{name = Name} <- AllPlugins, lists:member(Name, Names)]. -%% Read the enabled plugin names from disk. -read_enabled_plugins(PluginsFile) -> - case rabbit_file:read_term_file(PluginsFile) of - {ok, [Plugins]} -> Plugins; - {ok, []} -> []; - {ok, [_|_]} -> throw({error, {malformed_enabled_plugins_file, - PluginsFile}}); - {error, enoent} -> []; - {error, Reason} -> throw({error, {cannot_read_enabled_plugins_file, - PluginsFile, Reason}}) - end. - -%% Write the enabled plugin names on disk. -write_enabled_plugins(PluginsFile, Plugins) -> - case rabbit_file:write_term_file(PluginsFile, [Plugins]) of - ok -> ok; - {error, Reason} -> throw({error, {cannot_write_enabled_plugins_file, - PluginsFile, Reason}}) - end. - -calculate_required_plugins(Sources, AllPlugins) -> - calculate_dependencies(false, Sources, AllPlugins). -calculate_dependencies(Reverse, Sources, AllPlugins) -> - {ok, G} = rabbit_misc:build_acyclic_graph( - fun (App, _Deps) -> [{App, App}] end, - fun (App, Deps) -> [{App, Dep} || Dep <- Deps] end, - [{Name, Deps} - || #plugin{name = Name, dependencies = Deps} <- AllPlugins]), - Dests = case Reverse of - false -> digraph_utils:reachable(Sources, G); - true -> digraph_utils:reaching(Sources, G) - end, - true = digraph:delete(G), - Dests. - -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"), - case os:type() of - {win32, _OsName} -> - io:format("If you have RabbitMQ running as a service then you must" - " reinstall by running~n rabbitmq-service.bat stop~n" - " rabbitmq-service.bat install~n" - " rabbitmq-service.bat start~n~n"); - _ -> - ok - end. diff --git a/src/rabbit_plugins_main.erl b/src/rabbit_plugins_main.erl new file mode 100644 index 00000000..a27ad986 --- /dev/null +++ b/src/rabbit_plugins_main.erl @@ -0,0 +1,271 @@ +%% 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 VMware, Inc. +%% Copyright (c) 2011-2012 VMware, Inc. All rights reserved. +%% + +-module(rabbit_plugins_main). +-include("rabbit.hrl"). + +-export([start/0, stop/0]). + +-define(VERBOSE_OPT, "-v"). +-define(MINIMAL_OPT, "-m"). +-define(ENABLED_OPT, "-E"). +-define(ENABLED_ALL_OPT, "-e"). + +%%---------------------------------------------------------------------------- + +-ifdef(use_specs). + +-spec(start/0 :: () -> no_return()). +-spec(stop/0 :: () -> 'ok'). + +-endif. + +%%---------------------------------------------------------------------------- + +start() -> + {ok, [[PluginsFile|_]|_]} = + init:get_argument(enabled_plugins_file), + {ok, [[PluginsDir|_]|_]} = init:get_argument(plugins_dist_dir), + {[Command0 | Args], Opts} = + case rabbit_misc:get_options([{flag, ?VERBOSE_OPT}, + {flag, ?MINIMAL_OPT}, + {flag, ?ENABLED_OPT}, + {flag, ?ENABLED_ALL_OPT}], + init:get_plain_arguments()) of + {[], _Opts} -> usage(); + CmdArgsAndOpts -> CmdArgsAndOpts + end, + Command = list_to_atom(Command0), + PrintInvalidCommandError = + fun () -> + print_error("invalid command '~s'", + [string:join([atom_to_list(Command) | Args], " ")]) + end, + + case catch action(Command, Args, Opts, PluginsFile, PluginsDir) of + ok -> + rabbit_misc:quit(0); + {'EXIT', {function_clause, [{?MODULE, action, _} | _]}} -> + PrintInvalidCommandError(), + usage(); + {'EXIT', {function_clause, [{?MODULE, action, _, _} | _]}} -> + PrintInvalidCommandError(), + usage(); + {error, Reason} -> + print_error("~p", [Reason]), + rabbit_misc:quit(2); + {error_string, Reason} -> + print_error("~s", [Reason]), + rabbit_misc:quit(2); + Other -> + print_error("~p", [Other]), + rabbit_misc:quit(2) + end. + +stop() -> + ok. + +%%---------------------------------------------------------------------------- + +action(list, [], Opts, PluginsFile, PluginsDir) -> + action(list, [".*"], Opts, PluginsFile, PluginsDir); +action(list, [Pat], Opts, PluginsFile, PluginsDir) -> + format_plugins(Pat, Opts, PluginsFile, PluginsDir); + +action(enable, ToEnable0, _Opts, PluginsFile, PluginsDir) -> + case ToEnable0 of + [] -> throw({error_string, "Not enough arguments for 'enable'"}); + _ -> ok + end, + AllPlugins = rabbit_plugins:find_plugins(PluginsDir), + Enabled = rabbit_plugins:read_enabled_plugins(PluginsFile), + ImplicitlyEnabled = rabbit_plugins:calculate_plugin_dependencies(false, Enabled, AllPlugins), + ToEnable = [list_to_atom(Name) || Name <- ToEnable0], + Missing = ToEnable -- plugin_names(AllPlugins), + case Missing of + [] -> ok; + _ -> throw({error_string, + fmt_list("The following plugins could not be found:", + Missing)}) + end, + NewEnabled = lists:usort(Enabled ++ ToEnable), + write_enabled_plugins(PluginsFile, NewEnabled), + NewImplicitlyEnabled = rabbit_plugins:calculate_plugin_dependencies(false, NewEnabled, AllPlugins), + maybe_warn_mochiweb(NewImplicitlyEnabled), + case NewEnabled -- ImplicitlyEnabled of + [] -> io:format("Plugin configuration unchanged.~n"); + _ -> print_list("The following plugins have been enabled:", + NewImplicitlyEnabled -- ImplicitlyEnabled), + report_change() + end; + +action(disable, ToDisable0, _Opts, PluginsFile, PluginsDir) -> + case ToDisable0 of + [] -> throw({error_string, "Not enough arguments for 'disable'"}); + _ -> ok + end, + ToDisable = [list_to_atom(Name) || Name <- ToDisable0], + Enabled = rabbit_plugins:read_enabled_plugins(PluginsFile), + AllPlugins = rabbit_plugins:find_plugins(PluginsDir), + Missing = ToDisable -- plugin_names(AllPlugins), + case Missing of + [] -> ok; + _ -> print_list("Warning: the following plugins could not be found:", + Missing) + end, + ToDisableDeps = rabbit_plugins:calculate_plugin_dependencies(true, ToDisable, AllPlugins), + NewEnabled = Enabled -- ToDisableDeps, + case length(Enabled) =:= length(NewEnabled) of + true -> io:format("Plugin configuration unchanged.~n"); + false -> ImplicitlyEnabled = + rabbit_plugins:calculate_plugin_dependencies(false, Enabled, AllPlugins), + NewImplicitlyEnabled = + rabbit_plugins:calculate_plugin_dependencies(false, NewEnabled, AllPlugins), + print_list("The following plugins have been disabled:", + ImplicitlyEnabled -- NewImplicitlyEnabled), + write_enabled_plugins(PluginsFile, NewEnabled), + report_change() + end. + +%%---------------------------------------------------------------------------- + +print_error(Format, Args) -> + rabbit_misc:format_stderr("Error: " ++ Format ++ "~n", Args). + +usage() -> + io:format("~s", [rabbit_plugins_usage:usage()]), + rabbit_misc:quit(1). + +%% Pretty print a list of plugins. +format_plugins(Pattern, Opts, PluginsFile, PluginsDir) -> + Verbose = proplists:get_bool(?VERBOSE_OPT, Opts), + Minimal = proplists:get_bool(?MINIMAL_OPT, Opts), + Format = case {Verbose, Minimal} of + {false, false} -> normal; + {true, false} -> verbose; + {false, true} -> minimal; + {true, true} -> throw({error_string, + "Cannot specify -m and -v together"}) + end, + OnlyEnabled = proplists:get_bool(?ENABLED_OPT, Opts), + OnlyEnabledAll = proplists:get_bool(?ENABLED_ALL_OPT, Opts), + + AvailablePlugins = rabbit_plugins:find_plugins(PluginsDir), + EnabledExplicitly = rabbit_plugins:read_enabled_plugins(PluginsFile), + EnabledImplicitly = + rabbit_plugins:calculate_plugin_dependencies(false, EnabledExplicitly, AvailablePlugins) -- + EnabledExplicitly, + {ok, RE} = re:compile(Pattern), + Plugins = [ Plugin || + Plugin = #plugin{name = Name} <- AvailablePlugins, + re:run(atom_to_list(Name), RE, [{capture, none}]) =:= match, + if OnlyEnabled -> lists:member(Name, EnabledExplicitly); + true -> true + end, + if OnlyEnabledAll -> + lists:member(Name, EnabledImplicitly) or + lists:member(Name, EnabledExplicitly); + true -> + true + end], + Plugins1 = usort_plugins(Plugins), + MaxWidth = lists:max([length(atom_to_list(Name)) || + #plugin{name = Name} <- Plugins1] ++ [0]), + [format_plugin(P, EnabledExplicitly, EnabledImplicitly, Format, + MaxWidth) || P <- Plugins1], + ok. + +format_plugin(#plugin{name = Name, version = Version, + description = Description, dependencies = Deps}, + EnabledExplicitly, EnabledImplicitly, Format, MaxWidth) -> + Glyph = case {lists:member(Name, EnabledExplicitly), + lists:member(Name, EnabledImplicitly)} of + {true, false} -> "[E]"; + {false, true} -> "[e]"; + _ -> "[ ]" + end, + case Format of + minimal -> io:format("~s~n", [Name]); + normal -> io:format("~s ~-" ++ integer_to_list(MaxWidth) ++ + "w ~s~n", [Glyph, Name, Version]); + verbose -> io:format("~s ~w~n", [Glyph, Name]), + io:format(" Version: \t~s~n", [Version]), + case Deps of + [] -> ok; + _ -> io:format(" Dependencies:\t~p~n", [Deps]) + end, + io:format(" Description:\t~s~n", [Description]), + io:format("~n") + end. + +print_list(Header, Plugins) -> + io:format(fmt_list(Header, Plugins)). + +fmt_list(Header, Plugins) -> + lists:flatten( + [Header, $\n, [io_lib:format(" ~s~n", [P]) || P <- Plugins]]). + +usort_plugins(Plugins) -> + lists:usort(fun plugins_cmp/2, Plugins). + +plugins_cmp(#plugin{name = N1, version = V1}, + #plugin{name = N2, version = V2}) -> + {N1, V1} =< {N2, V2}. + +%% Return the names of the given plugins. +plugin_names(Plugins) -> + [Name || #plugin{name = Name} <- Plugins]. + +%% Write the enabled plugin names on disk. +write_enabled_plugins(PluginsFile, Plugins) -> + case rabbit_file:write_term_file(PluginsFile, [Plugins]) of + ok -> ok; + {error, Reason} -> throw({error, {cannot_write_enabled_plugins_file, + 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"), + case os:type() of + {win32, _OsName} -> + io:format("If you have RabbitMQ running as a service then you must" + " reinstall by running~n rabbitmq-service.bat stop~n" + " rabbitmq-service.bat install~n" + " rabbitmq-service.bat start~n~n"); + _ -> + ok + end. + -- cgit v1.2.1 From 84a88408c39c02d23e1c5a9165d9267074a6211b Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 21 May 2012 13:55:48 +0100 Subject: rework commit history for bug24792 --- src/rabbit_plugins.erl | 467 -------------------------------------------- src/rabbit_plugins_main.erl | 467 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 467 insertions(+), 467 deletions(-) delete mode 100644 src/rabbit_plugins.erl create mode 100644 src/rabbit_plugins_main.erl diff --git a/src/rabbit_plugins.erl b/src/rabbit_plugins.erl deleted file mode 100644 index 8a8e7052..00000000 --- a/src/rabbit_plugins.erl +++ /dev/null @@ -1,467 +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 VMware, Inc. -%% Copyright (c) 2011-2012 VMware, Inc. All rights reserved. -%% - --module(rabbit_plugins). --include("rabbit.hrl"). - --export([start/0, stop/0, prepare_plugins/0, active_plugins/0]). - --define(VERBOSE_OPT, "-v"). --define(MINIMAL_OPT, "-m"). --define(ENABLED_OPT, "-E"). --define(ENABLED_ALL_OPT, "-e"). - -%%---------------------------------------------------------------------------- - --ifdef(use_specs). - --spec(start/0 :: () -> no_return()). --spec(stop/0 :: () -> 'ok'). --spec(prepare_plugins/0 :: () -> [atom()]). --spec(active_plugins/0 :: () -> [atom()]). - --endif. - -%%---------------------------------------------------------------------------- - -start() -> - {ok, [[PluginsFile|_]|_]} = - init:get_argument(enabled_plugins_file), - {ok, [[PluginsDir|_]|_]} = init:get_argument(plugins_dist_dir), - {[Command0 | Args], Opts} = - case rabbit_misc:get_options([{flag, ?VERBOSE_OPT}, - {flag, ?MINIMAL_OPT}, - {flag, ?ENABLED_OPT}, - {flag, ?ENABLED_ALL_OPT}], - init:get_plain_arguments()) of - {[], _Opts} -> usage(); - CmdArgsAndOpts -> CmdArgsAndOpts - end, - Command = list_to_atom(Command0), - PrintInvalidCommandError = - fun () -> - print_error("invalid command '~s'", - [string:join([atom_to_list(Command) | Args], " ")]) - end, - - case catch action(Command, Args, Opts, PluginsFile, PluginsDir) of - ok -> - rabbit_misc:quit(0); - {'EXIT', {function_clause, [{?MODULE, action, _} | _]}} -> - PrintInvalidCommandError(), - usage(); - {'EXIT', {function_clause, [{?MODULE, action, _, _} | _]}} -> - PrintInvalidCommandError(), - usage(); - {error, Reason} -> - print_error("~p", [Reason]), - rabbit_misc:quit(2); - {error_string, Reason} -> - print_error("~s", [Reason]), - rabbit_misc:quit(2); - Other -> - print_error("~p", [Other]), - rabbit_misc:quit(2) - end. - -stop() -> - ok. - -prepare_plugins() -> - {ok, PluginDir} = application:get_env(rabbit, plugins_dir), - {ok, ExpandDir} = application:get_env(rabbit, plugins_expand_dir), - {ok, EnabledPluginsFile} = application:get_env(rabbit, - enabled_plugins_file), - prepare_plugins(EnabledPluginsFile, PluginDir, ExpandDir), - [prepare_dir_plugin(PluginName) || - PluginName <- filelib:wildcard(ExpandDir ++ "/*/ebin/*.app")]. - -active_plugins() -> - {ok, ExpandDir} = application:get_env(rabbit, plugins_expand_dir), - InstalledPlugins = [ P#plugin.name || P <- find_plugins(ExpandDir) ], - [App || {App, _, _} <- application:which_applications(), - lists:member(App, InstalledPlugins)]. - -%%---------------------------------------------------------------------------- - -prepare_plugins(EnabledPluginsFile, PluginsDistDir, DestDir) -> - AllPlugins = find_plugins(PluginsDistDir), - Enabled = read_enabled_plugins(EnabledPluginsFile), - ToUnpack = calculate_required_plugins(Enabled, AllPlugins), - ToUnpackPlugins = lookup_plugins(ToUnpack, AllPlugins), - - Missing = Enabled -- plugin_names(ToUnpackPlugins), - case Missing of - [] -> ok; - _ -> io:format("Warning: the following enabled plugins were " - "not found: ~p~n", [Missing]) - end, - - %% Eliminate the contents of the destination directory - case delete_recursively(DestDir) of - ok -> ok; - {error, E} -> rabbit_misc:terminate("Could not delete dir ~s (~p)", - [DestDir, E]) - end, - case filelib:ensure_dir(DestDir ++ "/") of - ok -> ok; - {error, E2} -> rabbit_misc:terminate("Could not create dir ~s (~p)", - [DestDir, E2]) - end, - - [prepare_plugin(Plugin, DestDir) || Plugin <- ToUnpackPlugins]. - -prepare_dir_plugin(PluginAppDescFn) -> - %% Add the plugin ebin directory to the load path - PluginEBinDirN = filename:dirname(PluginAppDescFn), - code:add_path(PluginEBinDirN), - - %% We want the second-last token - NameTokens = string:tokens(PluginAppDescFn,"/."), - PluginNameString = lists:nth(length(NameTokens) - 1, NameTokens), - list_to_atom(PluginNameString). - -%%---------------------------------------------------------------------------- - -action(list, [], Opts, PluginsFile, PluginsDir) -> - action(list, [".*"], Opts, PluginsFile, PluginsDir); -action(list, [Pat], Opts, PluginsFile, PluginsDir) -> - format_plugins(Pat, Opts, PluginsFile, PluginsDir); - -action(enable, ToEnable0, _Opts, PluginsFile, PluginsDir) -> - case ToEnable0 of - [] -> throw({error_string, "Not enough arguments for 'enable'"}); - _ -> ok - end, - AllPlugins = find_plugins(PluginsDir), - Enabled = read_enabled_plugins(PluginsFile), - ImplicitlyEnabled = calculate_required_plugins(Enabled, AllPlugins), - ToEnable = [list_to_atom(Name) || Name <- ToEnable0], - Missing = ToEnable -- plugin_names(AllPlugins), - case Missing of - [] -> ok; - _ -> throw({error_string, - fmt_list("The following plugins could not be found:", - Missing)}) - end, - NewEnabled = lists:usort(Enabled ++ ToEnable), - write_enabled_plugins(PluginsFile, NewEnabled), - NewImplicitlyEnabled = calculate_required_plugins(NewEnabled, AllPlugins), - maybe_warn_mochiweb(NewImplicitlyEnabled), - case NewEnabled -- ImplicitlyEnabled of - [] -> io:format("Plugin configuration unchanged.~n"); - _ -> print_list("The following plugins have been enabled:", - NewImplicitlyEnabled -- ImplicitlyEnabled), - report_change() - end; - -action(disable, ToDisable0, _Opts, PluginsFile, PluginsDir) -> - case ToDisable0 of - [] -> throw({error_string, "Not enough arguments for 'disable'"}); - _ -> ok - end, - ToDisable = [list_to_atom(Name) || Name <- ToDisable0], - Enabled = read_enabled_plugins(PluginsFile), - AllPlugins = find_plugins(PluginsDir), - Missing = ToDisable -- plugin_names(AllPlugins), - case Missing of - [] -> ok; - _ -> print_list("Warning: the following plugins could not be found:", - Missing) - end, - ToDisableDeps = calculate_dependencies(true, ToDisable, AllPlugins), - NewEnabled = Enabled -- ToDisableDeps, - case length(Enabled) =:= length(NewEnabled) of - true -> io:format("Plugin configuration unchanged.~n"); - false -> ImplicitlyEnabled = - calculate_required_plugins(Enabled, AllPlugins), - NewImplicitlyEnabled = - calculate_required_plugins(NewEnabled, AllPlugins), - print_list("The following plugins have been disabled:", - ImplicitlyEnabled -- NewImplicitlyEnabled), - write_enabled_plugins(PluginsFile, NewEnabled), - report_change() - end. - -%%---------------------------------------------------------------------------- - -print_error(Format, Args) -> - rabbit_misc:format_stderr("Error: " ++ Format ++ "~n", Args). - -usage() -> - io:format("~s", [rabbit_plugins_usage:usage()]), - rabbit_misc:quit(1). - -delete_recursively(Fn) -> - case rabbit_file:recursive_delete([Fn]) of - ok -> ok; - {error, {Path, E}} -> {error, {cannot_delete, Path, E}}; - Error -> Error - end. - -prepare_plugin(#plugin{type = ez, location = Location}, PluginDestDir) -> - zip:unzip(Location, [{cwd, PluginDestDir}]); -prepare_plugin(#plugin{type = dir, name = Name, location = Location}, - PluginsDestDir) -> - rabbit_file:recursive_copy(Location, - filename:join([PluginsDestDir, Name])). - -%% Get the #plugin{}s ready to be enabled. -find_plugins(PluginsDir) -> - EZs = [{ez, EZ} || EZ <- filelib:wildcard("*.ez", PluginsDir)], - FreeApps = [{app, App} || - App <- filelib:wildcard("*/ebin/*.app", PluginsDir)], - {Plugins, Problems} = - lists:foldl(fun ({error, EZ, Reason}, {Plugins1, Problems1}) -> - {Plugins1, [{EZ, Reason} | Problems1]}; - (Plugin = #plugin{}, {Plugins1, Problems1}) -> - {[Plugin|Plugins1], Problems1} - end, {[], []}, - [get_plugin_info(PluginsDir, Plug) || - Plug <- EZs ++ FreeApps]), - case Problems of - [] -> ok; - _ -> io:format("Warning: Problem reading some plugins: ~p~n", - [Problems]) - end, - Plugins. - -%% Get the #plugin{} from an .ez. -get_plugin_info(Base, {ez, EZ0}) -> - EZ = filename:join([Base, EZ0]), - case read_app_file(EZ) of - {application, Name, Props} -> mkplugin(Name, Props, ez, EZ); - {error, Reason} -> {error, EZ, Reason} - end; -%% Get the #plugin{} from an .app. -get_plugin_info(Base, {app, App0}) -> - App = filename:join([Base, App0]), - case rabbit_file:read_term_file(App) of - {ok, [{application, Name, Props}]} -> - mkplugin(Name, Props, dir, - filename:absname( - filename:dirname(filename:dirname(App)))); - {error, Reason} -> - {error, App, {invalid_app, Reason}} - end. - -mkplugin(Name, Props, Type, Location) -> - Version = proplists:get_value(vsn, Props, "0"), - Description = proplists:get_value(description, Props, ""), - Dependencies = - filter_applications(proplists:get_value(applications, Props, [])), - #plugin{name = Name, version = Version, description = Description, - dependencies = Dependencies, location = Location, type = Type}. - -%% Read the .app file from an ez. -read_app_file(EZ) -> - case zip:list_dir(EZ) of - {ok, [_|ZippedFiles]} -> - case find_app_files(ZippedFiles) of - [AppPath|_] -> - {ok, [{AppPath, AppFile}]} = - zip:extract(EZ, [{file_list, [AppPath]}, memory]), - parse_binary(AppFile); - [] -> - {error, no_app_file} - end; - {error, Reason} -> - {error, {invalid_ez, Reason}} - end. - -%% Return the path of the .app files in ebin/. -find_app_files(ZippedFiles) -> - {ok, RE} = re:compile("^.*/ebin/.*.app$"), - [Path || {zip_file, Path, _, _, _, _} <- ZippedFiles, - re:run(Path, RE, [{capture, none}]) =:= match]. - -%% Parse a binary into a term. -parse_binary(Bin) -> - try - {ok, Ts, _} = erl_scan:string(binary_to_list(Bin)), - {ok, Term} = erl_parse:parse_term(Ts), - Term - catch - Err -> {error, {invalid_app, Err}} - end. - -%% Pretty print a list of plugins. -format_plugins(Pattern, Opts, PluginsFile, PluginsDir) -> - Verbose = proplists:get_bool(?VERBOSE_OPT, Opts), - Minimal = proplists:get_bool(?MINIMAL_OPT, Opts), - Format = case {Verbose, Minimal} of - {false, false} -> normal; - {true, false} -> verbose; - {false, true} -> minimal; - {true, true} -> throw({error_string, - "Cannot specify -m and -v together"}) - end, - OnlyEnabled = proplists:get_bool(?ENABLED_OPT, Opts), - OnlyEnabledAll = proplists:get_bool(?ENABLED_ALL_OPT, Opts), - - AvailablePlugins = find_plugins(PluginsDir), - EnabledExplicitly = read_enabled_plugins(PluginsFile), - EnabledImplicitly = - calculate_required_plugins(EnabledExplicitly, AvailablePlugins) -- - EnabledExplicitly, - {ok, RE} = re:compile(Pattern), - Plugins = [ Plugin || - Plugin = #plugin{name = Name} <- AvailablePlugins, - re:run(atom_to_list(Name), RE, [{capture, none}]) =:= match, - if OnlyEnabled -> lists:member(Name, EnabledExplicitly); - true -> true - end, - if OnlyEnabledAll -> - lists:member(Name, EnabledImplicitly) or - lists:member(Name, EnabledExplicitly); - true -> - true - end], - Plugins1 = usort_plugins(Plugins), - MaxWidth = lists:max([length(atom_to_list(Name)) || - #plugin{name = Name} <- Plugins1] ++ [0]), - [format_plugin(P, EnabledExplicitly, EnabledImplicitly, Format, - MaxWidth) || P <- Plugins1], - ok. - -format_plugin(#plugin{name = Name, version = Version, - description = Description, dependencies = Deps}, - EnabledExplicitly, EnabledImplicitly, Format, MaxWidth) -> - Glyph = case {lists:member(Name, EnabledExplicitly), - lists:member(Name, EnabledImplicitly)} of - {true, false} -> "[E]"; - {false, true} -> "[e]"; - _ -> "[ ]" - end, - case Format of - minimal -> io:format("~s~n", [Name]); - normal -> io:format("~s ~-" ++ integer_to_list(MaxWidth) ++ - "w ~s~n", [Glyph, Name, Version]); - verbose -> io:format("~s ~w~n", [Glyph, Name]), - io:format(" Version: \t~s~n", [Version]), - case Deps of - [] -> ok; - _ -> io:format(" Dependencies:\t~p~n", [Deps]) - end, - io:format(" Description:\t~s~n", [Description]), - io:format("~n") - end. - -print_list(Header, Plugins) -> - io:format(fmt_list(Header, Plugins)). - -fmt_list(Header, Plugins) -> - lists:flatten( - [Header, $\n, [io_lib:format(" ~s~n", [P]) || P <- Plugins]]). - -usort_plugins(Plugins) -> - lists:usort(fun plugins_cmp/2, Plugins). - -plugins_cmp(#plugin{name = N1, version = V1}, - #plugin{name = N2, version = V2}) -> - {N1, V1} =< {N2, V2}. - -%% Filter out applications that can be loaded *right now*. -filter_applications(Applications) -> - [Application || Application <- Applications, - not is_available_app(Application)]. - -%% Return whether is application is already available (and hence -%% doesn't need enabling). -is_available_app(Application) -> - case application:load(Application) of - {error, {already_loaded, _}} -> true; - ok -> application:unload(Application), - true; - _ -> false - end. - -%% Return the names of the given plugins. -plugin_names(Plugins) -> - [Name || #plugin{name = Name} <- Plugins]. - -%% Find plugins by name in a list of plugins. -lookup_plugins(Names, AllPlugins) -> - [P || P = #plugin{name = Name} <- AllPlugins, lists:member(Name, Names)]. - -%% Read the enabled plugin names from disk. -read_enabled_plugins(PluginsFile) -> - case rabbit_file:read_term_file(PluginsFile) of - {ok, [Plugins]} -> Plugins; - {ok, []} -> []; - {ok, [_|_]} -> throw({error, {malformed_enabled_plugins_file, - PluginsFile}}); - {error, enoent} -> []; - {error, Reason} -> throw({error, {cannot_read_enabled_plugins_file, - PluginsFile, Reason}}) - end. - -%% Write the enabled plugin names on disk. -write_enabled_plugins(PluginsFile, Plugins) -> - case rabbit_file:write_term_file(PluginsFile, [Plugins]) of - ok -> ok; - {error, Reason} -> throw({error, {cannot_write_enabled_plugins_file, - PluginsFile, Reason}}) - end. - -calculate_required_plugins(Sources, AllPlugins) -> - calculate_dependencies(false, Sources, AllPlugins). - -calculate_dependencies(Reverse, Sources, AllPlugins) -> - {ok, G} = rabbit_misc:build_acyclic_graph( - fun (App, _Deps) -> [{App, App}] end, - fun (App, Deps) -> [{App, Dep} || Dep <- Deps] end, - [{Name, Deps} - || #plugin{name = Name, dependencies = Deps} <- AllPlugins]), - Dests = case Reverse of - false -> digraph_utils:reachable(Sources, G); - true -> digraph_utils:reaching(Sources, G) - end, - true = digraph:delete(G), - Dests. - -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"), - case os:type() of - {win32, _OsName} -> - io:format("If you have RabbitMQ running as a service then you must" - " reinstall by running~n rabbitmq-service.bat stop~n" - " rabbitmq-service.bat install~n" - " rabbitmq-service.bat start~n~n"); - _ -> - ok - end. - diff --git a/src/rabbit_plugins_main.erl b/src/rabbit_plugins_main.erl new file mode 100644 index 00000000..8a8e7052 --- /dev/null +++ b/src/rabbit_plugins_main.erl @@ -0,0 +1,467 @@ +%% 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 VMware, Inc. +%% Copyright (c) 2011-2012 VMware, Inc. All rights reserved. +%% + +-module(rabbit_plugins). +-include("rabbit.hrl"). + +-export([start/0, stop/0, prepare_plugins/0, active_plugins/0]). + +-define(VERBOSE_OPT, "-v"). +-define(MINIMAL_OPT, "-m"). +-define(ENABLED_OPT, "-E"). +-define(ENABLED_ALL_OPT, "-e"). + +%%---------------------------------------------------------------------------- + +-ifdef(use_specs). + +-spec(start/0 :: () -> no_return()). +-spec(stop/0 :: () -> 'ok'). +-spec(prepare_plugins/0 :: () -> [atom()]). +-spec(active_plugins/0 :: () -> [atom()]). + +-endif. + +%%---------------------------------------------------------------------------- + +start() -> + {ok, [[PluginsFile|_]|_]} = + init:get_argument(enabled_plugins_file), + {ok, [[PluginsDir|_]|_]} = init:get_argument(plugins_dist_dir), + {[Command0 | Args], Opts} = + case rabbit_misc:get_options([{flag, ?VERBOSE_OPT}, + {flag, ?MINIMAL_OPT}, + {flag, ?ENABLED_OPT}, + {flag, ?ENABLED_ALL_OPT}], + init:get_plain_arguments()) of + {[], _Opts} -> usage(); + CmdArgsAndOpts -> CmdArgsAndOpts + end, + Command = list_to_atom(Command0), + PrintInvalidCommandError = + fun () -> + print_error("invalid command '~s'", + [string:join([atom_to_list(Command) | Args], " ")]) + end, + + case catch action(Command, Args, Opts, PluginsFile, PluginsDir) of + ok -> + rabbit_misc:quit(0); + {'EXIT', {function_clause, [{?MODULE, action, _} | _]}} -> + PrintInvalidCommandError(), + usage(); + {'EXIT', {function_clause, [{?MODULE, action, _, _} | _]}} -> + PrintInvalidCommandError(), + usage(); + {error, Reason} -> + print_error("~p", [Reason]), + rabbit_misc:quit(2); + {error_string, Reason} -> + print_error("~s", [Reason]), + rabbit_misc:quit(2); + Other -> + print_error("~p", [Other]), + rabbit_misc:quit(2) + end. + +stop() -> + ok. + +prepare_plugins() -> + {ok, PluginDir} = application:get_env(rabbit, plugins_dir), + {ok, ExpandDir} = application:get_env(rabbit, plugins_expand_dir), + {ok, EnabledPluginsFile} = application:get_env(rabbit, + enabled_plugins_file), + prepare_plugins(EnabledPluginsFile, PluginDir, ExpandDir), + [prepare_dir_plugin(PluginName) || + PluginName <- filelib:wildcard(ExpandDir ++ "/*/ebin/*.app")]. + +active_plugins() -> + {ok, ExpandDir} = application:get_env(rabbit, plugins_expand_dir), + InstalledPlugins = [ P#plugin.name || P <- find_plugins(ExpandDir) ], + [App || {App, _, _} <- application:which_applications(), + lists:member(App, InstalledPlugins)]. + +%%---------------------------------------------------------------------------- + +prepare_plugins(EnabledPluginsFile, PluginsDistDir, DestDir) -> + AllPlugins = find_plugins(PluginsDistDir), + Enabled = read_enabled_plugins(EnabledPluginsFile), + ToUnpack = calculate_required_plugins(Enabled, AllPlugins), + ToUnpackPlugins = lookup_plugins(ToUnpack, AllPlugins), + + Missing = Enabled -- plugin_names(ToUnpackPlugins), + case Missing of + [] -> ok; + _ -> io:format("Warning: the following enabled plugins were " + "not found: ~p~n", [Missing]) + end, + + %% Eliminate the contents of the destination directory + case delete_recursively(DestDir) of + ok -> ok; + {error, E} -> rabbit_misc:terminate("Could not delete dir ~s (~p)", + [DestDir, E]) + end, + case filelib:ensure_dir(DestDir ++ "/") of + ok -> ok; + {error, E2} -> rabbit_misc:terminate("Could not create dir ~s (~p)", + [DestDir, E2]) + end, + + [prepare_plugin(Plugin, DestDir) || Plugin <- ToUnpackPlugins]. + +prepare_dir_plugin(PluginAppDescFn) -> + %% Add the plugin ebin directory to the load path + PluginEBinDirN = filename:dirname(PluginAppDescFn), + code:add_path(PluginEBinDirN), + + %% We want the second-last token + NameTokens = string:tokens(PluginAppDescFn,"/."), + PluginNameString = lists:nth(length(NameTokens) - 1, NameTokens), + list_to_atom(PluginNameString). + +%%---------------------------------------------------------------------------- + +action(list, [], Opts, PluginsFile, PluginsDir) -> + action(list, [".*"], Opts, PluginsFile, PluginsDir); +action(list, [Pat], Opts, PluginsFile, PluginsDir) -> + format_plugins(Pat, Opts, PluginsFile, PluginsDir); + +action(enable, ToEnable0, _Opts, PluginsFile, PluginsDir) -> + case ToEnable0 of + [] -> throw({error_string, "Not enough arguments for 'enable'"}); + _ -> ok + end, + AllPlugins = find_plugins(PluginsDir), + Enabled = read_enabled_plugins(PluginsFile), + ImplicitlyEnabled = calculate_required_plugins(Enabled, AllPlugins), + ToEnable = [list_to_atom(Name) || Name <- ToEnable0], + Missing = ToEnable -- plugin_names(AllPlugins), + case Missing of + [] -> ok; + _ -> throw({error_string, + fmt_list("The following plugins could not be found:", + Missing)}) + end, + NewEnabled = lists:usort(Enabled ++ ToEnable), + write_enabled_plugins(PluginsFile, NewEnabled), + NewImplicitlyEnabled = calculate_required_plugins(NewEnabled, AllPlugins), + maybe_warn_mochiweb(NewImplicitlyEnabled), + case NewEnabled -- ImplicitlyEnabled of + [] -> io:format("Plugin configuration unchanged.~n"); + _ -> print_list("The following plugins have been enabled:", + NewImplicitlyEnabled -- ImplicitlyEnabled), + report_change() + end; + +action(disable, ToDisable0, _Opts, PluginsFile, PluginsDir) -> + case ToDisable0 of + [] -> throw({error_string, "Not enough arguments for 'disable'"}); + _ -> ok + end, + ToDisable = [list_to_atom(Name) || Name <- ToDisable0], + Enabled = read_enabled_plugins(PluginsFile), + AllPlugins = find_plugins(PluginsDir), + Missing = ToDisable -- plugin_names(AllPlugins), + case Missing of + [] -> ok; + _ -> print_list("Warning: the following plugins could not be found:", + Missing) + end, + ToDisableDeps = calculate_dependencies(true, ToDisable, AllPlugins), + NewEnabled = Enabled -- ToDisableDeps, + case length(Enabled) =:= length(NewEnabled) of + true -> io:format("Plugin configuration unchanged.~n"); + false -> ImplicitlyEnabled = + calculate_required_plugins(Enabled, AllPlugins), + NewImplicitlyEnabled = + calculate_required_plugins(NewEnabled, AllPlugins), + print_list("The following plugins have been disabled:", + ImplicitlyEnabled -- NewImplicitlyEnabled), + write_enabled_plugins(PluginsFile, NewEnabled), + report_change() + end. + +%%---------------------------------------------------------------------------- + +print_error(Format, Args) -> + rabbit_misc:format_stderr("Error: " ++ Format ++ "~n", Args). + +usage() -> + io:format("~s", [rabbit_plugins_usage:usage()]), + rabbit_misc:quit(1). + +delete_recursively(Fn) -> + case rabbit_file:recursive_delete([Fn]) of + ok -> ok; + {error, {Path, E}} -> {error, {cannot_delete, Path, E}}; + Error -> Error + end. + +prepare_plugin(#plugin{type = ez, location = Location}, PluginDestDir) -> + zip:unzip(Location, [{cwd, PluginDestDir}]); +prepare_plugin(#plugin{type = dir, name = Name, location = Location}, + PluginsDestDir) -> + rabbit_file:recursive_copy(Location, + filename:join([PluginsDestDir, Name])). + +%% Get the #plugin{}s ready to be enabled. +find_plugins(PluginsDir) -> + EZs = [{ez, EZ} || EZ <- filelib:wildcard("*.ez", PluginsDir)], + FreeApps = [{app, App} || + App <- filelib:wildcard("*/ebin/*.app", PluginsDir)], + {Plugins, Problems} = + lists:foldl(fun ({error, EZ, Reason}, {Plugins1, Problems1}) -> + {Plugins1, [{EZ, Reason} | Problems1]}; + (Plugin = #plugin{}, {Plugins1, Problems1}) -> + {[Plugin|Plugins1], Problems1} + end, {[], []}, + [get_plugin_info(PluginsDir, Plug) || + Plug <- EZs ++ FreeApps]), + case Problems of + [] -> ok; + _ -> io:format("Warning: Problem reading some plugins: ~p~n", + [Problems]) + end, + Plugins. + +%% Get the #plugin{} from an .ez. +get_plugin_info(Base, {ez, EZ0}) -> + EZ = filename:join([Base, EZ0]), + case read_app_file(EZ) of + {application, Name, Props} -> mkplugin(Name, Props, ez, EZ); + {error, Reason} -> {error, EZ, Reason} + end; +%% Get the #plugin{} from an .app. +get_plugin_info(Base, {app, App0}) -> + App = filename:join([Base, App0]), + case rabbit_file:read_term_file(App) of + {ok, [{application, Name, Props}]} -> + mkplugin(Name, Props, dir, + filename:absname( + filename:dirname(filename:dirname(App)))); + {error, Reason} -> + {error, App, {invalid_app, Reason}} + end. + +mkplugin(Name, Props, Type, Location) -> + Version = proplists:get_value(vsn, Props, "0"), + Description = proplists:get_value(description, Props, ""), + Dependencies = + filter_applications(proplists:get_value(applications, Props, [])), + #plugin{name = Name, version = Version, description = Description, + dependencies = Dependencies, location = Location, type = Type}. + +%% Read the .app file from an ez. +read_app_file(EZ) -> + case zip:list_dir(EZ) of + {ok, [_|ZippedFiles]} -> + case find_app_files(ZippedFiles) of + [AppPath|_] -> + {ok, [{AppPath, AppFile}]} = + zip:extract(EZ, [{file_list, [AppPath]}, memory]), + parse_binary(AppFile); + [] -> + {error, no_app_file} + end; + {error, Reason} -> + {error, {invalid_ez, Reason}} + end. + +%% Return the path of the .app files in ebin/. +find_app_files(ZippedFiles) -> + {ok, RE} = re:compile("^.*/ebin/.*.app$"), + [Path || {zip_file, Path, _, _, _, _} <- ZippedFiles, + re:run(Path, RE, [{capture, none}]) =:= match]. + +%% Parse a binary into a term. +parse_binary(Bin) -> + try + {ok, Ts, _} = erl_scan:string(binary_to_list(Bin)), + {ok, Term} = erl_parse:parse_term(Ts), + Term + catch + Err -> {error, {invalid_app, Err}} + end. + +%% Pretty print a list of plugins. +format_plugins(Pattern, Opts, PluginsFile, PluginsDir) -> + Verbose = proplists:get_bool(?VERBOSE_OPT, Opts), + Minimal = proplists:get_bool(?MINIMAL_OPT, Opts), + Format = case {Verbose, Minimal} of + {false, false} -> normal; + {true, false} -> verbose; + {false, true} -> minimal; + {true, true} -> throw({error_string, + "Cannot specify -m and -v together"}) + end, + OnlyEnabled = proplists:get_bool(?ENABLED_OPT, Opts), + OnlyEnabledAll = proplists:get_bool(?ENABLED_ALL_OPT, Opts), + + AvailablePlugins = find_plugins(PluginsDir), + EnabledExplicitly = read_enabled_plugins(PluginsFile), + EnabledImplicitly = + calculate_required_plugins(EnabledExplicitly, AvailablePlugins) -- + EnabledExplicitly, + {ok, RE} = re:compile(Pattern), + Plugins = [ Plugin || + Plugin = #plugin{name = Name} <- AvailablePlugins, + re:run(atom_to_list(Name), RE, [{capture, none}]) =:= match, + if OnlyEnabled -> lists:member(Name, EnabledExplicitly); + true -> true + end, + if OnlyEnabledAll -> + lists:member(Name, EnabledImplicitly) or + lists:member(Name, EnabledExplicitly); + true -> + true + end], + Plugins1 = usort_plugins(Plugins), + MaxWidth = lists:max([length(atom_to_list(Name)) || + #plugin{name = Name} <- Plugins1] ++ [0]), + [format_plugin(P, EnabledExplicitly, EnabledImplicitly, Format, + MaxWidth) || P <- Plugins1], + ok. + +format_plugin(#plugin{name = Name, version = Version, + description = Description, dependencies = Deps}, + EnabledExplicitly, EnabledImplicitly, Format, MaxWidth) -> + Glyph = case {lists:member(Name, EnabledExplicitly), + lists:member(Name, EnabledImplicitly)} of + {true, false} -> "[E]"; + {false, true} -> "[e]"; + _ -> "[ ]" + end, + case Format of + minimal -> io:format("~s~n", [Name]); + normal -> io:format("~s ~-" ++ integer_to_list(MaxWidth) ++ + "w ~s~n", [Glyph, Name, Version]); + verbose -> io:format("~s ~w~n", [Glyph, Name]), + io:format(" Version: \t~s~n", [Version]), + case Deps of + [] -> ok; + _ -> io:format(" Dependencies:\t~p~n", [Deps]) + end, + io:format(" Description:\t~s~n", [Description]), + io:format("~n") + end. + +print_list(Header, Plugins) -> + io:format(fmt_list(Header, Plugins)). + +fmt_list(Header, Plugins) -> + lists:flatten( + [Header, $\n, [io_lib:format(" ~s~n", [P]) || P <- Plugins]]). + +usort_plugins(Plugins) -> + lists:usort(fun plugins_cmp/2, Plugins). + +plugins_cmp(#plugin{name = N1, version = V1}, + #plugin{name = N2, version = V2}) -> + {N1, V1} =< {N2, V2}. + +%% Filter out applications that can be loaded *right now*. +filter_applications(Applications) -> + [Application || Application <- Applications, + not is_available_app(Application)]. + +%% Return whether is application is already available (and hence +%% doesn't need enabling). +is_available_app(Application) -> + case application:load(Application) of + {error, {already_loaded, _}} -> true; + ok -> application:unload(Application), + true; + _ -> false + end. + +%% Return the names of the given plugins. +plugin_names(Plugins) -> + [Name || #plugin{name = Name} <- Plugins]. + +%% Find plugins by name in a list of plugins. +lookup_plugins(Names, AllPlugins) -> + [P || P = #plugin{name = Name} <- AllPlugins, lists:member(Name, Names)]. + +%% Read the enabled plugin names from disk. +read_enabled_plugins(PluginsFile) -> + case rabbit_file:read_term_file(PluginsFile) of + {ok, [Plugins]} -> Plugins; + {ok, []} -> []; + {ok, [_|_]} -> throw({error, {malformed_enabled_plugins_file, + PluginsFile}}); + {error, enoent} -> []; + {error, Reason} -> throw({error, {cannot_read_enabled_plugins_file, + PluginsFile, Reason}}) + end. + +%% Write the enabled plugin names on disk. +write_enabled_plugins(PluginsFile, Plugins) -> + case rabbit_file:write_term_file(PluginsFile, [Plugins]) of + ok -> ok; + {error, Reason} -> throw({error, {cannot_write_enabled_plugins_file, + PluginsFile, Reason}}) + end. + +calculate_required_plugins(Sources, AllPlugins) -> + calculate_dependencies(false, Sources, AllPlugins). + +calculate_dependencies(Reverse, Sources, AllPlugins) -> + {ok, G} = rabbit_misc:build_acyclic_graph( + fun (App, _Deps) -> [{App, App}] end, + fun (App, Deps) -> [{App, Dep} || Dep <- Deps] end, + [{Name, Deps} + || #plugin{name = Name, dependencies = Deps} <- AllPlugins]), + Dests = case Reverse of + false -> digraph_utils:reachable(Sources, G); + true -> digraph_utils:reaching(Sources, G) + end, + true = digraph:delete(G), + Dests. + +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"), + case os:type() of + {win32, _OsName} -> + io:format("If you have RabbitMQ running as a service then you must" + " reinstall by running~n rabbitmq-service.bat stop~n" + " rabbitmq-service.bat install~n" + " rabbitmq-service.bat start~n~n"); + _ -> + ok + end. + -- cgit v1.2.1 From f2afc268cbadfa22fb3c292eae91862e72d7319f Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 21 May 2012 14:36:10 +0100 Subject: rework e4649f81c3a6 commit history for renaming --- src/rabbit_control.erl | 581 -------------------------------------------- src/rabbit_control_main.erl | 581 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 581 insertions(+), 581 deletions(-) delete mode 100644 src/rabbit_control.erl create mode 100644 src/rabbit_control_main.erl diff --git a/src/rabbit_control.erl b/src/rabbit_control.erl deleted file mode 100644 index 6dc8d445..00000000 --- a/src/rabbit_control.erl +++ /dev/null @@ -1,581 +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 VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. -%% - --module(rabbit_control). --include("rabbit.hrl"). - --export([start/0, stop/0, action/5]). - --define(RPC_TIMEOUT, infinity). --define(EXTERNAL_CHECK_INTERVAL, 1000). - --define(QUIET_OPT, "-q"). --define(NODE_OPT, "-n"). --define(VHOST_OPT, "-p"). - --define(GLOBAL_QUERIES, - [{"Connections", rabbit_networking, connection_info_all, - connection_info_keys}, - {"Channels", rabbit_channel, info_all, info_keys}]). - --define(VHOST_QUERIES, - [{"Queues", rabbit_amqqueue, info_all, info_keys}, - {"Exchanges", rabbit_exchange, info_all, info_keys}, - {"Bindings", rabbit_binding, info_all, info_keys}, - {"Consumers", rabbit_amqqueue, consumers_all, consumer_info_keys}, - {"Permissions", rabbit_auth_backend_internal, list_vhost_permissions, - vhost_perms_info_keys}]). - -%%---------------------------------------------------------------------------- - --ifdef(use_specs). - --spec(start/0 :: () -> no_return()). --spec(stop/0 :: () -> 'ok'). --spec(action/5 :: - (atom(), node(), [string()], [{string(), any()}], - fun ((string(), [any()]) -> 'ok')) - -> 'ok'). --spec(usage/0 :: () -> no_return()). - --endif. - -%%---------------------------------------------------------------------------- - -start() -> - {ok, [[NodeStr|_]|_]} = init:get_argument(nodename), - {[Command0 | Args], Opts} = - case rabbit_misc:get_options([{flag, ?QUIET_OPT}, - {option, ?NODE_OPT, NodeStr}, - {option, ?VHOST_OPT, "/"}], - init:get_plain_arguments()) of - {[], _Opts} -> usage(); - CmdArgsAndOpts -> CmdArgsAndOpts - end, - Opts1 = [case K of - ?NODE_OPT -> {?NODE_OPT, rabbit_nodes:make(V)}; - _ -> {K, V} - end || {K, V} <- Opts], - Command = list_to_atom(Command0), - Quiet = proplists:get_bool(?QUIET_OPT, Opts1), - Node = proplists:get_value(?NODE_OPT, Opts1), - Inform = case Quiet of - true -> fun (_Format, _Args1) -> ok end; - false -> fun (Format, Args1) -> - io:format(Format ++ " ...~n", Args1) - end - end, - PrintInvalidCommandError = - fun () -> - print_error("invalid command '~s'", - [string:join([atom_to_list(Command) | Args], " ")]) - end, - - %% The reason we don't use a try/catch here is that rpc:call turns - %% thrown errors into normal return values - case catch action(Command, Node, Args, Opts, Inform) of - ok -> - case Quiet of - true -> ok; - false -> io:format("...done.~n") - end, - rabbit_misc:quit(0); - {'EXIT', {function_clause, [{?MODULE, action, _} | _]}} -> %% < R15 - PrintInvalidCommandError(), - usage(); - {'EXIT', {function_clause, [{?MODULE, action, _, _} | _]}} -> %% >= R15 - PrintInvalidCommandError(), - usage(); - {'EXIT', {badarg, _}} -> - print_error("invalid parameter: ~p", [Args]), - usage(); - {error, Reason} -> - print_error("~p", [Reason]), - rabbit_misc:quit(2); - {error_string, Reason} -> - print_error("~s", [Reason]), - rabbit_misc:quit(2); - {badrpc, {'EXIT', Reason}} -> - print_error("~p", [Reason]), - rabbit_misc:quit(2); - {badrpc, Reason} -> - print_error("unable to connect to node ~w: ~w", [Node, Reason]), - print_badrpc_diagnostics(Node), - rabbit_misc:quit(2); - Other -> - print_error("~p", [Other]), - rabbit_misc:quit(2) - end. - -fmt_stderr(Format, Args) -> rabbit_misc:format_stderr(Format ++ "~n", Args). - -print_report(Node, {Descr, Module, InfoFun, KeysFun}) -> - io:format("~s:~n", [Descr]), - print_report0(Node, {Module, InfoFun, KeysFun}, []). - -print_report(Node, {Descr, Module, InfoFun, KeysFun}, VHostArg) -> - io:format("~s on ~s:~n", [Descr, VHostArg]), - print_report0(Node, {Module, InfoFun, KeysFun}, VHostArg). - -print_report0(Node, {Module, InfoFun, KeysFun}, VHostArg) -> - case Results = rpc_call(Node, Module, InfoFun, VHostArg) of - [_|_] -> InfoItems = rpc_call(Node, Module, KeysFun, []), - display_row([atom_to_list(I) || I <- InfoItems]), - display_info_list(Results, InfoItems); - _ -> ok - end, - io:nl(). - -print_error(Format, Args) -> fmt_stderr("Error: " ++ Format, Args). - -print_badrpc_diagnostics(Node) -> - fmt_stderr(rabbit_nodes:diagnostics([Node]), []). - -stop() -> - ok. - -usage() -> - io:format("~s", [rabbit_ctl_usage:usage()]), - rabbit_misc:quit(1). - -%%---------------------------------------------------------------------------- - -action(stop, Node, Args, _Opts, Inform) -> - Inform("Stopping and halting node ~p", [Node]), - Res = call(Node, {rabbit, stop_and_halt, []}), - case {Res, Args} of - {ok, [PidFile]} -> wait_for_process_death( - read_pid_file(PidFile, false)); - {ok, [_, _| _]} -> exit({badarg, Args}); - _ -> ok - end, - Res; - -action(stop_app, Node, [], _Opts, Inform) -> - Inform("Stopping node ~p", [Node]), - call(Node, {rabbit, stop, []}); - -action(start_app, Node, [], _Opts, Inform) -> - Inform("Starting node ~p", [Node]), - call(Node, {rabbit, start, []}); - -action(reset, Node, [], _Opts, Inform) -> - Inform("Resetting node ~p", [Node]), - call(Node, {rabbit_mnesia, reset, []}); - -action(force_reset, Node, [], _Opts, Inform) -> - Inform("Forcefully resetting node ~p", [Node]), - call(Node, {rabbit_mnesia, force_reset, []}); - -action(cluster, Node, ClusterNodeSs, _Opts, Inform) -> - ClusterNodes = lists:map(fun list_to_atom/1, ClusterNodeSs), - Inform("Clustering node ~p with ~p", - [Node, ClusterNodes]), - rpc_call(Node, rabbit_mnesia, cluster, [ClusterNodes]); - -action(force_cluster, Node, ClusterNodeSs, _Opts, Inform) -> - ClusterNodes = lists:map(fun list_to_atom/1, ClusterNodeSs), - Inform("Forcefully clustering node ~p with ~p (ignoring offline nodes)", - [Node, ClusterNodes]), - rpc_call(Node, rabbit_mnesia, force_cluster, [ClusterNodes]); - -action(wait, Node, [PidFile], _Opts, Inform) -> - Inform("Waiting for ~p", [Node]), - wait_for_application(Node, PidFile, rabbit, Inform); - -action(wait, Node, [PidFile, App], _Opts, Inform) -> - Inform("Waiting for ~p on ~p", [App, Node]), - wait_for_application(Node, PidFile, list_to_atom(App), Inform); - -action(status, Node, [], _Opts, Inform) -> - Inform("Status of node ~p", [Node]), - display_call_result(Node, {rabbit, status, []}); - -action(cluster_status, Node, [], _Opts, Inform) -> - Inform("Cluster status of node ~p", [Node]), - display_call_result(Node, {rabbit_mnesia, status, []}); - -action(environment, Node, _App, _Opts, Inform) -> - Inform("Application environment of node ~p", [Node]), - display_call_result(Node, {rabbit, environment, []}); - -action(rotate_logs, Node, [], _Opts, Inform) -> - Inform("Reopening logs for node ~p", [Node]), - call(Node, {rabbit, rotate_logs, [""]}); -action(rotate_logs, Node, Args = [Suffix], _Opts, Inform) -> - Inform("Rotating logs to files with suffix ~p", [Suffix]), - call(Node, {rabbit, rotate_logs, Args}); - -action(close_connection, Node, [PidStr, Explanation], _Opts, Inform) -> - Inform("Closing connection ~s", [PidStr]), - rpc_call(Node, rabbit_networking, close_connection, - [rabbit_misc:string_to_pid(PidStr), Explanation]); - -action(add_user, Node, Args = [Username, _Password], _Opts, Inform) -> - Inform("Creating user ~p", [Username]), - call(Node, {rabbit_auth_backend_internal, add_user, Args}); - -action(delete_user, Node, Args = [_Username], _Opts, Inform) -> - Inform("Deleting user ~p", Args), - call(Node, {rabbit_auth_backend_internal, delete_user, Args}); - -action(change_password, Node, Args = [Username, _Newpassword], _Opts, Inform) -> - Inform("Changing password for user ~p", [Username]), - call(Node, {rabbit_auth_backend_internal, change_password, Args}); - -action(clear_password, Node, Args = [Username], _Opts, Inform) -> - Inform("Clearing password for user ~p", [Username]), - call(Node, {rabbit_auth_backend_internal, clear_password, Args}); - -action(set_user_tags, Node, [Username | TagsStr], _Opts, Inform) -> - Tags = [list_to_atom(T) || T <- TagsStr], - Inform("Setting tags for user ~p to ~p", [Username, Tags]), - rpc_call(Node, rabbit_auth_backend_internal, set_tags, - [list_to_binary(Username), Tags]); - -action(list_users, Node, [], _Opts, Inform) -> - Inform("Listing users", []), - display_info_list( - call(Node, {rabbit_auth_backend_internal, list_users, []}), - rabbit_auth_backend_internal:user_info_keys()); - -action(add_vhost, Node, Args = [_VHostPath], _Opts, Inform) -> - Inform("Creating vhost ~p", Args), - call(Node, {rabbit_vhost, add, Args}); - -action(delete_vhost, Node, Args = [_VHostPath], _Opts, Inform) -> - Inform("Deleting vhost ~p", Args), - call(Node, {rabbit_vhost, delete, Args}); - -action(list_vhosts, Node, Args, _Opts, Inform) -> - Inform("Listing vhosts", []), - ArgAtoms = default_if_empty(Args, [name]), - display_info_list(call(Node, {rabbit_vhost, info_all, []}), ArgAtoms); - -action(list_user_permissions, Node, Args = [_Username], _Opts, Inform) -> - Inform("Listing permissions for user ~p", Args), - display_info_list(call(Node, {rabbit_auth_backend_internal, - list_user_permissions, Args}), - rabbit_auth_backend_internal:user_perms_info_keys()); - -action(list_queues, Node, Args, Opts, Inform) -> - Inform("Listing queues", []), - VHostArg = list_to_binary(proplists:get_value(?VHOST_OPT, Opts)), - ArgAtoms = default_if_empty(Args, [name, messages]), - display_info_list(rpc_call(Node, rabbit_amqqueue, info_all, - [VHostArg, ArgAtoms]), - ArgAtoms); - -action(list_exchanges, Node, Args, Opts, Inform) -> - Inform("Listing exchanges", []), - VHostArg = list_to_binary(proplists:get_value(?VHOST_OPT, Opts)), - ArgAtoms = default_if_empty(Args, [name, type]), - display_info_list(rpc_call(Node, rabbit_exchange, info_all, - [VHostArg, ArgAtoms]), - ArgAtoms); - -action(list_bindings, Node, Args, Opts, Inform) -> - Inform("Listing bindings", []), - VHostArg = list_to_binary(proplists:get_value(?VHOST_OPT, Opts)), - ArgAtoms = default_if_empty(Args, [source_name, source_kind, - destination_name, destination_kind, - routing_key, arguments]), - display_info_list(rpc_call(Node, rabbit_binding, info_all, - [VHostArg, ArgAtoms]), - ArgAtoms); - -action(list_connections, Node, Args, _Opts, Inform) -> - Inform("Listing connections", []), - ArgAtoms = default_if_empty(Args, [user, peer_address, peer_port, state]), - display_info_list(rpc_call(Node, rabbit_networking, connection_info_all, - [ArgAtoms]), - ArgAtoms); - -action(list_channels, Node, Args, _Opts, Inform) -> - Inform("Listing channels", []), - ArgAtoms = default_if_empty(Args, [pid, user, consumer_count, - messages_unacknowledged]), - display_info_list(rpc_call(Node, rabbit_channel, info_all, [ArgAtoms]), - ArgAtoms); - -action(list_consumers, Node, _Args, Opts, Inform) -> - Inform("Listing consumers", []), - VHostArg = list_to_binary(proplists:get_value(?VHOST_OPT, Opts)), - display_info_list(rpc_call(Node, rabbit_amqqueue, consumers_all, [VHostArg]), - rabbit_amqqueue:consumer_info_keys()); - -action(trace_on, Node, [], Opts, Inform) -> - VHost = proplists:get_value(?VHOST_OPT, Opts), - Inform("Starting tracing for vhost ~p", [VHost]), - rpc_call(Node, rabbit_trace, start, [list_to_binary(VHost)]); - -action(trace_off, Node, [], Opts, Inform) -> - VHost = proplists:get_value(?VHOST_OPT, Opts), - Inform("Stopping tracing for vhost ~p", [VHost]), - rpc_call(Node, rabbit_trace, stop, [list_to_binary(VHost)]); - -action(set_vm_memory_high_watermark, Node, [Arg], _Opts, Inform) -> - Frac = list_to_float(case string:chr(Arg, $.) of - 0 -> Arg ++ ".0"; - _ -> Arg - end), - Inform("Setting memory threshold on ~p to ~p", [Node, Frac]), - rpc_call(Node, vm_memory_monitor, set_vm_memory_high_watermark, [Frac]); - -action(set_permissions, Node, [Username, CPerm, WPerm, RPerm], Opts, Inform) -> - VHost = proplists:get_value(?VHOST_OPT, Opts), - Inform("Setting permissions for user ~p in vhost ~p", [Username, VHost]), - call(Node, {rabbit_auth_backend_internal, set_permissions, - [Username, VHost, CPerm, WPerm, RPerm]}); - -action(clear_permissions, Node, [Username], Opts, Inform) -> - VHost = proplists:get_value(?VHOST_OPT, Opts), - Inform("Clearing permissions for user ~p in vhost ~p", [Username, VHost]), - call(Node, {rabbit_auth_backend_internal, clear_permissions, - [Username, VHost]}); - -action(list_permissions, Node, [], Opts, Inform) -> - VHost = proplists:get_value(?VHOST_OPT, Opts), - Inform("Listing permissions in vhost ~p", [VHost]), - display_info_list(call(Node, {rabbit_auth_backend_internal, - list_vhost_permissions, [VHost]}), - rabbit_auth_backend_internal:vhost_perms_info_keys()); - -action(set_parameter, Node, [Component, Key, Value], _Opts, Inform) -> - Inform("Setting runtime parameter ~p for component ~p to ~p", - [Key, Component, Value]), - rpc_call(Node, rabbit_runtime_parameters, parse_set, - [list_to_binary(Component), list_to_binary(Key), Value]); - -action(clear_parameter, Node, [Component, Key], _Opts, Inform) -> - Inform("Clearing runtime parameter ~p for component ~p", [Key, Component]), - rpc_call(Node, rabbit_runtime_parameters, clear, [list_to_binary(Component), - list_to_binary(Key)]); - -action(list_parameters, Node, Args = [], _Opts, Inform) -> - Inform("Listing runtime parameters", []), - display_info_list( - rpc_call(Node, rabbit_runtime_parameters, list_formatted, Args), - rabbit_runtime_parameters:info_keys()); - -action(report, Node, _Args, _Opts, Inform) -> - io:format("Reporting server status on ~p~n~n", [erlang:universaltime()]), - [begin ok = action(Action, N, [], [], Inform), io:nl() end || - N <- unsafe_rpc(Node, rabbit_mnesia, running_clustered_nodes, []), - Action <- [status, cluster_status, environment]], - VHosts = unsafe_rpc(Node, rabbit_vhost, list, []), - [print_report(Node, Q) || Q <- ?GLOBAL_QUERIES], - [print_report(Node, Q, [V]) || Q <- ?VHOST_QUERIES, V <- VHosts], - io:format("End of server status report~n"), - ok; - -action(eval, Node, [Expr], _Opts, _Inform) -> - case erl_scan:string(Expr) of - {ok, Scanned, _} -> - case erl_parse:parse_exprs(Scanned) of - {ok, Parsed} -> - {value, Value, _} = unsafe_rpc( - Node, erl_eval, exprs, [Parsed, []]), - io:format("~p~n", [Value]), - ok; - {error, E} -> - {error_string, format_parse_error(E)} - end; - {error, E, _} -> - {error_string, format_parse_error(E)} - end. - -%%---------------------------------------------------------------------------- - -wait_for_application(Node, PidFile, Application, Inform) -> - Pid = read_pid_file(PidFile, true), - Inform("pid is ~s", [Pid]), - wait_for_application(Node, Pid, Application). - -wait_for_application(Node, Pid, Application) -> - while_process_is_alive(Node, Pid, - fun() -> rabbit_nodes:is_running(Node, Application) end). - -wait_for_startup(Node, Pid) -> - while_process_is_alive(Node, Pid, - fun() -> rpc:call(Node, rabbit, await_startup, []) =:= ok end). - -while_process_is_alive(Node, Pid, Activity) -> - case process_up(Pid) of - true -> case Activity() of - true -> ok; - Other -> timer:sleep(?EXTERNAL_CHECK_INTERVAL), - while_process_is_alive(Node, Pid, Activity) - end; - false -> {error, process_not_running} - end. - -wait_for_process_death(Pid) -> - case process_up(Pid) of - true -> timer:sleep(?EXTERNAL_CHECK_INTERVAL), - wait_for_process_death(Pid); - false -> ok - end. - -read_pid_file(PidFile, Wait) -> - case {file:read_file(PidFile), Wait} of - {{ok, Bin}, _} -> - S = string:strip(binary_to_list(Bin), right, $\n), - try list_to_integer(S) - catch error:badarg -> - exit({error, {garbage_in_pid_file, PidFile}}) - end, - S; - {{error, enoent}, true} -> - timer:sleep(?EXTERNAL_CHECK_INTERVAL), - read_pid_file(PidFile, Wait); - {{error, _} = E, _} -> - exit({error, {could_not_read_pid, E}}) - end. - -% Test using some OS clunkiness since we shouldn't trust -% rpc:call(os, getpid, []) at this point -process_up(Pid) -> - with_os([{unix, fun () -> - system("ps -p " ++ Pid - ++ " >/dev/null 2>&1") =:= 0 - end}, - {win32, fun () -> - Res = os:cmd("tasklist /nh /fi \"pid eq " ++ - Pid ++ "\" 2>&1"), - case re:run(Res, "erl\\.exe", [{capture, none}]) of - match -> true; - _ -> false - end - end}]). - -with_os(Handlers) -> - {OsFamily, _} = os:type(), - case proplists:get_value(OsFamily, Handlers) of - undefined -> throw({unsupported_os, OsFamily}); - Handler -> Handler() - end. - -% Like system(3) -system(Cmd) -> - ShCmd = "sh -c '" ++ escape_quotes(Cmd) ++ "'", - Port = erlang:open_port({spawn, ShCmd}, [exit_status,nouse_stdio]), - receive {Port, {exit_status, Status}} -> Status end. - -% Escape the quotes in a shell command so that it can be used in "sh -c 'cmd'" -escape_quotes(Cmd) -> - lists:flatten(lists:map(fun ($') -> "'\\''"; (Ch) -> Ch end, Cmd)). - -format_parse_error({_Line, Mod, Err}) -> - lists:flatten(Mod:format_error(Err)). - -%%---------------------------------------------------------------------------- - -default_if_empty(List, Default) when is_list(List) -> - if List == [] -> Default; - true -> [list_to_atom(X) || X <- List] - end. - -display_info_list(Results, InfoItemKeys) when is_list(Results) -> - lists:foreach( - fun (Result) -> display_row( - [format_info_item(proplists:get_value(X, Result)) || - X <- InfoItemKeys]) - end, Results), - ok; -display_info_list(Other, _) -> - Other. - -display_row(Row) -> - io:fwrite(string:join(Row, "\t")), - io:nl(). - --define(IS_U8(X), (X >= 0 andalso X =< 255)). --define(IS_U16(X), (X >= 0 andalso X =< 65535)). - -format_info_item(#resource{name = Name}) -> - escape(Name); -format_info_item({N1, N2, N3, N4} = Value) when - ?IS_U8(N1), ?IS_U8(N2), ?IS_U8(N3), ?IS_U8(N4) -> - rabbit_misc:ntoa(Value); -format_info_item({K1, K2, K3, K4, K5, K6, K7, K8} = Value) when - ?IS_U16(K1), ?IS_U16(K2), ?IS_U16(K3), ?IS_U16(K4), - ?IS_U16(K5), ?IS_U16(K6), ?IS_U16(K7), ?IS_U16(K8) -> - rabbit_misc:ntoa(Value); -format_info_item(Value) when is_pid(Value) -> - rabbit_misc:pid_to_string(Value); -format_info_item(Value) when is_binary(Value) -> - escape(Value); -format_info_item(Value) when is_atom(Value) -> - escape(atom_to_list(Value)); -format_info_item([{TableEntryKey, TableEntryType, _TableEntryValue} | _] = - Value) when is_binary(TableEntryKey) andalso - is_atom(TableEntryType) -> - io_lib:format("~1000000000000p", [prettify_amqp_table(Value)]); -format_info_item([T | _] = Value) - when is_tuple(T) orelse is_pid(T) orelse is_binary(T) orelse is_atom(T) orelse - is_list(T) -> - "[" ++ - lists:nthtail(2, lists:append( - [", " ++ format_info_item(E) || E <- Value])) ++ "]"; -format_info_item(Value) -> - io_lib:format("~w", [Value]). - -display_call_result(Node, MFA) -> - case call(Node, MFA) of - {badrpc, _} = Res -> throw(Res); - Res -> io:format("~p~n", [Res]), - ok - end. - -unsafe_rpc(Node, Mod, Fun, Args) -> - case rpc_call(Node, Mod, Fun, Args) of - {badrpc, _} = Res -> throw(Res); - Normal -> Normal - end. - -call(Node, {Mod, Fun, Args}) -> - rpc_call(Node, Mod, Fun, lists:map(fun list_to_binary/1, Args)). - -rpc_call(Node, Mod, Fun, Args) -> - rpc:call(Node, Mod, Fun, Args, ?RPC_TIMEOUT). - -%% escape does C-style backslash escaping of non-printable ASCII -%% characters. We don't escape characters above 127, since they may -%% form part of UTF-8 strings. - -escape(Atom) when is_atom(Atom) -> escape(atom_to_list(Atom)); -escape(Bin) when is_binary(Bin) -> escape(binary_to_list(Bin)); -escape(L) when is_list(L) -> escape_char(lists:reverse(L), []). - -escape_char([$\\ | T], Acc) -> - escape_char(T, [$\\, $\\ | Acc]); -escape_char([X | T], Acc) when X >= 32, X /= 127 -> - escape_char(T, [X | Acc]); -escape_char([X | T], Acc) -> - escape_char(T, [$\\, $0 + (X bsr 6), $0 + (X band 8#070 bsr 3), - $0 + (X band 7) | Acc]); -escape_char([], Acc) -> - Acc. - -prettify_amqp_table(Table) -> - [{escape(K), prettify_typed_amqp_value(T, V)} || {K, T, V} <- Table]. - -prettify_typed_amqp_value(longstr, Value) -> escape(Value); -prettify_typed_amqp_value(table, Value) -> prettify_amqp_table(Value); -prettify_typed_amqp_value(array, Value) -> [prettify_typed_amqp_value(T, V) || - {T, V} <- Value]; -prettify_typed_amqp_value(_Type, Value) -> Value. diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl new file mode 100644 index 00000000..6dc8d445 --- /dev/null +++ b/src/rabbit_control_main.erl @@ -0,0 +1,581 @@ +%% 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 VMware, Inc. +%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% + +-module(rabbit_control). +-include("rabbit.hrl"). + +-export([start/0, stop/0, action/5]). + +-define(RPC_TIMEOUT, infinity). +-define(EXTERNAL_CHECK_INTERVAL, 1000). + +-define(QUIET_OPT, "-q"). +-define(NODE_OPT, "-n"). +-define(VHOST_OPT, "-p"). + +-define(GLOBAL_QUERIES, + [{"Connections", rabbit_networking, connection_info_all, + connection_info_keys}, + {"Channels", rabbit_channel, info_all, info_keys}]). + +-define(VHOST_QUERIES, + [{"Queues", rabbit_amqqueue, info_all, info_keys}, + {"Exchanges", rabbit_exchange, info_all, info_keys}, + {"Bindings", rabbit_binding, info_all, info_keys}, + {"Consumers", rabbit_amqqueue, consumers_all, consumer_info_keys}, + {"Permissions", rabbit_auth_backend_internal, list_vhost_permissions, + vhost_perms_info_keys}]). + +%%---------------------------------------------------------------------------- + +-ifdef(use_specs). + +-spec(start/0 :: () -> no_return()). +-spec(stop/0 :: () -> 'ok'). +-spec(action/5 :: + (atom(), node(), [string()], [{string(), any()}], + fun ((string(), [any()]) -> 'ok')) + -> 'ok'). +-spec(usage/0 :: () -> no_return()). + +-endif. + +%%---------------------------------------------------------------------------- + +start() -> + {ok, [[NodeStr|_]|_]} = init:get_argument(nodename), + {[Command0 | Args], Opts} = + case rabbit_misc:get_options([{flag, ?QUIET_OPT}, + {option, ?NODE_OPT, NodeStr}, + {option, ?VHOST_OPT, "/"}], + init:get_plain_arguments()) of + {[], _Opts} -> usage(); + CmdArgsAndOpts -> CmdArgsAndOpts + end, + Opts1 = [case K of + ?NODE_OPT -> {?NODE_OPT, rabbit_nodes:make(V)}; + _ -> {K, V} + end || {K, V} <- Opts], + Command = list_to_atom(Command0), + Quiet = proplists:get_bool(?QUIET_OPT, Opts1), + Node = proplists:get_value(?NODE_OPT, Opts1), + Inform = case Quiet of + true -> fun (_Format, _Args1) -> ok end; + false -> fun (Format, Args1) -> + io:format(Format ++ " ...~n", Args1) + end + end, + PrintInvalidCommandError = + fun () -> + print_error("invalid command '~s'", + [string:join([atom_to_list(Command) | Args], " ")]) + end, + + %% The reason we don't use a try/catch here is that rpc:call turns + %% thrown errors into normal return values + case catch action(Command, Node, Args, Opts, Inform) of + ok -> + case Quiet of + true -> ok; + false -> io:format("...done.~n") + end, + rabbit_misc:quit(0); + {'EXIT', {function_clause, [{?MODULE, action, _} | _]}} -> %% < R15 + PrintInvalidCommandError(), + usage(); + {'EXIT', {function_clause, [{?MODULE, action, _, _} | _]}} -> %% >= R15 + PrintInvalidCommandError(), + usage(); + {'EXIT', {badarg, _}} -> + print_error("invalid parameter: ~p", [Args]), + usage(); + {error, Reason} -> + print_error("~p", [Reason]), + rabbit_misc:quit(2); + {error_string, Reason} -> + print_error("~s", [Reason]), + rabbit_misc:quit(2); + {badrpc, {'EXIT', Reason}} -> + print_error("~p", [Reason]), + rabbit_misc:quit(2); + {badrpc, Reason} -> + print_error("unable to connect to node ~w: ~w", [Node, Reason]), + print_badrpc_diagnostics(Node), + rabbit_misc:quit(2); + Other -> + print_error("~p", [Other]), + rabbit_misc:quit(2) + end. + +fmt_stderr(Format, Args) -> rabbit_misc:format_stderr(Format ++ "~n", Args). + +print_report(Node, {Descr, Module, InfoFun, KeysFun}) -> + io:format("~s:~n", [Descr]), + print_report0(Node, {Module, InfoFun, KeysFun}, []). + +print_report(Node, {Descr, Module, InfoFun, KeysFun}, VHostArg) -> + io:format("~s on ~s:~n", [Descr, VHostArg]), + print_report0(Node, {Module, InfoFun, KeysFun}, VHostArg). + +print_report0(Node, {Module, InfoFun, KeysFun}, VHostArg) -> + case Results = rpc_call(Node, Module, InfoFun, VHostArg) of + [_|_] -> InfoItems = rpc_call(Node, Module, KeysFun, []), + display_row([atom_to_list(I) || I <- InfoItems]), + display_info_list(Results, InfoItems); + _ -> ok + end, + io:nl(). + +print_error(Format, Args) -> fmt_stderr("Error: " ++ Format, Args). + +print_badrpc_diagnostics(Node) -> + fmt_stderr(rabbit_nodes:diagnostics([Node]), []). + +stop() -> + ok. + +usage() -> + io:format("~s", [rabbit_ctl_usage:usage()]), + rabbit_misc:quit(1). + +%%---------------------------------------------------------------------------- + +action(stop, Node, Args, _Opts, Inform) -> + Inform("Stopping and halting node ~p", [Node]), + Res = call(Node, {rabbit, stop_and_halt, []}), + case {Res, Args} of + {ok, [PidFile]} -> wait_for_process_death( + read_pid_file(PidFile, false)); + {ok, [_, _| _]} -> exit({badarg, Args}); + _ -> ok + end, + Res; + +action(stop_app, Node, [], _Opts, Inform) -> + Inform("Stopping node ~p", [Node]), + call(Node, {rabbit, stop, []}); + +action(start_app, Node, [], _Opts, Inform) -> + Inform("Starting node ~p", [Node]), + call(Node, {rabbit, start, []}); + +action(reset, Node, [], _Opts, Inform) -> + Inform("Resetting node ~p", [Node]), + call(Node, {rabbit_mnesia, reset, []}); + +action(force_reset, Node, [], _Opts, Inform) -> + Inform("Forcefully resetting node ~p", [Node]), + call(Node, {rabbit_mnesia, force_reset, []}); + +action(cluster, Node, ClusterNodeSs, _Opts, Inform) -> + ClusterNodes = lists:map(fun list_to_atom/1, ClusterNodeSs), + Inform("Clustering node ~p with ~p", + [Node, ClusterNodes]), + rpc_call(Node, rabbit_mnesia, cluster, [ClusterNodes]); + +action(force_cluster, Node, ClusterNodeSs, _Opts, Inform) -> + ClusterNodes = lists:map(fun list_to_atom/1, ClusterNodeSs), + Inform("Forcefully clustering node ~p with ~p (ignoring offline nodes)", + [Node, ClusterNodes]), + rpc_call(Node, rabbit_mnesia, force_cluster, [ClusterNodes]); + +action(wait, Node, [PidFile], _Opts, Inform) -> + Inform("Waiting for ~p", [Node]), + wait_for_application(Node, PidFile, rabbit, Inform); + +action(wait, Node, [PidFile, App], _Opts, Inform) -> + Inform("Waiting for ~p on ~p", [App, Node]), + wait_for_application(Node, PidFile, list_to_atom(App), Inform); + +action(status, Node, [], _Opts, Inform) -> + Inform("Status of node ~p", [Node]), + display_call_result(Node, {rabbit, status, []}); + +action(cluster_status, Node, [], _Opts, Inform) -> + Inform("Cluster status of node ~p", [Node]), + display_call_result(Node, {rabbit_mnesia, status, []}); + +action(environment, Node, _App, _Opts, Inform) -> + Inform("Application environment of node ~p", [Node]), + display_call_result(Node, {rabbit, environment, []}); + +action(rotate_logs, Node, [], _Opts, Inform) -> + Inform("Reopening logs for node ~p", [Node]), + call(Node, {rabbit, rotate_logs, [""]}); +action(rotate_logs, Node, Args = [Suffix], _Opts, Inform) -> + Inform("Rotating logs to files with suffix ~p", [Suffix]), + call(Node, {rabbit, rotate_logs, Args}); + +action(close_connection, Node, [PidStr, Explanation], _Opts, Inform) -> + Inform("Closing connection ~s", [PidStr]), + rpc_call(Node, rabbit_networking, close_connection, + [rabbit_misc:string_to_pid(PidStr), Explanation]); + +action(add_user, Node, Args = [Username, _Password], _Opts, Inform) -> + Inform("Creating user ~p", [Username]), + call(Node, {rabbit_auth_backend_internal, add_user, Args}); + +action(delete_user, Node, Args = [_Username], _Opts, Inform) -> + Inform("Deleting user ~p", Args), + call(Node, {rabbit_auth_backend_internal, delete_user, Args}); + +action(change_password, Node, Args = [Username, _Newpassword], _Opts, Inform) -> + Inform("Changing password for user ~p", [Username]), + call(Node, {rabbit_auth_backend_internal, change_password, Args}); + +action(clear_password, Node, Args = [Username], _Opts, Inform) -> + Inform("Clearing password for user ~p", [Username]), + call(Node, {rabbit_auth_backend_internal, clear_password, Args}); + +action(set_user_tags, Node, [Username | TagsStr], _Opts, Inform) -> + Tags = [list_to_atom(T) || T <- TagsStr], + Inform("Setting tags for user ~p to ~p", [Username, Tags]), + rpc_call(Node, rabbit_auth_backend_internal, set_tags, + [list_to_binary(Username), Tags]); + +action(list_users, Node, [], _Opts, Inform) -> + Inform("Listing users", []), + display_info_list( + call(Node, {rabbit_auth_backend_internal, list_users, []}), + rabbit_auth_backend_internal:user_info_keys()); + +action(add_vhost, Node, Args = [_VHostPath], _Opts, Inform) -> + Inform("Creating vhost ~p", Args), + call(Node, {rabbit_vhost, add, Args}); + +action(delete_vhost, Node, Args = [_VHostPath], _Opts, Inform) -> + Inform("Deleting vhost ~p", Args), + call(Node, {rabbit_vhost, delete, Args}); + +action(list_vhosts, Node, Args, _Opts, Inform) -> + Inform("Listing vhosts", []), + ArgAtoms = default_if_empty(Args, [name]), + display_info_list(call(Node, {rabbit_vhost, info_all, []}), ArgAtoms); + +action(list_user_permissions, Node, Args = [_Username], _Opts, Inform) -> + Inform("Listing permissions for user ~p", Args), + display_info_list(call(Node, {rabbit_auth_backend_internal, + list_user_permissions, Args}), + rabbit_auth_backend_internal:user_perms_info_keys()); + +action(list_queues, Node, Args, Opts, Inform) -> + Inform("Listing queues", []), + VHostArg = list_to_binary(proplists:get_value(?VHOST_OPT, Opts)), + ArgAtoms = default_if_empty(Args, [name, messages]), + display_info_list(rpc_call(Node, rabbit_amqqueue, info_all, + [VHostArg, ArgAtoms]), + ArgAtoms); + +action(list_exchanges, Node, Args, Opts, Inform) -> + Inform("Listing exchanges", []), + VHostArg = list_to_binary(proplists:get_value(?VHOST_OPT, Opts)), + ArgAtoms = default_if_empty(Args, [name, type]), + display_info_list(rpc_call(Node, rabbit_exchange, info_all, + [VHostArg, ArgAtoms]), + ArgAtoms); + +action(list_bindings, Node, Args, Opts, Inform) -> + Inform("Listing bindings", []), + VHostArg = list_to_binary(proplists:get_value(?VHOST_OPT, Opts)), + ArgAtoms = default_if_empty(Args, [source_name, source_kind, + destination_name, destination_kind, + routing_key, arguments]), + display_info_list(rpc_call(Node, rabbit_binding, info_all, + [VHostArg, ArgAtoms]), + ArgAtoms); + +action(list_connections, Node, Args, _Opts, Inform) -> + Inform("Listing connections", []), + ArgAtoms = default_if_empty(Args, [user, peer_address, peer_port, state]), + display_info_list(rpc_call(Node, rabbit_networking, connection_info_all, + [ArgAtoms]), + ArgAtoms); + +action(list_channels, Node, Args, _Opts, Inform) -> + Inform("Listing channels", []), + ArgAtoms = default_if_empty(Args, [pid, user, consumer_count, + messages_unacknowledged]), + display_info_list(rpc_call(Node, rabbit_channel, info_all, [ArgAtoms]), + ArgAtoms); + +action(list_consumers, Node, _Args, Opts, Inform) -> + Inform("Listing consumers", []), + VHostArg = list_to_binary(proplists:get_value(?VHOST_OPT, Opts)), + display_info_list(rpc_call(Node, rabbit_amqqueue, consumers_all, [VHostArg]), + rabbit_amqqueue:consumer_info_keys()); + +action(trace_on, Node, [], Opts, Inform) -> + VHost = proplists:get_value(?VHOST_OPT, Opts), + Inform("Starting tracing for vhost ~p", [VHost]), + rpc_call(Node, rabbit_trace, start, [list_to_binary(VHost)]); + +action(trace_off, Node, [], Opts, Inform) -> + VHost = proplists:get_value(?VHOST_OPT, Opts), + Inform("Stopping tracing for vhost ~p", [VHost]), + rpc_call(Node, rabbit_trace, stop, [list_to_binary(VHost)]); + +action(set_vm_memory_high_watermark, Node, [Arg], _Opts, Inform) -> + Frac = list_to_float(case string:chr(Arg, $.) of + 0 -> Arg ++ ".0"; + _ -> Arg + end), + Inform("Setting memory threshold on ~p to ~p", [Node, Frac]), + rpc_call(Node, vm_memory_monitor, set_vm_memory_high_watermark, [Frac]); + +action(set_permissions, Node, [Username, CPerm, WPerm, RPerm], Opts, Inform) -> + VHost = proplists:get_value(?VHOST_OPT, Opts), + Inform("Setting permissions for user ~p in vhost ~p", [Username, VHost]), + call(Node, {rabbit_auth_backend_internal, set_permissions, + [Username, VHost, CPerm, WPerm, RPerm]}); + +action(clear_permissions, Node, [Username], Opts, Inform) -> + VHost = proplists:get_value(?VHOST_OPT, Opts), + Inform("Clearing permissions for user ~p in vhost ~p", [Username, VHost]), + call(Node, {rabbit_auth_backend_internal, clear_permissions, + [Username, VHost]}); + +action(list_permissions, Node, [], Opts, Inform) -> + VHost = proplists:get_value(?VHOST_OPT, Opts), + Inform("Listing permissions in vhost ~p", [VHost]), + display_info_list(call(Node, {rabbit_auth_backend_internal, + list_vhost_permissions, [VHost]}), + rabbit_auth_backend_internal:vhost_perms_info_keys()); + +action(set_parameter, Node, [Component, Key, Value], _Opts, Inform) -> + Inform("Setting runtime parameter ~p for component ~p to ~p", + [Key, Component, Value]), + rpc_call(Node, rabbit_runtime_parameters, parse_set, + [list_to_binary(Component), list_to_binary(Key), Value]); + +action(clear_parameter, Node, [Component, Key], _Opts, Inform) -> + Inform("Clearing runtime parameter ~p for component ~p", [Key, Component]), + rpc_call(Node, rabbit_runtime_parameters, clear, [list_to_binary(Component), + list_to_binary(Key)]); + +action(list_parameters, Node, Args = [], _Opts, Inform) -> + Inform("Listing runtime parameters", []), + display_info_list( + rpc_call(Node, rabbit_runtime_parameters, list_formatted, Args), + rabbit_runtime_parameters:info_keys()); + +action(report, Node, _Args, _Opts, Inform) -> + io:format("Reporting server status on ~p~n~n", [erlang:universaltime()]), + [begin ok = action(Action, N, [], [], Inform), io:nl() end || + N <- unsafe_rpc(Node, rabbit_mnesia, running_clustered_nodes, []), + Action <- [status, cluster_status, environment]], + VHosts = unsafe_rpc(Node, rabbit_vhost, list, []), + [print_report(Node, Q) || Q <- ?GLOBAL_QUERIES], + [print_report(Node, Q, [V]) || Q <- ?VHOST_QUERIES, V <- VHosts], + io:format("End of server status report~n"), + ok; + +action(eval, Node, [Expr], _Opts, _Inform) -> + case erl_scan:string(Expr) of + {ok, Scanned, _} -> + case erl_parse:parse_exprs(Scanned) of + {ok, Parsed} -> + {value, Value, _} = unsafe_rpc( + Node, erl_eval, exprs, [Parsed, []]), + io:format("~p~n", [Value]), + ok; + {error, E} -> + {error_string, format_parse_error(E)} + end; + {error, E, _} -> + {error_string, format_parse_error(E)} + end. + +%%---------------------------------------------------------------------------- + +wait_for_application(Node, PidFile, Application, Inform) -> + Pid = read_pid_file(PidFile, true), + Inform("pid is ~s", [Pid]), + wait_for_application(Node, Pid, Application). + +wait_for_application(Node, Pid, Application) -> + while_process_is_alive(Node, Pid, + fun() -> rabbit_nodes:is_running(Node, Application) end). + +wait_for_startup(Node, Pid) -> + while_process_is_alive(Node, Pid, + fun() -> rpc:call(Node, rabbit, await_startup, []) =:= ok end). + +while_process_is_alive(Node, Pid, Activity) -> + case process_up(Pid) of + true -> case Activity() of + true -> ok; + Other -> timer:sleep(?EXTERNAL_CHECK_INTERVAL), + while_process_is_alive(Node, Pid, Activity) + end; + false -> {error, process_not_running} + end. + +wait_for_process_death(Pid) -> + case process_up(Pid) of + true -> timer:sleep(?EXTERNAL_CHECK_INTERVAL), + wait_for_process_death(Pid); + false -> ok + end. + +read_pid_file(PidFile, Wait) -> + case {file:read_file(PidFile), Wait} of + {{ok, Bin}, _} -> + S = string:strip(binary_to_list(Bin), right, $\n), + try list_to_integer(S) + catch error:badarg -> + exit({error, {garbage_in_pid_file, PidFile}}) + end, + S; + {{error, enoent}, true} -> + timer:sleep(?EXTERNAL_CHECK_INTERVAL), + read_pid_file(PidFile, Wait); + {{error, _} = E, _} -> + exit({error, {could_not_read_pid, E}}) + end. + +% Test using some OS clunkiness since we shouldn't trust +% rpc:call(os, getpid, []) at this point +process_up(Pid) -> + with_os([{unix, fun () -> + system("ps -p " ++ Pid + ++ " >/dev/null 2>&1") =:= 0 + end}, + {win32, fun () -> + Res = os:cmd("tasklist /nh /fi \"pid eq " ++ + Pid ++ "\" 2>&1"), + case re:run(Res, "erl\\.exe", [{capture, none}]) of + match -> true; + _ -> false + end + end}]). + +with_os(Handlers) -> + {OsFamily, _} = os:type(), + case proplists:get_value(OsFamily, Handlers) of + undefined -> throw({unsupported_os, OsFamily}); + Handler -> Handler() + end. + +% Like system(3) +system(Cmd) -> + ShCmd = "sh -c '" ++ escape_quotes(Cmd) ++ "'", + Port = erlang:open_port({spawn, ShCmd}, [exit_status,nouse_stdio]), + receive {Port, {exit_status, Status}} -> Status end. + +% Escape the quotes in a shell command so that it can be used in "sh -c 'cmd'" +escape_quotes(Cmd) -> + lists:flatten(lists:map(fun ($') -> "'\\''"; (Ch) -> Ch end, Cmd)). + +format_parse_error({_Line, Mod, Err}) -> + lists:flatten(Mod:format_error(Err)). + +%%---------------------------------------------------------------------------- + +default_if_empty(List, Default) when is_list(List) -> + if List == [] -> Default; + true -> [list_to_atom(X) || X <- List] + end. + +display_info_list(Results, InfoItemKeys) when is_list(Results) -> + lists:foreach( + fun (Result) -> display_row( + [format_info_item(proplists:get_value(X, Result)) || + X <- InfoItemKeys]) + end, Results), + ok; +display_info_list(Other, _) -> + Other. + +display_row(Row) -> + io:fwrite(string:join(Row, "\t")), + io:nl(). + +-define(IS_U8(X), (X >= 0 andalso X =< 255)). +-define(IS_U16(X), (X >= 0 andalso X =< 65535)). + +format_info_item(#resource{name = Name}) -> + escape(Name); +format_info_item({N1, N2, N3, N4} = Value) when + ?IS_U8(N1), ?IS_U8(N2), ?IS_U8(N3), ?IS_U8(N4) -> + rabbit_misc:ntoa(Value); +format_info_item({K1, K2, K3, K4, K5, K6, K7, K8} = Value) when + ?IS_U16(K1), ?IS_U16(K2), ?IS_U16(K3), ?IS_U16(K4), + ?IS_U16(K5), ?IS_U16(K6), ?IS_U16(K7), ?IS_U16(K8) -> + rabbit_misc:ntoa(Value); +format_info_item(Value) when is_pid(Value) -> + rabbit_misc:pid_to_string(Value); +format_info_item(Value) when is_binary(Value) -> + escape(Value); +format_info_item(Value) when is_atom(Value) -> + escape(atom_to_list(Value)); +format_info_item([{TableEntryKey, TableEntryType, _TableEntryValue} | _] = + Value) when is_binary(TableEntryKey) andalso + is_atom(TableEntryType) -> + io_lib:format("~1000000000000p", [prettify_amqp_table(Value)]); +format_info_item([T | _] = Value) + when is_tuple(T) orelse is_pid(T) orelse is_binary(T) orelse is_atom(T) orelse + is_list(T) -> + "[" ++ + lists:nthtail(2, lists:append( + [", " ++ format_info_item(E) || E <- Value])) ++ "]"; +format_info_item(Value) -> + io_lib:format("~w", [Value]). + +display_call_result(Node, MFA) -> + case call(Node, MFA) of + {badrpc, _} = Res -> throw(Res); + Res -> io:format("~p~n", [Res]), + ok + end. + +unsafe_rpc(Node, Mod, Fun, Args) -> + case rpc_call(Node, Mod, Fun, Args) of + {badrpc, _} = Res -> throw(Res); + Normal -> Normal + end. + +call(Node, {Mod, Fun, Args}) -> + rpc_call(Node, Mod, Fun, lists:map(fun list_to_binary/1, Args)). + +rpc_call(Node, Mod, Fun, Args) -> + rpc:call(Node, Mod, Fun, Args, ?RPC_TIMEOUT). + +%% escape does C-style backslash escaping of non-printable ASCII +%% characters. We don't escape characters above 127, since they may +%% form part of UTF-8 strings. + +escape(Atom) when is_atom(Atom) -> escape(atom_to_list(Atom)); +escape(Bin) when is_binary(Bin) -> escape(binary_to_list(Bin)); +escape(L) when is_list(L) -> escape_char(lists:reverse(L), []). + +escape_char([$\\ | T], Acc) -> + escape_char(T, [$\\, $\\ | Acc]); +escape_char([X | T], Acc) when X >= 32, X /= 127 -> + escape_char(T, [X | Acc]); +escape_char([X | T], Acc) -> + escape_char(T, [$\\, $0 + (X bsr 6), $0 + (X band 8#070 bsr 3), + $0 + (X band 7) | Acc]); +escape_char([], Acc) -> + Acc. + +prettify_amqp_table(Table) -> + [{escape(K), prettify_typed_amqp_value(T, V)} || {K, T, V} <- Table]. + +prettify_typed_amqp_value(longstr, Value) -> escape(Value); +prettify_typed_amqp_value(table, Value) -> prettify_amqp_table(Value); +prettify_typed_amqp_value(array, Value) -> [prettify_typed_amqp_value(T, V) || + {T, V} <- Value]; +prettify_typed_amqp_value(_Type, Value) -> Value. -- cgit v1.2.1 From 00b3101afc25ae56d47d740d35d6f4a35a982ea7 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 21 May 2012 14:41:57 +0100 Subject: manually merge 3c20079f0601 --- scripts/rabbitmqctl | 2 +- scripts/rabbitmqctl.bat | 2 +- src/rabbit_control_main.erl | 2 +- src/rabbit_tests.erl | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/rabbitmqctl b/scripts/rabbitmqctl index 4aad6b8f..a5fade72 100755 --- a/scripts/rabbitmqctl +++ b/scripts/rabbitmqctl @@ -32,6 +32,6 @@ exec erl \ -hidden \ ${RABBITMQ_CTL_ERL_ARGS} \ -sname rabbitmqctl$$ \ - -s rabbit_control \ + -s rabbit_control_main \ -nodename $RABBITMQ_NODENAME \ -extra "$@" diff --git a/scripts/rabbitmqctl.bat b/scripts/rabbitmqctl.bat index f37fae48..55a3d8b2 100755 --- a/scripts/rabbitmqctl.bat +++ b/scripts/rabbitmqctl.bat @@ -43,7 +43,7 @@ if not exist "!ERLANG_HOME!\bin\erl.exe" ( exit /B ) -"!ERLANG_HOME!\bin\erl.exe" -pa "!TDP0!..\ebin" -noinput -hidden !RABBITMQ_CTL_ERL_ARGS! -sname rabbitmqctl!RANDOM! -s rabbit_control -nodename !RABBITMQ_NODENAME! -extra !STAR! +"!ERLANG_HOME!\bin\erl.exe" -pa "!TDP0!..\ebin" -noinput -hidden !RABBITMQ_CTL_ERL_ARGS! -sname rabbitmqctl!RANDOM! -s rabbit_control_main -nodename !RABBITMQ_NODENAME! -extra !STAR! endlocal endlocal diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index 6dc8d445..b1c120bd 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -14,7 +14,7 @@ %% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. %% --module(rabbit_control). +-module(rabbit_control_main). -include("rabbit.hrl"). -export([start/0, stop/0, action/5]). diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 96b5fa38..c73a51c9 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -1573,7 +1573,7 @@ control_action(Command, Args, NewOpts) -> expand_options(default_options(), NewOpts)). control_action(Command, Node, Args, Opts) -> - case catch rabbit_control:action( + case catch rabbit_control_main:action( Command, Node, Args, Opts, fun (Format, Args1) -> io:format(Format ++ " ...~n", Args1) -- cgit v1.2.1 From 0ecf08e6ebab4354d6e6c541f1841e636780e51a Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 21 May 2012 14:10:09 +0100 Subject: fix variable handling in rabbitmq-server preventing background node startup from working --- scripts/rabbitmq-server | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/rabbitmq-server b/scripts/rabbitmq-server index ba18766c..05998dd3 100755 --- a/scripts/rabbitmq-server +++ b/scripts/rabbitmq-server @@ -58,7 +58,8 @@ DEFAULT_NODE_PORT=5672 ##--- End of overridden variables RABBITMQ_START_RABBIT= -[ "x" = "x$RABBITMQ_ALLOW_INPUT" ] && RABBITMQ_START_RABBIT='-noinput' +[ "x" = "x$RABBITMQ_ALLOW_INPUT" ] && RABBITMQ_START_RABBIT=" -noinput" +[ "x" = "x$RABBITMQ_NODE_ONLY" ] && RABBITMQ_START_RABBIT="$RABBITMQ_START_RABBIT -s rabbit boot " case "$(uname -s)" in CYGWIN*) # we make no attempt to record the cygwin pid; rabbitmqctl wait -- cgit v1.2.1 From 724a889df13a04ebc2dfe3d5bbac7b1055b3f15f Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Wed, 16 May 2012 13:48:29 +0100 Subject: rationalise rabbit_misc:terminate/1/2 with rabbit_misc:quit/1 --- src/rabbit_misc.erl | 33 +++++++++++++++------------------ src/rabbit_plugins.erl | 4 ++-- src/rabbit_prelaunch.erl | 6 +++--- 3 files changed, 20 insertions(+), 23 deletions(-) diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index cc1417e9..ebef3e65 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -19,7 +19,7 @@ -include("rabbit_framing.hrl"). -export([method_record_type/1, polite_pause/0, polite_pause/1]). --export([die/1, frame_error/2, amqp_error/4, terminate/1, terminate/2, +-export([die/1, frame_error/2, amqp_error/4, quit/1, quit/2, protocol_error/3, protocol_error/4, protocol_error/1]). -export([not_found/1, assert_args_equivalence/4]). -export([dirty_read/1]). @@ -58,7 +58,6 @@ -export([format_message_queue/2]). -export([append_rpc_all_nodes/4]). -export([multi_call/2]). --export([quit/1]). -export([os_cmd/1]). -export([gb_sets_difference/2]). @@ -87,8 +86,8 @@ -spec(die/1 :: (rabbit_framing:amqp_exception()) -> channel_or_connection_exit()). --spec(terminate/1 :: (integer()) -> any()). --spec(terminate/2 :: (string(), integer()) -> any()). +-spec(quit/1 :: (integer()) -> any()). +-spec(quit/2 :: (string(), [term()]) -> any()). -spec(frame_error/2 :: (rabbit_framing:amqp_method_name(), binary()) -> rabbit_types:connection_exit()). @@ -207,7 +206,6 @@ -spec(append_rpc_all_nodes/4 :: ([node()], atom(), atom(), [any()]) -> [any()]). -spec(multi_call/2 :: ([pid()], any()) -> {[{pid(), any()}], [{pid(), any()}]}). --spec(quit/1 :: (integer() | string()) -> no_return()). -spec(os_cmd/1 :: (string()) -> string()). -spec(gb_sets_difference/2 :: (gb_set(), gb_set()) -> gb_set()). @@ -388,14 +386,20 @@ report_coverage_percentage(File, Cov, NotCov, Mod) -> confirm_to_sender(Pid, MsgSeqNos) -> gen_server2:cast(Pid, {confirm, MsgSeqNos, self()}). -terminate(Fmt, Args) -> +%% +%% @doc Halts the emulator after printing out an error message io-formatted with +%% the supplied arguments. The exit status of the beam process will be set to 1. +%% +quit(Fmt, Args) -> io:format("ERROR: " ++ Fmt ++ "~n", Args), - terminate(1). + quit(1). -%% like quit/1, uses a slower shutdown on windows -%% (required to flush stdout), however terminate/1 also blocks -%% indefinitely until the flush has completed. -terminate(Status) -> +%% +%% @doc Halts the emulator returning the given status code to the os. +%% On Windows this function will block indefinitely so as to give the io +%% subsystem time to flush stdout completely. +%% +quit(Status) -> case os:type() of {unix, _} -> halt(Status); {win32, _} -> init:stop(Status), @@ -898,13 +902,6 @@ receive_multi_call([{Mref, Pid} | MonitorPids], Good, Bad) -> receive_multi_call(MonitorPids, Good, [{Pid, Reason} | Bad]) end. -%% the slower shutdown on windows required to flush stdout -quit(Status) -> - case os:type() of - {unix, _} -> halt(Status); - {win32, _} -> init:stop(Status) - end. - os_cmd(Command) -> Exec = hd(string:tokens(Command, " ")), case os:find_executable(Exec) of diff --git a/src/rabbit_plugins.erl b/src/rabbit_plugins.erl index 06773cdb..aa60cf2c 100644 --- a/src/rabbit_plugins.erl +++ b/src/rabbit_plugins.erl @@ -122,12 +122,12 @@ prepare_plugins(EnabledPluginsFile, PluginsDistDir, DestDir) -> %% Eliminate the contents of the destination directory case delete_recursively(DestDir) of ok -> ok; - {error, E} -> rabbit_misc:terminate("Could not delete dir ~s (~p)", + {error, E} -> rabbit_misc:quit("Could not delete dir ~s (~p)", [DestDir, E]) end, case filelib:ensure_dir(DestDir ++ "/") of ok -> ok; - {error, E2} -> rabbit_misc:terminate("Could not create dir ~s (~p)", + {error, E2} -> rabbit_misc:quit("Could not create dir ~s (~p)", [DestDir, E2]) end, diff --git a/src/rabbit_prelaunch.erl b/src/rabbit_prelaunch.erl index d58f54b9..5aa01d18 100644 --- a/src/rabbit_prelaunch.erl +++ b/src/rabbit_prelaunch.erl @@ -39,7 +39,7 @@ start() -> [NodeStr] = init:get_plain_arguments(), ok = duplicate_node_check(NodeStr), - rabbit_misc:terminate(0), + rabbit_misc:quit(0), ok. stop() -> @@ -61,11 +61,11 @@ duplicate_node_check(NodeStr) -> "already running on ~p~n", [NodeName, NodeHost]), io:format(rabbit_nodes:diagnostics([Node]) ++ "~n"), - rabbit_misc:terminate(?ERROR_CODE); + rabbit_misc:quit(?ERROR_CODE); false -> ok end; {error, EpmdReason} -> - rabbit_misc:terminate("epmd error for host ~p: ~p (~s)~n", + rabbit_misc:quit("epmd error for host ~p: ~p (~s)~n", [NodeHost, EpmdReason, case EpmdReason of address -> "unable to establish tcp connection"; -- cgit v1.2.1 From 2a91df5d9e34764f9ab70ebb715f5480718c0d0b Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Wed, 16 May 2012 13:59:21 +0100 Subject: cosmetic --- scripts/rabbitmqctl.bat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/rabbitmqctl.bat b/scripts/rabbitmqctl.bat index 55a3d8b2..9f549f1e 100755 --- a/scripts/rabbitmqctl.bat +++ b/scripts/rabbitmqctl.bat @@ -34,7 +34,7 @@ if "!RABBITMQ_NODENAME!"=="" ( if not exist "!ERLANG_HOME!\bin\erl.exe" ( echo. echo ****************************** - echo ERLANG_HOME not set correctly. + echo ERLANG_HOME not set correctly. echo ****************************** echo. echo Please either set ERLANG_HOME to point to your Erlang installation or place the -- cgit v1.2.1 From 28fb14043bc4a1eb30b049a986ccc72762140adf Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Wed, 16 May 2012 14:46:11 +0100 Subject: revert to expected RABBITMQ_NODE_ONLY behaviour on unix --- scripts/rabbitmq-server | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/rabbitmq-server b/scripts/rabbitmq-server index 05998dd3..9d8710af 100755 --- a/scripts/rabbitmq-server +++ b/scripts/rabbitmq-server @@ -61,6 +61,9 @@ RABBITMQ_START_RABBIT= [ "x" = "x$RABBITMQ_ALLOW_INPUT" ] && RABBITMQ_START_RABBIT=" -noinput" [ "x" = "x$RABBITMQ_NODE_ONLY" ] && RABBITMQ_START_RABBIT="$RABBITMQ_START_RABBIT -s rabbit boot " +RABBITMQ_START_RABBIT= +[ "x" = "x$RABBITMQ_NODE_ONLY" ] && RABBITMQ_START_RABBIT="$RABBITMQ_START_RABBIT -s rabbit boot" + case "$(uname -s)" in CYGWIN*) # we make no attempt to record the cygwin pid; rabbitmqctl wait # will not be able to make sense of it anyway @@ -98,7 +101,6 @@ exec erl \ ${RABBITMQ_START_RABBIT} \ -sname ${RABBITMQ_NODENAME} \ -boot start_sasl \ - -s rabbit boot \ ${RABBITMQ_CONFIG_ARG} \ +W w \ ${RABBITMQ_SERVER_ERL_ARGS} \ -- cgit v1.2.1 From 5619b90f552bea9882dd145c69d2630657e87083 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 16 May 2012 15:22:46 +0100 Subject: get a node to clean up after old incarnations of itself ...in case other nodes don't do so in time or, if they themselves stop/restart, ever. --- src/rabbit_amqqueue.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index c1673504..c7747571 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -166,6 +166,7 @@ [queue_name, channel_pid, consumer_tag, ack_required]). start() -> + on_node_down(node()), %% clear out remnants of old incarnation DurableQueues = find_durable_queues(), {ok, BQ} = application:get_env(rabbit, backing_queue_module), ok = BQ:start([QName || #amqqueue{name = QName} <- DurableQueues]), -- cgit v1.2.1 From b928966c4beb3a5c77ddda202fe5b0b55ba45281 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Wed, 16 May 2012 15:38:21 +0100 Subject: on windows, die in the correct manner when node name is already in use --- scripts/rabbitmq-server.bat | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/scripts/rabbitmq-server.bat b/scripts/rabbitmq-server.bat index 58f085af..09d4661f 100755 --- a/scripts/rabbitmq-server.bat +++ b/scripts/rabbitmq-server.bat @@ -89,13 +89,15 @@ if "!RABBITMQ_ENABLED_PLUGINS_FILE!"=="" ( set RABBITMQ_PLUGINS_DIR=!TDP0!..\plugins set RABBITMQ_EBIN_ROOT=!TDP0!..\ebin -if not "!ERLANG_HOME!\bin\erl.exe" ^ +"!ERLANG_HOME!\bin\erl.exe" ^ -pa "!RABBITMQ_EBIN_ROOT!" ^ -noinput -hidden ^ -s rabbit_prelaunch ^ -sname rabbitmqprelaunch!RANDOM! ^ - -extra "!RABBITMQ_NODENAME!" ( - exit /B + -extra "!RABBITMQ_NODENAME!" + +if ERRORLEVEL 1 ( + exit /B 1 ) set RABBITMQ_EBIN_PATH="-pa !RABBITMQ_EBIN_ROOT!" -- cgit v1.2.1 From 484fb54209baaaeba7b05a701cc0701f0612811d Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Wed, 16 May 2012 16:26:27 +0100 Subject: be mindful of windows line endings whilst parsing pid files --- src/rabbit_control_main.erl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index b1c120bd..2878e8e2 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -405,6 +405,8 @@ wait_for_application(Node, PidFile, Application, Inform) -> Inform("pid is ~s", [Pid]), wait_for_application(Node, Pid, Application). +wait_for_application(Node, Pid, rabbit) -> + wait_for_startup(Node, Pid); wait_for_application(Node, Pid, Application) -> while_process_is_alive(Node, Pid, fun() -> rabbit_nodes:is_running(Node, Application) end). @@ -433,7 +435,7 @@ wait_for_process_death(Pid) -> read_pid_file(PidFile, Wait) -> case {file:read_file(PidFile), Wait} of {{ok, Bin}, _} -> - S = string:strip(binary_to_list(Bin), right, $\n), + S = re:replace(Bin, "\\s", "", [global, {return, list}]), try list_to_integer(S) catch error:badarg -> exit({error, {garbage_in_pid_file, PidFile}}) -- cgit v1.2.1 From 13657010f4d829baebc0be7035feed8c596a8e6e Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Wed, 16 May 2012 16:49:11 +0100 Subject: remove message about re-installing windows service after plugin changes --- src/rabbit_control_main.erl | 2 +- src/rabbit_plugins_main.erl | 11 +---------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index 2878e8e2..d33195ad 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -419,7 +419,7 @@ while_process_is_alive(Node, Pid, Activity) -> case process_up(Pid) of true -> case Activity() of true -> ok; - Other -> timer:sleep(?EXTERNAL_CHECK_INTERVAL), + _Other -> timer:sleep(?EXTERNAL_CHECK_INTERVAL), while_process_is_alive(Node, Pid, Activity) end; false -> {error, process_not_running} diff --git a/src/rabbit_plugins_main.erl b/src/rabbit_plugins_main.erl index a27ad986..0500d2c1 100644 --- a/src/rabbit_plugins_main.erl +++ b/src/rabbit_plugins_main.erl @@ -258,14 +258,5 @@ maybe_warn_mochiweb(Enabled) -> report_change() -> io:format("Plugin configuration has changed. " - "Restart RabbitMQ for changes to take effect.~n"), - case os:type() of - {win32, _OsName} -> - io:format("If you have RabbitMQ running as a service then you must" - " reinstall by running~n rabbitmq-service.bat stop~n" - " rabbitmq-service.bat install~n" - " rabbitmq-service.bat start~n~n"); - _ -> - ok - end. + "Restart RabbitMQ for changes to take effect.~n"). -- cgit v1.2.1 From eb992a73cdc7d9010b9485a4354dc0c9ce9da492 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 17 May 2012 17:52:01 +0100 Subject: First sketch of a policy mechanism. --- include/rabbit.hrl | 4 +- src/rabbit_amqqueue.erl | 30 +++++- src/rabbit_amqqueue_process.erl | 52 ++++++--- src/rabbit_exchange.erl | 75 +++++++------ src/rabbit_exchange_decorator.erl | 7 +- src/rabbit_exchange_type.erl | 6 +- src/rabbit_exchange_type_direct.erl | 3 +- src/rabbit_exchange_type_fanout.erl | 3 +- src/rabbit_exchange_type_headers.erl | 3 +- src/rabbit_exchange_type_invalid.erl | 3 +- src/rabbit_exchange_type_topic.erl | 4 +- src/rabbit_policy.erl | 202 +++++++++++++++++++++++++++++++++++ src/rabbit_upgrade_functions.erl | 29 +++++ 13 files changed, 361 insertions(+), 60 deletions(-) create mode 100644 src/rabbit_policy.erl diff --git a/include/rabbit.hrl b/include/rabbit.hrl index 8de31490..e8b4a623 100644 --- a/include/rabbit.hrl +++ b/include/rabbit.hrl @@ -43,11 +43,11 @@ -record(resource, {virtual_host, kind, name}). -record(exchange, {name, type, durable, auto_delete, internal, arguments, - scratches}). + scratches, policy}). -record(exchange_serial, {name, next}). -record(amqqueue, {name, durable, auto_delete, exclusive_owner = none, - arguments, pid, slave_pids, mirror_nodes}). + arguments, pid, slave_pids, mirror_nodes, policy}). %% mnesia doesn't like unary records, so we add a dummy 'value' field -record(route, {binding, value = const}). diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index c1673504..637a7d4d 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -28,7 +28,7 @@ -export([notify_sent/2, notify_sent_queue_down/1, unblock/2, flush_all/2]). -export([notify_down_all/2, limit_all/3]). -export([on_node_down/1]). --export([store_queue/1]). +-export([update/2, store_queue/1, policy_changed/2]). %% internal @@ -71,6 +71,9 @@ -spec(internal_declare/2 :: (rabbit_types:amqqueue(), boolean()) -> queue_or_not_found() | rabbit_misc:thunk(queue_or_not_found())). +-spec(update/2 :: + (name(), + fun((rabbit_types:amqqueue()) -> rabbit_types:amqqueue())) -> 'ok'). -spec(lookup/1 :: (name()) -> rabbit_types:ok(rabbit_types:amqqueue()) | rabbit_types:error('not_found'); @@ -157,6 +160,8 @@ -spec(on_node_down/1 :: (node()) -> 'ok'). -spec(pseudo_queue/2 :: (name(), pid()) -> rabbit_types:amqqueue()). -spec(store_queue/1 :: (rabbit_types:amqqueue()) -> 'ok'). +-spec(policy_changed/2 :: + (rabbit_types:amqqueue(), rabbit_types:amqqueue()) -> 'ok'). -endif. @@ -222,9 +227,10 @@ internal_declare(Q = #amqqueue{name = QueueName}, false) -> case mnesia:wread({rabbit_queue, QueueName}) of [] -> case mnesia:read({rabbit_durable_queue, QueueName}) of - [] -> ok = store_queue(Q), - B = add_default_binding(Q), - fun () -> B(), Q end; + [] -> Q1 = rabbit_policy:set(Q), + ok = store_queue(Q1), + B = add_default_binding(Q1), + fun () -> B(), Q1 end; %% Q exists on stopped node [_] -> rabbit_misc:const(not_found) end; @@ -237,6 +243,19 @@ internal_declare(Q = #amqqueue{name = QueueName}, false) -> end end). +update(Name, Fun) -> + case mnesia:wread({rabbit_queue, Name}) of + [Q = #amqqueue{durable = Durable}] -> + Q1 = Fun(Q), + ok = mnesia:write(rabbit_queue, Q1, write), + case Durable of + true -> ok = mnesia:write(rabbit_durable_queue, Q1, write); + _ -> ok + end; + [] -> + ok + end. + store_queue(Q = #amqqueue{durable = true}) -> ok = mnesia:write(rabbit_durable_queue, Q#amqqueue{slave_pids = []}, write), ok = mnesia:write(rabbit_queue, Q, write), @@ -245,6 +264,9 @@ store_queue(Q = #amqqueue{durable = false}) -> ok = mnesia:write(rabbit_queue, Q, write), ok. +policy_changed(_Q1, _Q2) -> + ok. + determine_queue_nodes(Args) -> Policy = rabbit_misc:table_lookup(Args, <<"x-ha-policy">>), PolicyParams = rabbit_misc:table_lookup(Args, <<"x-ha-policy-params">>), diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 5701efeb..bbc20a78 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -197,23 +197,41 @@ declare(Recover, From, State = #q{q = Q, backing_queue = BQ, backing_queue_state = undefined}) -> case rabbit_amqqueue:internal_declare(Q, Recover) of - not_found -> {stop, normal, not_found, State}; - Q -> gen_server2:reply(From, {new, Q}), - ok = file_handle_cache:register_callback( - rabbit_amqqueue, set_maximum_since_use, - [self()]), - ok = rabbit_memory_monitor:register( - self(), {rabbit_amqqueue, - set_ram_duration_target, [self()]}), - BQS = bq_init(BQ, Q, Recover), - State1 = process_args(State#q{backing_queue_state = BQS}), - rabbit_event:notify(queue_created, - infos(?CREATION_EVENT_KEYS, State1)), - rabbit_event:if_enabled(State1, #q.stats_timer, - fun() -> emit_stats(State1) end), - noreply(State1); - Q1 -> {stop, normal, {existing, Q1}, State} - end. + not_found -> + {stop, normal, not_found, State}; + Q1 -> + case matches(Recover, Q, Q1) of + true -> + gen_server2:reply(From, {new, Q}), + ok = file_handle_cache:register_callback( + rabbit_amqqueue, set_maximum_since_use, [self()]), + ok = rabbit_memory_monitor:register( + self(), {rabbit_amqqueue, + set_ram_duration_target, [self()]}), + BQS = bq_init(BQ, Q, Recover), + State1 = process_args(State#q{backing_queue_state = BQS}), + rabbit_event:notify(queue_created, + infos(?CREATION_EVENT_KEYS, State1)), + rabbit_event:if_enabled(State1, #q.stats_timer, + fun() -> emit_stats(State1) end), + noreply(State1); + false -> + {stop, normal, {existing, Q1}, State} + end + end. + +matches(true, Q, Q) -> true; +matches(true, Q, Q1) -> false; +matches(false, Q1, Q2) -> + %% i.e. not policy + Q1#amqqueue.name =:= Q2#amqqueue.name andalso + Q1#amqqueue.durable =:= Q2#amqqueue.durable andalso + Q1#amqqueue.auto_delete =:= Q2#amqqueue.auto_delete andalso + Q1#amqqueue.exclusive_owner =:= Q2#amqqueue.exclusive_owner andalso + Q1#amqqueue.arguments =:= Q2#amqqueue.arguments andalso + Q1#amqqueue.pid =:= Q2#amqqueue.pid andalso + Q1#amqqueue.slave_pids =:= Q2#amqqueue.slave_pids andalso + Q1#amqqueue.mirror_nodes =:= Q2#amqqueue.mirror_nodes. bq_init(BQ, Q, Recover) -> Self = self(), diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index 7455c797..d87c607d 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -18,13 +18,13 @@ -include("rabbit.hrl"). -include("rabbit_framing.hrl"). --export([recover/0, callback/4, declare/6, +-export([recover/0, policy_changed/2, callback/4, declare/6, assert_equivalence/6, assert_args_equivalence/2, check_type/1, lookup/1, lookup_or_die/1, list/1, lookup_scratch/2, update_scratch/3, info_keys/0, info/1, info/2, info_all/1, info_all/2, route/2, delete/2]). %% these must be run inside a mnesia tx --export([maybe_auto_delete/1, serial/1, peek_serial/1]). +-export([maybe_auto_delete/1, serial/1, peek_serial/1, update/2]). %%---------------------------------------------------------------------------- @@ -40,6 +40,8 @@ -spec(callback/4:: (rabbit_types:exchange(), fun_name(), non_neg_integer() | atom(), [any()]) -> 'ok'). +-spec(policy_changed/2 :: + (rabbit_types:exchange(), rabbit_types:exchange()) -> 'ok'). -spec(declare/6 :: (name(), type(), boolean(), boolean(), boolean(), rabbit_framing:amqp_table()) @@ -64,6 +66,9 @@ rabbit_types:ok(term()) | rabbit_types:error('not_found')). -spec(update_scratch/3 :: (name(), atom(), fun((any()) -> any())) -> 'ok'). +-spec(update/2 :: + (name(), + fun((rabbit_types:exchange()) -> rabbit_types:exchange())) -> 'ok'). -spec(info_keys/0 :: () -> rabbit_types:info_keys()). -spec(info/1 :: (rabbit_types:exchange()) -> rabbit_types:infos()). -spec(info/2 :: @@ -89,7 +94,8 @@ %%---------------------------------------------------------------------------- --define(INFO_KEYS, [name, type, durable, auto_delete, internal, arguments]). +-define(INFO_KEYS, [name, type, durable, auto_delete, internal, arguments, + policy]). recover() -> Xs = rabbit_misc:table_filter( @@ -119,6 +125,8 @@ callback(X = #exchange{type = XType}, Fun, Serial0, Args) -> Module = type_to_module(XType), apply(Module, Fun, [Serial(Module:serialise_events()) | Args]). +policy_changed(X1, X2) -> callback(X1, policy_changed, none, [X1, X2]). + serialise_events(X = #exchange{type = Type}) -> case [Serialise || M <- decorators(), Serialise <- [M:serialise_events(X)], @@ -140,12 +148,12 @@ decorators() -> [M || {_, M} <- rabbit_registry:lookup_all(exchange_decorator)]. declare(XName, Type, Durable, AutoDelete, Internal, Args) -> - X = #exchange{name = XName, - type = Type, - durable = Durable, - auto_delete = AutoDelete, - internal = Internal, - arguments = Args}, + X = rabbit_policy:set(#exchange{name = XName, + type = Type, + durable = Durable, + auto_delete = AutoDelete, + internal = Internal, + arguments = Args}), XT = type_to_module(Type), %% We want to upset things if it isn't ok ok = XT:validate(X), @@ -252,29 +260,35 @@ lookup_scratch(Name, App) -> update_scratch(Name, App, Fun) -> rabbit_misc:execute_mnesia_transaction( fun() -> - case mnesia:wread({rabbit_exchange, Name}) of - [X = #exchange{durable = Durable, scratches = Scratches0}] -> - Scratches1 = case Scratches0 of - undefined -> orddict:new(); - _ -> Scratches0 - end, - Scratch = case orddict:find(App, Scratches1) of - {ok, S} -> S; - error -> undefined - end, - Scratches2 = orddict:store(App, Fun(Scratch), Scratches1), - X1 = X#exchange{scratches = Scratches2}, - ok = mnesia:write(rabbit_exchange, X1, write), - case Durable of - true -> ok = mnesia:write(rabbit_durable_exchange, - X1, write); - _ -> ok - end; - [] -> - ok - end + update(Name, + fun(X = #exchange{scratches = Scratches0}) -> + Scratches1 = case Scratches0 of + undefined -> orddict:new(); + _ -> Scratches0 + end, + Scratch = case orddict:find(App, Scratches1) of + {ok, S} -> S; + error -> undefined + end, + Scratches2 = orddict:store( + App, Fun(Scratch), Scratches1), + X#exchange{scratches = Scratches2} + end) end). +update(Name, Fun) -> + case mnesia:wread({rabbit_exchange, Name}) of + [X = #exchange{durable = Durable}] -> + X1 = Fun(X), + ok = mnesia:write(rabbit_exchange, X1, write), + case Durable of + true -> ok = mnesia:write(rabbit_durable_exchange, X1, write); + _ -> ok + end; + [] -> + ok + end. + info_keys() -> ?INFO_KEYS. map(VHostPath, F) -> @@ -290,6 +304,7 @@ i(durable, #exchange{durable = Durable}) -> Durable; i(auto_delete, #exchange{auto_delete = AutoDelete}) -> AutoDelete; i(internal, #exchange{internal = Internal}) -> Internal; i(arguments, #exchange{arguments = Arguments}) -> Arguments; +i(policy, X) -> rabbit_policy:name(X); i(Item, _) -> throw({bad_argument, Item}). info(X = #exchange{}) -> infos(?INFO_KEYS, X). diff --git a/src/rabbit_exchange_decorator.erl b/src/rabbit_exchange_decorator.erl index 4fa87485..9f30688d 100644 --- a/src/rabbit_exchange_decorator.erl +++ b/src/rabbit_exchange_decorator.erl @@ -18,6 +18,8 @@ -export([behaviour_info/1]). +%% TODO make this into a modern typed callback + behaviour_info(callbacks) -> [ {description, 0}, @@ -40,7 +42,10 @@ behaviour_info(callbacks) -> {add_binding, 3}, %% called after bindings have been deleted. - {remove_bindings, 3} + {remove_bindings, 3}, + + %% called when the policy attached to this exchange changes. + {policy_changed, 3} ]; behaviour_info(_Other) -> undefined. diff --git a/src/rabbit_exchange_type.erl b/src/rabbit_exchange_type.erl index 1027570c..e6470b72 100644 --- a/src/rabbit_exchange_type.erl +++ b/src/rabbit_exchange_type.erl @@ -58,6 +58,10 @@ rabbit_framing:amqp_table()) -> 'ok' | rabbit_types:connection_exit(). +%% called when the policy attached to this exchange changes. +-callback policy_changed ( + serial(), rabbit_types:exchange(), rabbit_types:exchange()) -> 'ok'. + -else. -export([behaviour_info/1]). @@ -65,7 +69,7 @@ behaviour_info(callbacks) -> [{description, 0}, {serialise_events, 0}, {route, 2}, {validate, 1}, {create, 2}, {delete, 3}, {add_binding, 3}, {remove_bindings, 3}, - {assert_args_equivalence, 2}]; + {assert_args_equivalence, 2}, {policy_changed, 3}]; behaviour_info(_Other) -> undefined. diff --git a/src/rabbit_exchange_type_direct.erl b/src/rabbit_exchange_type_direct.erl index cdec1cb9..9a5665c0 100644 --- a/src/rabbit_exchange_type_direct.erl +++ b/src/rabbit_exchange_type_direct.erl @@ -20,7 +20,7 @@ -behaviour(rabbit_exchange_type). -export([description/0, serialise_events/0, route/2]). --export([validate/1, create/2, delete/3, +-export([validate/1, create/2, delete/3, policy_changed/3, add_binding/3, remove_bindings/3, assert_args_equivalence/2]). -rabbit_boot_step({?MODULE, @@ -43,6 +43,7 @@ route(#exchange{name = Name}, validate(_X) -> ok. create(_Tx, _X) -> ok. delete(_Tx, _X, _Bs) -> ok. +policy_changed(_Tx, _X1, _X2) -> ok. add_binding(_Tx, _X, _B) -> ok. remove_bindings(_Tx, _X, _Bs) -> ok. assert_args_equivalence(X, Args) -> diff --git a/src/rabbit_exchange_type_fanout.erl b/src/rabbit_exchange_type_fanout.erl index a64f2c29..d9a2f60f 100644 --- a/src/rabbit_exchange_type_fanout.erl +++ b/src/rabbit_exchange_type_fanout.erl @@ -20,7 +20,7 @@ -behaviour(rabbit_exchange_type). -export([description/0, serialise_events/0, route/2]). --export([validate/1, create/2, delete/3, add_binding/3, +-export([validate/1, create/2, delete/3, policy_changed/3, add_binding/3, remove_bindings/3, assert_args_equivalence/2]). -rabbit_boot_step({?MODULE, @@ -42,6 +42,7 @@ route(#exchange{name = Name}, _Delivery) -> validate(_X) -> ok. create(_Tx, _X) -> ok. delete(_Tx, _X, _Bs) -> ok. +policy_changed(_Tx, _X1, _X2) -> ok. add_binding(_Tx, _X, _B) -> ok. remove_bindings(_Tx, _X, _Bs) -> ok. assert_args_equivalence(X, Args) -> diff --git a/src/rabbit_exchange_type_headers.erl b/src/rabbit_exchange_type_headers.erl index 61917d8f..516b78e5 100644 --- a/src/rabbit_exchange_type_headers.erl +++ b/src/rabbit_exchange_type_headers.erl @@ -21,7 +21,7 @@ -behaviour(rabbit_exchange_type). -export([description/0, serialise_events/0, route/2]). --export([validate/1, create/2, delete/3, add_binding/3, +-export([validate/1, create/2, delete/3, policy_changed/3, add_binding/3, remove_bindings/3, assert_args_equivalence/2]). -rabbit_boot_step({?MODULE, @@ -116,6 +116,7 @@ headers_match([{PK, PT, PV} | PRest], [{DK, DT, DV} | DRest], validate(_X) -> ok. create(_Tx, _X) -> ok. delete(_Tx, _X, _Bs) -> ok. +policy_changed(_Tx, _X1, _X2) -> ok. add_binding(_Tx, _X, _B) -> ok. remove_bindings(_Tx, _X, _Bs) -> ok. assert_args_equivalence(X, Args) -> diff --git a/src/rabbit_exchange_type_invalid.erl b/src/rabbit_exchange_type_invalid.erl index 82d27960..101fe434 100644 --- a/src/rabbit_exchange_type_invalid.erl +++ b/src/rabbit_exchange_type_invalid.erl @@ -20,7 +20,7 @@ -behaviour(rabbit_exchange_type). -export([description/0, serialise_events/0, route/2]). --export([validate/1, create/2, delete/3, +-export([validate/1, create/2, delete/3, policy_changed/3, add_binding/3, remove_bindings/3, assert_args_equivalence/2]). description() -> @@ -40,6 +40,7 @@ route(#exchange{name = Name, type = Type}, _) -> validate(_X) -> ok. create(_Tx, _X) -> ok. delete(_Tx, _X, _Bs) -> ok. +policy_changed(_Tx, _X1, _X2) -> ok. add_binding(_Tx, _X, _B) -> ok. remove_bindings(_Tx, _X, _Bs) -> ok. assert_args_equivalence(X, Args) -> diff --git a/src/rabbit_exchange_type_topic.erl b/src/rabbit_exchange_type_topic.erl index 3160fdf4..644d9acf 100644 --- a/src/rabbit_exchange_type_topic.erl +++ b/src/rabbit_exchange_type_topic.erl @@ -21,7 +21,7 @@ -behaviour(rabbit_exchange_type). -export([description/0, serialise_events/0, route/2]). --export([validate/1, create/2, delete/3, add_binding/3, +-export([validate/1, create/2, delete/3, policy_changed/3, add_binding/3, remove_bindings/3, assert_args_equivalence/2]). -rabbit_boot_step({?MODULE, @@ -58,6 +58,8 @@ delete(transaction, #exchange{name = X}, _Bs) -> delete(none, _Exchange, _Bs) -> ok. +policy_changed(_Tx, _X1, _X2) -> ok. + add_binding(transaction, _Exchange, Binding) -> internal_add_binding(Binding); add_binding(none, _Exchange, _Binding) -> diff --git a/src/rabbit_policy.erl b/src/rabbit_policy.erl new file mode 100644 index 00000000..2697b93d --- /dev/null +++ b/src/rabbit_policy.erl @@ -0,0 +1,202 @@ +%% 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 VMware, Inc. +%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% + +-module(rabbit_policy). + +-behaviour(rabbit_runtime_parameter). + +-include("rabbit.hrl"). + +-import(rabbit_misc, [pget/2]). + +-export([register/0]). +-export([name/1, get/2, set/1]). +-export([validate/3, validate_clear/2, notify/3, notify_clear/2]). + +-rabbit_boot_step({?MODULE, + [{description, "policy parameters"}, + {mfa, {rabbit_policy, register, []}}, + {requires, rabbit_registry}, + {enables, recovery}]}). + +register() -> + rabbit_registry:register(runtime_parameter, <<"policy">>, ?MODULE). + +name(#amqqueue{policy = Policy}) -> name0(Policy); +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(X = #exchange{name = Name}) -> X#exchange{policy = set0(Name)}. + +set0(Name) -> match(Name, list()). + +get(Name, #amqqueue{policy = Policy}) -> get0(Name, Policy); +get(Name, #exchange{policy = Policy}) -> get0(Name, Policy). + +get0(_Name, undefined) -> {error, not_found}; +get0(Name, List) -> case pget(<<"policy">>, List) of + undefined -> {error, not_found}; + Policy -> case pget(Name, Policy) of + undefined -> {error, not_found}; + Value -> {ok, Value} + end + end. + +%%---------------------------------------------------------------------------- + +validate(<<"policy">>, _Name, Term) -> + assert_contents(policy_validation(), Term). + +validate_clear(<<"policy">>, _Name) -> + ok. + +notify(<<"policy">>, _Name, _Term) -> + update_policies(). + +notify_clear(<<"policy">>, _Name) -> + update_policies(). + +%%---------------------------------------------------------------------------- + +list() -> + [[{<<"name">>, pget(key, P)} | pget(value, P)] + || P <- rabbit_runtime_parameters:list(<<"policy">>)]. + +update_policies() -> + Policies = list(), + {Xs, Qs} = rabbit_misc:execute_mnesia_transaction( + fun() -> + {[update_exchange(X, Policies) || + VHost <- rabbit_vhost:list(), + X <- rabbit_exchange:list(VHost)], + [update_queue(Q, Policies) || + VHost <- rabbit_vhost:list(), + Q <- rabbit_amqqueue:list(VHost)]} + end), + [notify(X) || X <- Xs], + [notify(Q) || Q <- Qs], + ok. + +update_exchange(X = #exchange{name = XName, policy = OldPolicy}, Policies) -> + NewPolicy = match(XName, Policies), + case NewPolicy of + OldPolicy -> no_change; + _ -> rabbit_exchange:update( + XName, fun(X1) -> X1#exchange{policy = NewPolicy} end), + {X, X#exchange{policy = NewPolicy}} + end. + +update_queue(Q = #amqqueue{name = QName, policy = OldPolicy}, Policies) -> + NewPolicy = match(QName, Policies), + case NewPolicy of + OldPolicy -> no_change; + _ -> rabbit_amqqueue:update( + QName, fun(Q1) -> Q1#amqqueue{policy = NewPolicy} end), + {Q, Q#amqqueue{policy = NewPolicy}} + end. + +notify(no_change)-> + ok; +notify({X1 = #exchange{}, X2 = #exchange{}}) -> + rabbit_exchange:policy_changed(X1, X2); +notify({Q1 = #amqqueue{}, Q2 = #amqqueue{}}) -> + rabbit_amqqueue:policy_changed(Q1, Q2). + +match(Name, Policies) -> + case lists:sort(fun sort_pred/2, [P || P <- Policies, matches(Name, P)]) of + [] -> undefined; + [Policy | _Rest] -> Policy + end. + +matches(#resource{name = Name, virtual_host = VHost}, Policy) -> + Prefix = pget(<<"prefix">>, Policy), + case pget(<<"vhost">>, Policy) of + undefined -> prefix(Prefix, Name); + VHost -> prefix(Prefix, Name); + _ -> false + end. + +prefix(A, B) -> lists:prefix(binary_to_list(A), binary_to_list(B)). + +sort_pred(A, B) -> + R = size(pget(<<"prefix">>, A)) >= size(pget(<<"prefix">>, B)), + case {pget(<<"vhost">>, A), pget(<<"vhost">>, B)} of + {undefined, undefined} -> R; + {undefined, _} -> true; + {_, undefined} -> false; + _ -> R + end. + +%%---------------------------------------------------------------------------- + +policy_validation() -> + [{<<"vhost">>, binary, optional}, + {<<"prefix">>, binary, mandatory}, + {<<"policy">>, list, mandatory}]. + +%% TODO this is mostly duplicated from +%% rabbit_federation_parameters. Sort that out in some way. + +assert_type(Name, {Type, Opts}, Term) -> + assert_type(Name, Type, Term), + case lists:member(Term, Opts) of + true -> ok; + false -> {error, "~s must be one of ~p", [Name, Opts]} + end; + +assert_type(_Name, number, Term) when is_number(Term) -> + ok; + +assert_type(Name, number, Term) -> + {error, "~s should be number, actually was ~p", [Name, Term]}; + +assert_type(_Name, binary, Term) when is_binary(Term) -> + ok; + +assert_type(Name, binary, Term) -> + {error, "~s should be binary, actually was ~p", [Name, Term]}; + +assert_type(_Name, list, Term) when is_list(Term) -> + ok; + +assert_type(Name, list, Term) -> + {error, "~s should be list, actually was ~p", [Name, Term]}. + +assert_contents(Constraints, Term) when is_list(Term) -> + {Results, Remainder} + = lists:foldl( + fun ({Name, Constraint, Needed}, {Results0, Term0}) -> + case {lists:keytake(Name, 1, Term0), Needed} of + {{value, {Name, Value}, Term1}, _} -> + {[assert_type(Name, Constraint, Value) | Results0], + Term1}; + {false, mandatory} -> + {[{error, "Key \"~s\" not found", [Name]} | + Results0], Term0}; + {false, optional} -> + {Results0, Term0} + end + end, {[], Term}, Constraints), + case Remainder of + [] -> Results; + _ -> [{error, "Unrecognised terms ~p", [Remainder]} | Results] + end; + +assert_contents(_Constraints, Term) -> + {error, "Not a list ~p", [Term]}. diff --git a/src/rabbit_upgrade_functions.erl b/src/rabbit_upgrade_functions.erl index 87e560e8..18704807 100644 --- a/src/rabbit_upgrade_functions.erl +++ b/src/rabbit_upgrade_functions.erl @@ -38,6 +38,8 @@ -rabbit_upgrade({topic_trie_node, mnesia, []}). -rabbit_upgrade({runtime_parameters, mnesia, []}). -rabbit_upgrade({exchange_scratches, mnesia, [exchange_scratch]}). +-rabbit_upgrade({policy, mnesia, + [exchange_scratches, ha_mirrors]}). %% ------------------------------------------------------------------- @@ -59,6 +61,7 @@ -spec(mirrored_supervisor/0 :: () -> 'ok'). -spec(topic_trie_node/0 :: () -> 'ok'). -spec(runtime_parameters/0 :: () -> 'ok'). +-spec(policy/0 :: () -> 'ok'). -endif. @@ -211,6 +214,32 @@ exchange_scratches(Table) -> end, [name, type, durable, auto_delete, internal, arguments, scratches]). +policy() -> + ok = exchange_policy(rabbit_exchange), + ok = exchange_policy(rabbit_durable_exchange), + ok = queue_policy(rabbit_queue), + ok = queue_policy(rabbit_durable_queue). + +exchange_policy(Table) -> + transform( + Table, + fun ({exchange, Name, Type, Dur, AutoDel, Int, Args, Scratches}) -> + {exchange, Name, Type, Dur, AutoDel, Int, Args, Scratches, + undefined} + end, + [name, type, durable, auto_delete, internal, arguments, scratches, + policy]). + +queue_policy(Table) -> + transform( + Table, + fun ({amqqueue, Name, Dur, AutoDel, Excl, Args, Pid, SPids, MNodes}) -> + {amqqueue, Name, Dur, AutoDel, Excl, Args, Pid, SPids, MNodes, + undefined} + end, + [name, durable, auto_delete, exclusive_owner, arguments, pid, + slave_pids, mirror_nodes, policy]). + %%-------------------------------------------------------------------- transform(TableName, Fun, FieldList) -> -- cgit v1.2.1 From fa6eb0adc5386913806d843ea5f040c1922f30b2 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 18 May 2012 10:50:17 +0100 Subject: TODO--: Update behaviour --- src/rabbit_exchange_decorator.erl | 60 +++++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 22 deletions(-) diff --git a/src/rabbit_exchange_decorator.erl b/src/rabbit_exchange_decorator.erl index 9f30688d..b8583c82 100644 --- a/src/rabbit_exchange_decorator.erl +++ b/src/rabbit_exchange_decorator.erl @@ -16,36 +16,52 @@ -module(rabbit_exchange_decorator). --export([behaviour_info/1]). +-ifdef(use_specs). -%% TODO make this into a modern typed callback +-type(tx() :: 'transaction' | 'none'). +-type(serial() :: pos_integer() | tx()). -behaviour_info(callbacks) -> - [ - {description, 0}, +-callback description() -> [proplist:property()]. + +%% Should Rabbit ensure that all binding events that are +%% delivered to an individual exchange can be serialised? (they +%% might still be delivered out of order, but there'll be a +%% serial number). +-callback serialise_events(rabbit_types:exchange()) -> boolean(). + +%% The no_return is there so that we can have an "invalid" exchange +%% type (see rabbit_exchange_type_invalid). +-callback route(rabbit_types:exchange(), rabbit_types:delivery()) -> + rabbit_router:match_result(). - %% Should Rabbit ensure that all binding events that are - %% delivered to an individual exchange can be serialised? (they - %% might still be delivered out of order, but there'll be a - %% serial number). - {serialise_events, 1}, +%% called after declaration and recovery +-callback create(tx(), rabbit_types:exchange()) -> 'ok'. - {route, 2}, +%% called after exchange (auto)deletion. +-callback delete(tx(), rabbit_types:exchange(), [rabbit_types:binding()]) -> + 'ok'. - %% called after declaration and recovery - {create, 2}, +%% called after a binding has been added or recovered +-callback add_binding(serial(), rabbit_types:exchange(), + rabbit_types:binding()) -> 'ok'. - %% called after exchange (auto)deletion. - {delete, 3}, +%% called after bindings have been deleted. +-callback remove_bindings(serial(), rabbit_types:exchange(), + [rabbit_types:binding()]) -> 'ok'. - %% called after a binding has been added or recovered - {add_binding, 3}, +%% called when the policy attached to this exchange changes. +-callback policy_changed ( + serial(), rabbit_types:exchange(), rabbit_types:exchange()) -> 'ok'. - %% called after bindings have been deleted. - {remove_bindings, 3}, +-else. - %% called when the policy attached to this exchange changes. - {policy_changed, 3} - ]; +-export([behaviour_info/1]). + +behaviour_info(callbacks) -> + [{description, 0}, {serialise_events, 1}, {route, 2}, + {create, 2}, {delete, 3}, {add_binding, 3}, {remove_bindings, 3}, + {policy_changed, 3}]; behaviour_info(_Other) -> undefined. + +-endif. -- cgit v1.2.1 From 56e4d64cf0ac2041f5a6315ab022120a1063b68c Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 18 May 2012 10:51:33 +0100 Subject: Remove decorator:route/2. --- src/rabbit_exchange_decorator.erl | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/rabbit_exchange_decorator.erl b/src/rabbit_exchange_decorator.erl index b8583c82..bfb78201 100644 --- a/src/rabbit_exchange_decorator.erl +++ b/src/rabbit_exchange_decorator.erl @@ -29,11 +29,6 @@ %% serial number). -callback serialise_events(rabbit_types:exchange()) -> boolean(). -%% The no_return is there so that we can have an "invalid" exchange -%% type (see rabbit_exchange_type_invalid). --callback route(rabbit_types:exchange(), rabbit_types:delivery()) -> - rabbit_router:match_result(). - %% called after declaration and recovery -callback create(tx(), rabbit_types:exchange()) -> 'ok'. @@ -58,9 +53,8 @@ -export([behaviour_info/1]). behaviour_info(callbacks) -> - [{description, 0}, {serialise_events, 1}, {route, 2}, - {create, 2}, {delete, 3}, {add_binding, 3}, {remove_bindings, 3}, - {policy_changed, 3}]; + [{description, 0}, {serialise_events, 1}, {create, 2}, {delete, 3}, + {add_binding, 3}, {remove_bindings, 3}, {policy_changed, 3}]; behaviour_info(_Other) -> undefined. -- cgit v1.2.1 From 3218b0d34114546e8e5ec6d08aa767c44426912e Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 18 May 2012 10:59:25 +0100 Subject: Document policy info item. --- docs/rabbitmqctl.1.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/rabbitmqctl.1.xml b/docs/rabbitmqctl.1.xml index 1effd691..f64406e3 100644 --- a/docs/rabbitmqctl.1.xml +++ b/docs/rabbitmqctl.1.xml @@ -886,6 +886,10 @@ arguments Queue arguments. + + policy + Policy name applying to the queue. + pid Id of the Erlang process associated with the queue. @@ -998,6 +1002,10 @@ arguments Exchange arguments. + + policy + Policy name for applying to the exchange. + If no exchangeinfoitems are specified then -- cgit v1.2.1 From e0f07370152621f4788314457e46aa748b9bd493 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 18 May 2012 11:16:21 +0100 Subject: Unused variable warning --- src/rabbit_amqqueue_process.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index bbc20a78..8a3ae83e 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -221,7 +221,7 @@ declare(Recover, From, State = #q{q = Q, end. matches(true, Q, Q) -> true; -matches(true, Q, Q1) -> false; +matches(true, _Q, _Q1) -> false; matches(false, Q1, Q2) -> %% i.e. not policy Q1#amqqueue.name =:= Q2#amqqueue.name andalso -- cgit v1.2.1 From 464239f93d0590e7d011b44fb33cfcb370c58613 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 18 May 2012 11:36:06 +0100 Subject: Move parameter validation into its own module. --- src/rabbit_parameter_validation.erl | 61 ++++++++++++++++++++++++++++++++++++ src/rabbit_policy.erl | 62 ++++--------------------------------- 2 files changed, 67 insertions(+), 56 deletions(-) create mode 100644 src/rabbit_parameter_validation.erl diff --git a/src/rabbit_parameter_validation.erl b/src/rabbit_parameter_validation.erl new file mode 100644 index 00000000..af940dde --- /dev/null +++ b/src/rabbit_parameter_validation.erl @@ -0,0 +1,61 @@ +%% 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 VMware, Inc. +%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% + +-module(rabbit_parameter_validation). + +-export([number/2, binary/2, list/2, proplist/3]). + +number(_Name, Term) when is_number(Term) -> + ok; + +number(Name, Term) -> + {error, "~s should be number, actually was ~p", [Name, Term]}. + +binary(_Name, Term) when is_binary(Term) -> + ok; + +binary(Name, Term) -> + {error, "~s should be binary, actually was ~p", [Name, Term]}. + +list(_Name, Term) when is_list(Term) -> + ok; + +list(Name, Term) -> + {error, "~s should be list, actually was ~p", [Name, Term]}. + +proplist(Name, Constraints, Term) when is_list(Term) -> + {Results, Remainder} + = lists:foldl( + fun ({Key, Fun, Needed}, {Results0, Term0}) -> + case {lists:keytake(Key, 1, Term0), Needed} of + {{value, {Key, Value}, Term1}, _} -> + {[Fun(Key, Value) | Results0], + Term1}; + {false, mandatory} -> + {[{error, "Key \"~s\" not found in ~s", + [Key, Name]} | Results0], Term0}; + {false, optional} -> + {Results0, Term0} + end + end, {[], Term}, Constraints), + case Remainder of + [] -> Results; + _ -> [{error, "Unrecognised terms ~p in ~s", [Remainder, Name]} + | Results] + end; + +proplist(Name, _Constraints, Term) -> + {error, "~s not a list ~p", [Name, Term]}. diff --git a/src/rabbit_policy.erl b/src/rabbit_policy.erl index 2697b93d..d4c2dee0 100644 --- a/src/rabbit_policy.erl +++ b/src/rabbit_policy.erl @@ -60,8 +60,9 @@ get0(Name, List) -> case pget(<<"policy">>, List) of %%---------------------------------------------------------------------------- -validate(<<"policy">>, _Name, Term) -> - assert_contents(policy_validation(), Term). +validate(<<"policy">>, Name, Term) -> + rabbit_parameter_validation:proplist( + Name, policy_validation(), Term). validate_clear(<<"policy">>, _Name) -> ok. @@ -146,57 +147,6 @@ sort_pred(A, B) -> %%---------------------------------------------------------------------------- policy_validation() -> - [{<<"vhost">>, binary, optional}, - {<<"prefix">>, binary, mandatory}, - {<<"policy">>, list, mandatory}]. - -%% TODO this is mostly duplicated from -%% rabbit_federation_parameters. Sort that out in some way. - -assert_type(Name, {Type, Opts}, Term) -> - assert_type(Name, Type, Term), - case lists:member(Term, Opts) of - true -> ok; - false -> {error, "~s must be one of ~p", [Name, Opts]} - end; - -assert_type(_Name, number, Term) when is_number(Term) -> - ok; - -assert_type(Name, number, Term) -> - {error, "~s should be number, actually was ~p", [Name, Term]}; - -assert_type(_Name, binary, Term) when is_binary(Term) -> - ok; - -assert_type(Name, binary, Term) -> - {error, "~s should be binary, actually was ~p", [Name, Term]}; - -assert_type(_Name, list, Term) when is_list(Term) -> - ok; - -assert_type(Name, list, Term) -> - {error, "~s should be list, actually was ~p", [Name, Term]}. - -assert_contents(Constraints, Term) when is_list(Term) -> - {Results, Remainder} - = lists:foldl( - fun ({Name, Constraint, Needed}, {Results0, Term0}) -> - case {lists:keytake(Name, 1, Term0), Needed} of - {{value, {Name, Value}, Term1}, _} -> - {[assert_type(Name, Constraint, Value) | Results0], - Term1}; - {false, mandatory} -> - {[{error, "Key \"~s\" not found", [Name]} | - Results0], Term0}; - {false, optional} -> - {Results0, Term0} - end - end, {[], Term}, Constraints), - case Remainder of - [] -> Results; - _ -> [{error, "Unrecognised terms ~p", [Remainder]} | Results] - end; - -assert_contents(_Constraints, Term) -> - {error, "Not a list ~p", [Term]}. + [{<<"vhost">>, fun rabbit_parameter_validation:binary/2, optional}, + {<<"prefix">>, fun rabbit_parameter_validation:binary/2, mandatory}, + {<<"policy">>, fun rabbit_parameter_validation:list/2, mandatory}]. -- cgit v1.2.1 From 14d4e0b41ee8f2db7ff0dec0a64bd10ae2a2c9bc Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Fri, 18 May 2012 12:01:09 +0100 Subject: moved functions around in rabbit_mnesia --- src/rabbit_mnesia.erl | 904 ++++++++++++++++++++++++++------------------------ 1 file changed, 468 insertions(+), 436 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 8427ae2a..8cde1702 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -45,53 +45,50 @@ -type(node_type() :: disc_only | disc | ram | unknown). -type(node_status() :: {[node()], [node()], [node()]}). --spec(status/0 :: () -> [{'nodes', [{node_type(), [node()]}]} | - {'running_nodes', [node()]}]). --spec(dir/0 :: () -> file:filename()). --spec(ensure_mnesia_dir/0 :: () -> 'ok'). + +%% Main interface -spec(init/0 :: () -> 'ok'). --spec(init_db/4 :: ([node()], boolean(), boolean(), boolean()) -> 'ok'). --spec(is_db_empty/0 :: () -> boolean()). -spec(join_cluster/2 :: ([node()], boolean()) -> 'ok'). -spec(reset/0 :: () -> 'ok'). -spec(force_reset/0 :: () -> 'ok'). + +%% Various queries to get the status of the db +-spec(status/0 :: () -> [{'nodes', [{node_type(), [node()]}]} | + {'running_nodes', [node()]}]). +-spec(is_db_empty/0 :: () -> boolean()). -spec(is_clustered/0 :: () -> boolean()). -spec(running_clustered_nodes/0 :: () -> [node()]). -spec(all_clustered_nodes/0 :: () -> [node()]). +-spec(is_disc_node/0 :: () -> boolean()). +-spec(dir/0 :: () -> file:filename()). +-spec(table_names/0 :: () -> [atom()]). +-spec(cluster_status_if_running/0 :: () -> 'error' | node_status()). + +%% Operations on the db and utils, mainly used in `rabbit_upgrade' and `rabbit' +-spec(init_db/4 :: ([node()], boolean(), boolean(), boolean()) -> 'ok'). +-spec(ensure_mnesia_dir/0 :: () -> 'ok'). -spec(empty_ram_only_tables/0 :: () -> 'ok'). -spec(create_tables/0 :: () -> 'ok'). -spec(copy_db/1 :: (file:filename()) -> rabbit_types:ok_or_error(any())). -spec(wait_for_tables/1 :: ([atom()]) -> 'ok'). +-spec(should_be_disc_node/1 :: ([node()]) -> boolean()). +-spec(check_cluster_consistency/0 :: () -> 'ok' | no_return()). + +%% Functions to handle the cluster status file -spec(write_cluster_nodes_status/1 :: (node_status()) -> 'ok'). -spec(read_cluster_nodes_status/0 :: () -> node_status()). -spec(initialize_cluster_nodes_status/0 :: () -> 'ok'). -spec(update_cluster_nodes_status/0 :: () -> 'ok'). --spec(is_disc_node/0 :: () -> boolean()). + +%% Hooks used in `rabbit_node_monitor' -spec(on_node_up/1 :: (node()) -> 'ok'). -spec(on_node_down/1 :: (node()) -> 'ok'). --spec(should_be_disc_node/1 :: ([node()]) -> boolean()). - --spec(table_names/0 :: () -> [atom()]). -endif. %%---------------------------------------------------------------------------- - -status() -> - [{nodes, case mnesia:system_info(is_running) of - yes -> [{Key, Nodes} || - {Key, CopyType} <- [{disc_only, disc_only_copies}, - {disc, disc_copies}, - {ram, ram_copies}], - begin - Nodes = nodes_of_type(CopyType), - Nodes =/= [] - end]; - no -> [{unknown, all_clustered_nodes()}]; - Reason when Reason =:= starting; Reason =:= stopping -> - exit({rabbit_busy, try_again_later}) - end}, - {running_nodes, running_clustered_nodes()}]. +%% Main interface +%%---------------------------------------------------------------------------- init() -> ensure_mnesia_running(), @@ -104,10 +101,6 @@ init() -> ok = global:sync(), ok. -is_db_empty() -> - lists:all(fun (Tab) -> mnesia:dirty_first(Tab) == '$end_of_table' end, - table_names()). - %% Make the node join a cluster. The node will be reset automatically before we %% actually cluster it. The nodes provided will be used to find out about the %% nodes in the cluster. @@ -171,6 +164,79 @@ join_cluster(DiscoveryNodes, WantDiscNode) -> reset() -> reset(false). force_reset() -> reset(true). +reset(Force) -> + rabbit_misc:local_info_msg("Resetting Rabbit~s~n", + [if Force -> " forcefully"; + true -> "" + end]), + ensure_mnesia_not_running(), + case not Force andalso is_disc_and_clustered() andalso + is_only_disc_node(node()) + of + true -> throw({error, {standalone_ram_node, + "You can't reset a node if it's the only disc " + "node in a cluster. Please convert another node" + " of the cluster to a disc node first."}}); + false -> ok + end, + Node = node(), + Nodes = all_clustered_nodes(), + case Force of + true -> + ok; + false -> + ensure_mnesia_dir(), + start_mnesia(), + %% Reconnecting so that we will get an up to date RunningNodes + RunningNodes = + try + %% Force=true here so that reset still works when clustered + %% with a node which is down + {_, DiscNodes, _} = read_cluster_nodes_status(), + ok = init_db(DiscNodes, should_be_disc_node(DiscNodes), + true), + running_clustered_nodes() + after + stop_mnesia() + end, + leave_cluster(Nodes, RunningNodes), + rabbit_misc:ensure_ok(mnesia:delete_schema([Node]), + cannot_delete_schema) + end, + %% We need to make sure that we don't end up in a distributed Erlang system + %% with nodes while not being in an Mnesia cluster with them. We don't + %% handle that well. + [erlang:disconnect_node(N) || N <- Nodes], + ok = reset_cluster_nodes_status(), + %% remove persisted messages and any other garbage we find + ok = rabbit_file:recursive_delete(filelib:wildcard(dir() ++ "/*")), + ok. + +%%---------------------------------------------------------------------------- +%% Queries +%%---------------------------------------------------------------------------- + +status() -> + [{nodes, case mnesia:system_info(is_running) of + yes -> [{Key, Nodes} || + {Key, CopyType} <- [{disc_only, disc_only_copies}, + {disc, disc_copies}, + {ram, ram_copies}], + begin + Nodes = nodes_of_type(CopyType), + Nodes =/= [] + end]; + no -> [{unknown, all_clustered_nodes()}]; + Reason when Reason =:= starting; Reason =:= stopping -> + exit({rabbit_busy, try_again_later}) + end}, + {running_nodes, running_clustered_nodes()}]. + + +is_db_empty() -> + lists:all(fun (Tab) -> mnesia:dirty_first(Tab) == '$end_of_table' end, + table_names()). + is_clustered() -> RunningNodes = running_clustered_nodes(), [node()] /= RunningNodes andalso [] /= RunningNodes. @@ -178,7 +244,7 @@ is_clustered() -> is_disc_and_clustered() -> is_disc_node() andalso is_clustered(). -%% The situations with functions that retrieve the nodes in the cluster is +%% The situation with functions that retrieve the nodes in the cluster is %% messy. %% %% * If we want to get all the nodes or the running nodes, we can do that @@ -264,104 +330,378 @@ cluster_status_if_running() -> mnesia:system_info(running_db_nodes)} end). -empty_ram_only_tables() -> - Node = node(), - lists:foreach( - fun (TabName) -> - case lists:member(Node, mnesia:table_info(TabName, ram_copies)) of - true -> {atomic, ok} = mnesia:clear_table(TabName); - false -> ok - end - end, table_names()), - ok. +is_disc_node() -> mnesia:system_info(use_dir). -discover_cluster([]) -> - {error, {cannot_discover_cluster, - "The cluster nodes provided are either offline or not running."}}; -discover_cluster([Node | Nodes]) -> - case rpc:call(Node, rabbit_mnesia, cluster_status_if_running, []) of - {badrpc, _Reason} -> discover_cluster(Nodes); - error -> discover_cluster(Nodes); - {ok, Res} -> {ok, Res} - end. +dir() -> mnesia:system_info(directory). -%% This does not guarantee us much, but it avoids some situations that will -%% definitely end in disaster (a node starting and trying to merge its schema -%% to another node which is not clustered with it). -check_cluster_consistency() -> - ThisNode = node(), - lists:foreach( - fun(Node) -> - case rpc:call(Node, rabbit_mnesia, cluster_status_if_running, []) - of - {badrpc, _Reason} -> ok; - error -> ok; - {ok, {AllNodes, _, _}} -> - case lists:member(ThisNode, AllNodes) of - true -> - ok; - false -> - throw({error, - {inconsistent_cluster, - rabbit_misc:format( - "Node ~p thinks it's clustered with " - "node ~p, but ~p disagrees", - [ThisNode, Node, Node])}}) - end - end - end, rabbit_mnesia:all_clustered_nodes()). +table_names() -> + [Tab || {Tab, _} <- table_definitions()]. -%%-------------------------------------------------------------------- +%%---------------------------------------------------------------------------- +%% Operations on the db +%%---------------------------------------------------------------------------- -nodes_of_type(Type) -> - %% This function should return the nodes of a certain type (ram, - %% disc or disc_only) in the current cluster. The type of nodes - %% is determined when the cluster is initially configured. - mnesia:table_info(schema, Type). +init_db(ClusterNodes, WantDiscNode, Force) -> + init_db(ClusterNodes, WantDiscNode, Force, true). -%% The tables aren't supposed to be on disk on a ram node -table_definitions(disc) -> - table_definitions(); -table_definitions(ram) -> - [{Tab, copy_type_to_ram(TabDef)} || {Tab, TabDef} <- table_definitions()]. +%% Take a cluster node config and create the right kind of node - a +%% standalone disk node, or disk or ram node connected to the +%% specified cluster nodes. If Force is false, don't allow +%% connections to offline nodes. +init_db(ClusterNodes, WantDiscNode, Force, Upgrade) -> + UClusterNodes = lists:usort(ClusterNodes), + ProperClusterNodes = UClusterNodes -- [node()], + case mnesia:change_config(extra_db_nodes, ProperClusterNodes) of + {ok, []} when not Force andalso ProperClusterNodes =/= [] -> + throw({error, {failed_to_cluster_with, ProperClusterNodes, + "Mnesia could not connect to any disc nodes."}}); + {ok, Nodes} -> + WasDiscNode = is_disc_node(), + %% We create a new db (on disk, or in ram) in the first + %% two cases and attempt to upgrade the in the other two + case {Nodes, WasDiscNode, WantDiscNode} of + {[], _, false} -> + %% New ram node; start from scratch + ok = create_schema(ram); + {[], false, true} -> + %% Nothing there at all, start from scratch + ok = create_schema(disc); + {[], true, true} -> + %% We're the first node up + case rabbit_upgrade:maybe_upgrade_local() of + ok -> ensure_schema_integrity(); + version_not_available -> ok = schema_ok_or_move() + end; + {[AnotherNode|_], _, _} -> + %% Subsequent node in cluster, catch up + ensure_version_ok( + rpc:call(AnotherNode, rabbit_version, recorded, [])), + {CopyType, CopyTypeAlt} = case WantDiscNode of + true -> {disc, disc_copies}; + false -> {ram, ram_copies} + end, + ok = wait_for_replicated_tables(), + ok = create_local_table_copy(schema, CopyTypeAlt), + ok = create_local_table_copies(CopyType), -table_definitions() -> - [{rabbit_user, - [{record_name, internal_user}, - {attributes, record_info(fields, internal_user)}, - {disc_copies, [node()]}, - {match, #internal_user{_='_'}}]}, - {rabbit_user_permission, - [{record_name, user_permission}, - {attributes, record_info(fields, user_permission)}, - {disc_copies, [node()]}, - {match, #user_permission{user_vhost = #user_vhost{_='_'}, - permission = #permission{_='_'}, - _='_'}}]}, - {rabbit_vhost, - [{record_name, vhost}, - {attributes, record_info(fields, vhost)}, - {disc_copies, [node()]}, - {match, #vhost{_='_'}}]}, - {rabbit_listener, - [{record_name, listener}, - {attributes, record_info(fields, listener)}, - {type, bag}, - {match, #listener{_='_'}}]}, - {rabbit_durable_route, - [{record_name, route}, - {attributes, record_info(fields, route)}, - {disc_copies, [node()]}, - {match, #route{binding = binding_match(), _='_'}}]}, - {rabbit_semi_durable_route, - [{record_name, route}, - {attributes, record_info(fields, route)}, - {type, ordered_set}, - {match, #route{binding = binding_match(), _='_'}}]}, - {rabbit_route, - [{record_name, route}, - {attributes, record_info(fields, route)}, - {type, ordered_set}, + %% Write the status now that mnesia is running and clustered + update_cluster_nodes_status(), + + ok = case Upgrade of + true -> + case rabbit_upgrade:maybe_upgrade_local() of + ok -> + ok; + %% If we're just starting up a new node we + %% won't have a version + starting_from_scratch -> + rabbit_version:record_desired() + end; + false -> + ok + end, + + %% We've taken down mnesia, so ram nodes will need + %% to re-sync + case is_disc_node() of + false -> start_mnesia(), + mnesia:change_config(extra_db_nodes, + ProperClusterNodes), + wait_for_replicated_tables(); + true -> ok + end, + + ensure_schema_integrity(), + ok + end; + {error, Reason} -> + %% one reason we may end up here is if we try to join + %% nodes together that are currently running standalone or + %% are members of a different cluster + throw({error, {unable_to_join_cluster, ClusterNodes, Reason}}) + end. + +ensure_mnesia_dir() -> + MnesiaDir = dir() ++ "/", + case filelib:ensure_dir(MnesiaDir) of + {error, Reason} -> + throw({error, {cannot_create_mnesia_dir, MnesiaDir, Reason}}); + ok -> + ok + end. + +ensure_mnesia_running() -> + case mnesia:system_info(is_running) of + yes -> + ok; + starting -> + wait_for(mnesia_running), + ensure_mnesia_running(); + Reason when Reason =:= no; Reason =:= stopping -> + throw({error, mnesia_not_running}) + end. + +ensure_mnesia_not_running() -> + case mnesia:system_info(is_running) of + no -> + ok; + stopping -> + wait_for(mnesia_not_running), + ensure_mnesia_not_running(); + Reason when Reason =:= yes; Reason =:= starting -> + throw({error, mnesia_unexpectedly_running}) + end. + +ensure_schema_integrity() -> + case check_schema_integrity() of + ok -> + ok; + {error, Reason} -> + throw({error, {schema_integrity_check_failed, Reason}}) + end. + +check_schema_integrity() -> + Tables = mnesia:system_info(tables), + case check_tables(fun (Tab, TabDef) -> + case lists:member(Tab, Tables) of + false -> {error, {table_missing, Tab}}; + true -> check_table_attributes(Tab, TabDef) + end + end) of + ok -> ok = wait_for_tables(), + check_tables(fun check_table_content/2); + Other -> Other + end. + +empty_ram_only_tables() -> + Node = node(), + lists:foreach( + fun (TabName) -> + case lists:member(Node, mnesia:table_info(TabName, ram_copies)) of + true -> {atomic, ok} = mnesia:clear_table(TabName); + false -> ok + end + end, table_names()), + ok. + +create_tables() -> create_tables(disc). + +create_tables(Type) -> + lists:foreach(fun ({Tab, TabDef}) -> + TabDef1 = proplists:delete(match, TabDef), + case mnesia:create_table(Tab, TabDef1) of + {atomic, ok} -> ok; + {aborted, Reason} -> + throw({error, {table_creation_failed, + Tab, TabDef1, Reason}}) + end + end, + table_definitions(Type)), + ok. + +copy_db(Destination) -> + ok = ensure_mnesia_not_running(), + rabbit_file:recursive_copy(dir(), Destination). + +wait_for_replicated_tables() -> wait_for_tables(replicated_table_names()). + +wait_for_tables() -> wait_for_tables(table_names()). + +wait_for_tables(TableNames) -> + case mnesia:wait_for_tables(TableNames, 30000) of + ok -> + ok; + {timeout, BadTabs} -> + throw({error, {timeout_waiting_for_tables, BadTabs}}); + {error, Reason} -> + throw({error, {failed_waiting_for_tables, Reason}}) + end. + +should_be_disc_node(DiscNodes) -> + DiscNodes == [] orelse lists:member(node(), DiscNodes). + +%% This does not guarantee us much, but it avoids some situations that will +%% definitely end in disaster (a node starting and trying to merge its schema +%% to another node which is not clustered with it). +check_cluster_consistency() -> + ThisNode = node(), + lists:foreach( + fun(Node) -> + case rpc:call(Node, rabbit_mnesia, cluster_status_if_running, []) + of + {badrpc, _Reason} -> ok; + error -> ok; + {ok, {AllNodes, _, _}} -> + case lists:member(ThisNode, AllNodes) of + true -> + ok; + false -> + throw({error, + {inconsistent_cluster, + rabbit_misc:format( + "Node ~p thinks it's clustered with " + "node ~p, but ~p disagrees", + [ThisNode, Node, Node])}}) + end + end + end, rabbit_mnesia:all_clustered_nodes()). + +%%---------------------------------------------------------------------------- +%% Cluster status file functions +%%---------------------------------------------------------------------------- + +%% The cluster node status file contains all we need to know about the cluster: +%% +%% * All the clustered nodes +%% * The disc nodes +%% * The running nodes. +%% +%% If the current node is a disc node it will be included in the disc nodes +%% list. +%% +%% We strive to keep the file up to date and we rely on this assumption in +%% various situations. Obviously when mnesia is offline the information we have +%% will be outdated, but it can't be otherwise. + +cluster_nodes_status_filename() -> + dir() ++ "/cluster_nodes.config". + +%% Creates a status file with the default data (one disc node), only if an +%% existing cluster does not exist. +initialize_cluster_nodes_status() -> + try read_cluster_nodes_status() of + _ -> ok + catch + throw:{error, {cannot_read_cluster_nodes_status, _, enoent}} -> + write_cluster_nodes_status({[node()], [node()], [node()]}) + end. + +write_cluster_nodes_status(Status) -> + FileName = cluster_nodes_status_filename(), + case rabbit_file:write_term_file(FileName, [Status]) of + ok -> ok; + {error, Reason} -> + throw({error, {cannot_write_cluster_nodes_status, + FileName, Reason}}) + end. + +read_cluster_nodes_status() -> + FileName = cluster_nodes_status_filename(), + case rabbit_file:read_term_file(FileName) of + {ok, [{_, _, _} = Status]} -> Status; + {error, Reason} -> + throw({error, {cannot_read_cluster_nodes_status, FileName, Reason}}) + end. + +reset_cluster_nodes_status() -> + FileName = cluster_nodes_status_filename(), + case file:delete(FileName) of + ok -> ok; + {error, enoent} -> ok; + {error, Reason} -> + throw({error, {cannot_delete_cluster_nodes_status, + FileName, Reason}}) + end, + write_cluster_nodes_status({[node()], [node()], [node()]}). + +%% To update the cluster status when mnesia is running. +update_cluster_nodes_status() -> + {ok, Status} = cluster_status_if_running(), + write_cluster_nodes_status(Status). + +%% The cluster config contains the nodes that the node should try to contact to +%% form a cluster, and whether the node should be a disc node. When starting the +%% database, if the nodes in the cluster status are the initial ones, we try to +%% read the cluster config. +read_cluster_nodes_config() -> + {AllNodes, DiscNodes, _} = read_cluster_nodes_status(), + Node = node(), + case AllNodes of + [Node] -> {ok, Config} = application:get_env(rabbit, cluster_nodes), + Config; + _ -> {AllNodes, should_be_disc_node(DiscNodes)} + end. + +%%-------------------------------------------------------------------- +%% Hooks for `rabbit_node_monitor' +%%-------------------------------------------------------------------- + +on_node_up(Node) -> + update_cluster_nodes_status(), + case is_only_disc_node(Node) of + true -> rabbit_log:info("cluster contains disc nodes again~n"); + false -> ok + end. + +on_node_down(Node) -> + case is_only_disc_node(Node) of + true -> rabbit_log:info("only running disc node went down~n"); + false -> ok + end. + +%%-------------------------------------------------------------------- +%% Internal helpers +%%-------------------------------------------------------------------- + +discover_cluster([]) -> + {error, {cannot_discover_cluster, + "The cluster nodes provided are either offline or not running."}}; +discover_cluster([Node | Nodes]) -> + case rpc:call(Node, rabbit_mnesia, cluster_status_if_running, []) of + {badrpc, _Reason} -> discover_cluster(Nodes); + error -> discover_cluster(Nodes); + {ok, Res} -> {ok, Res} + end. + +nodes_of_type(Type) -> + %% This function should return the nodes of a certain type (ram, + %% disc or disc_only) in the current cluster. The type of nodes + %% is determined when the cluster is initially configured. + mnesia:table_info(schema, Type). + +%% The tables aren't supposed to be on disk on a ram node +table_definitions(disc) -> + table_definitions(); +table_definitions(ram) -> + [{Tab, copy_type_to_ram(TabDef)} || {Tab, TabDef} <- table_definitions()]. + +table_definitions() -> + [{rabbit_user, + [{record_name, internal_user}, + {attributes, record_info(fields, internal_user)}, + {disc_copies, [node()]}, + {match, #internal_user{_='_'}}]}, + {rabbit_user_permission, + [{record_name, user_permission}, + {attributes, record_info(fields, user_permission)}, + {disc_copies, [node()]}, + {match, #user_permission{user_vhost = #user_vhost{_='_'}, + permission = #permission{_='_'}, + _='_'}}]}, + {rabbit_vhost, + [{record_name, vhost}, + {attributes, record_info(fields, vhost)}, + {disc_copies, [node()]}, + {match, #vhost{_='_'}}]}, + {rabbit_listener, + [{record_name, listener}, + {attributes, record_info(fields, listener)}, + {type, bag}, + {match, #listener{_='_'}}]}, + {rabbit_durable_route, + [{record_name, route}, + {attributes, record_info(fields, route)}, + {disc_copies, [node()]}, + {match, #route{binding = binding_match(), _='_'}}]}, + {rabbit_semi_durable_route, + [{record_name, route}, + {attributes, record_info(fields, route)}, + {type, ordered_set}, + {match, #route{binding = binding_match(), _='_'}}]}, + {rabbit_route, + [{record_name, route}, + {attributes, record_info(fields, route)}, + {type, ordered_set}, {match, #route{binding = binding_match(), _='_'}}]}, {rabbit_reverse_route, [{record_name, reverse_route}, @@ -438,68 +778,11 @@ queue_name_match() -> resource_match(Kind) -> #resource{kind = Kind, _='_'}. -table_names() -> - [Tab || {Tab, _} <- table_definitions()]. - -replicated_table_names() -> - [Tab || {Tab, TabDef} <- table_definitions(), - not lists:member({local_content, true}, TabDef) - ]. - -dir() -> mnesia:system_info(directory). - -ensure_mnesia_dir() -> - MnesiaDir = dir() ++ "/", - case filelib:ensure_dir(MnesiaDir) of - {error, Reason} -> - throw({error, {cannot_create_mnesia_dir, MnesiaDir, Reason}}); - ok -> - ok - end. - -ensure_mnesia_running() -> - case mnesia:system_info(is_running) of - yes -> - ok; - starting -> - wait_for(mnesia_running), - ensure_mnesia_running(); - Reason when Reason =:= no; Reason =:= stopping -> - throw({error, mnesia_not_running}) - end. - -ensure_mnesia_not_running() -> - case mnesia:system_info(is_running) of - no -> - ok; - stopping -> - wait_for(mnesia_not_running), - ensure_mnesia_not_running(); - Reason when Reason =:= yes; Reason =:= starting -> - throw({error, mnesia_unexpectedly_running}) - end. - -ensure_schema_integrity() -> - case check_schema_integrity() of - ok -> - ok; - {error, Reason} -> - throw({error, {schema_integrity_check_failed, Reason}}) - end. - -check_schema_integrity() -> - Tables = mnesia:system_info(tables), - case check_tables(fun (Tab, TabDef) -> - case lists:member(Tab, Tables) of - false -> {error, {table_missing, Tab}}; - true -> check_table_attributes(Tab, TabDef) - end - end) of - ok -> ok = wait_for_tables(), - check_tables(fun check_table_content/2); - Other -> Other - end. - +replicated_table_names() -> + [Tab || {Tab, TabDef} <- table_definitions(), + not lists:member({local_content, true}, TabDef) + ]. + check_table_attributes(Tab, TabDef) -> {_, ExpAttrs} = proplists:lookup(attributes, TabDef), case mnesia:table_info(Tab, attributes) of @@ -535,158 +818,6 @@ check_tables(Fun) -> Errors -> {error, Errors} end. -%% The cluster node status file contains all we need to know about the cluster: -%% -%% * All the clustered nodes -%% * The disc nodes -%% * The running nodes. -%% -%% If the current node is a disc node it will be included in the disc nodes -%% list. -%% -%% We strive to keep the file up to date and we rely on this assumption in -%% various situations. Obviously when mnesia is offline the information we have -%% will be outdated, but it can't be otherwise. - -cluster_nodes_status_filename() -> - dir() ++ "/cluster_nodes.config". - -%% Creates a status file with the default data (one disc node), only if an -%% existing cluster does not exist. -initialize_cluster_nodes_status() -> - try read_cluster_nodes_status() of - _ -> ok - catch - throw:{error, {cannot_read_cluster_nodes_status, _, enoent}} -> - write_cluster_nodes_status({[node()], [node()], [node()]}) - end. - -write_cluster_nodes_status(Status) -> - FileName = cluster_nodes_status_filename(), - case rabbit_file:write_term_file(FileName, [Status]) of - ok -> ok; - {error, Reason} -> - throw({error, {cannot_write_cluster_nodes_status, - FileName, Reason}}) - end. - -read_cluster_nodes_status() -> - FileName = cluster_nodes_status_filename(), - case rabbit_file:read_term_file(FileName) of - {ok, [{_, _, _} = Status]} -> Status; - {error, Reason} -> - throw({error, {cannot_read_cluster_nodes_status, FileName, Reason}}) - end. - -reset_cluster_nodes_status() -> - FileName = cluster_nodes_status_filename(), - case file:delete(FileName) of - ok -> ok; - {error, enoent} -> ok; - {error, Reason} -> - throw({error, {cannot_delete_cluster_nodes_status, - FileName, Reason}}) - end, - write_cluster_nodes_status({[node()], [node()], [node()]}). - -%% To update the cluster status when mnesia is running. -update_cluster_nodes_status() -> - {ok, Status} = cluster_status_if_running(), - write_cluster_nodes_status(Status). - -%% The cluster config contains the nodes that the node should try to contact to -%% form a cluster, and whether the node should be a disc node. When starting the -%% database, if the nodes in the cluster status are the initial ones, we try to -%% read the cluster config. -read_cluster_nodes_config() -> - {AllNodes, DiscNodes, _} = read_cluster_nodes_status(), - Node = node(), - case AllNodes of - [Node] -> {ok, Config} = application:get_env(rabbit, cluster_nodes), - Config; - _ -> {AllNodes, should_be_disc_node(DiscNodes)} - end. - -init_db(ClusterNodes, WantDiscNode, Force) -> - init_db(ClusterNodes, WantDiscNode, Force, true). - -%% Take a cluster node config and create the right kind of node - a -%% standalone disk node, or disk or ram node connected to the -%% specified cluster nodes. If Force is false, don't allow -%% connections to offline nodes. -init_db(ClusterNodes, WantDiscNode, Force, Upgrade) -> - UClusterNodes = lists:usort(ClusterNodes), - ProperClusterNodes = UClusterNodes -- [node()], - case mnesia:change_config(extra_db_nodes, ProperClusterNodes) of - {ok, []} when not Force andalso ProperClusterNodes =/= [] -> - throw({error, {failed_to_cluster_with, ProperClusterNodes, - "Mnesia could not connect to any disc nodes."}}); - {ok, Nodes} -> - WasDiscNode = is_disc_node(), - %% We create a new db (on disk, or in ram) in the first - %% two cases and attempt to upgrade the in the other two - case {Nodes, WasDiscNode, WantDiscNode} of - {[], _, false} -> - %% New ram node; start from scratch - ok = create_schema(ram); - {[], false, true} -> - %% Nothing there at all, start from scratch - ok = create_schema(disc); - {[], true, true} -> - %% We're the first node up - case rabbit_upgrade:maybe_upgrade_local() of - ok -> ensure_schema_integrity(); - version_not_available -> ok = schema_ok_or_move() - end; - {[AnotherNode|_], _, _} -> - %% Subsequent node in cluster, catch up - ensure_version_ok( - rpc:call(AnotherNode, rabbit_version, recorded, [])), - {CopyType, CopyTypeAlt} = case WantDiscNode of - true -> {disc, disc_copies}; - false -> {ram, ram_copies} - end, - ok = wait_for_replicated_tables(), - ok = create_local_table_copy(schema, CopyTypeAlt), - ok = create_local_table_copies(CopyType), - - %% Write the status now that mnesia is running and clustered - update_cluster_nodes_status(), - - ok = case Upgrade of - true -> - case rabbit_upgrade:maybe_upgrade_local() of - ok -> - ok; - %% If we're just starting up a new node we - %% won't have a version - starting_from_scratch -> - rabbit_version:record_desired() - end; - false -> - ok - end, - - %% We've taken down mnesia, so ram nodes will need - %% to re-sync - case is_disc_node() of - false -> start_mnesia(), - mnesia:change_config(extra_db_nodes, - ProperClusterNodes), - wait_for_replicated_tables(); - true -> ok - end, - - ensure_schema_integrity(), - ok - end; - {error, Reason} -> - %% one reason we may end up here is if we try to join - %% nodes together that are currently running standalone or - %% are members of a different cluster - throw({error, {unable_to_join_cluster, ClusterNodes, Reason}}) - end. - schema_ok_or_move() -> case check_schema_integrity() of ok -> @@ -725,11 +856,6 @@ create_schema(Type) -> ensure_schema_integrity(), ok = rabbit_version:record_desired(). -is_disc_node() -> mnesia:system_info(use_dir). - -should_be_disc_node(DiscNodes) -> - DiscNodes == [] orelse lists:member(node(), DiscNodes). - move_db() -> stop_mnesia(), MnesiaDir = filename:dirname(dir() ++ "/"), @@ -751,25 +877,6 @@ move_db() -> start_mnesia(), ok. -copy_db(Destination) -> - ok = ensure_mnesia_not_running(), - rabbit_file:recursive_copy(dir(), Destination). - -create_tables() -> create_tables(disc). - -create_tables(Type) -> - lists:foreach(fun ({Tab, TabDef}) -> - TabDef1 = proplists:delete(match, TabDef), - case mnesia:create_table(Tab, TabDef1) of - {atomic, ok} -> ok; - {aborted, Reason} -> - throw({error, {table_creation_failed, - Tab, TabDef1, Reason}}) - end - end, - table_definitions(Type)), - ok. - copy_type_to_ram(TabDef) -> [{disc_copies, []}, {ram_copies, [node()]} | proplists:delete(ram_copies, proplists:delete(disc_copies, TabDef))]. @@ -818,68 +925,6 @@ create_local_table_copy(Tab, Type) -> end, ok. -wait_for_replicated_tables() -> wait_for_tables(replicated_table_names()). - -wait_for_tables() -> wait_for_tables(table_names()). - -wait_for_tables(TableNames) -> - case mnesia:wait_for_tables(TableNames, 30000) of - ok -> - ok; - {timeout, BadTabs} -> - throw({error, {timeout_waiting_for_tables, BadTabs}}); - {error, Reason} -> - throw({error, {failed_waiting_for_tables, Reason}}) - end. - -reset(Force) -> - rabbit_misc:local_info_msg("Resetting Rabbit~s~n", - [if Force -> " forcefully"; - true -> "" - end]), - ensure_mnesia_not_running(), - case not Force andalso is_disc_and_clustered() andalso - is_only_disc_node(node()) - of - true -> throw({error, {standalone_ram_node, - "You can't reset a node if it's the only disc " - "node in a cluster. Please convert another node" - " of the cluster to a disc node first."}}); - false -> ok - end, - Node = node(), - Nodes = all_clustered_nodes(), - case Force of - true -> - ok; - false -> - ensure_mnesia_dir(), - start_mnesia(), - %% Reconnecting so that we will get an up to date RunningNodes - RunningNodes = - try - %% Force=true here so that reset still works when clustered - %% with a node which is down - {_, DiscNodes, _} = read_cluster_nodes_status(), - ok = init_db(DiscNodes, should_be_disc_node(DiscNodes), - true), - running_clustered_nodes() - after - stop_mnesia() - end, - leave_cluster(Nodes, RunningNodes), - rabbit_misc:ensure_ok(mnesia:delete_schema([Node]), - cannot_delete_schema) - end, - %% We need to make sure that we don't end up in a distributed Erlang system - %% with nodes while not being in an Mnesia cluster with them. We don't - %% handle that well. - [erlang:disconnect_node(N) || N <- Nodes], - ok = reset_cluster_nodes_status(), - %% remove persisted messages and any other garbage we find - ok = rabbit_file:recursive_delete(filelib:wildcard(dir() ++ "/*")), - ok. - leave_cluster([], _) -> ok; leave_cluster(Nodes, RunningNodes0) -> case RunningNodes0 -- [node()] of @@ -912,19 +957,6 @@ wait_for(Condition) -> error_logger:info_msg("Waiting for ~p...~n", [Condition]), timer:sleep(1000). -on_node_up(Node) -> - update_cluster_nodes_status(), - case is_only_disc_node(Node) of - true -> rabbit_log:info("cluster contains disc nodes again~n"); - false -> ok - end. - -on_node_down(Node) -> - case is_only_disc_node(Node) of - true -> rabbit_log:info("only running disc node went down~n"); - false -> ok - end. - is_only_disc_node(Node) -> Nodes = running_clustered_disc_nodes(), [Node] =:= Nodes. -- cgit v1.2.1 From 46aaf209f4e2d2ea7b40cb6546ad44673b7582c8 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 18 May 2012 16:05:12 +0100 Subject: We can't now write the serial record when we declare the exchange; an exchange can become federated at any time. --- src/rabbit_exchange.erl | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index d87c607d..ab548f38 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -185,13 +185,7 @@ declare(XName, Type, Durable, AutoDelete, Internal, Args) -> map_create_tx(true) -> transaction; map_create_tx(false) -> none. -store(X = #exchange{name = Name}) -> - ok = mnesia:write(rabbit_exchange, X, write), - case serialise_events(X) of - true -> S = #exchange_serial{name = Name, next = 1}, - ok = mnesia:write(rabbit_exchange_serial, S, write); - false -> ok - end. +store(X) -> ok = mnesia:write(rabbit_exchange, X, write). %% Used with binaries sent over the wire; the type may not exist. check_type(TypeBin) -> @@ -415,8 +409,10 @@ unconditional_delete(X = #exchange{name = XName}) -> {deleted, X, Bindings, rabbit_binding:remove_for_destination(XName)}. next_serial(XName) -> - [#exchange_serial{next = Serial}] = - mnesia:read(rabbit_exchange_serial, XName, write), + Serial = case mnesia:read(rabbit_exchange_serial, XName, write) of + [#exchange_serial{next = Next}] -> Next; + [] -> 1 + end, ok = mnesia:write(rabbit_exchange_serial, #exchange_serial{name = XName, next = Serial + 1}, write), Serial. -- cgit v1.2.1 From c1ce34814b899d025537fa3ed4e7039ba0c10e8e Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 21 May 2012 14:13:48 +0100 Subject: fix variable handling in rabbitmq-server preventing background node startup from working (rebase foo) --- scripts/rabbitmq-server | 3 --- 1 file changed, 3 deletions(-) diff --git a/scripts/rabbitmq-server b/scripts/rabbitmq-server index 9d8710af..81a5e572 100755 --- a/scripts/rabbitmq-server +++ b/scripts/rabbitmq-server @@ -61,9 +61,6 @@ RABBITMQ_START_RABBIT= [ "x" = "x$RABBITMQ_ALLOW_INPUT" ] && RABBITMQ_START_RABBIT=" -noinput" [ "x" = "x$RABBITMQ_NODE_ONLY" ] && RABBITMQ_START_RABBIT="$RABBITMQ_START_RABBIT -s rabbit boot " -RABBITMQ_START_RABBIT= -[ "x" = "x$RABBITMQ_NODE_ONLY" ] && RABBITMQ_START_RABBIT="$RABBITMQ_START_RABBIT -s rabbit boot" - case "$(uname -s)" in CYGWIN*) # we make no attempt to record the cygwin pid; rabbitmqctl wait # will not be able to make sense of it anyway -- cgit v1.2.1 From db612dc0138eacf20b47b4389ffcc018dd956b0f Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 18 May 2012 16:58:03 +0100 Subject: It's really misleading to print "-- plugins running" when there are no plugins running. --- src/rabbit.erl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/rabbit.erl b/src/rabbit.erl index db0c1e83..1dff4d53 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -638,6 +638,8 @@ force_event_refresh() -> %%--------------------------------------------------------------------------- %% misc +print_plugin_info([]) -> + ok; print_plugin_info(Plugins) -> io:format("~n-- plugins running~n"), [print_plugin_info(AppName, element(2, application:get_key(AppName, vsn))) -- cgit v1.2.1 From 7569caad5307c5fb71672b08809dc917a82426e1 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Mon, 21 May 2012 11:39:49 +0100 Subject: change rabbitmqctl `join_cluster', add `recluster' and `change_node_type' also, remove `cluster'. Description for the commands: * recluster Instruct an offline node to try to cluster with the nodes provided when it comes back online. With "cluster" I mean cluster directly, only if the other nodes think the node is in the cluster already. This command is useful if we bring a node down and then change the cluster while it is offline. * change_node_type (ram|disc) Changed the node to the provided node type. The change will fail if the user is trying to create a standalone ram node. * join_cluster clusternode Same as before, but only takes one node as "discovery" node. We think this is better since we don't really make any attempt to verify that all the provided nodes are in the same cluster anyways. --- src/rabbit_control.erl | 27 +++++---- src/rabbit_mnesia.erl | 154 +++++++++++++++++++++++++++++++++++++------------ 2 files changed, 133 insertions(+), 48 deletions(-) diff --git a/src/rabbit_control.erl b/src/rabbit_control.erl index 478155a1..e51c4c6a 100644 --- a/src/rabbit_control.erl +++ b/src/rabbit_control.erl @@ -182,18 +182,23 @@ action(force_reset, Node, [], _Opts, Inform) -> Inform("Forcefully resetting node ~p", [Node]), call(Node, {rabbit_mnesia, force_reset, []}); -action(cluster, Node, ClusterNodeSs, _Opts, Inform) -> - io:format("'cluster' is deprecated, please us 'join_cluster'.~n"), - ClusterNodes = lists:map(fun list_to_atom/1, ClusterNodeSs), - DiscNode = rabbit_mnesia:should_be_disc_node(ClusterNodes), - Inform("Clustering node ~p with ~p", [Node, ClusterNodes]), - rpc_call(Node, rabbit_mnesia, join_cluster, [ClusterNodes, DiscNode]); - -action(join_cluster, Node, ClusterNodeSs, Opts, Inform) -> - ClusterNodes = lists:map(fun list_to_atom/1, ClusterNodeSs), +action(join_cluster, Node, [ClusterNodeS], Opts, Inform) -> + ClusterNode = list_to_atom(ClusterNodeS), DiscNode = not proplists:get_bool(?RAM_OPT, Opts), - Inform("Clustering node ~p with ~p", [Node, ClusterNodes]), - rpc_call(Node, rabbit_mnesia, join_cluster, [ClusterNodes, DiscNode]); + Inform("Clustering node ~p with ~p", [Node, ClusterNode]), + rpc_call(Node, rabbit_mnesia, join_cluster, [ClusterNode, DiscNode]); + +action(change_node_type, Node, ["ram"], _Opts, Inform) -> + Inform("Turning ~p into a ram node", [Node]), + rpc_call(Node, rabbit_mnesia, change_node_type, [ram]); +action(change_node_type, Node, ["disc"], _Opts, Inform) -> + Inform("Turning ~p into a disc node", [Node]), + rpc_call(Node, rabbit_mnesia, change_node_type, [disc]); + +action(recluster, Node, [ClusterNodeS], _Opts, Inform) -> + ClusterNode = list_to_atom(ClusterNodeS), + Inform("Re-clustering ~p with ~p", [Node, ClusterNode]), + rpc_call(Node, rabbit_mnesia, recluster, [ClusterNode]); action(wait, Node, [PidFile], _Opts, Inform) -> Inform("Waiting for ~p", [Node]), diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 8cde1702..741392c8 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -24,7 +24,8 @@ wait_for_tables/1, initialize_cluster_nodes_status/0, write_cluster_nodes_status/1, read_cluster_nodes_status/0, update_cluster_nodes_status/0, is_disc_node/0, on_node_down/1, - on_node_up/1, should_be_disc_node/1]). + on_node_up/1, should_be_disc_node/1, change_node_type/1, + recluster/1]). -export([table_names/0]). @@ -105,38 +106,40 @@ init() -> %% actually cluster it. The nodes provided will be used to find out about the %% nodes in the cluster. %% This function will fail if: -%% +%% %% * The node is currently the only disc node of its cluster %% * We can't connect to any of the nodes provided %% * The node is currently already clustered with the cluster of the nodes %% provided -%% +%% %% Note that we make no attempt to verify that the nodes provided are all in the %% same cluster, we simply pick the first online node and we cluster to its %% cluster. -join_cluster(DiscoveryNodes, WantDiscNode) -> +join_cluster(DiscoveryNode, WantDiscNode) -> case is_disc_and_clustered() andalso is_only_disc_node(node()) of true -> throw({error, {standalone_ram_node, "You can't cluster a node if it's the only " - "disc node in its existing cluster."}}); + "disc node in its existing cluster. If new nodes " + "joined while this node was offline, use \"recluster\" " + "to add them manually"}}); _ -> ok end, ensure_mnesia_not_running(), ensure_mnesia_dir(), - ProperDiscoveryNodes = DiscoveryNodes -- [node()], - {ClusterNodes, DiscNodes, _} = - case discover_cluster(ProperDiscoveryNodes) of + Status = {ClusterNodes, DiscNodes, _} = + case discover_cluster(DiscoveryNode) of {ok, Res} -> Res; {error, Reason} -> throw({error, Reason}) end, - + case lists:member(node(), ClusterNodes) of true -> throw({error, {already_clustered, "You are already clustered with the nodes you " - "have selected."}}); + "have selected"}}), + write_cluster_nodes_status(Status); false -> ok end, @@ -149,12 +152,7 @@ join_cluster(DiscoveryNodes, WantDiscNode) -> rabbit_misc:local_info_msg("Clustering with ~p~s~n", [ClusterNodes]), %% Join the cluster - start_mnesia(), - try - ok = init_db(DiscNodes, WantDiscNode, false) - after - stop_mnesia() - end, + ok = start_and_init_db(DiscNodes, WantDiscNode, false), ok. @@ -212,6 +210,63 @@ reset(Force) -> ok = rabbit_file:recursive_delete(filelib:wildcard(dir() ++ "/*")), ok. +change_node_type(Type) -> + ensure_mnesia_dir(), + ensure_mnesia_not_running(), + case is_clustered() of + false -> throw({error, {not_clustered, + "Non-clustered nodes can only be disc nodes"}}); + true -> ok + end, + + check_cluster_consistency(), + DiscoveryNodes = all_clustered_nodes(), + ClusterNodes = + case discover_cluster(DiscoveryNodes) of + {ok, {ClusterNodes0, _, _}} -> + ClusterNodes0; + {error, _Reason} -> + throw({error, + {cannot_connect_to_cluster, + "Could not connect to the cluster nodes present in " + "this node status file. If the cluster has changed, " + "you can use the \"recluster\" command to point to the " + "new cluster nodes"}}) + end, + + WantDiscNode = case Type of + ram -> false; + disc -> true + end, + + ok = start_and_init_db(ClusterNodes, WantDiscNode, false), + + ok. + +recluster(DiscoveryNode) -> + ensure_mnesia_not_running(), + ensure_mnesia_dir(), + + ClusterNodes = + case discover_cluster(DiscoveryNode) of + {ok, {ClusterNodes0, _, _}} -> + ClusterNodes0; + {error, _Reason} -> + throw({error, + {cannot_connect_to_node, + "Could not connect to the cluster node provided"}}) + end, + + case lists:member(node(), ClusterNodes) of + true -> start_and_init_db(ClusterNodes, is_disc_node(), false); + false -> throw({error, + {inconsistent_cluster, + "The nodes provided do not have this node as part of " + "the cluster"}}) + end, + + ok. + %%---------------------------------------------------------------------------- %% Queries %%---------------------------------------------------------------------------- @@ -238,21 +293,21 @@ is_db_empty() -> table_names()). is_clustered() -> - RunningNodes = running_clustered_nodes(), - [node()] /= RunningNodes andalso [] /= RunningNodes. + Nodes = all_clustered_nodes(), + [node()] /= Nodes andalso [] /= Nodes. is_disc_and_clustered() -> is_disc_node() andalso is_clustered(). %% The situation with functions that retrieve the nodes in the cluster is %% messy. -%% +%% %% * If we want to get all the nodes or the running nodes, we can do that %% while mnesia is offline *if* the node is a disc node. If the node is ram, %% the result will always be [node()]. %% * If we want to get the cluster disc nodes (running or not), we need to %% start mnesia in any case. -%% +%% %% In the following functions we try to get the data from mnesia when we can, %% otherwise we fall back to the cluster status file. @@ -376,13 +431,19 @@ init_db(ClusterNodes, WantDiscNode, Force, Upgrade) -> %% Subsequent node in cluster, catch up ensure_version_ok( rpc:call(AnotherNode, rabbit_version, recorded, [])), - {CopyType, CopyTypeAlt} = case WantDiscNode of - true -> {disc, disc_copies}; - false -> {ram, ram_copies} - end, ok = wait_for_replicated_tables(), - ok = create_local_table_copy(schema, CopyTypeAlt), - ok = create_local_table_copies(CopyType), + + %% The sequence in which we delete the schema and then the + %% other tables is important: if we delete the schema first + %% when moving to RAM mnesia will loudly complain since it + %% doesn't make much sense to do that. But when moving to + %% disc, we need to move the schema first. + case WantDiscNode of + true -> create_local_table_copy(schema, disc_copies), + create_local_table_copies(disc); + false -> create_local_table_copies(ram), + create_local_table_copy(schema, ram_copies) + end, %% Write the status now that mnesia is running and clustered update_cluster_nodes_status(), @@ -544,21 +605,21 @@ check_cluster_consistency() -> [ThisNode, Node, Node])}}) end end - end, rabbit_mnesia:all_clustered_nodes()). + end, all_clustered_nodes()). %%---------------------------------------------------------------------------- %% Cluster status file functions %%---------------------------------------------------------------------------- %% The cluster node status file contains all we need to know about the cluster: -%% +%% %% * All the clustered nodes %% * The disc nodes %% * The running nodes. %% %% If the current node is a disc node it will be included in the disc nodes %% list. -%% +%% %% We strive to keep the file up to date and we rely on this assumption in %% various situations. Obviously when mnesia is offline the information we have %% will be outdated, but it can't be otherwise. @@ -643,14 +704,24 @@ on_node_down(Node) -> %% Internal helpers %%-------------------------------------------------------------------- -discover_cluster([]) -> - {error, {cannot_discover_cluster, - "The cluster nodes provided are either offline or not running."}}; -discover_cluster([Node | Nodes]) -> - case rpc:call(Node, rabbit_mnesia, cluster_status_if_running, []) of - {badrpc, _Reason} -> discover_cluster(Nodes); - error -> discover_cluster(Nodes); - {ok, Res} -> {ok, Res} +discover_cluster(Nodes) when is_list(Nodes) -> + lists:foldl(fun (_, {ok, Res}) -> {ok, Res}; + (Node, {error, _}) -> discover_cluster(Node) + end, + {error, {cannot_discover_cluster, + "The nodes provided is either offline or not running"}}, + Nodes); +discover_cluster(Node) -> + case Node =:= node() of + true -> + {error, {cannot_discover_cluster, + "You provided the current node as node to cluster with"}}; + false -> + case rpc:call(Node, rabbit_mnesia, cluster_status_if_running, []) of + {badrpc, _Reason} -> discover_cluster([]); + error -> discover_cluster([]); + {ok, Res} -> {ok, Res} + end end. nodes_of_type(Type) -> @@ -968,3 +1039,12 @@ start_mnesia() -> stop_mnesia() -> stopped = mnesia:stop(), ensure_mnesia_not_running(). + +start_and_init_db(ClusterNodes, WantDiscNode, Force) -> + mnesia:start(), + try + ok = init_db(ClusterNodes, WantDiscNode, Force) + after + mnesia:stop() + end, + ok. -- cgit v1.2.1 From 5df3d696f567fecb1d74e928bb7c966ecf38f4ea Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Mon, 21 May 2012 14:38:23 +0100 Subject: change the cluster nodes functions to rely on the cluster status file only + some other fixes based on the fact that now we firmly assume that the cluster status is as up to date as it can be. --- src/rabbit_mnesia.erl | 152 ++++++++++++-------------------------------------- 1 file changed, 36 insertions(+), 116 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 741392c8..44ee2725 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -1,4 +1,4 @@ -%% The contents of this file are subject to the Mozilla Public License +% 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/ @@ -20,12 +20,12 @@ -export([ensure_mnesia_dir/0, dir/0, status/0, init/0, is_db_empty/0, join_cluster/2, check_cluster_consistency/0, reset/0, force_reset/0, init_db/4, is_clustered/0, running_clustered_nodes/0, - all_clustered_nodes/0, empty_ram_only_tables/0, copy_db/1, - wait_for_tables/1, initialize_cluster_nodes_status/0, - write_cluster_nodes_status/1, read_cluster_nodes_status/0, - update_cluster_nodes_status/0, is_disc_node/0, on_node_down/1, - on_node_up/1, should_be_disc_node/1, change_node_type/1, - recluster/1]). + all_clustered_nodes/0, all_clustered_disc_nodes/0, + empty_ram_only_tables/0, copy_db/1, wait_for_tables/1, + initialize_cluster_nodes_status/0, write_cluster_nodes_status/1, + read_cluster_nodes_status/0, update_cluster_nodes_status/0, + is_disc_node/0, on_node_down/1, on_node_up/1, should_be_disc_node/1, + change_node_type/1, recluster/1]). -export([table_names/0]). @@ -52,6 +52,8 @@ -spec(join_cluster/2 :: ([node()], boolean()) -> 'ok'). -spec(reset/0 :: () -> 'ok'). -spec(force_reset/0 :: () -> 'ok'). +-spec(recluster/1 :: (node()) -> 'ok'). +-spec(change_node_type/1 :: ('ram' | 'disc') -> 'ok'). %% Various queries to get the status of the db -spec(status/0 :: () -> [{'nodes', [{node_type(), [node()]}]} | @@ -129,17 +131,15 @@ join_cluster(DiscoveryNode, WantDiscNode) -> ensure_mnesia_not_running(), ensure_mnesia_dir(), - Status = {ClusterNodes, DiscNodes, _} = - case discover_cluster(DiscoveryNode) of - {ok, Res} -> Res; - {error, Reason} -> throw({error, Reason}) - end, + {ClusterNodes, DiscNodes, _} = case discover_cluster(DiscoveryNode) of + {ok, Res} -> Res; + {error, Reason} -> throw({error, Reason}) + end, case lists:member(node(), ClusterNodes) of true -> throw({error, {already_clustered, "You are already clustered with the nodes you " - "have selected"}}), - write_cluster_nodes_status(Status); + "have selected"}}); false -> ok end, @@ -205,9 +205,9 @@ reset(Force) -> %% with nodes while not being in an Mnesia cluster with them. We don't %% handle that well. [erlang:disconnect_node(N) || N <- Nodes], - ok = reset_cluster_nodes_status(), %% remove persisted messages and any other garbage we find ok = rabbit_file:recursive_delete(filelib:wildcard(dir() ++ "/*")), + ok = initialize_cluster_nodes_status(), ok. change_node_type(Type) -> @@ -272,22 +272,10 @@ recluster(DiscoveryNode) -> %%---------------------------------------------------------------------------- status() -> - [{nodes, case mnesia:system_info(is_running) of - yes -> [{Key, Nodes} || - {Key, CopyType} <- [{disc_only, disc_only_copies}, - {disc, disc_copies}, - {ram, ram_copies}], - begin - Nodes = nodes_of_type(CopyType), - Nodes =/= [] - end]; - no -> [{unknown, all_clustered_nodes()}]; - Reason when Reason =:= starting; Reason =:= stopping -> - exit({rabbit_busy, try_again_later}) - end}, + [{nodes, + {{disc, all_clustered_disc_nodes()}, {ram, all_clustered_ram_nodes()}}}, {running_nodes, running_clustered_nodes()}]. - is_db_empty() -> lists:all(fun (Tab) -> mnesia:dirty_first(Tab) == '$end_of_table' end, table_names()). @@ -299,79 +287,29 @@ is_clustered() -> is_disc_and_clustered() -> is_disc_node() andalso is_clustered(). -%% The situation with functions that retrieve the nodes in the cluster is -%% messy. -%% -%% * If we want to get all the nodes or the running nodes, we can do that -%% while mnesia is offline *if* the node is a disc node. If the node is ram, -%% the result will always be [node()]. -%% * If we want to get the cluster disc nodes (running or not), we need to -%% start mnesia in any case. -%% -%% In the following functions we try to get the data from mnesia when we can, -%% otherwise we fall back to the cluster status file. - -check_mnesia_running(Fun) -> - case mnesia:system_info(is_running) of - yes -> {ok, Fun()}; - no -> error - end. - -check_disc_or_mnesia_running(Fun) -> - case is_disc_node() of - true -> {ok, Fun()}; - false -> case check_mnesia_running(Fun) of - {ok, Res} -> {ok, Res}; - error -> error - end - end. - -check_or_cluster_status(Fun, Check) -> - case Check(Fun) of - {ok, Res} -> {ok, Res}; - error -> {status, read_cluster_nodes_status()} - end. +%% Functions that retrieve the nodes in the cluster completely rely on the +%% cluster status file. For obvious reason, if rabbit is down, they might return +%% out of date information. all_clustered_nodes() -> - case check_or_cluster_status( - fun () -> mnesia:system_info(db_nodes) end, - fun check_disc_or_mnesia_running/1) - of - {ok, Nodes} -> Nodes; - {status, {Nodes, _, _}} -> Nodes - end. + {AllNodes, _, _} = read_cluster_nodes_status(), + AllNodes. all_clustered_disc_nodes() -> - case check_or_cluster_status( - fun () -> nodes_of_type(disc_copies) end, - fun check_mnesia_running/1) - of - {ok, Nodes} -> Nodes; - {status, {_, Nodes, _}} -> Nodes - end. + {_, DiscNodes, _} = read_cluster_nodes_status(), + DiscNodes. + +all_clustered_ram_nodes() -> + {AllNodes, DiscNodes, _} = read_cluster_nodes_status(), + sets:to_list(sets:subtract(sets:from_list(AllNodes), + sets:from_list(DiscNodes))). running_clustered_nodes() -> - case check_or_cluster_status( - fun () -> mnesia:system_info(running_db_nodes) end, - fun check_disc_or_mnesia_running/1) - of - {ok, Nodes} -> Nodes; - {status, {_, _, Nodes}} -> Nodes - end. + {_, _, RunningNodes} = read_cluster_nodes_status(), + RunningNodes. running_clustered_disc_nodes() -> - {DiscNodes, RunningNodes} = - case check_or_cluster_status( - fun () -> - {all_clustered_disc_nodes(), running_clustered_nodes()} - end, - fun check_mnesia_running/1) - of - {ok, Nodes} -> - Nodes; - {status, {_, DiscNodes0, RunningNodes0}} -> - {DiscNodes0, RunningNodes0} - end, + {_, DiscNodes, RunningNodes} = read_cluster_nodes_status(), sets:to_list(sets:intersection(sets:from_list(DiscNodes), sets:from_list(RunningNodes))). @@ -379,11 +317,10 @@ running_clustered_disc_nodes() -> %% the node is actually online. This is because when we discover the nodes we %% want online, "working" nodes only. cluster_status_if_running() -> - check_mnesia_running( - fun () -> - {mnesia:system_info(db_nodes), nodes_of_type(disc_copies), - mnesia:system_info(running_db_nodes)} - end). + case mnesia:system_info(is_running) of + no -> error; + yes -> {ok, read_cluster_nodes_status()} + end. is_disc_node() -> mnesia:system_info(use_dir). @@ -654,17 +591,6 @@ read_cluster_nodes_status() -> throw({error, {cannot_read_cluster_nodes_status, FileName, Reason}}) end. -reset_cluster_nodes_status() -> - FileName = cluster_nodes_status_filename(), - case file:delete(FileName) of - ok -> ok; - {error, enoent} -> ok; - {error, Reason} -> - throw({error, {cannot_delete_cluster_nodes_status, - FileName, Reason}}) - end, - write_cluster_nodes_status({[node()], [node()], [node()]}). - %% To update the cluster status when mnesia is running. update_cluster_nodes_status() -> {ok, Status} = cluster_status_if_running(), @@ -724,12 +650,6 @@ discover_cluster(Node) -> end end. -nodes_of_type(Type) -> - %% This function should return the nodes of a certain type (ram, - %% disc or disc_only) in the current cluster. The type of nodes - %% is determined when the cluster is initially configured. - mnesia:table_info(schema, Type). - %% The tables aren't supposed to be on disk on a ram node table_definitions(disc) -> table_definitions(); -- cgit v1.2.1 From 9d4a66b9a86c9d2d6b98d8432a122a8777cfa68c Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Mon, 21 May 2012 16:41:26 +0100 Subject: forgot to replace old function --- src/rabbit_control.erl | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/rabbit_control.erl b/src/rabbit_control.erl index e51c4c6a..235c1625 100644 --- a/src/rabbit_control.erl +++ b/src/rabbit_control.erl @@ -383,14 +383,8 @@ action(list_parameters, Node, Args = [], _Opts, Inform) -> action(report, Node, _Args, _Opts, Inform) -> io:format("Reporting server status on ~p~n~n", [erlang:universaltime()]), - Nodes = - case unsafe_rpc(Node, rabbit_mnesia, running_clustered_nodes_safe, []) - of - {ok, Res} -> Res; - {error, Reason} -> throw({error, Reason}) - end, [begin ok = action(Action, N, [], [], Inform), io:nl() end || - N <- Nodes, + N <- unsafe_rpc(Node, rabbit_mnesia, running_clustered_nodes, []), Action <- [status, cluster_status, environment]], VHosts = unsafe_rpc(Node, rabbit_vhost, list, []), [print_report(Node, Q) || Q <- ?GLOBAL_QUERIES], -- cgit v1.2.1 From 5d19fcfcd1be9e9f1fba0ab2f025b5a57ddec0c6 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Tue, 22 May 2012 11:34:16 +0100 Subject: fix but in `rabbit_mnesia:status/0' --- src/rabbit_mnesia.erl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 44ee2725..69bc0d3a 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -272,8 +272,11 @@ recluster(DiscoveryNode) -> %%---------------------------------------------------------------------------- status() -> - [{nodes, - {{disc, all_clustered_disc_nodes()}, {ram, all_clustered_ram_nodes()}}}, + IfNonEmpty = fun (_, []) -> []; + (Type, Nodes) -> [{Type, Nodes}] + end, + [{nodes, (IfNonEmpty(disc, all_clustered_disc_nodes()) ++ + IfNonEmpty(ram, all_clustered_ram_nodes()))}, {running_nodes, running_clustered_nodes()}]. is_db_empty() -> -- cgit v1.2.1 From 6c5c0722248003f6f52591a471d910a982e04697 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Tue, 22 May 2012 16:21:32 +0100 Subject: ooops... do not update the cluster status file with its own data --- src/rabbit_mnesia.erl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 69bc0d3a..882f154d 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -322,7 +322,9 @@ running_clustered_disc_nodes() -> cluster_status_if_running() -> case mnesia:system_info(is_running) of no -> error; - yes -> {ok, read_cluster_nodes_status()} + yes -> {ok, {mnesia:system_info(db_nodes), + mnesia:table_info(schema, disc_copies), + mnesia:system_info(running_db_nodes)}} end. is_disc_node() -> mnesia:system_info(use_dir). -- cgit v1.2.1 From 4bbee5a77bda32c1ad28e1f4198560d1e188e121 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Wed, 23 May 2012 12:05:27 +0100 Subject: stricter cluster consistency check Now checking for matching OTP & rabbit version --- src/rabbit_mnesia.erl | 67 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 47 insertions(+), 20 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 882f154d..b64d85d0 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -30,7 +30,7 @@ -export([table_names/0]). %% Used internally in rpc calls, see `discover_nodes/1' --export([cluster_status_if_running/0]). +-export([cluster_status_if_running/0, node_info/0]). %% create_tables/0 exported for helping embed RabbitMQ in or alongside %% other mnesia-using Erlang applications, such as ejabberd @@ -327,6 +327,10 @@ cluster_status_if_running() -> mnesia:system_info(running_db_nodes)}} end. +node_info() -> + {erlang:system_info(otp_release), application:get_env(rabbit, vsn), + cluster_status_if_running()}. + is_disc_node() -> mnesia:system_info(use_dir). dir() -> mnesia:system_info(directory). @@ -524,28 +528,51 @@ should_be_disc_node(DiscNodes) -> DiscNodes == [] orelse lists:member(node(), DiscNodes). %% This does not guarantee us much, but it avoids some situations that will -%% definitely end in disaster (a node starting and trying to merge its schema -%% to another node which is not clustered with it). +%% definitely end up badly check_cluster_consistency() -> - ThisNode = node(), + CheckVsn = fun (This, This, _) -> + ok; + (This, Remote, Name) -> + throw({error, + {inconsistent_cluster, + rabbit_misc:format( + "~s version mismatch: local node is ~s, " + "remote node ~s", [Name, This, Remote])}}) + end, + CheckOTP = + fun (OTP) -> CheckVsn(erlang:system_info(otp_release), OTP, "OTP") end, + CheckRabbit = + fun (Rabbit) -> + CheckVsn(application:get_env(rabbit, vsn), Rabbit, "Rabbit") + end, + + CheckNodes = fun (Node, AllNodes) -> + ThisNode = node(), + case lists:member(ThisNode, AllNodes) of + true -> + ok; + false -> + throw({error, + {inconsistent_cluster, + rabbit_misc:format( + "Node ~p thinks it's clustered " + "with node ~p, but ~p disagrees", + [ThisNode, Node, Node])}}) + end + end, + lists:foreach( fun(Node) -> - case rpc:call(Node, rabbit_mnesia, cluster_status_if_running, []) - of - {badrpc, _Reason} -> ok; - error -> ok; - {ok, {AllNodes, _, _}} -> - case lists:member(ThisNode, AllNodes) of - true -> - ok; - false -> - throw({error, - {inconsistent_cluster, - rabbit_misc:format( - "Node ~p thinks it's clustered with " - "node ~p, but ~p disagrees", - [ThisNode, Node, Node])}}) - end + case rpc:call(Node, rabbit_mnesia, node_info, []) of + {badrpc, _Reason} -> + ok; + {OTP, Rabbit, error} -> + CheckOTP(OTP), + CheckRabbit(Rabbit); + {OTP, Rabbit, {ok, {AllNodes, _, _}}} -> + CheckOTP(OTP), + CheckRabbit(Rabbit), + CheckNodes(Node, AllNodes) end end, all_clustered_nodes()). -- cgit v1.2.1 From 769e1f0b494914a8926fd05762cfa1338892e5c0 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Wed, 23 May 2012 12:26:41 +0100 Subject: deleted percent by mistake --- src/rabbit_mnesia.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index b64d85d0..a6843020 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -1,4 +1,4 @@ -% The contents of this file are subject to the Mozilla Public License +%% 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/ -- cgit v1.2.1 From 7bb9cc75b1c1187b6b06936df6cc5ca509a32295 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Wed, 23 May 2012 13:14:02 +0100 Subject: add `rabbit_misc:rabbit_version/0' also, `get_key', not `get_env'. --- src/rabbit_misc.erl | 6 ++++++ src/rabbit_mnesia.erl | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 706de835..29571ecf 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -62,6 +62,7 @@ -export([quit/1]). -export([os_cmd/1]). -export([gb_sets_difference/2]). +-export([rabbit_version/0]). %%---------------------------------------------------------------------------- @@ -209,6 +210,7 @@ -spec(quit/1 :: (integer() | string()) -> no_return()). -spec(os_cmd/1 :: (string()) -> string()). -spec(gb_sets_difference/2 :: (gb_set(), gb_set()) -> gb_set()). +-spec(rabbit_version/0 :: () -> string()). -endif. @@ -925,3 +927,7 @@ os_cmd(Command) -> gb_sets_difference(S1, S2) -> gb_sets:fold(fun gb_sets:delete_any/2, S1, S2). + +rabbit_version() -> + {ok, VSN} = application:get_key(rabbit, vsn), + VSN. diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index a6843020..f2ede9a1 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -328,7 +328,7 @@ cluster_status_if_running() -> end. node_info() -> - {erlang:system_info(otp_release), application:get_env(rabbit, vsn), + {erlang:system_info(otp_release), rabbit_misc:rabbit_version(), cluster_status_if_running()}. is_disc_node() -> mnesia:system_info(use_dir). @@ -543,7 +543,7 @@ check_cluster_consistency() -> fun (OTP) -> CheckVsn(erlang:system_info(otp_release), OTP, "OTP") end, CheckRabbit = fun (Rabbit) -> - CheckVsn(application:get_env(rabbit, vsn), Rabbit, "Rabbit") + CheckVsn(rabbit_misc:rabbit_version(), Rabbit, "Rabbit") end, CheckNodes = fun (Node, AllNodes) -> -- cgit v1.2.1 From 5a3cc15ed43779405eabf01164d7b3772dac4e76 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 23 May 2012 14:04:24 +0100 Subject: Cosmetic --- src/rabbit_prelaunch.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_prelaunch.erl b/src/rabbit_prelaunch.erl index 5aa01d18..d56211b5 100644 --- a/src/rabbit_prelaunch.erl +++ b/src/rabbit_prelaunch.erl @@ -45,7 +45,7 @@ start() -> stop() -> ok. -%%---------------------------------- +%%---------------------------------------------------------------------------- %% Check whether a node with the same name is already running duplicate_node_check([]) -> -- cgit v1.2.1 From 055b292858b7c51b68a2162d988c08814a28e167 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 23 May 2012 14:06:27 +0100 Subject: Cosmetic --- src/rabbit_control_main.erl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index 53d1c462..dd98b2f2 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -410,12 +410,12 @@ wait_for_application(Node, PidFile, Application, Inform) -> wait_for_application(Node, Pid, rabbit) -> wait_for_startup(Node, Pid); wait_for_application(Node, Pid, Application) -> - while_process_is_alive(Node, Pid, - fun() -> rabbit_nodes:is_running(Node, Application) end). + while_process_is_alive( + Node, Pid, fun() -> rabbit_nodes:is_running(Node, Application) end). wait_for_startup(Node, Pid) -> - while_process_is_alive(Node, Pid, - fun() -> rpc:call(Node, rabbit, await_startup, []) =:= ok end). + while_process_is_alive( + Node, Pid, fun() -> rpc:call(Node, rabbit, await_startup, []) =:= ok end). while_process_is_alive(Node, Pid, Activity) -> case process_up(Pid) of -- cgit v1.2.1 From 69f187d1e3de348796c15b054bc39d59d3811edd Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 23 May 2012 14:07:28 +0100 Subject: OK, it's slightly cheeky to pass this as the name of an application. But I think it's better than passing 'rabbit' and having that not mean the 'rabbit' application. --- src/rabbit_control_main.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index dd98b2f2..124fa0f9 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -194,7 +194,7 @@ action(force_cluster, Node, ClusterNodeSs, _Opts, Inform) -> action(wait, Node, [PidFile], _Opts, Inform) -> Inform("Waiting for ~p", [Node]), - wait_for_application(Node, PidFile, rabbit, Inform); + wait_for_application(Node, PidFile, rabbit_and_plugins, Inform); action(wait, Node, [PidFile, App], _Opts, Inform) -> Inform("Waiting for ~p on ~p", [App, Node]), @@ -407,7 +407,7 @@ wait_for_application(Node, PidFile, Application, Inform) -> Inform("pid is ~s", [Pid]), wait_for_application(Node, Pid, Application). -wait_for_application(Node, Pid, rabbit) -> +wait_for_application(Node, Pid, rabbit_and_plugins) -> wait_for_startup(Node, Pid); wait_for_application(Node, Pid, Application) -> while_process_is_alive( -- cgit v1.2.1 From 470c7592fc6aef6a3ac6f20af6c439ae713c497b Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Wed, 23 May 2012 14:28:51 +0100 Subject: fiddling with `rabbit_mnesia' specs --- src/rabbit_mnesia.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index f2ede9a1..5364c354 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -44,7 +44,7 @@ -export_type([node_type/0, node_status/0]). --type(node_type() :: disc_only | disc | ram | unknown). +-type(node_type() :: disc | ram). -type(node_status() :: {[node()], [node()], [node()]}). %% Main interface @@ -53,7 +53,7 @@ -spec(reset/0 :: () -> 'ok'). -spec(force_reset/0 :: () -> 'ok'). -spec(recluster/1 :: (node()) -> 'ok'). --spec(change_node_type/1 :: ('ram' | 'disc') -> 'ok'). +-spec(change_node_type/1 :: (node_type()) -> 'ok'). %% Various queries to get the status of the db -spec(status/0 :: () -> [{'nodes', [{node_type(), [node()]}]} | -- cgit v1.2.1 From d6bbdb17df9fb0aadd149b1f1423bb8ef3fb1e6f Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 23 May 2012 15:17:31 +0100 Subject: Fix dialyzer warnings. --- src/rabbit_misc.erl | 4 ++-- src/rabbit_plugins_main.erl | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index c8b172ec..d41aa09b 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -86,8 +86,8 @@ -spec(die/1 :: (rabbit_framing:amqp_exception()) -> channel_or_connection_exit()). --spec(quit/1 :: (integer()) -> any()). --spec(quit/2 :: (string(), [term()]) -> any()). +-spec(quit/1 :: (integer()) -> no_return()). +-spec(quit/2 :: (string(), [term()]) -> no_return()). -spec(frame_error/2 :: (rabbit_framing:amqp_method_name(), binary()) -> rabbit_types:connection_exit()). diff --git a/src/rabbit_plugins_main.erl b/src/rabbit_plugins_main.erl index 60f662c1..7a81f0dd 100644 --- a/src/rabbit_plugins_main.erl +++ b/src/rabbit_plugins_main.erl @@ -42,6 +42,7 @@ -spec(start/0 :: () -> no_return()). -spec(stop/0 :: () -> 'ok'). +-spec(usage/0 :: () -> no_return()). -endif. -- cgit v1.2.1 From 9bc2c057e586e917264a696e18ce6f0c11ef27e1 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 23 May 2012 15:30:09 +0100 Subject: Cosmetic --- src/rabbit_control_main.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index c9734314..b85cf8c0 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -473,9 +473,9 @@ wait_for_startup(Node, Pid) -> while_process_is_alive(Node, Pid, Activity) -> case process_up(Pid) of true -> case Activity() of - true -> ok; - _Other -> timer:sleep(?EXTERNAL_CHECK_INTERVAL), - while_process_is_alive(Node, Pid, Activity) + true -> ok; + _Other -> timer:sleep(?EXTERNAL_CHECK_INTERVAL), + while_process_is_alive(Node, Pid, Activity) end; false -> {error, process_not_running} end. -- cgit v1.2.1 From 7e18428c9b81cdc93f6a753b9971fc95c6a7c131 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Wed, 23 May 2012 16:32:13 +0100 Subject: added `rabbit_mnesia:remove_node/1' to remove node from clusters remotely Maybe I'll add more explicit checks - e.g. whether the node to be remote is offline - for better error messages. Also, as discussed with Simon, we probably need to add machinery to monitor when offline nodes leave clusters. --- src/rabbit_control.erl | 5 +++++ src/rabbit_mnesia.erl | 20 +++++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/rabbit_control.erl b/src/rabbit_control.erl index 235c1625..c677d8b9 100644 --- a/src/rabbit_control.erl +++ b/src/rabbit_control.erl @@ -200,6 +200,11 @@ action(recluster, Node, [ClusterNodeS], _Opts, Inform) -> Inform("Re-clustering ~p with ~p", [Node, ClusterNode]), rpc_call(Node, rabbit_mnesia, recluster, [ClusterNode]); +action(remove_node, Node, [ClusterNodeS], _Opts, Inform) -> + ClusterNode = list_to_atom(ClusterNodeS), + Inform("Removing node ~p from cluster", [ClusterNode]), + rpc_call(Node, rabbit_mnesia, remove_node, [ClusterNode]); + action(wait, Node, [PidFile], _Opts, Inform) -> Inform("Waiting for ~p", [Node]), wait_for_application(Node, PidFile, rabbit, Inform); diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 5364c354..4a2b37ec 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -25,7 +25,7 @@ initialize_cluster_nodes_status/0, write_cluster_nodes_status/1, read_cluster_nodes_status/0, update_cluster_nodes_status/0, is_disc_node/0, on_node_down/1, on_node_up/1, should_be_disc_node/1, - change_node_type/1, recluster/1]). + change_node_type/1, recluster/1, remove_node/1]). -export([table_names/0]). @@ -54,6 +54,7 @@ -spec(force_reset/0 :: () -> 'ok'). -spec(recluster/1 :: (node()) -> 'ok'). -spec(change_node_type/1 :: (node_type()) -> 'ok'). +-spec(remove_node/1 :: (node()) -> 'ok'). %% Various queries to get the status of the db -spec(status/0 :: () -> [{'nodes', [{node_type(), [node()]}]} | @@ -267,6 +268,22 @@ recluster(DiscoveryNode) -> ok. +remove_node(Node) -> + case mnesia:system_info(is_running) of + yes -> {atomic, ok} = mnesia:del_table_copy(schema, Node), + update_cluster_nodes_status(), + {_, []} = rpc:multicall(running_clustered_nodes(), rabbit_mnesia, + update_cluster_nodes_status, []); + no -> start_mnesia(), + try + [mnesia:force_load_table(T) || T <- rabbit_mnesia:table_names()], + remove_node(Node) + after + stop_mnesia() + end + end, + ok. + %%---------------------------------------------------------------------------- %% Queries %%---------------------------------------------------------------------------- @@ -653,6 +670,7 @@ on_node_up(Node) -> end. on_node_down(Node) -> + update_cluster_nodes_status(), case is_only_disc_node(Node) of true -> rabbit_log:info("only running disc node went down~n"); false -> ok -- cgit v1.2.1 From 811b2095f8316260e38e6ee58937e04219952f97 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 24 May 2012 16:41:48 +0100 Subject: Add special case for i18n reasons. --- src/rabbit_control.erl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/rabbit_control.erl b/src/rabbit_control.erl index 2a84f7b2..ad350d28 100644 --- a/src/rabbit_control.erl +++ b/src/rabbit_control.erl @@ -156,6 +156,11 @@ start() -> {'EXIT', {badarg, _}} -> print_error("invalid parameter: ~p", [Args]), usage(); + {error, {Problem, Reason}} when is_atom(Problem); is_binary(Reason) -> + %% We handle this common case specially to avoid ~p since + %% that has i18n issues + print_error("~s: ~s", [Problem, Reason]), + rabbit_misc:quit(2); {error, Reason} -> print_error("~p", [Reason]), rabbit_misc:quit(2); -- cgit v1.2.1 From f1ca209672cc32affe408aeb4cd771c6e06a9015 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Thu, 24 May 2012 16:54:29 +0100 Subject: handle legacy `cluster_nodes.config' and `running_nodes_at_shutdown' --- src/rabbit.erl | 5 +-- src/rabbit_mnesia.erl | 115 ++++++++++++++++++++++++++++++++++--------------- src/rabbit_upgrade.erl | 3 +- 3 files changed, 85 insertions(+), 38 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index e4c196a3..d5a2b792 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -288,8 +288,7 @@ split0([I | Is], [L | Ls]) -> split0(Is, Ls ++ [[I | L]]). prepare() -> ok = ensure_working_log_handlers(), ok = rabbit_mnesia:ensure_mnesia_dir(), - ok = rabbit_mnesia:initialize_cluster_nodes_status(), - ok = rabbit_mnesia:check_cluster_consistency(), + ok = rabbit_mnesia:prepare(), ok = rabbit_upgrade:maybe_upgrade_mnesia(). start() -> @@ -514,7 +513,7 @@ sort_boot_steps(UnsortedSteps) -> end. boot_step_error({error, {timeout_waiting_for_tables, _}}, _Stacktrace) -> - {AllNodes, _, _} = rabbit_mnesia:read_cluster_nodes_status(), + AllNodes = rabbit_mnesia:all_clustered_nodes(), {Err, Nodes} = case AllNodes -- [node()] of [] -> {"Timeout contacting cluster nodes. Since RabbitMQ was" diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 4a2b37ec..78632668 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -21,11 +21,10 @@ join_cluster/2, check_cluster_consistency/0, reset/0, force_reset/0, init_db/4, is_clustered/0, running_clustered_nodes/0, all_clustered_nodes/0, all_clustered_disc_nodes/0, - empty_ram_only_tables/0, copy_db/1, wait_for_tables/1, - initialize_cluster_nodes_status/0, write_cluster_nodes_status/1, - read_cluster_nodes_status/0, update_cluster_nodes_status/0, - is_disc_node/0, on_node_down/1, on_node_up/1, should_be_disc_node/1, - change_node_type/1, recluster/1, remove_node/1]). + empty_ram_only_tables/0, copy_db/1, wait_for_tables/1, is_disc_node/0, + on_node_down/1, on_node_up/1, should_be_disc_node/1, + change_node_type/1, recluster/1, remove_node/1, prepare/0, + update_cluster_nodes_status/0]). -export([table_names/0]). @@ -48,6 +47,7 @@ -type(node_status() :: {[node()], [node()], [node()]}). %% Main interface +-spec(prepare/0 :: () -> 'ok'). -spec(init/0 :: () -> 'ok'). -spec(join_cluster/2 :: ([node()], boolean()) -> 'ok'). -spec(reset/0 :: () -> 'ok'). @@ -81,7 +81,6 @@ %% Functions to handle the cluster status file -spec(write_cluster_nodes_status/1 :: (node_status()) -> 'ok'). -spec(read_cluster_nodes_status/0 :: () -> node_status()). --spec(initialize_cluster_nodes_status/0 :: () -> 'ok'). -spec(update_cluster_nodes_status/0 :: () -> 'ok'). %% Hooks used in `rabbit_node_monitor' @@ -94,11 +93,43 @@ %% Main interface %%---------------------------------------------------------------------------- +%% Sets up the cluster status file when needed, taking care of the legacy +%% files +prepare() -> + NotPresent = + fun (AllNodes0, WantDiscNode) -> + ThisNode = [node()], + + RunningNodes0 = legacy_read_previously_running_nodes(), + legacy_delete_previously_running_nodes(), + + RunningNodes = lists:usort(RunningNodes0 ++ ThisNode), + AllNodes = + lists:usort(AllNodes0 ++ RunningNodes), + DiscNodes = case WantDiscNode of + true -> ThisNode; + false -> [] + end, + + ok = write_cluster_nodes_status({AllNodes, DiscNodes, RunningNodes}) + end, + case try_read_cluster_nodes_status() of + {ok, _} -> + ok; + {error, {invalid_term, _, [AllNodes]}} -> + NotPresent(AllNodes, should_be_disc_node(AllNodes)); + {error, {cannot_read_file, _, enoent}} -> + {ok, {AllNodes, WantDiscNode}} = + application:get_env(rabbit, cluster_nodes), + NotPresent(AllNodes, WantDiscNode) + end. + init() -> ensure_mnesia_running(), ensure_mnesia_dir(), - {DiscNodes, WantDiscNode} = read_cluster_nodes_config(), - ok = init_db(DiscNodes, WantDiscNode, WantDiscNode), + {AllNodes, _, DiscNodes} = read_cluster_nodes_status(), + WantDiscNode = should_be_disc_node(DiscNodes), + ok = init_db(AllNodes, WantDiscNode, WantDiscNode), %% We intuitively expect the global name server to be synced when %% Mnesia is up. In fact that's not guaranteed to be the case - let's %% make it so. @@ -208,7 +239,7 @@ reset(Force) -> [erlang:disconnect_node(N) || N <- Nodes], %% remove persisted messages and any other garbage we find ok = rabbit_file:recursive_delete(filelib:wildcard(dir() ++ "/*")), - ok = initialize_cluster_nodes_status(), + ok = write_cluster_nodes_status(initial_cluster_status()), ok. change_node_type(Type) -> @@ -613,15 +644,8 @@ check_cluster_consistency() -> cluster_nodes_status_filename() -> dir() ++ "/cluster_nodes.config". -%% Creates a status file with the default data (one disc node), only if an -%% existing cluster does not exist. -initialize_cluster_nodes_status() -> - try read_cluster_nodes_status() of - _ -> ok - catch - throw:{error, {cannot_read_cluster_nodes_status, _, enoent}} -> - write_cluster_nodes_status({[node()], [node()], [node()]}) - end. +initial_cluster_status() -> + {[node()], [node()], [node()]}. write_cluster_nodes_status(Status) -> FileName = cluster_nodes_status_filename(), @@ -632,12 +656,23 @@ write_cluster_nodes_status(Status) -> FileName, Reason}}) end. -read_cluster_nodes_status() -> +try_read_cluster_nodes_status() -> FileName = cluster_nodes_status_filename(), case rabbit_file:read_term_file(FileName) of - {ok, [{_, _, _} = Status]} -> Status; + {ok, [{_, _, _} = Status]} -> + {ok, Status}; + {ok, Term} -> + {error, {invalid_term, FileName, Term}}; {error, Reason} -> - throw({error, {cannot_read_cluster_nodes_status, FileName, Reason}}) + {error, {cannot_read_file, FileName, Reason}} + end. + +read_cluster_nodes_status() -> + case try_read_cluster_nodes_status() of + {ok, Status} -> + Status; + {error, Reason} -> + throw({error, {cannot_read_cluster_nodes_status, Reason}}) end. %% To update the cluster status when mnesia is running. @@ -645,19 +680,6 @@ update_cluster_nodes_status() -> {ok, Status} = cluster_status_if_running(), write_cluster_nodes_status(Status). -%% The cluster config contains the nodes that the node should try to contact to -%% form a cluster, and whether the node should be a disc node. When starting the -%% database, if the nodes in the cluster status are the initial ones, we try to -%% read the cluster config. -read_cluster_nodes_config() -> - {AllNodes, DiscNodes, _} = read_cluster_nodes_status(), - Node = node(), - case AllNodes of - [Node] -> {ok, Config} = application:get_env(rabbit, cluster_nodes), - Config; - _ -> {AllNodes, should_be_disc_node(DiscNodes)} - end. - %%-------------------------------------------------------------------- %% Hooks for `rabbit_node_monitor' %%-------------------------------------------------------------------- @@ -1018,3 +1040,28 @@ start_and_init_db(ClusterNodes, WantDiscNode, Force) -> mnesia:stop() end, ok. + +%%-------------------------------------------------------------------- +%% Legacy functions related to the "running nodes" file +%%-------------------------------------------------------------------- + +legacy_running_nodes_filename() -> + filename:join(dir(), "nodes_running_at_shutdown"). + +legacy_read_previously_running_nodes() -> + FileName = legacy_running_nodes_filename(), + case rabbit_file:read_term_file(FileName) of + {ok, [Nodes]} -> Nodes; + {error, enoent} -> []; + {error, Reason} -> throw({error, {cannot_read_previous_nodes_file, + FileName, Reason}}) + end. + +legacy_delete_previously_running_nodes() -> + FileName = legacy_running_nodes_filename(), + case file:delete(FileName) of + ok -> ok; + {error, enoent} -> ok; + {error, Reason} -> throw({error, {cannot_delete_previous_nodes_file, + FileName, Reason}}) + end. diff --git a/src/rabbit_upgrade.erl b/src/rabbit_upgrade.erl index 8c95e9dd..85bcff25 100644 --- a/src/rabbit_upgrade.erl +++ b/src/rabbit_upgrade.erl @@ -147,7 +147,8 @@ maybe_upgrade_mnesia() -> upgrade_mode(AllNodes) -> case nodes_running(AllNodes) of [] -> - case {is_disc_node_legacy(), AllNodes} of + AfterUs = rabbit_mnesia:running_clustered_nodes() -- [node()], + case {is_disc_node_legacy(), AfterUs} of {true, []} -> primary; {true, _} -> -- cgit v1.2.1 From 2d3034a8357a42ae12552c78be919357c8863be3 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Fri, 25 May 2012 12:23:53 +0100 Subject: forgot `check_cluster_consistency/0' call in `prepare/0' --- src/rabbit_mnesia.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 78632668..b0a016dc 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -115,6 +115,7 @@ prepare() -> end, case try_read_cluster_nodes_status() of {ok, _} -> + check_cluster_consistency(), ok; {error, {invalid_term, _, [AllNodes]}} -> NotPresent(AllNodes, should_be_disc_node(AllNodes)); -- cgit v1.2.1 From 88c0055652a53c6fbd8dfd081192e010c56e66bb Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Fri, 25 May 2012 13:20:44 +0100 Subject: comments in `prepare/0' --- src/rabbit_mnesia.erl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index b0a016dc..2e34b65e 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -115,9 +115,14 @@ prepare() -> end, case try_read_cluster_nodes_status() of {ok, _} -> + %% We check the consistency only when the cluster status exists, + %% since when it doesn't exist it means that we just started a fresh + %% node, and when we have a legacy node with an old + %% "cluster_nodes.config" we can't check the consistency anyway check_cluster_consistency(), ok; {error, {invalid_term, _, [AllNodes]}} -> + %% Legacy file NotPresent(AllNodes, should_be_disc_node(AllNodes)); {error, {cannot_read_file, _, enoent}} -> {ok, {AllNodes, WantDiscNode}} = -- cgit v1.2.1 From f50ccf89e6fbe776ac863f0f98da343c890d832f Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 25 May 2012 13:48:24 +0100 Subject: Don't prioritise anything protocol-ish over anything else. --- src/rabbit_amqqueue_process.erl | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 5701efeb..f2833c26 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -984,8 +984,6 @@ prioritise_call(Msg, _From, _State) -> info -> 9; {info, _Items} -> 9; consumers -> 9; - {basic_consume, _, _, _, _, _, _} -> 7; - {basic_cancel, _, _, _} -> 7; stat -> 7; _ -> 0 end. @@ -995,10 +993,6 @@ prioritise_cast(Msg, _State) -> delete_immediately -> 8; {set_ram_duration_target, _Duration} -> 8; {set_maximum_since_use, _Age} -> 8; - {ack, _AckTags, _ChPid} -> 7; - {reject, _AckTags, _Requeue, _ChPid} -> 7; - {notify_sent, _ChPid, _Credit} -> 7; - {unblock, _ChPid} -> 7; {run_backing_queue, _Mod, _Fun} -> 6; _ -> 0 end. -- cgit v1.2.1 From dacdf4e3616d49f3091c1f2831ca38cb64452d79 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 28 May 2012 13:41:57 +0100 Subject: rename rabbit_plugins API calls to be less verbose --- src/rabbit.erl | 6 +++--- src/rabbit_plugins.erl | 34 +++++++++++++++++----------------- src/rabbit_plugins_main.erl | 24 ++++++++++++------------ 3 files changed, 32 insertions(+), 32 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 1dff4d53..8a25d9ff 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -302,13 +302,13 @@ start() -> start_it(fun() -> ok = prepare(), ok = app_utils:start_applications(app_startup_order()), - ok = print_plugin_info(rabbit_plugins:active_plugins()) + ok = print_plugin_info(rabbit_plugins:active()) end). boot() -> start_it(fun() -> ok = prepare(), - Plugins = rabbit_plugins:prepare_plugins(), + Plugins = rabbit_plugins:setup(), ToBeLoaded = Plugins ++ ?APPS, ok = app_utils:load_applications(ToBeLoaded), StartupApps = app_utils:app_dependency_order(ToBeLoaded, false), @@ -421,7 +421,7 @@ app_startup_order() -> app_utils:app_dependency_order(?APPS, false). app_shutdown_order() -> - Apps = ?APPS ++ rabbit_plugins:active_plugins(), + Apps = ?APPS ++ rabbit_plugins:active(), app_utils:app_dependency_order(Apps, true). %%--------------------------------------------------------------------------- diff --git a/src/rabbit_plugins.erl b/src/rabbit_plugins.erl index 84f559b9..7cf6eea9 100644 --- a/src/rabbit_plugins.erl +++ b/src/rabbit_plugins.erl @@ -17,8 +17,8 @@ -module(rabbit_plugins). -include("rabbit.hrl"). --export([prepare_plugins/0, active_plugins/0, read_enabled_plugins/1, - find_plugins/1, calculate_plugin_dependencies/3]). +-export([setup/0, active/0, read_enabled/1, + list/1, dependencies/3]). -define(VERBOSE_DEF, {?VERBOSE_OPT, flag}). -define(MINIMAL_DEF, {?MINIMAL_OPT, flag}). @@ -36,11 +36,11 @@ -ifdef(use_specs). --spec(prepare_plugins/0 :: () -> [atom()]). --spec(active_plugins/0 :: () -> [atom()]). --spec(find_plugins/1 :: (string()) -> [#plugin{}]). --spec(read_enabled_plugins/1 :: (file:filename()) -> [atom()]). --spec(calculate_plugin_dependencies/3 :: +-spec(setup/0 :: () -> [atom()]). +-spec(active/0 :: () -> [atom()]). +-spec(list/1 :: (string()) -> [#plugin{}]). +-spec(read_enabled/1 :: (file:filename()) -> [atom()]). +-spec(dependencies/3 :: (boolean(), [atom()], [#plugin{}]) -> [atom()]). -endif. @@ -50,7 +50,7 @@ %% %% @doc Prepares the file system and installs all enabled plugins. %% -prepare_plugins() -> +setup() -> {ok, PluginDir} = application:get_env(rabbit, plugins_dir), {ok, ExpandDir} = application:get_env(rabbit, plugins_expand_dir), {ok, EnabledPluginsFile} = application:get_env(rabbit, @@ -60,14 +60,14 @@ prepare_plugins() -> PluginName <- filelib:wildcard(ExpandDir ++ "/*/ebin/*.app")]. %% @doc Lists the plugins which are currently running. -active_plugins() -> +active() -> {ok, ExpandDir} = application:get_env(rabbit, plugins_expand_dir), - InstalledPlugins = [ P#plugin.name || P <- find_plugins(ExpandDir) ], + InstalledPlugins = [ P#plugin.name || P <- list(ExpandDir) ], [App || {App, _, _} <- application:which_applications(), lists:member(App, InstalledPlugins)]. %% @doc Get the list of plugins which are ready to be enabled. -find_plugins(PluginsDir) -> +list(PluginsDir) -> EZs = [{ez, EZ} || EZ <- filelib:wildcard("*.ez", PluginsDir)], FreeApps = [{app, App} || App <- filelib:wildcard("*/ebin/*.app", PluginsDir)], @@ -87,7 +87,7 @@ find_plugins(PluginsDir) -> Plugins. %% @doc Read the list of enabled plugins from the supplied term file. -read_enabled_plugins(PluginsFile) -> +read_enabled(PluginsFile) -> case rabbit_file:read_term_file(PluginsFile) of {ok, [Plugins]} -> Plugins; {ok, []} -> []; @@ -103,7 +103,7 @@ read_enabled_plugins(PluginsFile) -> %% When Reverse =:= true the bottom/leaf level applications are returned in %% the resulting list, otherwise they're skipped. %% -calculate_plugin_dependencies(Reverse, Sources, AllPlugins) -> +dependencies(Reverse, Sources, AllPlugins) -> {ok, G} = rabbit_misc:build_acyclic_graph( fun (App, _Deps) -> [{App, App}] end, fun (App, Deps) -> [{App, Dep} || Dep <- Deps] end, @@ -119,9 +119,9 @@ calculate_plugin_dependencies(Reverse, Sources, AllPlugins) -> %%---------------------------------------------------------------------------- prepare_plugins(EnabledPluginsFile, PluginsDistDir, DestDir) -> - AllPlugins = find_plugins(PluginsDistDir), - Enabled = read_enabled_plugins(EnabledPluginsFile), - ToUnpack = calculate_plugin_dependencies(false, Enabled, AllPlugins), + AllPlugins = list(PluginsDistDir), + Enabled = read_enabled(EnabledPluginsFile), + ToUnpack = dependencies(false, Enabled, AllPlugins), ToUnpackPlugins = lookup_plugins(ToUnpack, AllPlugins), Missing = Enabled -- plugin_names(ToUnpackPlugins), @@ -251,4 +251,4 @@ plugin_names(Plugins) -> %% Find plugins by name in a list of plugins. lookup_plugins(Names, AllPlugins) -> - [P || P = #plugin{name = Name} <- AllPlugins, lists:member(Name, Names)]. \ No newline at end of file + [P || P = #plugin{name = Name} <- AllPlugins, lists:member(Name, Names)]. diff --git a/src/rabbit_plugins_main.erl b/src/rabbit_plugins_main.erl index 7a81f0dd..dbca391c 100644 --- a/src/rabbit_plugins_main.erl +++ b/src/rabbit_plugins_main.erl @@ -102,9 +102,9 @@ action(enable, ToEnable0, _Opts, PluginsFile, PluginsDir) -> [] -> throw({error_string, "Not enough arguments for 'enable'"}); _ -> ok end, - AllPlugins = rabbit_plugins:find_plugins(PluginsDir), - Enabled = rabbit_plugins:read_enabled_plugins(PluginsFile), - ImplicitlyEnabled = rabbit_plugins:calculate_plugin_dependencies(false, Enabled, AllPlugins), + AllPlugins = rabbit_plugins:list(PluginsDir), + Enabled = rabbit_plugins:read_enabled(PluginsFile), + ImplicitlyEnabled = rabbit_plugins:dependencies(false, Enabled, AllPlugins), ToEnable = [list_to_atom(Name) || Name <- ToEnable0], Missing = ToEnable -- plugin_names(AllPlugins), case Missing of @@ -115,7 +115,7 @@ action(enable, ToEnable0, _Opts, PluginsFile, PluginsDir) -> end, NewEnabled = lists:usort(Enabled ++ ToEnable), write_enabled_plugins(PluginsFile, NewEnabled), - NewImplicitlyEnabled = rabbit_plugins:calculate_plugin_dependencies(false, NewEnabled, AllPlugins), + NewImplicitlyEnabled = rabbit_plugins:dependencies(false, NewEnabled, AllPlugins), maybe_warn_mochiweb(NewImplicitlyEnabled), case NewEnabled -- ImplicitlyEnabled of [] -> io:format("Plugin configuration unchanged.~n"); @@ -130,22 +130,22 @@ action(disable, ToDisable0, _Opts, PluginsFile, PluginsDir) -> _ -> ok end, ToDisable = [list_to_atom(Name) || Name <- ToDisable0], - Enabled = rabbit_plugins:read_enabled_plugins(PluginsFile), - AllPlugins = rabbit_plugins:find_plugins(PluginsDir), + Enabled = rabbit_plugins:read_enabled(PluginsFile), + AllPlugins = rabbit_plugins:list(PluginsDir), Missing = ToDisable -- plugin_names(AllPlugins), case Missing of [] -> ok; _ -> print_list("Warning: the following plugins could not be found:", Missing) end, - ToDisableDeps = rabbit_plugins:calculate_plugin_dependencies(true, ToDisable, AllPlugins), + ToDisableDeps = rabbit_plugins:dependencies(true, ToDisable, AllPlugins), NewEnabled = Enabled -- ToDisableDeps, case length(Enabled) =:= length(NewEnabled) of true -> io:format("Plugin configuration unchanged.~n"); false -> ImplicitlyEnabled = - rabbit_plugins:calculate_plugin_dependencies(false, Enabled, AllPlugins), + rabbit_plugins:dependencies(false, Enabled, AllPlugins), NewImplicitlyEnabled = - rabbit_plugins:calculate_plugin_dependencies(false, NewEnabled, AllPlugins), + rabbit_plugins:dependencies(false, NewEnabled, AllPlugins), print_list("The following plugins have been disabled:", ImplicitlyEnabled -- NewImplicitlyEnabled), write_enabled_plugins(PluginsFile, NewEnabled), @@ -175,10 +175,10 @@ format_plugins(Pattern, Opts, PluginsFile, PluginsDir) -> OnlyEnabled = proplists:get_bool(?ENABLED_OPT, Opts), OnlyEnabledAll = proplists:get_bool(?ENABLED_ALL_OPT, Opts), - AvailablePlugins = rabbit_plugins:find_plugins(PluginsDir), - EnabledExplicitly = rabbit_plugins:read_enabled_plugins(PluginsFile), + AvailablePlugins = rabbit_plugins:list(PluginsDir), + EnabledExplicitly = rabbit_plugins:read_enabled(PluginsFile), EnabledImplicitly = - rabbit_plugins:calculate_plugin_dependencies(false, EnabledExplicitly, AvailablePlugins) -- + rabbit_plugins:dependencies(false, EnabledExplicitly, AvailablePlugins) -- EnabledExplicitly, {ok, RE} = re:compile(Pattern), Plugins = [ Plugin || -- cgit v1.2.1 From 64dbee37e5377532c68f14ac28913aaea0902fb7 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 28 May 2012 13:46:32 +0100 Subject: cosmetic --- src/rabbit.erl | 9 ++++++--- src/rabbit_plugins_main.erl | 16 ++++++++++------ 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 8a25d9ff..6895dcb6 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -311,7 +311,8 @@ boot() -> Plugins = rabbit_plugins:setup(), ToBeLoaded = Plugins ++ ?APPS, ok = app_utils:load_applications(ToBeLoaded), - StartupApps = app_utils:app_dependency_order(ToBeLoaded, false), + StartupApps = app_utils:app_dependency_order(ToBeLoaded, + false), ok = app_utils:start_applications(StartupApps), ok = print_plugin_info(Plugins) end). @@ -468,7 +469,8 @@ sort_boot_steps(UnsortedSteps) -> %% there is one, otherwise fail). SortedSteps = lists:reverse( [begin - {StepName, Step} = digraph:vertex(G, StepName), + {StepName, Step} = digraph:vertex(G, + StepName), Step end || StepName <- digraph_utils:topsort(G)]), digraph:delete(G), @@ -550,7 +552,8 @@ insert_default_data() -> ok = rabbit_vhost:add(DefaultVHost), ok = rabbit_auth_backend_internal:add_user(DefaultUser, DefaultPass), ok = rabbit_auth_backend_internal:set_tags(DefaultUser, DefaultTags), - ok = rabbit_auth_backend_internal:set_permissions(DefaultUser, DefaultVHost, + ok = rabbit_auth_backend_internal:set_permissions(DefaultUser, + DefaultVHost, DefaultConfigurePerm, DefaultWritePerm, DefaultReadPerm), diff --git a/src/rabbit_plugins_main.erl b/src/rabbit_plugins_main.erl index dbca391c..572cf150 100644 --- a/src/rabbit_plugins_main.erl +++ b/src/rabbit_plugins_main.erl @@ -104,7 +104,8 @@ action(enable, ToEnable0, _Opts, PluginsFile, PluginsDir) -> end, AllPlugins = rabbit_plugins:list(PluginsDir), Enabled = rabbit_plugins:read_enabled(PluginsFile), - ImplicitlyEnabled = rabbit_plugins:dependencies(false, Enabled, AllPlugins), + ImplicitlyEnabled = rabbit_plugins:dependencies(false, + Enabled, AllPlugins), ToEnable = [list_to_atom(Name) || Name <- ToEnable0], Missing = ToEnable -- plugin_names(AllPlugins), case Missing of @@ -115,7 +116,8 @@ action(enable, ToEnable0, _Opts, PluginsFile, PluginsDir) -> end, NewEnabled = lists:usort(Enabled ++ ToEnable), write_enabled_plugins(PluginsFile, NewEnabled), - NewImplicitlyEnabled = rabbit_plugins:dependencies(false, NewEnabled, AllPlugins), + NewImplicitlyEnabled = rabbit_plugins:dependencies(false, + NewEnabled, AllPlugins), maybe_warn_mochiweb(NewImplicitlyEnabled), case NewEnabled -- ImplicitlyEnabled of [] -> io:format("Plugin configuration unchanged.~n"); @@ -145,7 +147,8 @@ action(disable, ToDisable0, _Opts, PluginsFile, PluginsDir) -> false -> ImplicitlyEnabled = rabbit_plugins:dependencies(false, Enabled, AllPlugins), NewImplicitlyEnabled = - rabbit_plugins:dependencies(false, NewEnabled, AllPlugins), + rabbit_plugins:dependencies(false, + NewEnabled, AllPlugins), print_list("The following plugins have been disabled:", ImplicitlyEnabled -- NewImplicitlyEnabled), write_enabled_plugins(PluginsFile, NewEnabled), @@ -178,14 +181,15 @@ format_plugins(Pattern, Opts, PluginsFile, PluginsDir) -> AvailablePlugins = rabbit_plugins:list(PluginsDir), EnabledExplicitly = rabbit_plugins:read_enabled(PluginsFile), EnabledImplicitly = - rabbit_plugins:dependencies(false, EnabledExplicitly, AvailablePlugins) -- - EnabledExplicitly, + rabbit_plugins:dependencies(false, EnabledExplicitly, + AvailablePlugins) -- EnabledExplicitly, {ok, RE} = re:compile(Pattern), Plugins = [ Plugin || Plugin = #plugin{name = Name} <- AvailablePlugins, re:run(atom_to_list(Name), RE, [{capture, none}]) =:= match, if OnlyEnabled -> lists:member(Name, EnabledExplicitly); - OnlyEnabledAll -> (lists:member(Name, EnabledExplicitly) or + OnlyEnabledAll -> (lists:member(Name, + EnabledExplicitly) or lists:member(Name, EnabledImplicitly)); true -> true end], -- cgit v1.2.1 From b476416eb490d453560491060f34d264a00c4e11 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Mon, 28 May 2012 16:35:02 +0100 Subject: remove cluster managment tests, adjust the others For some misterious reason if we stop/start cover in `run_cluster_dependent_tests' things fail. Also, the proxy is not working and I can't test the qpid suite, but I wouldn't expect surprises. --- src/rabbit_tests.erl | 200 +-------------------------------------------------- 1 file changed, 1 insertion(+), 199 deletions(-) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index bae4928d..31706d46 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -51,7 +51,6 @@ all_tests() -> passed = test_log_management_during_startup(), passed = test_statistics(), passed = test_arguments_parser(), - passed = test_cluster_management(), passed = test_user_management(), passed = test_runtime_parameters(), passed = test_server_status(), @@ -73,12 +72,9 @@ maybe_run_cluster_dependent_tests() -> run_cluster_dependent_tests(SecondaryNode) -> SecondaryNodeS = atom_to_list(SecondaryNode), - cover:stop(SecondaryNode), ok = control_action(stop_app, []), - ok = control_action(reset, []), - ok = control_action(cluster, [SecondaryNodeS]), + ok = control_action(join_cluster, [SecondaryNodeS]), ok = control_action(start_app, []), - cover:start(SecondaryNode), ok = control_action(start_app, SecondaryNode, [], []), io:format("Running cluster dependent tests with node ~p~n", [SecondaryNode]), @@ -855,200 +851,6 @@ test_arguments_parser() -> passed. -test_cluster_management() -> - %% 'cluster' and 'reset' should only work if the app is stopped - {error, _} = control_action(cluster, []), - {error, _} = control_action(reset, []), - {error, _} = control_action(force_reset, []), - - ok = control_action(stop_app, []), - - %% various ways of creating a standalone node - NodeS = atom_to_list(node()), - ClusteringSequence = [[], - [NodeS], - ["invalid@invalid", NodeS], - [NodeS, "invalid@invalid"]], - - ok = control_action(reset, []), - lists:foreach(fun (Arg) -> - ok = control_action(force_cluster, Arg), - ok - end, - ClusteringSequence), - lists:foreach(fun (Arg) -> - ok = control_action(reset, []), - ok = control_action(force_cluster, Arg), - ok - end, - ClusteringSequence), - ok = control_action(reset, []), - lists:foreach(fun (Arg) -> - ok = control_action(force_cluster, Arg), - ok = control_action(start_app, []), - ok = control_action(stop_app, []), - ok - end, - ClusteringSequence), - lists:foreach(fun (Arg) -> - ok = control_action(reset, []), - ok = control_action(force_cluster, Arg), - ok = control_action(start_app, []), - ok = control_action(stop_app, []), - ok - end, - ClusteringSequence), - - %% convert a disk node into a ram node - ok = control_action(reset, []), - ok = control_action(start_app, []), - ok = control_action(stop_app, []), - ok = assert_disc_node(), - ok = control_action(force_cluster, ["invalid1@invalid", - "invalid2@invalid"]), - ok = assert_ram_node(), - - %% join a non-existing cluster as a ram node - ok = control_action(reset, []), - ok = control_action(force_cluster, ["invalid1@invalid", - "invalid2@invalid"]), - ok = assert_ram_node(), - - ok = control_action(reset, []), - - SecondaryNode = rabbit_nodes:make("hare"), - case net_adm:ping(SecondaryNode) of - pong -> passed = test_cluster_management2(SecondaryNode); - pang -> io:format("Skipping clustering tests with node ~p~n", - [SecondaryNode]) - end, - - ok = control_action(start_app, []), - passed. - -test_cluster_management2(SecondaryNode) -> - NodeS = atom_to_list(node()), - SecondaryNodeS = atom_to_list(SecondaryNode), - - %% make a disk node - ok = control_action(cluster, [NodeS]), - ok = assert_disc_node(), - %% make a ram node - ok = control_action(reset, []), - ok = control_action(cluster, [SecondaryNodeS]), - ok = assert_ram_node(), - - %% join cluster as a ram node - ok = control_action(reset, []), - ok = control_action(force_cluster, [SecondaryNodeS, "invalid1@invalid"]), - ok = control_action(start_app, []), - ok = control_action(stop_app, []), - ok = assert_ram_node(), - - %% ram node will not start by itself - ok = control_action(stop_app, []), - ok = control_action(stop_app, SecondaryNode, [], []), - {error, _} = control_action(start_app, []), - ok = control_action(start_app, SecondaryNode, [], []), - ok = control_action(start_app, []), - ok = control_action(stop_app, []), - - %% change cluster config while remaining in same cluster - ok = control_action(force_cluster, ["invalid2@invalid", SecondaryNodeS]), - ok = control_action(start_app, []), - ok = control_action(stop_app, []), - - %% join non-existing cluster as a ram node - ok = control_action(force_cluster, ["invalid1@invalid", - "invalid2@invalid"]), - {error, _} = control_action(start_app, []), - ok = assert_ram_node(), - - %% join empty cluster as a ram node (converts to disc) - ok = control_action(cluster, []), - ok = control_action(start_app, []), - ok = control_action(stop_app, []), - ok = assert_disc_node(), - - %% make a new ram node - ok = control_action(reset, []), - ok = control_action(force_cluster, [SecondaryNodeS]), - ok = control_action(start_app, []), - ok = control_action(stop_app, []), - ok = assert_ram_node(), - - %% turn ram node into disk node - ok = control_action(cluster, [SecondaryNodeS, NodeS]), - ok = control_action(start_app, []), - ok = control_action(stop_app, []), - ok = assert_disc_node(), - - %% convert a disk node into a ram node - ok = assert_disc_node(), - ok = control_action(force_cluster, ["invalid1@invalid", - "invalid2@invalid"]), - ok = assert_ram_node(), - - %% make a new disk node - ok = control_action(force_reset, []), - ok = control_action(start_app, []), - ok = control_action(stop_app, []), - ok = assert_disc_node(), - - %% turn a disk node into a ram node - ok = control_action(reset, []), - ok = control_action(cluster, [SecondaryNodeS]), - ok = control_action(start_app, []), - ok = control_action(stop_app, []), - ok = assert_ram_node(), - - %% NB: this will log an inconsistent_database error, which is harmless - %% Turning cover on / off is OK even if we're not in general using cover, - %% it just turns the engine on / off, doesn't actually log anything. - cover:stop([SecondaryNode]), - true = disconnect_node(SecondaryNode), - pong = net_adm:ping(SecondaryNode), - cover:start([SecondaryNode]), - - %% leaving a cluster as a ram node - ok = control_action(reset, []), - %% ...and as a disk node - ok = control_action(cluster, [SecondaryNodeS, NodeS]), - ok = control_action(start_app, []), - ok = control_action(stop_app, []), - cover:stop(SecondaryNode), - ok = control_action(reset, []), - cover:start(SecondaryNode), - - %% attempt to leave cluster when no other node is alive - ok = control_action(cluster, [SecondaryNodeS, NodeS]), - ok = control_action(start_app, []), - ok = control_action(stop_app, SecondaryNode, [], []), - ok = control_action(stop_app, []), - {error, {no_running_cluster_nodes, _, _}} = - control_action(reset, []), - - %% attempt to change type when no other node is alive - {error, {no_running_cluster_nodes, _, _}} = - control_action(cluster, [SecondaryNodeS]), - - %% leave system clustered, with the secondary node as a ram node - ok = control_action(force_reset, []), - ok = control_action(start_app, []), - %% Yes, this is rather ugly. But since we're a clustered Mnesia - %% node and we're telling another clustered node to reset itself, - %% we will get disconnected half way through causing a - %% badrpc. This never happens in real life since rabbitmqctl is - %% not a clustered Mnesia node. - cover:stop(SecondaryNode), - {badrpc, nodedown} = control_action(force_reset, SecondaryNode, [], []), - pong = net_adm:ping(SecondaryNode), - cover:start(SecondaryNode), - ok = control_action(cluster, SecondaryNode, [NodeS], []), - ok = control_action(start_app, SecondaryNode, [], []), - - passed. - test_user_management() -> %% lots if stuff that should fail -- cgit v1.2.1 From 9c2c1ed3703860eb55836eac2ee3d28881b570b5 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Tue, 29 May 2012 11:48:03 +0100 Subject: set up cluster beforehand in tests --- src/rabbit_tests.erl | 46 +++++++++++++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 31706d46..001544f4 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -31,6 +31,7 @@ -define(CLEANUP_QUEUE_NAME, <<"cleanup-queue">>). all_tests() -> + ok = setup_cluster(), passed = gm_tests:all_tests(), passed = mirrored_supervisor_tests:all_tests(), application:set_env(rabbit, file_handles_high_watermark, 10, infinity), @@ -55,28 +56,47 @@ all_tests() -> passed = test_runtime_parameters(), passed = test_server_status(), passed = test_confirms(), - passed = maybe_run_cluster_dependent_tests(), + passed = + do_if_secondary_node( + fun run_cluster_dependent_tests/1, + fun (SecondaryNode) -> + io:format("Skipping cluster dependent tests with node ~p~n", + [SecondaryNode]), + passed + end), passed = test_configurable_server_properties(), passed. -maybe_run_cluster_dependent_tests() -> +do_if_secondary_node(Up, Down) -> SecondaryNode = rabbit_nodes:make("hare"), case net_adm:ping(SecondaryNode) of - pong -> passed = run_cluster_dependent_tests(SecondaryNode); - pang -> io:format("Skipping cluster dependent tests with node ~p~n", - [SecondaryNode]) - end, - passed. + pong -> Up(SecondaryNode); + pang -> Down(SecondaryNode) + end. -run_cluster_dependent_tests(SecondaryNode) -> - SecondaryNodeS = atom_to_list(SecondaryNode), +setup_cluster() -> + do_if_secondary_node( + fun (SecondaryNode) -> + ok = control_action(stop_app, []), + ok = control_action(join_cluster, + [atom_to_list(SecondaryNode)]), + ok = control_action(start_app, []), + ok = control_action(start_app, SecondaryNode, [], []) + end, + fun (_) -> ok end). - ok = control_action(stop_app, []), - ok = control_action(join_cluster, [SecondaryNodeS]), - ok = control_action(start_app, []), - ok = control_action(start_app, SecondaryNode, [], []), +maybe_run_cluster_dependent_tests() -> + do_if_secondary_node( + fun (SecondaryNode) -> + passed = run_cluster_dependent_tests(SecondaryNode) + end, + fun (SecondaryNode) -> + io:format("Skipping cluster dependent tests with node ~p~n", + [SecondaryNode]) + end). +run_cluster_dependent_tests(SecondaryNode) -> io:format("Running cluster dependent tests with node ~p~n", [SecondaryNode]), passed = test_delegates_async(SecondaryNode), passed = test_delegates_sync(SecondaryNode), -- cgit v1.2.1 From c1f805a18fddb59fc7d1bf798724d0a02285611a Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 29 May 2012 15:48:15 +0100 Subject: This can only be true or false. Plus cosmetic. --- src/rabbit_control_main.erl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index b85cf8c0..1bd89933 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -472,11 +472,11 @@ wait_for_startup(Node, Pid) -> while_process_is_alive(Node, Pid, Activity) -> case process_up(Pid) of - true -> case Activity() of - true -> ok; - _Other -> timer:sleep(?EXTERNAL_CHECK_INTERVAL), - while_process_is_alive(Node, Pid, Activity) - end; + true -> case Activity() of + true -> ok; + false -> timer:sleep(?EXTERNAL_CHECK_INTERVAL), + while_process_is_alive(Node, Pid, Activity) + end; false -> {error, process_not_running} end. -- cgit v1.2.1 From 03ad73e631b7d3b003cbfb2ec11a501259b3e74a Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 29 May 2012 15:53:17 +0100 Subject: Cosmetic. --- src/rabbit.erl | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 6895dcb6..5b212ba2 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -300,21 +300,21 @@ prepare() -> start() -> start_it(fun() -> - ok = prepare(), - ok = app_utils:start_applications(app_startup_order()), - ok = print_plugin_info(rabbit_plugins:active()) + ok = prepare(), + ok = app_utils:start_applications(app_startup_order()), + ok = print_plugin_info(rabbit_plugins:active()) end). boot() -> start_it(fun() -> - ok = prepare(), - Plugins = rabbit_plugins:setup(), - ToBeLoaded = Plugins ++ ?APPS, - ok = app_utils:load_applications(ToBeLoaded), - StartupApps = app_utils:app_dependency_order(ToBeLoaded, - false), - ok = app_utils:start_applications(StartupApps), - ok = print_plugin_info(Plugins) + ok = prepare(), + Plugins = rabbit_plugins:setup(), + ToBeLoaded = Plugins ++ ?APPS, + ok = app_utils:load_applications(ToBeLoaded), + StartupApps = app_utils:app_dependency_order(ToBeLoaded, + false), + ok = app_utils:start_applications(StartupApps), + ok = print_plugin_info(Plugins) end). start_it(StartFun) -> -- cgit v1.2.1 From f2962567856d53255025cb98e39d5632ccce109e Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 29 May 2012 15:57:35 +0100 Subject: Don't print the plugin list as output from "rabbitmqctl start_app". --- src/rabbit.erl | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 5b212ba2..f69c8d1b 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -644,10 +644,16 @@ force_event_refresh() -> print_plugin_info([]) -> ok; print_plugin_info(Plugins) -> - io:format("~n-- plugins running~n"), - [print_plugin_info(AppName, element(2, application:get_key(AppName, vsn))) - || AppName <- Plugins], - ok. + %% This gets invoked by rabbitmqctl start_app, outside the context + %% of the rabbit application + rabbit_misc:with_local_io( + fun() -> + io:format("~n-- plugins running~n"), + [print_plugin_info( + AppName, element(2, application:get_key(AppName, vsn))) + || AppName <- Plugins], + ok + end). print_plugin_info(Plugin, Vsn) -> Len = 76 - length(Vsn), -- cgit v1.2.1 From 18c5645a6a19cc49b93f1b74a5bf5c71b0c763ff Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 29 May 2012 18:23:47 +0100 Subject: Those backticks are... not quite necessary. And led to endless confusion with node.js. --- scripts/rabbitmq-server | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/scripts/rabbitmq-server b/scripts/rabbitmq-server index 81a5e572..34915b3d 100755 --- a/scripts/rabbitmq-server +++ b/scripts/rabbitmq-server @@ -71,13 +71,12 @@ case "$(uname -s)" in esac RABBITMQ_EBIN_ROOT="${RABBITMQ_HOME}/ebin" -if ! `erl \ - -pa "$RABBITMQ_EBIN_ROOT" \ +if ! erl -pa "$RABBITMQ_EBIN_ROOT" \ -noinput \ -hidden \ -s rabbit_prelaunch \ -sname rabbitmqprelaunch$$ \ - -extra "${RABBITMQ_NODENAME}"`; + -extra "${RABBITMQ_NODENAME}"; then exit 1; fi -- cgit v1.2.1 From 35c694af82da96770869cd05026cb99c23bdf8ea Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 30 May 2012 10:53:27 +0100 Subject: Oops, bug 24792 failed to take into account that HiPE compilation was inserted into the boot script by rabbit_prelaunch. So fix that. We need to hipe after ensuring the rabbit app is loaded, so we might as well inline rabbit:prepare/0 now. And we shouldn't really try mnesia upgrades when just starting the app; that feels dubious. Finally, strip out now-unused exports. --- src/rabbit.erl | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index f69c8d1b..b7ba7144 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -18,7 +18,7 @@ -behaviour(application). --export([maybe_hipe_compile/0, prepare/0, start/0, boot/0, stop/0, +-export([start/0, boot/0, stop/0, stop_and_halt/0, await_startup/0, status/0, is_running/0, is_running/1, environment/0, rotate_logs/1, force_event_refresh/0]). @@ -216,8 +216,6 @@ -type(log_location() :: 'tty' | 'undefined' | file:filename()). -type(param() :: atom()). --spec(maybe_hipe_compile/0 :: () -> 'ok'). --spec(prepare/0 :: () -> 'ok'). -spec(start/0 :: () -> 'ok'). -spec(boot/0 :: () -> 'ok'). -spec(stop/0 :: () -> 'ok'). @@ -287,27 +285,31 @@ split(L, N) -> split0(L, [[] || _ <- lists:seq(1, N)]). split0([], Ls) -> Ls; split0([I | Is], [L | Ls]) -> split0(Is, Ls ++ [[I | L]]). -prepare() -> - %% this ends up looking at the rabbit app's env, so it - %% needs to be loaded, but during the tests, it may end up - %% getting loaded twice, so guard against that +ensure_application_loaded() -> + %% We end up looking at the rabbit app's env for HiPE and log + %% handling, so it needs to be loaded. But during the tests, it + %% may end up getting loaded twice, so guard against that. case application:load(rabbit) of ok -> ok; {error, {already_loaded, rabbit}} -> ok - end, - ok = ensure_working_log_handlers(), - ok = rabbit_upgrade:maybe_upgrade_mnesia(). + end. start() -> start_it(fun() -> - ok = prepare(), + %% We do not want to HiPE compile or upgrade + %% mnesia after just restarting the app + ok = ensure_application_loaded(), + ok = ensure_working_log_handlers(), ok = app_utils:start_applications(app_startup_order()), ok = print_plugin_info(rabbit_plugins:active()) end). boot() -> start_it(fun() -> - ok = prepare(), + ok = ensure_application_loaded(), + maybe_hipe_compile(), + ok = ensure_working_log_handlers(), + ok = rabbit_upgrade:maybe_upgrade_mnesia(), Plugins = rabbit_plugins:setup(), ToBeLoaded = Plugins ++ ?APPS, ok = app_utils:load_applications(ToBeLoaded), -- cgit v1.2.1 From d500ab674797f49fcd48e12ea7978237f228b176 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 31 May 2012 12:34:01 +0100 Subject: Add the "safe" SSL-related modules [ssl_connection, ssl_record, gen_fsm, ssl] to the list. --- src/rabbit.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index b7ba7144..109cf4e6 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -199,7 +199,8 @@ rabbit_queue_index, gen, dict, ordsets, file_handle_cache, rabbit_msg_store, array, rabbit_msg_store_ets_index, rabbit_msg_file, rabbit_exchange_type_fanout, rabbit_exchange_type_topic, mnesia, - mnesia_lib, rpc, mnesia_tm, qlc, sofs, proplists, credit_flow, pmon]). + mnesia_lib, rpc, mnesia_tm, qlc, sofs, proplists, credit_flow, pmon, + ssl_connection, ssl_record, gen_fsm, ssl]). %% HiPE compilation uses multiple cores anyway, but some bits are %% IO-bound so we can go faster if we parallelise a bit more. In -- cgit v1.2.1 From ecdacaeb56ff75e07d54ceedc8e2939db51d03db Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Thu, 31 May 2012 12:57:29 +0100 Subject: fix and improve `rabbit_mnesia:remove_node/1' in many ways --- src/rabbit_mnesia.erl | 150 +++++++++++++++++++++++++++++++------------------- 1 file changed, 94 insertions(+), 56 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index a83ddd72..f6caa9b3 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -28,8 +28,9 @@ -export([table_names/0]). -%% Used internally in rpc calls, see `discover_nodes/1' --export([cluster_status_if_running/0, node_info/0]). +%% Used internally in rpc calls +-export([cluster_status_if_running/0, node_info/0, + remove_node_if_mnesia_running/1]). %% create_tables/0 exported for helping embed RabbitMQ in or alongside %% other mnesia-using Erlang applications, such as ejabberd @@ -134,7 +135,7 @@ prepare() -> init() -> ensure_mnesia_running(), ensure_mnesia_dir(), - {AllNodes, _, DiscNodes} = read_cluster_nodes_status(), + {AllNodes, DiscNodes, _} = read_cluster_nodes_status(), WantDiscNode = should_be_disc_node(DiscNodes), ok = init_db(AllNodes, WantDiscNode, WantDiscNode), %% We intuitively expect the global name server to be synced when @@ -217,33 +218,30 @@ reset(Force) -> false -> ok end, Node = node(), - Nodes = all_clustered_nodes(), case Force of true -> ok; false -> ensure_mnesia_dir(), start_mnesia(), - %% Reconnecting so that we will get an up to date RunningNodes - RunningNodes = - try - %% Force=true here so that reset still works when clustered - %% with a node which is down - {_, DiscNodes, _} = read_cluster_nodes_status(), - ok = init_db(DiscNodes, should_be_disc_node(DiscNodes), - true), - running_clustered_nodes() - after - stop_mnesia() - end, - leave_cluster(Nodes, RunningNodes), + %% Reconnecting so that we will get an up to date nodes + try + {_, DiscNodes, _} = read_cluster_nodes_status(), + %% Force=true here so that reset still works when + %% clustered with a node which is down + ok = init_db(DiscNodes, should_be_disc_node(DiscNodes), + true) + after + stop_mnesia() + end, + leave_cluster(), rabbit_misc:ensure_ok(mnesia:delete_schema([Node]), cannot_delete_schema) end, %% We need to make sure that we don't end up in a distributed Erlang system %% with nodes while not being in an Mnesia cluster with them. We don't %% handle that well. - [erlang:disconnect_node(N) || N <- Nodes], + [erlang:disconnect_node(N) || N <- all_clustered_nodes()], %% remove persisted messages and any other garbage we find ok = rabbit_file:recursive_delete(filelib:wildcard(dir() ++ "/*")), ok = write_cluster_nodes_status(initial_cluster_status()), @@ -258,7 +256,6 @@ change_node_type(Type) -> true -> ok end, - check_cluster_consistency(), DiscoveryNodes = all_clustered_nodes(), ClusterNodes = case discover_cluster(DiscoveryNodes) of @@ -306,21 +303,40 @@ recluster(DiscoveryNode) -> ok. +%% We proceed like this: try to remove the node locally. If mnesia is offline +%% then we try to remove it remotely on some other node. If there are no other +%% nodes running, then *if the current node is a disk node* we force-load mnesia +%% and remove the node. remove_node(Node) -> - case mnesia:system_info(is_running) of - yes -> {atomic, ok} = mnesia:del_table_copy(schema, Node), - update_cluster_nodes_status(), - {_, []} = rpc:multicall(running_clustered_nodes(), rabbit_mnesia, - update_cluster_nodes_status, []); - no -> start_mnesia(), - try - [mnesia:force_load_table(T) || T <- rabbit_mnesia:table_names()], - remove_node(Node) - after - stop_mnesia() - end - end, - ok. + case remove_node_if_mnesia_running(Node) of + ok -> + ok; + {error, mnesia_not_running} -> + case remove_node_remotely(Node) of + ok -> + ok; + {error, no_running_cluster_nodes} -> + case is_disc_node() of + false -> + throw({error, + {removing_node_from_ram_node, + "There are no nodes running and this is a " + "RAM node"}}); + true -> + start_mnesia(), + try + [mnesia:force_load_table(T) || + T <- rabbit_mnesia:table_names()], + remove_node(Node), + ensure_mnesia_running() + after + stop_mnesia() + end + end + end; + {error, Reason} -> + throw({error, Reason}) + end. %%---------------------------------------------------------------------------- %% Queries @@ -699,11 +715,11 @@ on_node_up(Node) -> end. on_node_down(Node) -> - update_cluster_nodes_status(), case is_only_disc_node(Node) of true -> rabbit_log:info("only running disc node went down~n"); false -> ok - end. + end, + update_cluster_nodes_status(). %%-------------------------------------------------------------------- %% Internal helpers @@ -995,31 +1011,52 @@ create_local_table_copy(Tab, Type) -> end, ok. -leave_cluster([], _) -> ok; -leave_cluster(Nodes, RunningNodes0) -> - case RunningNodes0 -- [node()] of - [] -> ok; +remove_node_if_mnesia_running(Node) -> + case mnesia:system_info(is_running) of + yes -> %% Deleting the the schema copy of the node will result in the + %% node being removed from the cluster, with that change being + %% propagated to all nodes + case mnesia:del_table_copy(schema, Node) of + {atomic, ok} -> + update_cluster_nodes_status(), + {_, []} = rpc:multicall(running_clustered_nodes(), + rabbit_mnesia, + update_cluster_nodes_status, []), + ok; + {aborted, Reason} -> + {error, {failed_to_remove_node, Node, Reason}} + end; + no -> {error, mnesia_not_running} + end. + +leave_cluster() -> + remove_node_remotely(node()). + +remove_node_remotely(Removee) -> + case running_clustered_nodes() -- [Removee] of + [] -> + ok; RunningNodes -> - %% find at least one running cluster node and instruct it to remove - %% our schema copy which will in turn result in our node being - %% removed as a cluster node from the schema, with that change being - %% propagated to all nodes case lists:any( fun (Node) -> - case rpc:call(Node, mnesia, del_table_copy, - [schema, node()]) of - {atomic, ok} -> true; - {badrpc, nodedown} -> false; - {aborted, {node_not_running, _}} -> false; - {aborted, Reason} -> - throw({error, {failed_to_leave_cluster, - Nodes, RunningNodes, Reason}}) + case rpc:call(Node, rabbit_mnesia, + remove_node_if_mnesia_running, + [Removee]) + of + ok -> + true; + {error, mnesia_not_running} -> + false; + {error, Reason} -> + throw({error, Reason}); + {badrpc, nodedown} -> + false end end, - RunningNodes) of - true -> ok; - false -> throw({error, {no_running_cluster_nodes, - Nodes, RunningNodes}}) + RunningNodes) + of + true -> ok; + false -> {error, no_running_cluster_nodes} end end. @@ -1032,6 +1069,7 @@ is_only_disc_node(Node) -> [Node] =:= Nodes. start_mnesia() -> + check_cluster_consistency(), rabbit_misc:ensure_ok(mnesia:start(), cannot_start_mnesia), ensure_mnesia_running(). @@ -1040,7 +1078,7 @@ stop_mnesia() -> ensure_mnesia_not_running(). start_and_init_db(ClusterNodes, WantDiscNode, Force) -> - mnesia:start(), + start_mnesia(), try ok = init_db(ClusterNodes, WantDiscNode, Force) after -- cgit v1.2.1 From 7fe7c01fcafb8db6b7212351c9a845eab418e53e Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Thu, 31 May 2012 15:37:20 +0100 Subject: tidy exports and specs --- src/rabbit.erl | 1 - src/rabbit_mnesia.erl | 65 +++++++++++++++++++++++++++++++++------------------ 2 files changed, 42 insertions(+), 24 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 48df512a..a9af7335 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -409,7 +409,6 @@ start(normal, []) -> end. stop(_State) -> - ok = rabbit_mnesia:update_cluster_nodes_status(), terminated_ok = error_logger:delete_report_handler(rabbit_error_logger), ok = rabbit_alarm:stop(), ok = case rabbit_mnesia:is_clustered() of diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index f6caa9b3..c43a0ed4 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -17,20 +17,40 @@ -module(rabbit_mnesia). --export([ensure_mnesia_dir/0, dir/0, status/0, init/0, is_db_empty/0, - join_cluster/2, check_cluster_consistency/0, reset/0, force_reset/0, - init_db/4, is_clustered/0, running_clustered_nodes/0, - all_clustered_nodes/0, all_clustered_disc_nodes/0, - empty_ram_only_tables/0, copy_db/1, wait_for_tables/1, is_disc_node/0, - on_node_down/1, on_node_up/1, should_be_disc_node/1, - change_node_type/1, recluster/1, remove_node/1, prepare/0, - update_cluster_nodes_status/0]). - --export([table_names/0]). +-export([prepare/0, + init/0, + join_cluster/2, + reset/0, + force_reset/0, + recluster/1, + change_node_type/1, + remove_node/1, + + status/0, + is_db_empty/0, + is_clustered/0, + all_clustered_nodes/0, + all_clustered_disc_nodes/0, + running_clustered_nodes/0, + is_disc_node/0, + dir/0, + table_names/0, + wait_for_tables/1, + + init_db/4, + empty_ram_only_tables/0, + copy_db/1, + wait_for_tables/0, + + on_node_up/1, + on_node_down/1 + ]). %% Used internally in rpc calls --export([cluster_status_if_running/0, node_info/0, - remove_node_if_mnesia_running/1]). +-export([cluster_status_if_running/0, + node_info/0, + remove_node_if_mnesia_running/1 + ]). %% create_tables/0 exported for helping embed RabbitMQ in or alongside %% other mnesia-using Erlang applications, such as ejabberd @@ -42,7 +62,7 @@ -ifdef(use_specs). --export_type([node_type/0, node_status/0]). +-export_type([node_type/0]). -type(node_type() :: disc | ram). -type(node_status() :: {[node()], [node()], [node()]}). @@ -62,32 +82,31 @@ {'running_nodes', [node()]}]). -spec(is_db_empty/0 :: () -> boolean()). -spec(is_clustered/0 :: () -> boolean()). --spec(running_clustered_nodes/0 :: () -> [node()]). -spec(all_clustered_nodes/0 :: () -> [node()]). +-spec(all_clustered_disc_nodes/0 :: () -> [node()]). +-spec(running_clustered_nodes/0 :: () -> [node()]). -spec(is_disc_node/0 :: () -> boolean()). -spec(dir/0 :: () -> file:filename()). -spec(table_names/0 :: () -> [atom()]). --spec(cluster_status_if_running/0 :: () -> 'error' | node_status()). %% Operations on the db and utils, mainly used in `rabbit_upgrade' and `rabbit' -spec(init_db/4 :: ([node()], boolean(), boolean(), boolean()) -> 'ok'). --spec(ensure_mnesia_dir/0 :: () -> 'ok'). -spec(empty_ram_only_tables/0 :: () -> 'ok'). -spec(create_tables/0 :: () -> 'ok'). -spec(copy_db/1 :: (file:filename()) -> rabbit_types:ok_or_error(any())). -spec(wait_for_tables/1 :: ([atom()]) -> 'ok'). --spec(should_be_disc_node/1 :: ([node()]) -> boolean()). --spec(check_cluster_consistency/0 :: () -> 'ok' | no_return()). - -%% Functions to handle the cluster status file --spec(write_cluster_nodes_status/1 :: (node_status()) -> 'ok'). --spec(read_cluster_nodes_status/0 :: () -> node_status()). --spec(update_cluster_nodes_status/0 :: () -> 'ok'). %% Hooks used in `rabbit_node_monitor' -spec(on_node_up/1 :: (node()) -> 'ok'). -spec(on_node_down/1 :: (node()) -> 'ok'). +%% Functions used in internal rpc calls +-spec(cluster_status_if_running/0 :: () -> {'ok', node_status()} | 'error'). +-spec(node_info/0 :: () -> {string(), string(), + ({'ok', node_status()} | 'error')}). +-spec(remove_node_if_mnesia_running/1 :: (node()) -> 'ok' | + {'error', term()}). + -endif. %%---------------------------------------------------------------------------- -- cgit v1.2.1 From 150b908aab7b09843674bc7a6e10eb9a19ea6362 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Thu, 31 May 2012 17:54:11 +0100 Subject: print a different error atom when heartbeat timeout's occur --- src/rabbit_reader.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index b773f83b..707e576f 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -230,6 +230,7 @@ start_connection(Parent, ChannelSupSupPid, Collector, StartHeartbeatFun, Deb, catch Ex -> log(case Ex of connection_closed_abruptly -> warning; + {timeout, _CState} -> amqp_heartbeat_timeout; _ -> error end, "closing AMQP connection ~p (~s):~n~p~n", [self(), ConnStr, Ex]) -- cgit v1.2.1 From f7417110b04ca192702ab8d1512fcb6b320af689 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Fri, 1 Jun 2012 15:01:34 +0100 Subject: first attempt at refactoring `rabbit_mnesia:init_db' The only potentially dangerous thing is that I always upgrade locally, while before that was only done when we know that we need an upgrade. --- src/rabbit_mnesia.erl | 156 ++++++++++++++++++++----------------------------- src/rabbit_upgrade.erl | 2 +- 2 files changed, 64 insertions(+), 94 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index c43a0ed4..5e7907af 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -37,7 +37,7 @@ table_names/0, wait_for_tables/1, - init_db/4, + init_db/3, empty_ram_only_tables/0, copy_db/1, wait_for_tables/0, @@ -90,7 +90,7 @@ -spec(table_names/0 :: () -> [atom()]). %% Operations on the db and utils, mainly used in `rabbit_upgrade' and `rabbit' --spec(init_db/4 :: ([node()], boolean(), boolean(), boolean()) -> 'ok'). +-spec(init_db/3 :: ([node()], boolean(), boolean()) -> 'ok'). -spec(empty_ram_only_tables/0 :: () -> 'ok'). -spec(create_tables/0 :: () -> 'ok'). -spec(copy_db/1 :: (file:filename()) -> rabbit_types:ok_or_error(any())). @@ -154,9 +154,7 @@ prepare() -> init() -> ensure_mnesia_running(), ensure_mnesia_dir(), - {AllNodes, DiscNodes, _} = read_cluster_nodes_status(), - WantDiscNode = should_be_disc_node(DiscNodes), - ok = init_db(AllNodes, WantDiscNode, WantDiscNode), + ok = reinit_db(should_be_disc_node(all_clustered_disc_nodes())), %% We intuitively expect the global name server to be synced when %% Mnesia is up. In fact that's not guaranteed to be the case - let's %% make it so. @@ -211,7 +209,7 @@ join_cluster(DiscoveryNode, WantDiscNode) -> rabbit_misc:local_info_msg("Clustering with ~p~s~n", [ClusterNodes]), %% Join the cluster - ok = start_and_init_db(DiscNodes, WantDiscNode, false), + ok = init_db_and_upgrade(DiscNodes, WantDiscNode, false), ok. @@ -241,15 +239,11 @@ reset(Force) -> true -> ok; false -> - ensure_mnesia_dir(), - start_mnesia(), %% Reconnecting so that we will get an up to date nodes try - {_, DiscNodes, _} = read_cluster_nodes_status(), %% Force=true here so that reset still works when %% clustered with a node which is down - ok = init_db(DiscNodes, should_be_disc_node(DiscNodes), - true) + ok = reinit_db(true) after stop_mnesia() end, @@ -294,7 +288,7 @@ change_node_type(Type) -> disc -> true end, - ok = start_and_init_db(ClusterNodes, WantDiscNode, false), + ok = init_db_and_upgrade(ClusterNodes, WantDiscNode, false), ok. @@ -313,7 +307,7 @@ recluster(DiscoveryNode) -> end, case lists:member(node(), ClusterNodes) of - true -> start_and_init_db(ClusterNodes, is_disc_node(), false); + true -> init_db_and_upgrade(ClusterNodes, is_disc_node(), false); false -> throw({error, {inconsistent_cluster, "The nodes provided do not have this node as part of " @@ -432,43 +426,34 @@ table_names() -> %% Operations on the db %%---------------------------------------------------------------------------- +%% Starts mnesia if necessary, adds the provided nodes to the mnesia cluster, +%% creating a new schema if there is the need to and catching up if there are +%% other nodes in the cluster already. It also updates the cluster status file. init_db(ClusterNodes, WantDiscNode, Force) -> - init_db(ClusterNodes, WantDiscNode, Force, true). - -%% Take a cluster node config and create the right kind of node - a -%% standalone disk node, or disk or ram node connected to the -%% specified cluster nodes. If Force is false, don't allow -%% connections to offline nodes. -init_db(ClusterNodes, WantDiscNode, Force, Upgrade) -> - UClusterNodes = lists:usort(ClusterNodes), - ProperClusterNodes = UClusterNodes -- [node()], - case mnesia:change_config(extra_db_nodes, ProperClusterNodes) of - {ok, []} when not Force andalso ProperClusterNodes =/= [] -> - throw({error, {failed_to_cluster_with, ProperClusterNodes, - "Mnesia could not connect to any disc nodes."}}); + case mnesia:system_info(is_running) of + yes -> ok; + no -> start_mnesia() + end, + case change_extra_db_nodes(ClusterNodes, Force) of + {error, Reason} -> + throw({error, Reason}); {ok, Nodes} -> WasDiscNode = is_disc_node(), - %% We create a new db (on disk, or in ram) in the first - %% two cases and attempt to upgrade the in the other two case {Nodes, WasDiscNode, WantDiscNode} of {[], _, false} -> - %% New ram node; start from scratch - ok = create_schema(ram); + %% Standalone ram node, we don't want that + throw({error, cannot_create_standalone_ram_node}); {[], false, true} -> - %% Nothing there at all, start from scratch - ok = create_schema(disc); + %% RAM -> disc, starting from scratch + ok = create_schema(); {[], true, true} -> - %% We're the first node up - case rabbit_upgrade:maybe_upgrade_local() of - ok -> ensure_schema_integrity(); - version_not_available -> ok = schema_ok_or_move() - end; - {[AnotherNode|_], _, _} -> + %% First disc node up + ok; + {[AnotherNode | _], _, _} -> %% Subsequent node in cluster, catch up ensure_version_ok( rpc:call(AnotherNode, rabbit_version, recorded, [])), ok = wait_for_replicated_tables(), - %% The sequence in which we delete the schema and then the %% other tables is important: if we delete the schema first %% when moving to RAM mnesia will loudly complain since it @@ -479,44 +464,33 @@ init_db(ClusterNodes, WantDiscNode, Force, Upgrade) -> create_local_table_copies(disc); false -> create_local_table_copies(ram), create_local_table_copy(schema, ram_copies) - end, - - %% Write the status now that mnesia is running and clustered - update_cluster_nodes_status(), + end + end, + ensure_schema_integrity(), + update_cluster_nodes_status(), + ok + end. - ok = case Upgrade of - true -> - case rabbit_upgrade:maybe_upgrade_local() of - ok -> - ok; - %% If we're just starting up a new node we - %% won't have a version - starting_from_scratch -> - rabbit_version:record_desired() - end; - false -> - ok - end, +init_db_and_upgrade(ClusterNodes, WantDiscNode, Force) -> + ok = init_db(ClusterNodes, WantDiscNode, Force), + ok = case rabbit_upgrade:maybe_upgrade_local() of + ok -> ok; + starting_from_scratch -> rabbit_version:record_desired(); + version_not_available -> schema_ok_or_move() + end, + %% `maybe_upgrade_local' restarts mnesia, so ram nodes will forget about the + %% cluster + case WantDiscNode of + false -> start_mnesia(), + {ok, _} = change_extra_db_nodes(ClusterNodes, true), + wait_for_replicated_tables(); + true -> ok + end, + ok. - %% We've taken down mnesia, so ram nodes will need - %% to re-sync - case is_disc_node() of - false -> start_mnesia(), - mnesia:change_config(extra_db_nodes, - ProperClusterNodes), - wait_for_replicated_tables(); - true -> ok - end, - - ensure_schema_integrity(), - ok - end; - {error, Reason} -> - %% one reason we may end up here is if we try to join - %% nodes together that are currently running standalone or - %% are members of a different cluster - throw({error, {unable_to_join_cluster, ClusterNodes, Reason}}) - end. +reinit_db(Force) -> + {AllNodes, DiscNodes, _} = read_cluster_nodes_status(), + init_db_and_upgrade(AllNodes, should_be_disc_node(DiscNodes), Force). ensure_mnesia_dir() -> MnesiaDir = dir() ++ "/", @@ -935,7 +909,7 @@ schema_ok_or_move() -> "and recreating schema from scratch~n", [Reason]), ok = move_db(), - ok = create_schema(disc) + ok = create_schema() end. ensure_version_ok({ok, DiscVersion}) -> @@ -947,17 +921,12 @@ ensure_version_ok({ok, DiscVersion}) -> ensure_version_ok({error, _}) -> ok = rabbit_version:record_desired(). -create_schema(Type) -> +%% We only care about disc nodes since ram nodes are supposed to catch up only +create_schema() -> stop_mnesia(), - case Type of - disc -> rabbit_misc:ensure_ok(mnesia:create_schema([node()]), - cannot_create_schema); - ram -> %% remove the disc schema since this is a ram node - rabbit_misc:ensure_ok(mnesia:delete_schema([node()]), - cannot_delete_schema) - end, + rabbit_misc:ensure_ok(mnesia:create_schema([node()]), cannot_create_schema), start_mnesia(), - ok = create_tables(Type), + ok = create_tables(disc), ensure_schema_integrity(), ok = rabbit_version:record_desired(). @@ -1096,14 +1065,15 @@ stop_mnesia() -> stopped = mnesia:stop(), ensure_mnesia_not_running(). -start_and_init_db(ClusterNodes, WantDiscNode, Force) -> - start_mnesia(), - try - ok = init_db(ClusterNodes, WantDiscNode, Force) - after - mnesia:stop() - end, - ok. +change_extra_db_nodes(ClusterNodes0, Force) -> + ClusterNodes = lists:usort(ClusterNodes0) -- [node()], + case mnesia:change_config(extra_db_nodes, ClusterNodes) of + {ok, []} when not Force andalso ClusterNodes =/= [] -> + {error, {failed_to_cluster_with, ClusterNodes, + "Mnesia could not connect to any disc nodes."}}; + {ok, Nodes} -> + {ok, Nodes} + end. %%-------------------------------------------------------------------- %% Legacy functions related to the "running nodes" file diff --git a/src/rabbit_upgrade.erl b/src/rabbit_upgrade.erl index 85bcff25..c43ce236 100644 --- a/src/rabbit_upgrade.erl +++ b/src/rabbit_upgrade.erl @@ -218,7 +218,7 @@ secondary_upgrade(AllNodes) -> rabbit_misc:ensure_ok(mnesia:delete_schema([node()]), cannot_delete_schema), rabbit_misc:ensure_ok(mnesia:start(), cannot_start_mnesia), - ok = rabbit_mnesia:init_db(AllNodes, IsDiscNode, true, false), + ok = rabbit_mnesia:init_db(AllNodes, IsDiscNode, true), ok = rabbit_version:record_desired_for_scope(mnesia), ok. -- cgit v1.2.1 From 8a54a2828dabb78746463869650726cdbd7135da Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 1 Jun 2012 15:12:26 +0100 Subject: Look better when running with input enabled. --- src/rabbit.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index b7ba7144..73c39cf1 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -263,7 +263,7 @@ maybe_hipe_compile() -> hipe_compile() -> Count = length(?HIPE_WORTHY), - io:format("HiPE compiling: |~s|~n |", + io:format("~nHiPE compiling: |~s|~n |", [string:copies("-", Count)]), T1 = erlang:now(), PidMRefs = [spawn_monitor(fun () -> [begin -- cgit v1.2.1 From 017409e8b0a9e2b3291113e9fd2509426c2fda56 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Fri, 1 Jun 2012 15:51:25 +0100 Subject: stop mnesia after cluster operation --- src/rabbit_mnesia.erl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 5e7907af..7aac7f3f 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -210,6 +210,7 @@ join_cluster(DiscoveryNode, WantDiscNode) -> %% Join the cluster ok = init_db_and_upgrade(DiscNodes, WantDiscNode, false), + stop_mnesia(), ok. @@ -289,6 +290,7 @@ change_node_type(Type) -> end, ok = init_db_and_upgrade(ClusterNodes, WantDiscNode, false), + stop_mnesia(), ok. @@ -313,6 +315,7 @@ recluster(DiscoveryNode) -> "The nodes provided do not have this node as part of " "the cluster"}}) end, + stop_mnesia(), ok. @@ -1007,6 +1010,7 @@ remove_node_if_mnesia_running(Node) -> case mnesia:del_table_copy(schema, Node) of {atomic, ok} -> update_cluster_nodes_status(), + io:format("nodes: ~p~n", [running_clustered_disc_nodes()]), {_, []} = rpc:multicall(running_clustered_nodes(), rabbit_mnesia, update_cluster_nodes_status, []), -- cgit v1.2.1 From aa71048b52a74810b3bc95ca1fd94b49833a6181 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Fri, 1 Jun 2012 16:03:12 +0100 Subject: don't use re:replace/3 as it's unavailable on R12 --- src/rabbit_control_main.erl | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index f8b8c345..29408006 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -490,8 +490,12 @@ wait_for_process_death(Pid) -> read_pid_file(PidFile, Wait) -> case {file:read_file(PidFile), Wait} of {{ok, Bin}, _} -> - S = re:replace(Bin, "\\s", "", [global, {return, list}]), - try list_to_integer(S) + S = binary_to_list(Bin), + Spid = case string:words(S) > 1 of + true -> string:sub_word(S, 1); + false -> string:strip(binary_to_list(Bin), right, $\n) + end, + try list_to_integer(Spid) catch error:badarg -> exit({error, {garbage_in_pid_file, PidFile}}) end, -- cgit v1.2.1 From 34f7ad510a3caa1bd1362166e40e5da05a849ca0 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Fri, 1 Jun 2012 17:01:23 +0100 Subject: Emit upstart events when broker stops/starts --- packaging/debs/Debian/debian/rabbitmq-server.init | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/packaging/debs/Debian/debian/rabbitmq-server.init b/packaging/debs/Debian/debian/rabbitmq-server.init index f514b974..4bc32515 100644 --- a/packaging/debs/Debian/debian/rabbitmq-server.init +++ b/packaging/debs/Debian/debian/rabbitmq-server.init @@ -137,24 +137,28 @@ restart_end() { start_stop_end() { case "$RETVAL" in 0) - log_end_msg 0;; + [ -x /sbin/initctl ] && /sbin/initctl emit --no-wait "${NAME}-${1}" + log_end_msg 0 + ;; 3) log_warning_msg "${DESC} already ${1}" - log_end_msg 0;; + log_end_msg 0 + ;; *) log_warning_msg "FAILED - check ${INIT_LOG_DIR}/startup_\{log, _err\}" - log_end_msg 1;; + log_end_msg 1 + ;; esac } case "$1" in start) - log_daemon_msg "Starting ${DESC}" $NAME + log_daemon_msg "Starting ${DESC}:" $NAME start_rabbitmq - start_stop_end "started" + start_stop_end "running" ;; stop) - log_daemon_msg "Stopping ${DESC}" $NAME + log_daemon_msg "Stopping ${DESC}:" $NAME stop_rabbitmq start_stop_end "stopped" ;; @@ -162,17 +166,17 @@ case "$1" in status_rabbitmq ;; rotate-logs) - log_action_begin_msg "Rotating log files for ${DESC} ${NAME}" + log_action_begin_msg "Rotating log files for ${DESC}: ${NAME}" rotate_logs_rabbitmq log_action_end_msg $RETVAL ;; force-reload|reload|restart) - log_daemon_msg "Restarting ${DESC}" $NAME + log_daemon_msg "Restarting ${DESC}:" $NAME restart_rabbitmq restart_end ;; try-restart) - log_daemon_msg "Restarting ${DESC}" $NAME + log_daemon_msg "Restarting ${DESC}:" $NAME restart_running_rabbitmq restart_end ;; -- cgit v1.2.1 From e840d34c58ff1e19f2d558bd53b155de25408e52 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 5 Jun 2012 11:54:42 +0100 Subject: appease CI gods --- src/rabbit_tests.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 2760ef2d..c347e85c 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -1821,7 +1821,7 @@ on_disk_capture(OnDisk, Awaiting, Pid) -> Pid); stop -> done - after (case Awaiting of [] -> 200; _ -> 1000 end) -> + after (case Awaiting of [] -> 200; _ -> 5000 end) -> case Awaiting of [] -> Pid ! {self(), arrived}, on_disk_capture(); _ -> Pid ! {self(), timeout} -- cgit v1.2.1 From 938ccdeb9ccfa348e3fab09d7beecd39c88d9c58 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Tue, 5 Jun 2012 12:45:44 +0100 Subject: capture and swallow all stdio during 'rabbitmqctl wait' --- src/rabbit_control_main.erl | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index 29408006..bce20e88 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -511,8 +511,7 @@ read_pid_file(PidFile, Wait) -> % rpc:call(os, getpid, []) at this point process_up(Pid) -> with_os([{unix, fun () -> - system("ps -p " ++ Pid - ++ " >/dev/null 2>&1") =:= 0 + system("ps -p " ++ Pid) =:= 0 end}, {win32, fun () -> Res = os:cmd("tasklist /nh /fi \"pid eq " ++ @@ -532,13 +531,16 @@ with_os(Handlers) -> % Like system(3) system(Cmd) -> - ShCmd = "sh -c '" ++ escape_quotes(Cmd) ++ "'", - Port = erlang:open_port({spawn, ShCmd}, [exit_status,nouse_stdio]), - receive {Port, {exit_status, Status}} -> Status end. - -% Escape the quotes in a shell command so that it can be used in "sh -c 'cmd'" -escape_quotes(Cmd) -> - lists:flatten(lists:map(fun ($') -> "'\\''"; (Ch) -> Ch end, Cmd)). + Port = erlang:open_port({spawn, Cmd}, + [exit_status,{line, 16384}, + use_stdio, stderr_to_stdout]), + exit_loop(Port). + +exit_loop(Port) -> + receive + {Port, {exit_status, Rc}} -> Rc; + {Port, _} -> exit_loop(Port) + end. format_parse_error({_Line, Mod, Err}) -> lists:flatten(Mod:format_error(Err)). -- cgit v1.2.1 From fc936b5214db03bc64f65e69b8f02e6bae96631a Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Tue, 5 Jun 2012 12:55:24 +0100 Subject: return the correct value from rabbit_control_main:read_pid_file/2; don't call binary_to_list twice --- src/rabbit_control_main.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index bce20e88..3ca3d24d 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -493,13 +493,13 @@ read_pid_file(PidFile, Wait) -> S = binary_to_list(Bin), Spid = case string:words(S) > 1 of true -> string:sub_word(S, 1); - false -> string:strip(binary_to_list(Bin), right, $\n) + false -> string:strip(S, right, $\n) end, try list_to_integer(Spid) catch error:badarg -> exit({error, {garbage_in_pid_file, PidFile}}) end, - S; + Spid; {{error, enoent}, true} -> timer:sleep(?EXTERNAL_CHECK_INTERVAL), read_pid_file(PidFile, Wait); -- cgit v1.2.1 From 85019edb003baf95cd6b8263d3ad1e5bcd123018 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Tue, 5 Jun 2012 13:36:28 +0100 Subject: cosmetic --- src/rabbit_control_main.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index 3ca3d24d..95dcb212 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -491,15 +491,15 @@ read_pid_file(PidFile, Wait) -> case {file:read_file(PidFile), Wait} of {{ok, Bin}, _} -> S = binary_to_list(Bin), - Spid = case string:words(S) > 1 of + PidS = case string:words(S) > 1 of true -> string:sub_word(S, 1); false -> string:strip(S, right, $\n) end, - try list_to_integer(Spid) + try list_to_integer(PidS) catch error:badarg -> exit({error, {garbage_in_pid_file, PidFile}}) end, - Spid; + PidS; {{error, enoent}, true} -> timer:sleep(?EXTERNAL_CHECK_INTERVAL), read_pid_file(PidFile, Wait); -- cgit v1.2.1 From 1e67f7c9053025200a230857463a75677e0a5075 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Tue, 5 Jun 2012 13:38:38 +0100 Subject: rename rabbit_control_main:system/1 to run_ps, which more accurately describes what it does --- src/rabbit_control_main.erl | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index 95dcb212..82bee785 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -511,7 +511,7 @@ read_pid_file(PidFile, Wait) -> % rpc:call(os, getpid, []) at this point process_up(Pid) -> with_os([{unix, fun () -> - system("ps -p " ++ Pid) =:= 0 + run_ps(Pid) =:= 0 end}, {win32, fun () -> Res = os:cmd("tasklist /nh /fi \"pid eq " ++ @@ -529,9 +529,8 @@ with_os(Handlers) -> Handler -> Handler() end. -% Like system(3) -system(Cmd) -> - Port = erlang:open_port({spawn, Cmd}, +run_ps(Pid) -> + Port = erlang:open_port({spawn, "ps -p " ++ Pid}, [exit_status,{line, 16384}, use_stdio, stderr_to_stdout]), exit_loop(Port). -- cgit v1.2.1 From 2a28ed8851ed26ac5d949ef4972f0fa78efd30ba Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 6 Jun 2012 11:45:26 +0100 Subject: more sacrifices to the CI gods --- src/rabbit_tests.erl | 47 ++++++++++++++++++++--------------------------- 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index c347e85c..5545cccf 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -29,6 +29,7 @@ -define(PERSISTENT_MSG_STORE, msg_store_persistent). -define(TRANSIENT_MSG_STORE, msg_store_transient). -define(CLEANUP_QUEUE_NAME, <<"cleanup-queue">>). +-define(TIMEOUT, 5000). all_tests() -> passed = gm_tests:all_tests(), @@ -1240,7 +1241,7 @@ test_spawn() -> rabbit_limiter:make_token(self())), ok = rabbit_channel:do(Ch, #'channel.open'{}), receive #'channel.open_ok'{} -> ok - after 1000 -> throw(failed_to_receive_channel_open_ok) + after ?TIMEOUT -> throw(failed_to_receive_channel_open_ok) end, {Writer, Ch}. @@ -1261,7 +1262,7 @@ test_spawn_remote() -> end end), receive Res -> Res - after 1000 -> throw(failed_to_receive_result) + after ?TIMEOUT -> throw(failed_to_receive_result) end. user(Username) -> @@ -1281,13 +1282,10 @@ test_confirms() -> queue = Q0, exchange = <<"amq.direct">>, routing_key = "magic" }), - receive #'queue.bind_ok'{} -> - Q0 - after 1000 -> - throw(failed_to_bind_queue) + receive #'queue.bind_ok'{} -> Q0 + after ?TIMEOUT -> throw(failed_to_bind_queue) end - after 1000 -> - throw(failed_to_declare_queue) + after ?TIMEOUT -> throw(failed_to_declare_queue) end end, %% Declare and bind two queues @@ -1300,7 +1298,7 @@ test_confirms() -> rabbit_channel:do(Ch, #'confirm.select'{}), receive #'confirm.select_ok'{} -> ok - after 1000 -> throw(failed_to_enable_confirms) + after ?TIMEOUT -> throw(failed_to_enable_confirms) end, %% Publish a message rabbit_channel:do(Ch, #'basic.publish'{exchange = <<"amq.direct">>, @@ -1317,7 +1315,7 @@ test_confirms() -> receive #'basic.nack'{} -> ok; #'basic.ack'{} -> throw(received_ack_instead_of_nack) - after 2000 -> throw(did_not_receive_nack) + after ?TIMEOUT-> throw(did_not_receive_nack) end, receive #'basic.ack'{} -> throw(received_ack_when_none_expected) @@ -1327,7 +1325,7 @@ test_confirms() -> rabbit_channel:do(Ch, #'queue.delete'{queue = QName2}), receive #'queue.delete_ok'{} -> ok - after 1000 -> throw(failed_to_cleanup_queue) + after ?TIMEOUT -> throw(failed_to_cleanup_queue) end, unlink(Ch), ok = rabbit_channel:shutdown(Ch), @@ -1350,7 +1348,7 @@ test_statistics_receive_event1(Ch, Matcher) -> true -> Props; _ -> test_statistics_receive_event1(Ch, Matcher) end - after 1000 -> throw(failed_to_receive_event) + after ?TIMEOUT -> throw(failed_to_receive_event) end. test_statistics() -> @@ -1362,9 +1360,8 @@ test_statistics() -> %% Set up a channel and queue {_Writer, Ch} = test_spawn(), rabbit_channel:do(Ch, #'queue.declare'{}), - QName = receive #'queue.declare_ok'{queue = Q0} -> - Q0 - after 1000 -> throw(failed_to_receive_queue_declare_ok) + QName = receive #'queue.declare_ok'{queue = Q0} -> Q0 + after ?TIMEOUT -> throw(failed_to_receive_queue_declare_ok) end, {ok, Q} = rabbit_amqqueue:lookup(rabbit_misc:r(<<"/">>, queue, QName)), QPid = Q#amqqueue.pid, @@ -1444,7 +1441,7 @@ expect_event(Pid, Type) -> Pid -> ok; _ -> expect_event(Pid, Type) end - after 1000 -> throw({failed_to_receive_event, Type}) + after ?TIMEOUT -> throw({failed_to_receive_event, Type}) end. test_delegates_async(SecondaryNode) -> @@ -1468,7 +1465,7 @@ make_responder(FMsg) -> make_responder(FMsg, timeout). make_responder(FMsg, Throw) -> fun () -> receive Msg -> FMsg(Msg) - after 1000 -> throw(Throw) + after ?TIMEOUT -> throw(Throw) end end. @@ -1481,9 +1478,7 @@ await_response(Count) -> receive response -> ok, await_response(Count - 1) - after 1000 -> - io:format("Async reply not received~n"), - throw(timeout) + after ?TIMEOUT -> throw(timeout) end. must_exit(Fun) -> @@ -1550,7 +1545,7 @@ test_queue_cleanup(_SecondaryNode) -> rabbit_channel:do(Ch, #'queue.declare'{ queue = ?CLEANUP_QUEUE_NAME }), receive #'queue.declare_ok'{queue = ?CLEANUP_QUEUE_NAME} -> ok - after 1000 -> throw(failed_to_receive_queue_declare_ok) + after ?TIMEOUT -> throw(failed_to_receive_queue_declare_ok) end, rabbit_channel:shutdown(Ch), rabbit:stop(), @@ -1561,8 +1556,7 @@ test_queue_cleanup(_SecondaryNode) -> receive #'channel.close'{reply_code = ?NOT_FOUND} -> ok - after 2000 -> - throw(failed_to_receive_channel_exit) + after ?TIMEOUT -> throw(failed_to_receive_channel_exit) end, rabbit_channel:shutdown(Ch2), passed. @@ -1589,8 +1583,7 @@ test_declare_on_dead_queue(SecondaryNode) -> true = rabbit_misc:is_process_alive(Q#amqqueue.pid), {ok, 0} = rabbit_amqqueue:delete(Q, false, false), passed - after 2000 -> - throw(failed_to_create_and_kill_queue) + after ?TIMEOUT -> throw(failed_to_create_and_kill_queue) end. %%--------------------------------------------------------------------- @@ -1821,7 +1814,7 @@ on_disk_capture(OnDisk, Awaiting, Pid) -> Pid); stop -> done - after (case Awaiting of [] -> 200; _ -> 5000 end) -> + after (case Awaiting of [] -> 200; _ -> ?TIMEOUT end) -> case Awaiting of [] -> Pid ! {self(), arrived}, on_disk_capture(); _ -> Pid ! {self(), timeout} @@ -2374,7 +2367,7 @@ wait_for_confirms(Unconfirmed) -> wait_for_confirms( rabbit_misc:gb_sets_difference( Unconfirmed, gb_sets:from_list(Confirmed))) - after 5000 -> exit(timeout_waiting_for_confirm) + after ?TIMEOUT -> exit(timeout_waiting_for_confirm) end end. -- cgit v1.2.1 From 23c54ea3ecf8506def32b45bf0f2ef921b90d58e Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 6 Jun 2012 12:32:59 +0100 Subject: Revert to the 2.8.2 version until bug 24982 is resolved --- src/rabbit_control_main.erl | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index 29408006..4724555b 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -490,12 +490,8 @@ wait_for_process_death(Pid) -> read_pid_file(PidFile, Wait) -> case {file:read_file(PidFile), Wait} of {{ok, Bin}, _} -> - S = binary_to_list(Bin), - Spid = case string:words(S) > 1 of - true -> string:sub_word(S, 1); - false -> string:strip(binary_to_list(Bin), right, $\n) - end, - try list_to_integer(Spid) + S = string:strip(binary_to_list(Bin), right, $\n), + try list_to_integer(S) catch error:badarg -> exit({error, {garbage_in_pid_file, PidFile}}) end, -- cgit v1.2.1 From b6fb417ae8ce5f0fc9d635485d99693556aef19b Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Wed, 6 Jun 2012 13:24:25 +0100 Subject: Cosmetic: remove colons from sysv startup script --- packaging/debs/Debian/debian/rabbitmq-server.init | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packaging/debs/Debian/debian/rabbitmq-server.init b/packaging/debs/Debian/debian/rabbitmq-server.init index 4bc32515..2c064bcd 100644 --- a/packaging/debs/Debian/debian/rabbitmq-server.init +++ b/packaging/debs/Debian/debian/rabbitmq-server.init @@ -153,12 +153,12 @@ start_stop_end() { case "$1" in start) - log_daemon_msg "Starting ${DESC}:" $NAME + log_daemon_msg "Starting ${DESC}" $NAME start_rabbitmq start_stop_end "running" ;; stop) - log_daemon_msg "Stopping ${DESC}:" $NAME + log_daemon_msg "Stopping ${DESC}" $NAME stop_rabbitmq start_stop_end "stopped" ;; @@ -166,17 +166,17 @@ case "$1" in status_rabbitmq ;; rotate-logs) - log_action_begin_msg "Rotating log files for ${DESC}: ${NAME}" + log_action_begin_msg "Rotating log files for ${DESC} ${NAME}" rotate_logs_rabbitmq log_action_end_msg $RETVAL ;; force-reload|reload|restart) - log_daemon_msg "Restarting ${DESC}:" $NAME + log_daemon_msg "Restarting ${DESC}" $NAME restart_rabbitmq restart_end ;; try-restart) - log_daemon_msg "Restarting ${DESC}:" $NAME + log_daemon_msg "Restarting ${DESC}" $NAME restart_running_rabbitmq restart_end ;; -- cgit v1.2.1 From 3d031638d7829e4e965ca30094b4511c2363b30f Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Wed, 6 Jun 2012 13:27:45 +0100 Subject: Backed out changeset 6ee929c7f821 --- packaging/debs/Debian/debian/rabbitmq-server.init | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packaging/debs/Debian/debian/rabbitmq-server.init b/packaging/debs/Debian/debian/rabbitmq-server.init index 2c064bcd..4bc32515 100644 --- a/packaging/debs/Debian/debian/rabbitmq-server.init +++ b/packaging/debs/Debian/debian/rabbitmq-server.init @@ -153,12 +153,12 @@ start_stop_end() { case "$1" in start) - log_daemon_msg "Starting ${DESC}" $NAME + log_daemon_msg "Starting ${DESC}:" $NAME start_rabbitmq start_stop_end "running" ;; stop) - log_daemon_msg "Stopping ${DESC}" $NAME + log_daemon_msg "Stopping ${DESC}:" $NAME stop_rabbitmq start_stop_end "stopped" ;; @@ -166,17 +166,17 @@ case "$1" in status_rabbitmq ;; rotate-logs) - log_action_begin_msg "Rotating log files for ${DESC} ${NAME}" + log_action_begin_msg "Rotating log files for ${DESC}: ${NAME}" rotate_logs_rabbitmq log_action_end_msg $RETVAL ;; force-reload|reload|restart) - log_daemon_msg "Restarting ${DESC}" $NAME + log_daemon_msg "Restarting ${DESC}:" $NAME restart_rabbitmq restart_end ;; try-restart) - log_daemon_msg "Restarting ${DESC}" $NAME + log_daemon_msg "Restarting ${DESC}:" $NAME restart_running_rabbitmq restart_end ;; -- cgit v1.2.1 From 73558487fcfb27f05d0c41f556dd7ceb659030c1 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 6 Jun 2012 14:48:04 +0100 Subject: Cosmetic --- src/rabbit_control_main.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index 82bee785..a36fe90c 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -531,7 +531,7 @@ with_os(Handlers) -> run_ps(Pid) -> Port = erlang:open_port({spawn, "ps -p " ++ Pid}, - [exit_status,{line, 16384}, + [exit_status, {line, 16384}, use_stdio, stderr_to_stdout]), exit_loop(Port). -- cgit v1.2.1 From 2471f4530305e0948e8a3113dfed6472131b05bf Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Wed, 6 Jun 2012 15:51:21 +0100 Subject: simply reading pid files in a way that works on R12B --- src/rabbit_control_main.erl | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index a36fe90c..2e163cfb 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -491,10 +491,8 @@ read_pid_file(PidFile, Wait) -> case {file:read_file(PidFile), Wait} of {{ok, Bin}, _} -> S = binary_to_list(Bin), - PidS = case string:words(S) > 1 of - true -> string:sub_word(S, 1); - false -> string:strip(S, right, $\n) - end, + {match, [PidS]} = re:run(S, "[^\\s]+", + [{capture, all, list}]), try list_to_integer(PidS) catch error:badarg -> exit({error, {garbage_in_pid_file, PidFile}}) -- cgit v1.2.1 From 40b769597b02f3e5df03bb71ddef3511fbd91d4a Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 6 Jun 2012 15:57:00 +0100 Subject: Probably should have been part of bug 24792 but: now that we start applications ourselves rather than in the boot script, we can write nicer error messages. Let's do that. --- src/rabbit.erl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/rabbit.erl b/src/rabbit.erl index fda489fe..fc5d4e93 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -323,6 +323,8 @@ boot() -> start_it(StartFun) -> try StartFun() + catch _:Reason -> + boot_error("Error description:~n~n~p~n~n", [Reason]) after %% give the error loggers some time to catch up timer:sleep(100) -- cgit v1.2.1 From 4d2109231f30d60cc41ee3ee512fca3ed7b8e734 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Wed, 6 Jun 2012 16:31:23 +0100 Subject: Background daemon when starting up --- packaging/RPMS/Fedora/Makefile | 1 + packaging/common/rabbitmq-script-wrapper | 4 +++- packaging/debs/Debian/Makefile | 1 + packaging/debs/Debian/debian/rabbitmq-server.init | 5 +---- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/packaging/RPMS/Fedora/Makefile b/packaging/RPMS/Fedora/Makefile index 180500ed..03e513f8 100644 --- a/packaging/RPMS/Fedora/Makefile +++ b/packaging/RPMS/Fedora/Makefile @@ -42,6 +42,7 @@ ifeq "$(RPM_OS)" "fedora" SOURCES/rabbitmq-server.init endif sed -i -e 's|@SU_RABBITMQ_SH_C@|su rabbitmq -s /bin/sh -c|' \ + -e 's|@STDOUT_STDERR_REDIRECTION@||' \ SOURCES/rabbitmq-script-wrapper cp rabbitmq-server.logrotate SOURCES/rabbitmq-server.logrotate diff --git a/packaging/common/rabbitmq-script-wrapper b/packaging/common/rabbitmq-script-wrapper index 0e59c218..e832aed6 100644 --- a/packaging/common/rabbitmq-script-wrapper +++ b/packaging/common/rabbitmq-script-wrapper @@ -29,7 +29,9 @@ cd /var/lib/rabbitmq SCRIPT=`basename $0` -if [ `id -u` = `id -u rabbitmq` -o "$SCRIPT" = "rabbitmq-plugins" ] ; then +if [ `id -u` = `id -u rabbitmq` -a "$SCRIPT" = "rabbitmq-server" ] ; then + /usr/lib/rabbitmq/bin/rabbitmq-server "$@" @STDOUT_STDERR_REDIRECTION@ +elif [ `id -u` = `id -u rabbitmq` -o "$SCRIPT" = "rabbitmq-plugins" ] ; then /usr/lib/rabbitmq/bin/${SCRIPT} "$@" elif [ `id -u` = 0 ] ; then @SU_RABBITMQ_SH_C@ "/usr/lib/rabbitmq/bin/${SCRIPT} ${CMDLINE}" diff --git a/packaging/debs/Debian/Makefile b/packaging/debs/Debian/Makefile index 844388c6..1e4bf755 100644 --- a/packaging/debs/Debian/Makefile +++ b/packaging/debs/Debian/Makefile @@ -23,6 +23,7 @@ package: clean cp -r debian $(UNPACKED_DIR) cp $(COMMON_DIR)/* $(UNPACKED_DIR)/debian/ sed -i -e 's|@SU_RABBITMQ_SH_C@|su rabbitmq -s /bin/sh -c|' \ + -e 's|@STDOUT_STDERR_REDIRECTION@| > "/var/log/rabbitmq/startup_log" 2> "/var/log/rabbitmq/startup_err"|' \ $(UNPACKED_DIR)/debian/rabbitmq-script-wrapper chmod a+x $(UNPACKED_DIR)/debian/rules echo "This package was debianized by Tony Garnock-Jones on\nWed, 3 Jan 2007 15:43:44 +0000.\n\nIt was downloaded from http://www.rabbitmq.com/\n\n" > $(UNPACKED_DIR)/debian/copyright diff --git a/packaging/debs/Debian/debian/rabbitmq-server.init b/packaging/debs/Debian/debian/rabbitmq-server.init index 4bc32515..d14eebf2 100644 --- a/packaging/debs/Debian/debian/rabbitmq-server.init +++ b/packaging/debs/Debian/debian/rabbitmq-server.init @@ -60,10 +60,7 @@ start_rabbitmq () { set +e RABBITMQ_PID_FILE=$PID_FILE start-stop-daemon --quiet \ --chuid rabbitmq --start --exec $DAEMON \ - --pidfile "$RABBITMQ_PID_FILE" \ - > "${INIT_LOG_DIR}/startup_log" \ - 2> "${INIT_LOG_DIR}/startup_err" \ - 0<&- & + --pidfile "$RABBITMQ_PID_FILE" --background $CONTROL wait $PID_FILE >/dev/null 2>&1 RETVAL=$? set -e -- cgit v1.2.1 From 384b31a9420472ee175970a9940055dd814d36ac Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Wed, 6 Jun 2012 16:39:49 +0100 Subject: add node to existing cluster status instead of creating a new one on on_node_{up,down} This is because the invocation might come before the mnesia data is propagated correctly. --- src/rabbit.erl | 6 ++++-- src/rabbit_file.erl | 43 ++++++++++++++++++++++++++++++------------- src/rabbit_mnesia.erl | 41 ++++++++++++++++++++++++++++------------- src/rabbit_node_monitor.erl | 27 +++++++++++++++------------ 4 files changed, 77 insertions(+), 40 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index a9af7335..7ae6aa25 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -199,7 +199,8 @@ rabbit_queue_index, gen, dict, ordsets, file_handle_cache, rabbit_msg_store, array, rabbit_msg_store_ets_index, rabbit_msg_file, rabbit_exchange_type_fanout, rabbit_exchange_type_topic, mnesia, - mnesia_lib, rpc, mnesia_tm, qlc, sofs, proplists, credit_flow, pmon]). + mnesia_lib, rpc, mnesia_tm, qlc, sofs, proplists, credit_flow, pmon, + ssl_connection, ssl_record, gen_fsm, ssl]). %% HiPE compilation uses multiple cores anyway, but some bits are %% IO-bound so we can go faster if we parallelise a bit more. In @@ -263,7 +264,7 @@ maybe_hipe_compile() -> hipe_compile() -> Count = length(?HIPE_WORTHY), - io:format("HiPE compiling: |~s|~n |", + io:format("~nHiPE compiling: |~s|~n |", [string:copies("-", Count)]), T1 = erlang:now(), PidMRefs = [spawn_monitor(fun () -> [begin @@ -409,6 +410,7 @@ start(normal, []) -> end. stop(_State) -> + ok = rabbit_mnesia:update_cluster_nodes_status(), terminated_ok = error_logger:delete_report_handler(rabbit_error_logger), ok = rabbit_alarm:stop(), ok = case rabbit_mnesia:is_clustered() of diff --git a/src/rabbit_file.erl b/src/rabbit_file.erl index 59df14f3..02487d12 100644 --- a/src/rabbit_file.erl +++ b/src/rabbit_file.erl @@ -19,7 +19,8 @@ -include_lib("kernel/include/file.hrl"). -export([is_file/1, is_dir/1, file_size/1, ensure_dir/1, wildcard/2, list_dir/1]). --export([read_term_file/1, write_term_file/2, write_file/2, write_file/3]). +-export([read_term_file/1, write_term_file/2, map_term_file/2, write_file/2, + write_file/3]). -export([append_file/2, ensure_parent_dirs_exist/1]). -export([rename/2, delete/1, recursive_delete/1, recursive_copy/2]). -export([lock_file/1]). @@ -40,6 +41,9 @@ -spec(read_term_file/1 :: (file:filename()) -> {'ok', [any()]} | rabbit_types:error(any())). -spec(write_term_file/2 :: (file:filename(), [any()]) -> ok_or_error()). +-spec(map_term_file/2 :: + (fun(([any()]) -> [any()]), file:filename()) + -> {'ok', [any()]} | rabbit_types:error(any())). -spec(write_file/2 :: (file:filename(), iodata()) -> ok_or_error()). -spec(write_file/3 :: (file:filename(), iodata(), [any()]) -> ok_or_error()). -spec(append_file/2 :: (file:filename(), string()) -> ok_or_error()). @@ -107,18 +111,13 @@ with_fhc_handle(Fun) -> after ok = file_handle_cache:release() end. -read_term_file(File) -> - try - {ok, Data} = with_fhc_handle(fun () -> prim_file:read_file(File) end), - {ok, Tokens, _} = erl_scan:string(binary_to_list(Data)), - TokenGroups = group_tokens(Tokens), - {ok, [begin - {ok, Term} = erl_parse:parse_term(Tokens1), - Term - end || Tokens1 <- TokenGroups]} - catch - error:{badmatch, Error} -> Error - end. +binary_to_terms(Data) -> + {ok, Tokens, _} = erl_scan:string(binary_to_list(Data)), + TokenGroups = group_tokens(Tokens), + [begin + {ok, Term} = erl_parse:parse_term(Tokens1), + Term + end || Tokens1 <- TokenGroups]. group_tokens(Ts) -> [lists:reverse(G) || G <- group_tokens([], Ts)]. @@ -127,10 +126,28 @@ group_tokens(Cur, []) -> [Cur]; group_tokens(Cur, [T = {dot, _} | Ts]) -> [[T | Cur] | group_tokens([], Ts)]; group_tokens(Cur, [T | Ts]) -> group_tokens([T | Cur], Ts). +read_term_file(File) -> + try + {ok, Data} = with_fhc_handle(fun () -> prim_file:read_file(File) end), + {ok, binary_to_terms(Data)} + catch + error:{badmatch, Error} -> Error + end. + write_term_file(File, Terms) -> write_file(File, list_to_binary([io_lib:format("~w.~n", [Term]) || Term <- Terms])). +map_term_file(Fun, File) -> + try + with_fhc_handle(fun () -> + {ok, Data} = prim_file:read_file(File), + {ok, Fun(binary_to_terms(Data))} + end) + catch + error:{badmatch, Error} -> Error + end. + write_file(Path, Data) -> write_file(Path, Data, []). %% write_file/3 and make_binary/1 are both based on corresponding diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 7aac7f3f..f8e6cac6 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -41,8 +41,9 @@ empty_ram_only_tables/0, copy_db/1, wait_for_tables/0, + update_cluster_nodes_status/0, - on_node_up/1, + on_node_up/2, on_node_down/1 ]). @@ -65,7 +66,8 @@ -export_type([node_type/0]). -type(node_type() :: disc | ram). --type(node_status() :: {[node()], [node()], [node()]}). +-type(node_status() :: {[ordsets:ordset(node())], [ordsets:ordset(node())], + [ordsets:ordset(node())]}). %% Main interface -spec(prepare/0 :: () -> 'ok'). @@ -95,9 +97,10 @@ -spec(create_tables/0 :: () -> 'ok'). -spec(copy_db/1 :: (file:filename()) -> rabbit_types:ok_or_error(any())). -spec(wait_for_tables/1 :: ([atom()]) -> 'ok'). +-spec(update_cluster_nodes_status/0 :: () -> 'ok'). %% Hooks used in `rabbit_node_monitor' --spec(on_node_up/1 :: (node()) -> 'ok'). +-spec(on_node_up/2 :: (node(), boolean()) -> 'ok'). -spec(on_node_down/1 :: (node()) -> 'ok'). %% Functions used in internal rpc calls @@ -409,9 +412,9 @@ running_clustered_disc_nodes() -> cluster_status_if_running() -> case mnesia:system_info(is_running) of no -> error; - yes -> {ok, {mnesia:system_info(db_nodes), - mnesia:table_info(schema, disc_copies), - mnesia:system_info(running_db_nodes)}} + yes -> {ok, {ordsets:from_list(mnesia:system_info(db_nodes)), + ordsets:from_list(mnesia:table_info(schema, disc_copies)), + ordsets:from_list(mnesia:system_info(running_db_nodes))}} end. node_info() -> @@ -703,8 +706,16 @@ update_cluster_nodes_status() -> %% Hooks for `rabbit_node_monitor' %%-------------------------------------------------------------------- -on_node_up(Node) -> - update_cluster_nodes_status(), +on_node_up(Node, IsDiscNode) -> + {ok, _} = rabbit_file:map_term_file( + fun ([{AllNodes, DiscNodes, RunningNodes}]) -> + [{ordsets:add_element(Node, AllNodes), + case IsDiscNode of + true -> ordsets:add_element(Node, DiscNodes); + false -> DiscNodes + end, + ordsets:add_element(Node, RunningNodes)}] + end, cluster_nodes_status_filename()), case is_only_disc_node(Node) of true -> rabbit_log:info("cluster contains disc nodes again~n"); false -> ok @@ -715,7 +726,13 @@ on_node_down(Node) -> true -> rabbit_log:info("only running disc node went down~n"); false -> ok end, - update_cluster_nodes_status(). + {ok, _} = rabbit_file:map_term_file( + fun ([{AllNodes, DiscNodes, RunningNodes}]) -> + [{ordsets:del_element(Node, AllNodes), + ordsets:del_element(Node, DiscNodes), + ordsets:del_element(Node, RunningNodes)}] + end, cluster_nodes_status_filename()), + ok. %%-------------------------------------------------------------------- %% Internal helpers @@ -1009,11 +1026,9 @@ remove_node_if_mnesia_running(Node) -> %% propagated to all nodes case mnesia:del_table_copy(schema, Node) of {atomic, ok} -> - update_cluster_nodes_status(), - io:format("nodes: ~p~n", [running_clustered_disc_nodes()]), + on_node_down(Node), {_, []} = rpc:multicall(running_clustered_nodes(), - rabbit_mnesia, - update_cluster_nodes_status, []), + rabbit_mnesia, on_node_down, [Node]), ok; {aborted, Reason} -> {error, {failed_to_remove_node, Node, Reason}} diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index 323cf0ce..fee7c278 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -22,7 +22,7 @@ -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). --export([notify_cluster/0, rabbit_running_on/1]). +-export([notify_cluster/0, rabbit_running_on/2]). -define(SERVER, ?MODULE). -define(RABBIT_UP_RPC_TIMEOUT, 2000). @@ -32,7 +32,7 @@ -ifdef(use_specs). -spec(start_link/0 :: () -> rabbit_types:ok_pid_or_error()). --spec(rabbit_running_on/1 :: (node()) -> 'ok'). +-spec(rabbit_running_on/2 :: (node(), boolean()) -> 'ok'). -spec(notify_cluster/0 :: () -> 'ok'). -endif. @@ -42,20 +42,23 @@ start_link() -> gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). -rabbit_running_on(Node) -> - gen_server:cast(rabbit_node_monitor, {rabbit_running_on, Node}). +rabbit_running_on(Node, IsDiscNode) -> + gen_server:cast(rabbit_node_monitor, {rabbit_running_on, Node, IsDiscNode}). notify_cluster() -> Node = node(), - Nodes = rabbit_mnesia:running_clustered_nodes() -- [Node], + IsDiscNode = rabbit_mnesia:is_disc_node(), + RunningNodes = rabbit_mnesia:running_clustered_nodes() -- [Node], + DiscNodes = rabbit_mnesia:all_clustered_disc_nodes(), %% notify other rabbits of this rabbit - case rpc:multicall(Nodes, rabbit_node_monitor, rabbit_running_on, - [Node], ?RABBIT_UP_RPC_TIMEOUT) of + case rpc:multicall(RunningNodes, rabbit_node_monitor, rabbit_running_on, + [Node, IsDiscNode], ?RABBIT_UP_RPC_TIMEOUT) of {_, [] } -> ok; {_, Bad} -> rabbit_log:info("failed to contact nodes ~p~n", [Bad]) end, %% register other active rabbits with this rabbit - [ rabbit_running_on(N) || N <- Nodes ], + [rabbit_running_on(N, ordsets:is_element(N, DiscNodes)) || + N <- RunningNodes], ok. %%-------------------------------------------------------------------- @@ -66,12 +69,12 @@ init([]) -> handle_call(_Request, _From, State) -> {noreply, State}. -handle_cast({rabbit_running_on, Node}, Nodes) -> +handle_cast({rabbit_running_on, Node, IsDiscNode}, Nodes) -> case ordsets:is_element(Node, Nodes) of true -> {noreply, Nodes}; false -> rabbit_log:info("rabbit on node ~p up~n", [Node]), erlang:monitor(process, {rabbit, Node}), - ok = handle_live_rabbit(Node), + ok = handle_live_rabbit(Node, IsDiscNode), {noreply, ordsets:add_element(Node, Nodes)} end; handle_cast(_Msg, State) -> @@ -101,6 +104,6 @@ handle_dead_rabbit(Node) -> ok = rabbit_alarm:on_node_down(Node), ok = rabbit_mnesia:on_node_down(Node). -handle_live_rabbit(Node) -> +handle_live_rabbit(Node, IsDiscNode) -> ok = rabbit_alarm:on_node_up(Node), - ok = rabbit_mnesia:on_node_up(Node). + ok = rabbit_mnesia:on_node_up(Node, IsDiscNode). -- cgit v1.2.1 From 4265c5ab338940d3a0e19583c2f90660a96da3bb Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 6 Jun 2012 18:03:59 +0100 Subject: Move banner things from stdout to logs. Leave a vestigal banner so that people who have started Rabbit by hand have something to look at. --- src/rabbit.erl | 96 ++++++++++++++++++++++++---------------------------------- 1 file changed, 40 insertions(+), 56 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index fc5d4e93..30776387 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -252,6 +252,9 @@ %%---------------------------------------------------------------------------- +%% HiPE compilation happens before we have log handlers - so we have +%% to io:format/2, it's all we can do. + maybe_hipe_compile() -> {ok, Want} = application:get_env(rabbit, hipe_compile), Can = code:which(hipe) =/= non_existing, @@ -302,7 +305,7 @@ start() -> ok = ensure_application_loaded(), ok = ensure_working_log_handlers(), ok = app_utils:start_applications(app_startup_order()), - ok = print_plugin_info(rabbit_plugins:active()) + ok = log_broker_started(rabbit_plugins:active()) end). boot() -> @@ -317,7 +320,7 @@ boot() -> StartupApps = app_utils:app_dependency_order(ToBeLoaded, false), ok = app_utils:start_applications(StartupApps), - ok = print_plugin_info(Plugins) + ok = log_broker_started(Plugins) end). start_it(StartFun) -> @@ -402,8 +405,8 @@ start(normal, []) -> {ok, SupPid} = rabbit_sup:start_link(), true = register(rabbit, self()), print_banner(), + log_banner(), [ok = run_boot_step(Step) || Step <- boot_steps()], - io:format("~nbroker running~n"), {ok, SupPid}; Error -> Error @@ -433,22 +436,16 @@ app_shutdown_order() -> %%--------------------------------------------------------------------------- %% boot step logic -run_boot_step({StepName, Attributes}) -> - Description = case lists:keysearch(description, 1, Attributes) of - {value, {_, D}} -> D; - false -> StepName - end, +run_boot_step({_StepName, Attributes}) -> case [MFA || {mfa, MFA} <- Attributes] of [] -> - io:format("-- ~s~n", [Description]); + ok; MFAs -> - io:format("starting ~-60s ...", [Description]), [try apply(M,F,A) catch _:Reason -> boot_step_error(Reason, erlang:get_stacktrace()) end || {M,F,A} <- MFAs], - io:format("done~n"), ok end. @@ -646,23 +643,13 @@ force_event_refresh() -> %%--------------------------------------------------------------------------- %% misc -print_plugin_info([]) -> - ok; -print_plugin_info(Plugins) -> - %% This gets invoked by rabbitmqctl start_app, outside the context - %% of the rabbit application - rabbit_misc:with_local_io( - fun() -> - io:format("~n-- plugins running~n"), - [print_plugin_info( - AppName, element(2, application:get_key(AppName, vsn))) - || AppName <- Plugins], - ok - end). - -print_plugin_info(Plugin, Vsn) -> - Len = 76 - length(Vsn), - io:format("~-" ++ integer_to_list(Len) ++ "s ~s~n", [Plugin, Vsn]). +log_broker_started([]) -> + error_logger:info_msg("Server startup complete~n", []), + io:format("~nBroker running~n"); +log_broker_started(Plugins) -> + error_logger:info_msg("Server startup complete; plugins are:~n~n~p~n", + [Plugins]), + io:format("~nBroker running with ~p plugins.~n", [length(Plugins)]). erts_version_check() -> FoundVer = erlang:system_info(version), @@ -675,23 +662,14 @@ erts_version_check() -> print_banner() -> {ok, Product} = application:get_key(id), {ok, Version} = application:get_key(vsn), - ProductLen = string:len(Product), - io:format("~n" - "+---+ +---+~n" - "| | | |~n" - "| | | |~n" - "| | | |~n" - "| +---+ +-------+~n" - "| |~n" - "| ~s +---+ |~n" - "| | | |~n" - "| ~s +---+ |~n" - "| |~n" - "+-------------------+~n" - "~s~n~s~n~s~n~n", - [Product, string:right([$v|Version], ProductLen), - ?PROTOCOL_VERSION, - ?COPYRIGHT_MESSAGE, ?INFORMATION_MESSAGE]), + io:format("~n~s ~s. ~s~n~s~n~n", + [Product, Version, ?COPYRIGHT_MESSAGE, ?INFORMATION_MESSAGE]), + io:format("Logs: ~s~n ~s~n", [log_location(kernel), + log_location(sasl)]). + +log_banner() -> + {ok, Product} = application:get_key(id), + {ok, Version} = application:get_key(vsn), Settings = [{"node", node()}, {"app descriptor", app_location()}, {"home dir", home_dir()}, @@ -700,20 +678,26 @@ print_banner() -> {"log", log_location(kernel)}, {"sasl log", log_location(sasl)}, {"database dir", rabbit_mnesia:dir()}, - {"erlang version", erlang:system_info(version)}], + {"erlang version", erlang:system_info(otp_release)}], DescrLen = 1 + lists:max([length(K) || {K, _V} <- Settings]), Format = fun (K, V) -> - io:format("~-" ++ integer_to_list(DescrLen) ++ "s: ~s~n", - [K, V]) + rabbit_misc:format( + "~-" ++ integer_to_list(DescrLen) ++ "s: ~s~n", [K, V]) end, - lists:foreach(fun ({"config file(s)" = K, []}) -> - Format(K, "(none)"); - ({"config file(s)" = K, [V0 | Vs]}) -> - Format(K, V0), [Format("", V) || V <- Vs]; - ({K, V}) -> - Format(K, V) - end, Settings), - io:nl(). + Banner = iolist_to_binary( + rabbit_misc:format( + "~s ~s~n~s~n~s~n~s~n", + [Product, Version, ?PROTOCOL_VERSION, ?COPYRIGHT_MESSAGE, + ?INFORMATION_MESSAGE]) ++ + [case S of + {"config file(s)" = K, []} -> + Format(K, "(none)"); + {"config file(s)" = K, [V0 | Vs]} -> + Format(K, V0), [Format("", V) || V <- Vs]; + {K, V} -> + Format(K, V) + end || S <- Settings]), + error_logger:info_msg("~s~n", [Banner]). app_location() -> {ok, Application} = application:get_application(), -- cgit v1.2.1 From d3cd3d7aaadbb33ef19d673d52d20d69da341242 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 6 Jun 2012 18:12:15 +0100 Subject: Oops --- src/rabbit.erl | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 30776387..024bfdf6 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -644,12 +644,19 @@ force_event_refresh() -> %% misc log_broker_started([]) -> - error_logger:info_msg("Server startup complete~n", []), - io:format("~nBroker running~n"); + rabbit_misc:with_local_io( + fun() -> + error_logger:info_msg("Server startup complete~n", []), + io:format("~nBroker running~n") + end); log_broker_started(Plugins) -> - error_logger:info_msg("Server startup complete; plugins are:~n~n~p~n", - [Plugins]), - io:format("~nBroker running with ~p plugins.~n", [length(Plugins)]). + rabbit_misc:with_local_io( + fun() -> + error_logger:info_msg( + "Server startup complete; plugins are:~n~n~p~n", [Plugins]), + io:format("~nBroker running with ~p plugins.~n", + [length(Plugins)]) + end). erts_version_check() -> FoundVer = erlang:system_info(version), -- cgit v1.2.1 From 415cf35c55afc0c8d89eeb8f7e595aa30f8f6a44 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 6 Jun 2012 18:14:06 +0100 Subject: I never saw the point of that. --- src/rabbit.erl | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 024bfdf6..dca0b3d7 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -678,7 +678,6 @@ log_banner() -> {ok, Product} = application:get_key(id), {ok, Version} = application:get_key(vsn), Settings = [{"node", node()}, - {"app descriptor", app_location()}, {"home dir", home_dir()}, {"config file(s)", config_files()}, {"cookie hash", rabbit_nodes:cookie_hash()}, @@ -706,10 +705,6 @@ log_banner() -> end || S <- Settings]), error_logger:info_msg("~s~n", [Banner]). -app_location() -> - {ok, Application} = application:get_application(), - filename:absname(code:where_is_file(atom_to_list(Application) ++ ".app")). - home_dir() -> case init:get_argument(home) of {ok, [[Home]]} -> Home; -- cgit v1.2.1 From ff29e9de2c16a8ee2e9eb56125ea6e0d9e13d4db Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 7 Jun 2012 15:14:13 +0100 Subject: No, that makes no sense. --- src/rabbit_exchange.erl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index ab548f38..518e8818 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -113,7 +113,6 @@ recover() -> [XName || #exchange{name = XName} <- Xs]. callback(X = #exchange{type = XType}, Fun, Serial0, Args) -> - %% TODO cache this? Serial = fun (Bool) -> case Serial0 of _ when is_atom(Serial0) -> Serial0; -- cgit v1.2.1 From dd0d5f0e0763ff1f8e81b51cef0743bfbf78bbad Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 7 Jun 2012 15:55:44 +0100 Subject: Strict and non-strict list by component. --- src/rabbit_runtime_parameters.erl | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/rabbit_runtime_parameters.erl b/src/rabbit_runtime_parameters.erl index 172cee92..3a54e8f6 100644 --- a/src/rabbit_runtime_parameters.erl +++ b/src/rabbit_runtime_parameters.erl @@ -18,8 +18,8 @@ -include("rabbit.hrl"). --export([parse_set/3, set/3, clear/2, list/0, list/1, list_formatted/0, - lookup/2, value/2, value/3, info_keys/0]). +-export([parse_set/3, set/3, clear/2, list/0, list/1, list_strict/1, + list_formatted/0, lookup/2, value/2, value/3, info_keys/0]). %%---------------------------------------------------------------------------- @@ -31,7 +31,8 @@ -spec(set/3 :: (binary(), binary(), term()) -> ok_or_error_string()). -spec(clear/2 :: (binary(), binary()) -> ok_or_error_string()). -spec(list/0 :: () -> [rabbit_types:infos()]). --spec(list/1 :: (binary()) -> [rabbit_types:infos()] | 'not_found'). +-spec(list/1 :: (binary()) -> [rabbit_types:infos()]). +-spec(list_strict/1 :: (binary()) -> [rabbit_types:infos()] | 'not_found'). -spec(list_formatted/0 :: () -> [rabbit_types:infos()]). -spec(lookup/2 :: (binary(), binary()) -> rabbit_types:infos()). -spec(value/2 :: (binary(), binary()) -> term()). @@ -122,11 +123,14 @@ mnesia_clear(Component, Key) -> list() -> [p(P) || P <- rabbit_misc:dirty_read_all(?TABLE)]. -list(Component) -> +list(Component) -> list(Component, []). +list_strict(Component) -> list(Component, not_found). + +list(Component, Default) -> case lookup_component(Component) of {ok, _} -> Match = #runtime_parameters{key = {Component, '_'}, _ = '_'}, [p(P) || P <- mnesia:dirty_match_object(?TABLE, Match)]; - _ -> not_found + _ -> Default end. list_formatted() -> -- cgit v1.2.1 From e56438f93b1e50457e9aad14d2e1a71cb627631f Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Thu, 7 Jun 2012 17:00:38 +0100 Subject: Prevent coordinator process leakage --- src/rabbit_mirror_queue_coordinator.erl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/rabbit_mirror_queue_coordinator.erl b/src/rabbit_mirror_queue_coordinator.erl index 17e2ffb4..c99fc21e 100644 --- a/src/rabbit_mirror_queue_coordinator.erl +++ b/src/rabbit_mirror_queue_coordinator.erl @@ -402,6 +402,8 @@ handle_msg([CPid], _From, request_length = Msg) -> ok = gen_server2:cast(CPid, Msg); handle_msg([CPid], _From, {ensure_monitoring, _Pids} = Msg) -> ok = gen_server2:cast(CPid, Msg); +handle_msg([_CPid], _From, {delete_and_terminate, Reason}) -> + {stop, Reason}; handle_msg([_CPid], _From, _Msg) -> ok. -- cgit v1.2.1 From 0da0a59418c096a02016e032c5adc7b80374ccf9 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 7 Jun 2012 18:01:13 +0100 Subject: peek_serial/1 should not depend on having next_serial/1 run first. --- src/rabbit_exchange.erl | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index 518e8818..f43fbbe7 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -408,18 +408,15 @@ unconditional_delete(X = #exchange{name = XName}) -> {deleted, X, Bindings, rabbit_binding:remove_for_destination(XName)}. next_serial(XName) -> - Serial = case mnesia:read(rabbit_exchange_serial, XName, write) of - [#exchange_serial{next = Next}] -> Next; - [] -> 1 - end, + Serial = peek_serial(XName), ok = mnesia:write(rabbit_exchange_serial, #exchange_serial{name = XName, next = Serial + 1}, write), Serial. peek_serial(XName) -> - case mnesia:read({rabbit_exchange_serial, XName}) of + case mnesia:read(rabbit_exchange_serial, XName, write) of [#exchange_serial{next = Serial}] -> Serial; - _ -> undefined + _ -> 1 end. invalid_module(T) -> -- cgit v1.2.1 From fe06b054ea050520603c2d7e5a7e8aa1c9e7e5ec Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Fri, 8 Jun 2012 12:55:44 +0100 Subject: Stop mirror coordinator gen_server when deleting queue --- src/rabbit_mirror_queue_coordinator.erl | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/rabbit_mirror_queue_coordinator.erl b/src/rabbit_mirror_queue_coordinator.erl index c99fc21e..b2fb0856 100644 --- a/src/rabbit_mirror_queue_coordinator.erl +++ b/src/rabbit_mirror_queue_coordinator.erl @@ -354,7 +354,10 @@ handle_cast(request_length, State = #state { length_fun = LengthFun }) -> noreply(State); handle_cast({ensure_monitoring, Pids}, State = #state { monitors = Mons }) -> - noreply(State #state { monitors = pmon:monitor_all(Pids, Mons) }). + noreply(State #state { monitors = pmon:monitor_all(Pids, Mons) }); + +handle_cast({delete_and_terminate, Reason}, State = #state { monitors = Mons }) -> + {stop, Reason, State}. handle_info(send_gm_heartbeat, State = #state { gm = GM }) -> gm:broadcast(GM, heartbeat), @@ -402,7 +405,8 @@ handle_msg([CPid], _From, request_length = Msg) -> ok = gen_server2:cast(CPid, Msg); handle_msg([CPid], _From, {ensure_monitoring, _Pids} = Msg) -> ok = gen_server2:cast(CPid, Msg); -handle_msg([_CPid], _From, {delete_and_terminate, Reason}) -> +handle_msg([CPid], _From, {delete_and_terminate, Reason} = Msg) -> + ok = gen_server2:cast(CPid, Msg), {stop, Reason}; handle_msg([_CPid], _From, _Msg) -> ok. -- cgit v1.2.1 From a8c045167be7989a615957f44821660d721b5a2c Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 8 Jun 2012 15:20:45 +0100 Subject: Needed for tests --- src/rabbit_policy.erl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/rabbit_policy.erl b/src/rabbit_policy.erl index d4c2dee0..1551795f 100644 --- a/src/rabbit_policy.erl +++ b/src/rabbit_policy.erl @@ -16,6 +16,8 @@ -module(rabbit_policy). +%% TODO specs + -behaviour(rabbit_runtime_parameter). -include("rabbit.hrl"). @@ -47,7 +49,9 @@ set(X = #exchange{name = Name}) -> X#exchange{policy = set0(Name)}. set0(Name) -> match(Name, list()). get(Name, #amqqueue{policy = Policy}) -> get0(Name, Policy); -get(Name, #exchange{policy = Policy}) -> get0(Name, Policy). +get(Name, #exchange{policy = Policy}) -> get0(Name, Policy); +%% Caution - SLOW. +get(Name, EntityName = #resource{}) -> get0(Name, match(EntityName, list())). get0(_Name, undefined) -> {error, not_found}; get0(Name, List) -> case pget(<<"policy">>, List) of -- cgit v1.2.1 From 070390731eff798b8c518f418cdacd35afb5a293 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 8 Jun 2012 15:21:08 +0100 Subject: Don't take a write lock if we're not going to write. --- src/rabbit_exchange.erl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index f43fbbe7..4f6959ba 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -408,13 +408,15 @@ unconditional_delete(X = #exchange{name = XName}) -> {deleted, X, Bindings, rabbit_binding:remove_for_destination(XName)}. next_serial(XName) -> - Serial = peek_serial(XName), + Serial = peek_serial(XName, write), ok = mnesia:write(rabbit_exchange_serial, #exchange_serial{name = XName, next = Serial + 1}, write), Serial. -peek_serial(XName) -> - case mnesia:read(rabbit_exchange_serial, XName, write) of +peek_serial(XName) -> peek_serial(XName, read). + +peek_serial(XName, LockType) -> + case mnesia:read(rabbit_exchange_serial, XName, LockType) of [#exchange_serial{next = Serial}] -> Serial; _ -> 1 end. -- cgit v1.2.1 From 11c1f35ce004b0b7379ffcaca3a6cc6b89161309 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 8 Jun 2012 16:35:33 +0100 Subject: Unused var --- src/rabbit_mirror_queue_coordinator.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_mirror_queue_coordinator.erl b/src/rabbit_mirror_queue_coordinator.erl index b2fb0856..71e0507a 100644 --- a/src/rabbit_mirror_queue_coordinator.erl +++ b/src/rabbit_mirror_queue_coordinator.erl @@ -356,7 +356,7 @@ handle_cast(request_length, State = #state { length_fun = LengthFun }) -> handle_cast({ensure_monitoring, Pids}, State = #state { monitors = Mons }) -> noreply(State #state { monitors = pmon:monitor_all(Pids, Mons) }); -handle_cast({delete_and_terminate, Reason}, State = #state { monitors = Mons }) -> +handle_cast({delete_and_terminate, Reason}, State) -> {stop, Reason, State}. handle_info(send_gm_heartbeat, State = #state { gm = GM }) -> -- cgit v1.2.1 From b81d948d3a36cc79b8132326f24b66fa9394df03 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Mon, 11 Jun 2012 11:24:34 +0100 Subject: No error when stopping a stopped broker, or starting a running broker --- packaging/debs/Debian/debian/rabbitmq-server.init | 1 + 1 file changed, 1 insertion(+) diff --git a/packaging/debs/Debian/debian/rabbitmq-server.init b/packaging/debs/Debian/debian/rabbitmq-server.init index 4bc32515..1774663d 100644 --- a/packaging/debs/Debian/debian/rabbitmq-server.init +++ b/packaging/debs/Debian/debian/rabbitmq-server.init @@ -143,6 +143,7 @@ start_stop_end() { 3) log_warning_msg "${DESC} already ${1}" log_end_msg 0 + RETVAL=0 ;; *) log_warning_msg "FAILED - check ${INIT_LOG_DIR}/startup_\{log, _err\}" -- cgit v1.2.1 From 4f22e6c9cdf7572da96d056483a93a0c0598788e Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Mon, 11 Jun 2012 11:26:48 +0100 Subject: Cosmetic (again) --- packaging/debs/Debian/debian/rabbitmq-server.init | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packaging/debs/Debian/debian/rabbitmq-server.init b/packaging/debs/Debian/debian/rabbitmq-server.init index 1774663d..c1352078 100644 --- a/packaging/debs/Debian/debian/rabbitmq-server.init +++ b/packaging/debs/Debian/debian/rabbitmq-server.init @@ -154,12 +154,12 @@ start_stop_end() { case "$1" in start) - log_daemon_msg "Starting ${DESC}:" $NAME + log_daemon_msg "Starting ${DESC}" $NAME start_rabbitmq start_stop_end "running" ;; stop) - log_daemon_msg "Stopping ${DESC}:" $NAME + log_daemon_msg "Stopping ${DESC}" $NAME stop_rabbitmq start_stop_end "stopped" ;; @@ -172,12 +172,12 @@ case "$1" in log_action_end_msg $RETVAL ;; force-reload|reload|restart) - log_daemon_msg "Restarting ${DESC}:" $NAME + log_daemon_msg "Restarting ${DESC}" $NAME restart_rabbitmq restart_end ;; try-restart) - log_daemon_msg "Restarting ${DESC}:" $NAME + log_daemon_msg "Restarting ${DESC}" $NAME restart_running_rabbitmq restart_end ;; -- cgit v1.2.1 From cb501f4a27672d78a7341ac96ba11db9b45cad3b Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 11 Jun 2012 14:24:29 +0100 Subject: cosmetic --- src/gm.erl | 87 ++++++++++++++++++++++---------------------------------------- 1 file changed, 31 insertions(+), 56 deletions(-) diff --git a/src/gm.erl b/src/gm.erl index 97c81ec6..30fcdc5d 100644 --- a/src/gm.erl +++ b/src/gm.erl @@ -876,11 +876,9 @@ flush_broadcast_buffer(State = #state { self = Self, %% View construction and inspection %% --------------------------------------------------------------------------- -needs_view_update(ReqVer, {Ver, _View}) -> - Ver < ReqVer. +needs_view_update(ReqVer, {Ver, _View}) -> Ver < ReqVer. -view_version({Ver, _View}) -> - Ver. +view_version({Ver, _View}) -> Ver. is_member_alive({dead, _Member}) -> false; is_member_alive(_) -> true. @@ -899,17 +897,13 @@ store_view_member(VMember = #view_member { id = Id }, {Ver, View}) -> with_view_member(Fun, View, Id) -> store_view_member(Fun(fetch_view_member(Id, View)), View). -fetch_view_member(Id, {_Ver, View}) -> - ?DICT:fetch(Id, View). +fetch_view_member(Id, {_Ver, View}) -> ?DICT:fetch(Id, View). -find_view_member(Id, {_Ver, View}) -> - ?DICT:find(Id, View). +find_view_member(Id, {_Ver, View}) -> ?DICT:find(Id, View). -blank_view(Ver) -> - {Ver, ?DICT:new()}. +blank_view(Ver) -> {Ver, ?DICT:new()}. -alive_view_members({_Ver, View}) -> - ?DICT:fetch_keys(View). +alive_view_members({_Ver, View}) -> ?DICT:fetch_keys(View). all_known_members({_Ver, View}) -> ?DICT:fold( @@ -1150,10 +1144,8 @@ ensure_neighbour(Ver, Self, {RealNeighbour, MRef}, Neighbour) -> end, {Neighbour, maybe_monitor(Neighbour, Self)}. -maybe_monitor(Self, Self) -> - undefined; -maybe_monitor(Other, _Self) -> - erlang:monitor(process, get_pid(Other)). +maybe_monitor( Self, Self) -> undefined; +maybe_monitor(Other, _Self) -> erlang:monitor(process, get_pid(Other)). check_neighbours(State = #state { self = Self, left = Left, @@ -1242,23 +1234,19 @@ find_member_or_blank(Id, MembersState) -> error -> blank_member() end. -erase_member(Id, MembersState) -> - ?DICT:erase(Id, MembersState). +erase_member(Id, MembersState) -> ?DICT:erase(Id, MembersState). blank_member() -> #member { pending_ack = queue:new(), last_pub = -1, last_ack = -1 }. -blank_member_state() -> - ?DICT:new(). +blank_member_state() -> ?DICT:new(). store_member(Id, MemberState, MembersState) -> ?DICT:store(Id, MemberState, MembersState). -prepare_members_state(MembersState) -> - ?DICT:to_list(MembersState). +prepare_members_state(MembersState) -> ?DICT:to_list(MembersState). -build_members_state(MembersStateList) -> - ?DICT:from_list(MembersStateList). +build_members_state(MembersStateList) -> ?DICT:from_list(MembersStateList). make_member(GroupName) -> {case read_group(GroupName) of @@ -1280,16 +1268,12 @@ get_pids(Ids) -> [Pid || {_Version, Pid} <- Ids]. %% Activity assembly %% --------------------------------------------------------------------------- -activity_nil() -> - queue:new(). +activity_nil() -> queue:new(). -activity_cons(_Id, [], [], Tail) -> - Tail; -activity_cons(Sender, Pubs, Acks, Tail) -> - queue:in({Sender, Pubs, Acks}, Tail). +activity_cons( _Id, [], [], Tail) -> Tail; +activity_cons(Sender, Pubs, Acks, Tail) -> queue:in({Sender, Pubs, Acks}, Tail). -activity_finalise(Activity) -> - queue:to_list(Activity). +activity_finalise(Activity) -> queue:to_list(Activity). maybe_send_activity([], _State) -> ok; @@ -1393,34 +1377,25 @@ purge_confirms(Confirms) -> %% Msg transformation %% --------------------------------------------------------------------------- -acks_from_queue(Q) -> - [PubNum || {PubNum, _Msg} <- queue:to_list(Q)]. +acks_from_queue(Q) -> [PubNum || {PubNum, _Msg} <- queue:to_list(Q)]. -pubs_from_queue(Q) -> - queue:to_list(Q). +pubs_from_queue(Q) -> queue:to_list(Q). -queue_from_pubs(Pubs) -> - queue:from_list(Pubs). +queue_from_pubs(Pubs) -> queue:from_list(Pubs). -apply_acks([], Pubs) -> - Pubs; -apply_acks(List, Pubs) -> - {_, Pubs1} = queue:split(length(List), Pubs), - Pubs1. +apply_acks( [], Pubs) -> Pubs; +apply_acks(List, Pubs) -> {_, Pubs1} = queue:split(length(List), Pubs), + Pubs1. join_pubs(Q, []) -> Q; join_pubs(Q, Pubs) -> queue:join(Q, queue_from_pubs(Pubs)). -last_ack([], LA) -> - LA; -last_ack(List, LA) -> - LA1 = lists:last(List), - true = LA1 > LA, %% ASSERTION - LA1. - -last_pub([], LP) -> - LP; -last_pub(List, LP) -> - {PubNum, _Msg} = lists:last(List), - true = PubNum > LP, %% ASSERTION - PubNum. +last_ack( [], LA) -> LA; +last_ack(List, LA) -> LA1 = lists:last(List), + true = LA1 > LA, %% ASSERTION + LA1. + +last_pub( [], LP) -> LP; +last_pub(List, LP) -> {PubNum, _Msg} = lists:last(List), + true = PubNum > LP, %% ASSERTION + PubNum. -- cgit v1.2.1 From 438020c95cc578ded249905898b649f77a411c28 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 11 Jun 2012 17:17:47 +0100 Subject: Revert d86c121dfb0d, since, of course, it breaks the test suite. --- src/rabbit.erl | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index fc5d4e93..fda489fe 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -323,8 +323,6 @@ boot() -> start_it(StartFun) -> try StartFun() - catch _:Reason -> - boot_error("Error description:~n~n~p~n~n", [Reason]) after %% give the error loggers some time to catch up timer:sleep(100) -- cgit v1.2.1 From 3ab7d351449a0122f5daf2edd7c583200e510edc Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Tue, 12 Jun 2012 14:41:22 +0100 Subject: revert last commit I had completely misunderstood what fhc does. I'll put the atomic changes to the cluster status file in the `rabbit_node_monitor'. --- src/rabbit.erl | 6 ++---- src/rabbit_file.erl | 43 +++++++++++++------------------------------ src/rabbit_mnesia.erl | 41 +++++++++++++---------------------------- src/rabbit_node_monitor.erl | 27 ++++++++++++--------------- 4 files changed, 40 insertions(+), 77 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 7ae6aa25..a9af7335 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -199,8 +199,7 @@ rabbit_queue_index, gen, dict, ordsets, file_handle_cache, rabbit_msg_store, array, rabbit_msg_store_ets_index, rabbit_msg_file, rabbit_exchange_type_fanout, rabbit_exchange_type_topic, mnesia, - mnesia_lib, rpc, mnesia_tm, qlc, sofs, proplists, credit_flow, pmon, - ssl_connection, ssl_record, gen_fsm, ssl]). + mnesia_lib, rpc, mnesia_tm, qlc, sofs, proplists, credit_flow, pmon]). %% HiPE compilation uses multiple cores anyway, but some bits are %% IO-bound so we can go faster if we parallelise a bit more. In @@ -264,7 +263,7 @@ maybe_hipe_compile() -> hipe_compile() -> Count = length(?HIPE_WORTHY), - io:format("~nHiPE compiling: |~s|~n |", + io:format("HiPE compiling: |~s|~n |", [string:copies("-", Count)]), T1 = erlang:now(), PidMRefs = [spawn_monitor(fun () -> [begin @@ -410,7 +409,6 @@ start(normal, []) -> end. stop(_State) -> - ok = rabbit_mnesia:update_cluster_nodes_status(), terminated_ok = error_logger:delete_report_handler(rabbit_error_logger), ok = rabbit_alarm:stop(), ok = case rabbit_mnesia:is_clustered() of diff --git a/src/rabbit_file.erl b/src/rabbit_file.erl index 02487d12..59df14f3 100644 --- a/src/rabbit_file.erl +++ b/src/rabbit_file.erl @@ -19,8 +19,7 @@ -include_lib("kernel/include/file.hrl"). -export([is_file/1, is_dir/1, file_size/1, ensure_dir/1, wildcard/2, list_dir/1]). --export([read_term_file/1, write_term_file/2, map_term_file/2, write_file/2, - write_file/3]). +-export([read_term_file/1, write_term_file/2, write_file/2, write_file/3]). -export([append_file/2, ensure_parent_dirs_exist/1]). -export([rename/2, delete/1, recursive_delete/1, recursive_copy/2]). -export([lock_file/1]). @@ -41,9 +40,6 @@ -spec(read_term_file/1 :: (file:filename()) -> {'ok', [any()]} | rabbit_types:error(any())). -spec(write_term_file/2 :: (file:filename(), [any()]) -> ok_or_error()). --spec(map_term_file/2 :: - (fun(([any()]) -> [any()]), file:filename()) - -> {'ok', [any()]} | rabbit_types:error(any())). -spec(write_file/2 :: (file:filename(), iodata()) -> ok_or_error()). -spec(write_file/3 :: (file:filename(), iodata(), [any()]) -> ok_or_error()). -spec(append_file/2 :: (file:filename(), string()) -> ok_or_error()). @@ -111,13 +107,18 @@ with_fhc_handle(Fun) -> after ok = file_handle_cache:release() end. -binary_to_terms(Data) -> - {ok, Tokens, _} = erl_scan:string(binary_to_list(Data)), - TokenGroups = group_tokens(Tokens), - [begin - {ok, Term} = erl_parse:parse_term(Tokens1), - Term - end || Tokens1 <- TokenGroups]. +read_term_file(File) -> + try + {ok, Data} = with_fhc_handle(fun () -> prim_file:read_file(File) end), + {ok, Tokens, _} = erl_scan:string(binary_to_list(Data)), + TokenGroups = group_tokens(Tokens), + {ok, [begin + {ok, Term} = erl_parse:parse_term(Tokens1), + Term + end || Tokens1 <- TokenGroups]} + catch + error:{badmatch, Error} -> Error + end. group_tokens(Ts) -> [lists:reverse(G) || G <- group_tokens([], Ts)]. @@ -126,28 +127,10 @@ group_tokens(Cur, []) -> [Cur]; group_tokens(Cur, [T = {dot, _} | Ts]) -> [[T | Cur] | group_tokens([], Ts)]; group_tokens(Cur, [T | Ts]) -> group_tokens([T | Cur], Ts). -read_term_file(File) -> - try - {ok, Data} = with_fhc_handle(fun () -> prim_file:read_file(File) end), - {ok, binary_to_terms(Data)} - catch - error:{badmatch, Error} -> Error - end. - write_term_file(File, Terms) -> write_file(File, list_to_binary([io_lib:format("~w.~n", [Term]) || Term <- Terms])). -map_term_file(Fun, File) -> - try - with_fhc_handle(fun () -> - {ok, Data} = prim_file:read_file(File), - {ok, Fun(binary_to_terms(Data))} - end) - catch - error:{badmatch, Error} -> Error - end. - write_file(Path, Data) -> write_file(Path, Data, []). %% write_file/3 and make_binary/1 are both based on corresponding diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index f8e6cac6..7aac7f3f 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -41,9 +41,8 @@ empty_ram_only_tables/0, copy_db/1, wait_for_tables/0, - update_cluster_nodes_status/0, - on_node_up/2, + on_node_up/1, on_node_down/1 ]). @@ -66,8 +65,7 @@ -export_type([node_type/0]). -type(node_type() :: disc | ram). --type(node_status() :: {[ordsets:ordset(node())], [ordsets:ordset(node())], - [ordsets:ordset(node())]}). +-type(node_status() :: {[node()], [node()], [node()]}). %% Main interface -spec(prepare/0 :: () -> 'ok'). @@ -97,10 +95,9 @@ -spec(create_tables/0 :: () -> 'ok'). -spec(copy_db/1 :: (file:filename()) -> rabbit_types:ok_or_error(any())). -spec(wait_for_tables/1 :: ([atom()]) -> 'ok'). --spec(update_cluster_nodes_status/0 :: () -> 'ok'). %% Hooks used in `rabbit_node_monitor' --spec(on_node_up/2 :: (node(), boolean()) -> 'ok'). +-spec(on_node_up/1 :: (node()) -> 'ok'). -spec(on_node_down/1 :: (node()) -> 'ok'). %% Functions used in internal rpc calls @@ -412,9 +409,9 @@ running_clustered_disc_nodes() -> cluster_status_if_running() -> case mnesia:system_info(is_running) of no -> error; - yes -> {ok, {ordsets:from_list(mnesia:system_info(db_nodes)), - ordsets:from_list(mnesia:table_info(schema, disc_copies)), - ordsets:from_list(mnesia:system_info(running_db_nodes))}} + yes -> {ok, {mnesia:system_info(db_nodes), + mnesia:table_info(schema, disc_copies), + mnesia:system_info(running_db_nodes)}} end. node_info() -> @@ -706,16 +703,8 @@ update_cluster_nodes_status() -> %% Hooks for `rabbit_node_monitor' %%-------------------------------------------------------------------- -on_node_up(Node, IsDiscNode) -> - {ok, _} = rabbit_file:map_term_file( - fun ([{AllNodes, DiscNodes, RunningNodes}]) -> - [{ordsets:add_element(Node, AllNodes), - case IsDiscNode of - true -> ordsets:add_element(Node, DiscNodes); - false -> DiscNodes - end, - ordsets:add_element(Node, RunningNodes)}] - end, cluster_nodes_status_filename()), +on_node_up(Node) -> + update_cluster_nodes_status(), case is_only_disc_node(Node) of true -> rabbit_log:info("cluster contains disc nodes again~n"); false -> ok @@ -726,13 +715,7 @@ on_node_down(Node) -> true -> rabbit_log:info("only running disc node went down~n"); false -> ok end, - {ok, _} = rabbit_file:map_term_file( - fun ([{AllNodes, DiscNodes, RunningNodes}]) -> - [{ordsets:del_element(Node, AllNodes), - ordsets:del_element(Node, DiscNodes), - ordsets:del_element(Node, RunningNodes)}] - end, cluster_nodes_status_filename()), - ok. + update_cluster_nodes_status(). %%-------------------------------------------------------------------- %% Internal helpers @@ -1026,9 +1009,11 @@ remove_node_if_mnesia_running(Node) -> %% propagated to all nodes case mnesia:del_table_copy(schema, Node) of {atomic, ok} -> - on_node_down(Node), + update_cluster_nodes_status(), + io:format("nodes: ~p~n", [running_clustered_disc_nodes()]), {_, []} = rpc:multicall(running_clustered_nodes(), - rabbit_mnesia, on_node_down, [Node]), + rabbit_mnesia, + update_cluster_nodes_status, []), ok; {aborted, Reason} -> {error, {failed_to_remove_node, Node, Reason}} diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index fee7c278..323cf0ce 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -22,7 +22,7 @@ -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). --export([notify_cluster/0, rabbit_running_on/2]). +-export([notify_cluster/0, rabbit_running_on/1]). -define(SERVER, ?MODULE). -define(RABBIT_UP_RPC_TIMEOUT, 2000). @@ -32,7 +32,7 @@ -ifdef(use_specs). -spec(start_link/0 :: () -> rabbit_types:ok_pid_or_error()). --spec(rabbit_running_on/2 :: (node(), boolean()) -> 'ok'). +-spec(rabbit_running_on/1 :: (node()) -> 'ok'). -spec(notify_cluster/0 :: () -> 'ok'). -endif. @@ -42,23 +42,20 @@ start_link() -> gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). -rabbit_running_on(Node, IsDiscNode) -> - gen_server:cast(rabbit_node_monitor, {rabbit_running_on, Node, IsDiscNode}). +rabbit_running_on(Node) -> + gen_server:cast(rabbit_node_monitor, {rabbit_running_on, Node}). notify_cluster() -> Node = node(), - IsDiscNode = rabbit_mnesia:is_disc_node(), - RunningNodes = rabbit_mnesia:running_clustered_nodes() -- [Node], - DiscNodes = rabbit_mnesia:all_clustered_disc_nodes(), + Nodes = rabbit_mnesia:running_clustered_nodes() -- [Node], %% notify other rabbits of this rabbit - case rpc:multicall(RunningNodes, rabbit_node_monitor, rabbit_running_on, - [Node, IsDiscNode], ?RABBIT_UP_RPC_TIMEOUT) of + case rpc:multicall(Nodes, rabbit_node_monitor, rabbit_running_on, + [Node], ?RABBIT_UP_RPC_TIMEOUT) of {_, [] } -> ok; {_, Bad} -> rabbit_log:info("failed to contact nodes ~p~n", [Bad]) end, %% register other active rabbits with this rabbit - [rabbit_running_on(N, ordsets:is_element(N, DiscNodes)) || - N <- RunningNodes], + [ rabbit_running_on(N) || N <- Nodes ], ok. %%-------------------------------------------------------------------- @@ -69,12 +66,12 @@ init([]) -> handle_call(_Request, _From, State) -> {noreply, State}. -handle_cast({rabbit_running_on, Node, IsDiscNode}, Nodes) -> +handle_cast({rabbit_running_on, Node}, Nodes) -> case ordsets:is_element(Node, Nodes) of true -> {noreply, Nodes}; false -> rabbit_log:info("rabbit on node ~p up~n", [Node]), erlang:monitor(process, {rabbit, Node}), - ok = handle_live_rabbit(Node, IsDiscNode), + ok = handle_live_rabbit(Node), {noreply, ordsets:add_element(Node, Nodes)} end; handle_cast(_Msg, State) -> @@ -104,6 +101,6 @@ handle_dead_rabbit(Node) -> ok = rabbit_alarm:on_node_down(Node), ok = rabbit_mnesia:on_node_down(Node). -handle_live_rabbit(Node, IsDiscNode) -> +handle_live_rabbit(Node) -> ok = rabbit_alarm:on_node_up(Node), - ok = rabbit_mnesia:on_node_up(Node, IsDiscNode). + ok = rabbit_mnesia:on_node_up(Node). -- cgit v1.2.1 From 88bdcd465e8d9a55bbeb554c939d93ede37fd7a0 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 12 Jun 2012 17:03:51 +0100 Subject: cosmetic: move comment to a better place --- ebin/rabbit_app.in | 2 ++ src/rabbit_reader.erl | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ebin/rabbit_app.in b/ebin/rabbit_app.in index b7d14f20..ffe112a0 100644 --- a/ebin/rabbit_app.in +++ b/ebin/rabbit_app.in @@ -22,6 +22,8 @@ {disk_free_limit, {mem_relative, 1.0}}, {msg_store_index_module, rabbit_msg_store_ets_index}, {backing_queue_module, rabbit_variable_queue}, + %% 0 ("no limit") would make a better default, but that + %% breaks the QPid Java client {frame_max, 131072}, {msg_store_file_size_limit, 16777216}, {queue_index_max_journal_entries, 262144}, diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index b773f83b..07b39d8c 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -736,8 +736,6 @@ handle_method0(_Method, #v1{connection_state = S}) -> rabbit_misc:protocol_error( channel_error, "unexpected method in connection state ~w", [S]). -%% Compute frame_max for this instance. Could simply use 0, but breaks -%% QPid Java client. server_frame_max() -> {ok, FrameMax} = application:get_env(rabbit, frame_max), FrameMax. -- cgit v1.2.1 From f69956305a2ee1a0fe7542ef5e630b5c4514058e Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 12 Jun 2012 18:34:34 +0100 Subject: refactor: move constant and give it a better name --- include/rabbit.hrl | 9 +++++++++ src/rabbit.erl | 2 +- src/rabbit_binary_generator.erl | 29 ++++++++--------------------- 3 files changed, 18 insertions(+), 22 deletions(-) diff --git a/include/rabbit.hrl b/include/rabbit.hrl index 5c73c8b8..fdc8caff 100644 --- a/include/rabbit.hrl +++ b/include/rabbit.hrl @@ -93,6 +93,15 @@ -define(PROTOCOL_VERSION, "AMQP 0-9-1 / 0-9 / 0-8"). -define(ERTS_MINIMUM, "5.6.3"). +%% EMPTY_FRAME_SIZE, 8 = 1 + 2 + 4 + 1 +%% - 1 byte of frame type +%% - 2 bytes of channel number +%% - 4 bytes of frame payload length +%% - 1 byte of payload trailer FRAME_END byte +%% See rabbit_binary_generator:check_empty_frame_size/0, an assertion +%% called at startup. +-define(EMPTY_FRAME_SIZE, 8). + -define(MAX_WAIT, 16#ffffffff). -define(HIBERNATE_AFTER_MIN, 1000). diff --git a/src/rabbit.erl b/src/rabbit.erl index fda489fe..a6594c30 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -35,7 +35,7 @@ -rabbit_boot_step({codec_correctness_check, [{description, "codec correctness check"}, {mfa, {rabbit_binary_generator, - check_empty_content_body_frame_size, + check_empty_frame_size, []}}, {requires, pre_boot}, {enables, external_infrastructure}]}). diff --git a/src/rabbit_binary_generator.erl b/src/rabbit_binary_generator.erl index d69376fb..4700fa31 100644 --- a/src/rabbit_binary_generator.erl +++ b/src/rabbit_binary_generator.erl @@ -18,20 +18,11 @@ -include("rabbit_framing.hrl"). -include("rabbit.hrl"). -%% EMPTY_CONTENT_BODY_FRAME_SIZE, 8 = 1 + 2 + 4 + 1 -%% - 1 byte of frame type -%% - 2 bytes of channel number -%% - 4 bytes of frame payload length -%% - 1 byte of payload trailer FRAME_END byte -%% See definition of check_empty_content_body_frame_size/0, -%% an assertion called at startup. --define(EMPTY_CONTENT_BODY_FRAME_SIZE, 8). - -export([build_simple_method_frame/3, build_simple_content_frames/4, build_heartbeat_frame/0]). -export([generate_table/1, encode_properties/2]). --export([check_empty_content_body_frame_size/0]). +-export([check_empty_frame_size/0]). -export([ensure_content_encoded/2, clear_encoded_content/1]). -export([map_exception/3]). @@ -53,7 +44,7 @@ -spec(generate_table/1 :: (rabbit_framing:amqp_table()) -> binary()). -spec(encode_properties/2 :: ([rabbit_framing:amqp_property_type()], [any()]) -> binary()). --spec(check_empty_content_body_frame_size/0 :: () -> 'ok'). +-spec(check_empty_frame_size/0 :: () -> 'ok'). -spec(ensure_content_encoded/2 :: (rabbit_types:content(), rabbit_types:protocol()) -> rabbit_types:encoded_content()). @@ -88,10 +79,8 @@ build_simple_content_frames(ChannelInt, Content, FrameMax, Protocol) -> [HeaderFrame | ContentFrames]. build_content_frames(FragsRev, FrameMax, ChannelInt) -> - BodyPayloadMax = if FrameMax == 0 -> - iolist_size(FragsRev); - true -> - FrameMax - ?EMPTY_CONTENT_BODY_FRAME_SIZE + BodyPayloadMax = if FrameMax == 0 -> iolist_size(FragsRev); + true -> FrameMax - ?EMPTY_FRAME_SIZE end, build_content_frames(0, [], BodyPayloadMax, [], lists:reverse(FragsRev), BodyPayloadMax, ChannelInt). @@ -257,15 +246,13 @@ encode_property(timestamp, Int) -> encode_property(table, Table) -> table_to_binary(Table). -check_empty_content_body_frame_size() -> - %% Intended to ensure that EMPTY_CONTENT_BODY_FRAME_SIZE is - %% defined correctly. +check_empty_frame_size() -> + %% Intended to ensure that EMPTY_FRAME_SIZE is defined correctly. ComputedSize = iolist_size(create_frame(?FRAME_BODY, 0, <<>>)), - if ComputedSize == ?EMPTY_CONTENT_BODY_FRAME_SIZE -> + if ComputedSize == ?EMPTY_FRAME_SIZE -> ok; true -> - exit({incorrect_empty_content_body_frame_size, - ComputedSize, ?EMPTY_CONTENT_BODY_FRAME_SIZE}) + exit({incorrect_empty_frame_size, ComputedSize, ?EMPTY_FRAME_SIZE}) end. ensure_content_encoded(Content = #content{properties_bin = PropBin, -- cgit v1.2.1 From bafc4a909649767f3ed11142d52f8416be62d680 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 12 Jun 2012 18:34:48 +0100 Subject: enforce frame_max --- src/rabbit_reader.erl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 07b39d8c..735e7b67 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -536,6 +536,11 @@ post_process_frame({method, MethodName, _}, _ChPid, post_process_frame(_Frame, _ChPid, State) -> control_throttle(State). +handle_input(frame_header, <>, + #v1{connection = #connection{frame_max = FrameMax}}) + when FrameMax /= 0 andalso PayloadSize > FrameMax - ?EMPTY_FRAME_SIZE -> + throw({frame_too_large, Type, Channel, PayloadSize, + FrameMax - ?EMPTY_FRAME_SIZE}); handle_input(frame_header, <>, State) -> ensure_stats_timer( switch_callback(State, {frame_payload, Type, Channel, PayloadSize}, -- cgit v1.2.1 From eca7e0725db080a249bca1720d06f5d2fb49e7cd Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Wed, 13 Jun 2012 14:13:55 +0100 Subject: attempt to handle stale slave pids during intialisation routines --- src/rabbit_amqqueue.erl | 2 + src/rabbit_mirror_queue_misc.erl | 61 ++++++++++++++++++++--------- src/rabbit_mirror_queue_slave.erl | 82 ++++++++++++++++++++++++++++++++++++--- 3 files changed, 121 insertions(+), 24 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index c1673504..9879d9b4 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -439,6 +439,7 @@ force_event_refresh() -> force_event_refresh(QNames) -> Qs = [Q || Q <- list(), lists:member(Q#amqqueue.name, QNames)], + %% BUG-24942/3: could one of these pids could be stale!? {_, Bad} = rabbit_misc:multi_call( [Q#amqqueue.pid || Q <- Qs], force_event_refresh), FailedPids = [Pid || {Pid, _Reason} <- Bad], @@ -569,6 +570,7 @@ set_maximum_since_use(QPid, Age) -> on_node_down(Node) -> rabbit_misc:execute_mnesia_tx_with_tail( fun () -> QsDels = + %% BUG-24942/3: could one of these pids could be stale!? qlc:e(qlc:q([{{QName, Pid}, delete_queue(QName)} || #amqqueue{name = QName, pid = Pid, slave_pids = []} diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 180677fe..f434f524 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -130,25 +130,48 @@ add_mirror(VHostPath, QueueName, MirrorNode) -> add_mirror(rabbit_misc:r(VHostPath, queue, QueueName), MirrorNode). add_mirror(Queue, MirrorNode) -> - if_mirrored_queue( - Queue, - fun (#amqqueue { name = Name, pid = QPid, slave_pids = SPids } = Q) -> - case [Pid || Pid <- [QPid | SPids], node(Pid) =:= MirrorNode] of - [] -> case rabbit_mirror_queue_slave_sup:start_child( - MirrorNode, [Q]) of - {ok, undefined} -> %% Already running - ok; - {ok, SPid} -> - rabbit_log:info( - "Adding mirror of ~s on node ~p: ~p~n", - [rabbit_misc:rs(Name), MirrorNode, SPid]), - ok; - Other -> - Other - end; - [_] -> {error, {queue_already_mirrored_on_node, MirrorNode}} - end - end). + if_mirrored_queue(Queue, + fun (#amqqueue { name = Name, pid = QPid, slave_pids = SPids } = Q) -> + case [Pid || Pid <- [QPid | SPids], node(Pid) =:= MirrorNode] of + [] -> + start_child(Name, MirrorNode, Q); + [SPid] -> + case rabbit_misc:is_process_alive(SPid) of + true -> + %% TODO: this condition is silently ignored - should + %% we really be arriving at this state at all? + {error,{queue_already_mirrored_on_node,MirrorNode}}; + false -> + %% BUG-24942: we need to strip out this dead pid + %% now, so we do so directly - perhaps we ought + %% to start the txn sooner in order to get a more + %% coarse grained lock though.... + %% + %% BUG-24942: QUESTION - do we need to report that + %% something has changed (either via gm or via + %% the rabbit_event mechanisms) here? + Q1 = Q#amqqueue{ slave_pids = (SPids -- [SPid]) }, + rabbit_misc:execute_mnesia_transaction( + fun() -> + ok = rabbit_amqqueue:store_queue(Q1) + end), + start_child(Name, MirrorNode, Q1) + end + end + end). + +start_child(Name, MirrorNode, Q) -> + case rabbit_mirror_queue_slave_sup:start_child(MirrorNode, [Q]) of + {ok, undefined} -> %% Already running + ok; + {ok, SPid} -> rabbit_log:info( + "Adding mirror of ~s on node ~p: ~p~n", + [rabbit_misc:rs(Name), MirrorNode, SPid]), + ok; + Other -> %% BUG-24942: should this not be checked for + %% error conditions or something? + Other + end. if_mirrored_queue(Queue, Fun) -> rabbit_amqqueue:with( diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index e412fbbc..87fbfdfb 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -106,15 +106,83 @@ init(#amqqueue { name = QueueName } = Q) -> [Q1 = #amqqueue { pid = QPid, slave_pids = MPids }] = mnesia:read({rabbit_queue, QueueName}), case [Pid || Pid <- [QPid | MPids], node(Pid) =:= Node] of - [] -> MPids1 = MPids ++ [Self], - ok = rabbit_amqqueue:store_queue( + [] -> + MPids1 = MPids ++ [Self], + ok = rabbit_amqqueue:store_queue( Q1 #amqqueue { slave_pids = MPids1 }), - {new, QPid}; - [SPid] -> true = rabbit_misc:is_process_alive(SPid), - existing + {new, QPid}; + [MPid] when MPid =:= QPid -> + case rabbit_misc:is_process_alive(MPid) of + true -> + %% Well damn, this shouldn't really happen - + %% what this appears to mean is that this + %% node is attempting to start a slave, but + %% a pid already exists for this node and + %% it is *already* the master! This state + %% probably requires a bit more thought. + existing; + false -> + %% We were the master, died, came back + %% online (but not after 3 days!) and + %% our death hasn't been registered by the + %% rest of our unbelieving flock yet! + %% + %% Actually, this is worse than it seems, + %% because the master is now down but the + %% pid in mnesia is from an old incarnation + %% of this node, so messages to it will be + %% silently dropped by Erlang (with some + %% helpful noise in the logs). + %% + %% I'm not sure how we're supposed to + %% recover from this. Won't messages get + %% lost, or at least lose their ordering + %% in this situation? Because a slave that + %% comes back online doesn't contain any + %% state (a slave will start off empty as + %% if they have no mirrored content at all) + %% then don't we fine ourselves in a slightly + %% inconsistent position here? + %% + %% In this scenario, I wonder whether we + %% should call init_with_backing_queue_state + %% to try and recover? + {stale, MPid} + end; + [SPid] -> + case rabbit_misc:is_process_alive(SPid) of + true -> + existing; + false -> + %% we need to replace this pid as it + %% is stale, from an old incarnation + %% of this node. + %% + %% NB: I *do* think we should completely + %% initialise this process before exiting + %% the mnesia txn in this case - once + %% we're *comitted* then I'd expect this + %% slave to become subject to any and all + %% invariants that members of the + %% slave_pids list should enforce, and + %% I'm *not* convinced this is possible + %% immediately after the txn commits + %% as the remaining initialisation code + %% could take arbitrary time to complete. + + MPids1 = (MPids -- [SPid]) ++ [Self], + ok = rabbit_amqqueue:store_queue( + Q1#amqqueue{slave_pids=MPids1}), + {new, QPid} + end end end) of {new, MPid} -> + %% BUG-24942: *should* we move the whole initialisation process (bar + %% obviously the trap_exit and erlang:monitor/2 calls) into the + %% mnesia transaction? We could optionally return {ready, State} + %% from it, and in that case immediately return.... + process_flag(trap_exit, true), %% amqqueue_process traps exits too. {ok, GM} = gm:start_link(QueueName, ?MODULE, [self()]), receive {joined, GM} -> @@ -150,6 +218,10 @@ init(#amqqueue { name = QueueName } = Q) -> {ok, State, hibernate, {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}; + {stale, OldPid} -> + %% NB: placeholder for doing something actually useful here... + %% such as {error, {stale_master_pid, OldPid}} + ignore; existing -> ignore end. -- cgit v1.2.1 From 0bcd6f7a5d73086353512bccae6564efd40596f5 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 14 Jun 2012 13:08:56 +0100 Subject: Report more clearly when we fail to monitor disk space on an unsupported platform. --- src/rabbit_disk_monitor.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_disk_monitor.erl b/src/rabbit_disk_monitor.erl index d9e8e8e4..ed29bd80 100644 --- a/src/rabbit_disk_monitor.erl +++ b/src/rabbit_disk_monitor.erl @@ -168,8 +168,8 @@ get_disk_free(Dir, {unix, _}) -> parse_free_unix(rabbit_misc:os_cmd("/bin/df -kP " ++ Dir)); get_disk_free(Dir, {win32, _}) -> parse_free_win32(os:cmd("dir /-C /W \"" ++ Dir ++ [$"])); -get_disk_free(_, _) -> - unknown. +get_disk_free(_, Platform) -> + {unknown, Platform}. parse_free_unix(CommandResult) -> [_, Stats | _] = string:tokens(CommandResult, "\n"), -- cgit v1.2.1 From 7755742bb43796e32c086247fbaf0cd5d17522e6 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 14 Jun 2012 13:09:30 +0100 Subject: Disable disk space alarms by default. --- ebin/rabbit_app.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ebin/rabbit_app.in b/ebin/rabbit_app.in index ffe112a0..96fdd0aa 100644 --- a/ebin/rabbit_app.in +++ b/ebin/rabbit_app.in @@ -19,7 +19,7 @@ {ssl_listeners, []}, {ssl_options, []}, {vm_memory_high_watermark, 0.4}, - {disk_free_limit, {mem_relative, 1.0}}, + {disk_free_limit, {mem_relative, 0}}, {msg_store_index_module, rabbit_msg_store_ets_index}, {backing_queue_module, rabbit_variable_queue}, %% 0 ("no limit") would make a better default, but that -- cgit v1.2.1 From 253da8b76322449db4120aa52928e4ead5e2931c Mon Sep 17 00:00:00 2001 From: Steve Losh Date: Thu, 14 Jun 2012 14:59:28 +0100 Subject: clean up listeners during node start/recovery --- src/rabbit.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rabbit.erl b/src/rabbit.erl index fda489fe..c53db720 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -537,6 +537,7 @@ boot_delegate() -> rabbit_sup:start_supervisor_child(delegate_sup, [Count]). recover() -> + rabbit_networking:on_node_down(node()), rabbit_binding:recover(rabbit_exchange:recover(), rabbit_amqqueue:start()). maybe_insert_default_data() -> -- cgit v1.2.1 From 6c7fcdc61d7915ed9ec4f4be72f52cc26ad84fa7 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Thu, 14 Jun 2012 15:24:59 +0100 Subject: Rotate logs without consuming excessive memory --- src/rabbit_error_logger_file_h.erl | 2 +- src/rabbit_file.erl | 15 ++++++++++++--- src/rabbit_sasl_report_file_h.erl | 2 +- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/rabbit_error_logger_file_h.erl b/src/rabbit_error_logger_file_h.erl index 042ab23c..0bb0ebbd 100644 --- a/src/rabbit_error_logger_file_h.erl +++ b/src/rabbit_error_logger_file_h.erl @@ -33,7 +33,7 @@ %% Used only when swapping handlers in log rotation init({{File, Suffix}, []}) -> - case rabbit_file:append_file(File, Suffix) of + case rabbit_file:copy_file(File, {[File, Suffix], [append]}) of ok -> file:delete(File), ok; {error, Error} -> diff --git a/src/rabbit_file.erl b/src/rabbit_file.erl index 59df14f3..b0cceb90 100644 --- a/src/rabbit_file.erl +++ b/src/rabbit_file.erl @@ -22,7 +22,7 @@ -export([read_term_file/1, write_term_file/2, write_file/2, write_file/3]). -export([append_file/2, ensure_parent_dirs_exist/1]). -export([rename/2, delete/1, recursive_delete/1, recursive_copy/2]). --export([lock_file/1]). +-export([copy_file/2, lock_file/1]). %%---------------------------------------------------------------------------- @@ -53,6 +53,8 @@ -spec(recursive_copy/2 :: (file:filename(), file:filename()) -> rabbit_types:ok_or_error({file:filename(), file:filename(), any()})). +-spec(copy_file/2 :: + (file:filename(), file:filename()) -> ok_or_error()). -spec(lock_file/1 :: (file:filename()) -> rabbit_types:ok_or_error('eexist')). -endif. @@ -102,9 +104,12 @@ read_file_info(File) -> with_fhc_handle(fun () -> prim_file:read_file_info(File) end). with_fhc_handle(Fun) -> - ok = file_handle_cache:obtain(), + with_fhc_handle(1, Fun). + +with_fhc_handle(N, Fun) -> + [ ok = file_handle_cache:obtain() || _ <- lists:seq(1, N)], try Fun() - after ok = file_handle_cache:release() + after [ ok = file_handle_cache:release() || _ <- lists:seq(1, N)] end. read_term_file(File) -> @@ -241,6 +246,10 @@ is_symlink_no_handle(File) -> _ -> false end. +copy_file(File1, File2) -> + with_fhc_handle(2, fun () -> file:copy(File1, File2) + end). + recursive_copy(Src, Dest) -> %% Note that this uses the 'file' module and, hence, shouldn't be %% run on many processes at once. diff --git a/src/rabbit_sasl_report_file_h.erl b/src/rabbit_sasl_report_file_h.erl index e8beecfe..9863c611 100644 --- a/src/rabbit_sasl_report_file_h.erl +++ b/src/rabbit_sasl_report_file_h.erl @@ -34,7 +34,7 @@ %% Used only when swapping handlers and performing %% log rotation init({{File, Suffix}, []}) -> - case rabbit_file:append_file(File, Suffix) of + case rabbit_file:copy_file(File, {[File, Suffix], [append]}) of ok -> file:delete(File), ok; {error, Error} -> -- cgit v1.2.1 From ff0fe300a6c76599d02a926c0bb3cb5db6cc9276 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 14 Jun 2012 15:25:51 +0100 Subject: Improve comment --- src/rabbit_amqqueue.erl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index c7747571..56bff257 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -166,7 +166,9 @@ [queue_name, channel_pid, consumer_tag, ack_required]). start() -> - on_node_down(node()), %% clear out remnants of old incarnation + %% Clear out remnants of old incarnation, in case we restarted + %% faster than other nodes handled DOWN messages from us. + on_node_down(node()), DurableQueues = find_durable_queues(), {ok, BQ} = application:get_env(rabbit, backing_queue_module), ok = BQ:start([QName || #amqqueue{name = QName} <- DurableQueues]), -- cgit v1.2.1 From 8374d76a0bc311c4bc17b1ec63bc84b5fb57cea8 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 14 Jun 2012 15:26:18 +0100 Subject: This has little to do with recovery - move to networking boot. --- src/rabbit.erl | 1 - src/rabbit_networking.erl | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index c53db720..fda489fe 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -537,7 +537,6 @@ boot_delegate() -> rabbit_sup:start_supervisor_child(delegate_sup, [Count]). recover() -> - rabbit_networking:on_node_down(node()), rabbit_binding:recover(rabbit_exchange:recover(), rabbit_amqqueue:start()). maybe_insert_default_data() -> diff --git a/src/rabbit_networking.erl b/src/rabbit_networking.erl index 94a5a2b7..89ec7084 100644 --- a/src/rabbit_networking.erl +++ b/src/rabbit_networking.erl @@ -117,6 +117,9 @@ %%---------------------------------------------------------------------------- boot() -> + %% Clear out remnants of old incarnation, in case we restarted + %% faster than other nodes handled DOWN messages from us. + on_node_down(node()), ok = start(), ok = boot_tcp(), ok = boot_ssl(). -- cgit v1.2.1 From af8542126d6d1387645ecb211a62a99e86f2e2aa Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Thu, 14 Jun 2012 15:39:43 +0100 Subject: dummy commit to recover default --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 49bf926a..bd9e1c0c 100644 --- a/Makefile +++ b/Makefile @@ -363,3 +363,4 @@ ifneq "$(strip $(patsubst clean%,,$(patsubst %clean,,$(TESTABLEGOALS))))" "" endif .PHONY: run-qc + -- cgit v1.2.1 From 9f0212fe7467e309c0cd59815be9a59a49455422 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Thu, 14 Jun 2012 15:40:15 +0100 Subject: revert dummy commit --- Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/Makefile b/Makefile index bd9e1c0c..49bf926a 100644 --- a/Makefile +++ b/Makefile @@ -363,4 +363,3 @@ ifneq "$(strip $(patsubst clean%,,$(patsubst %clean,,$(TESTABLEGOALS))))" "" endif .PHONY: run-qc - -- cgit v1.2.1 From 0b89fc81ac1e40d5beeb5e2b873ee744e736ea51 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Thu, 14 Jun 2012 16:28:10 +0100 Subject: Rotate logs differently because type file:mode() is not exported --- src/rabbit_error_logger_file_h.erl | 2 +- src/rabbit_file.erl | 18 +++++++++++------- src/rabbit_sasl_report_file_h.erl | 2 +- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/rabbit_error_logger_file_h.erl b/src/rabbit_error_logger_file_h.erl index 0bb0ebbd..aa47ed61 100644 --- a/src/rabbit_error_logger_file_h.erl +++ b/src/rabbit_error_logger_file_h.erl @@ -33,7 +33,7 @@ %% Used only when swapping handlers in log rotation init({{File, Suffix}, []}) -> - case rabbit_file:copy_file(File, {[File, Suffix], [append]}) of + case rabbit_file:append_file_using_copy(File, Suffix) of ok -> file:delete(File), ok; {error, Error} -> diff --git a/src/rabbit_file.erl b/src/rabbit_file.erl index b0cceb90..d8955dee 100644 --- a/src/rabbit_file.erl +++ b/src/rabbit_file.erl @@ -20,9 +20,9 @@ -export([is_file/1, is_dir/1, file_size/1, ensure_dir/1, wildcard/2, list_dir/1]). -export([read_term_file/1, write_term_file/2, write_file/2, write_file/3]). --export([append_file/2, ensure_parent_dirs_exist/1]). +-export([append_file_using_copy/2, append_file/2, ensure_parent_dirs_exist/1]). -export([rename/2, delete/1, recursive_delete/1, recursive_copy/2]). --export([copy_file/2, lock_file/1]). +-export([lock_file/1]). %%---------------------------------------------------------------------------- @@ -42,6 +42,7 @@ -spec(write_term_file/2 :: (file:filename(), [any()]) -> ok_or_error()). -spec(write_file/2 :: (file:filename(), iodata()) -> ok_or_error()). -spec(write_file/3 :: (file:filename(), iodata(), [any()]) -> ok_or_error()). +-spec(append_file_using_copy/2 :: (file:filename(), string()) -> ok_or_error()). -spec(append_file/2 :: (file:filename(), string()) -> ok_or_error()). -spec(ensure_parent_dirs_exist/1 :: (string()) -> 'ok'). -spec(rename/2 :: @@ -53,8 +54,6 @@ -spec(recursive_copy/2 :: (file:filename(), file:filename()) -> rabbit_types:ok_or_error({file:filename(), file:filename(), any()})). --spec(copy_file/2 :: - (file:filename(), file:filename()) -> ok_or_error()). -spec(lock_file/1 :: (file:filename()) -> rabbit_types:ok_or_error('eexist')). -endif. @@ -170,6 +169,14 @@ make_binary(List) -> {error, Reason} end. +%% this version will not read the entire file into memory first +append_file_using_copy(File, Suffix) -> + case with_fhc_handle(2, fun () -> + file:copy(File, {[File, Suffix], [append]}) + end) of + {ok, _BytesCopied} -> ok; + Error -> Error + end. append_file(File, Suffix) -> case read_file_info(File) of @@ -246,9 +253,6 @@ is_symlink_no_handle(File) -> _ -> false end. -copy_file(File1, File2) -> - with_fhc_handle(2, fun () -> file:copy(File1, File2) - end). recursive_copy(Src, Dest) -> %% Note that this uses the 'file' module and, hence, shouldn't be diff --git a/src/rabbit_sasl_report_file_h.erl b/src/rabbit_sasl_report_file_h.erl index 9863c611..5f4080e9 100644 --- a/src/rabbit_sasl_report_file_h.erl +++ b/src/rabbit_sasl_report_file_h.erl @@ -34,7 +34,7 @@ %% Used only when swapping handlers and performing %% log rotation init({{File, Suffix}, []}) -> - case rabbit_file:copy_file(File, {[File, Suffix], [append]}) of + case rabbit_file:append_file_using_copy(File, Suffix) of ok -> file:delete(File), ok; {error, Error} -> -- cgit v1.2.1 From a2c2b284d45ba13a926215d870ed9a5846f85357 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 14 Jun 2012 16:43:44 +0100 Subject: Ensure we only clear out records of queues that are actually dead. --- src/rabbit_amqqueue.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 56bff257..eca1017c 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -576,7 +576,8 @@ on_node_down(Node) -> #amqqueue{name = QName, pid = Pid, slave_pids = []} <- mnesia:table(rabbit_queue), - node(Pid) == Node])), + node(Pid) == Node andalso + not is_process_alive(Pid)])), {Qs, Dels} = lists:unzip(QsDels), T = rabbit_binding:process_deletions( lists:foldl(fun rabbit_binding:combine_deletions/2, -- cgit v1.2.1 From c1681d2f066d4935bc1643eb645ec922247d437d Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 14 Jun 2012 16:45:24 +0100 Subject: Strip this out again, we can't make it idempotent and it might just remove the listener records just after we've readded them. --- src/rabbit_networking.erl | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/rabbit_networking.erl b/src/rabbit_networking.erl index 89ec7084..94a5a2b7 100644 --- a/src/rabbit_networking.erl +++ b/src/rabbit_networking.erl @@ -117,9 +117,6 @@ %%---------------------------------------------------------------------------- boot() -> - %% Clear out remnants of old incarnation, in case we restarted - %% faster than other nodes handled DOWN messages from us. - on_node_down(node()), ok = start(), ok = boot_tcp(), ok = boot_ssl(). -- cgit v1.2.1 From 06eb66bc24c1c37f4e5197d53f03a281071a558b Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 14 Jun 2012 17:33:26 +0100 Subject: Actually, it seems slightly cleaner to do this. --- ebin/rabbit_app.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ebin/rabbit_app.in b/ebin/rabbit_app.in index 96fdd0aa..3cbb33af 100644 --- a/ebin/rabbit_app.in +++ b/ebin/rabbit_app.in @@ -19,7 +19,7 @@ {ssl_listeners, []}, {ssl_options, []}, {vm_memory_high_watermark, 0.4}, - {disk_free_limit, {mem_relative, 0}}, + {disk_free_limit, 0}, {msg_store_index_module, rabbit_msg_store_ets_index}, {backing_queue_module, rabbit_variable_queue}, %% 0 ("no limit") would make a better default, but that -- cgit v1.2.1 From 35f4f77cd44045e0adf882eb33af7f3c9702a429 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Thu, 14 Jun 2012 18:07:01 +0100 Subject: add a "leave cluster" hook in `rabbit_node_monitor', handle cluster nodes joins/departures better --- src/rabbit_mnesia.erl | 46 +++++++++++++++++++++++++++++--------------- src/rabbit_node_monitor.erl | 47 ++++++++++++++++++++++++++++++++++----------- 2 files changed, 66 insertions(+), 27 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 7aac7f3f..679f1c84 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -42,8 +42,9 @@ copy_db/1, wait_for_tables/0, - on_node_up/1, - on_node_down/1 + on_node_up/2, + on_node_down/1, + on_node_leave/1 ]). %% Used internally in rpc calls @@ -65,7 +66,8 @@ -export_type([node_type/0]). -type(node_type() :: disc | ram). --type(node_status() :: {[node()], [node()], [node()]}). +-type(node_status() :: {ordsets:ordset(node()), ordsets:ordset(node()), + ordsets:ordset(node())}). %% Main interface -spec(prepare/0 :: () -> 'ok'). @@ -97,8 +99,9 @@ -spec(wait_for_tables/1 :: ([atom()]) -> 'ok'). %% Hooks used in `rabbit_node_monitor' --spec(on_node_up/1 :: (node()) -> 'ok'). +-spec(on_node_up/2 :: (node(), boolean()) -> 'ok'). -spec(on_node_down/1 :: (node()) -> 'ok'). +-spec(on_node_leave/1 :: (node()) -> 'ok'). %% Functions used in internal rpc calls -spec(cluster_status_if_running/0 :: () -> {'ok', node_status()} | 'error'). @@ -409,9 +412,9 @@ running_clustered_disc_nodes() -> cluster_status_if_running() -> case mnesia:system_info(is_running) of no -> error; - yes -> {ok, {mnesia:system_info(db_nodes), - mnesia:table_info(schema, disc_copies), - mnesia:system_info(running_db_nodes)}} + yes -> {ok, {ordsets:from_list(mnesia:system_info(db_nodes)), + ordsets:from_list(mnesia:table_info(schema, disc_copies)), + ordsets:from_list(mnesia:system_info(running_db_nodes))}} end. node_info() -> @@ -703,8 +706,15 @@ update_cluster_nodes_status() -> %% Hooks for `rabbit_node_monitor' %%-------------------------------------------------------------------- -on_node_up(Node) -> - update_cluster_nodes_status(), +on_node_up(Node, IsDiscNode) -> + {AllNodes, DiscNodes, RunningNodes} = read_cluster_nodes_status(), + write_cluster_nodes_status({ordsets:add_element(Node, AllNodes), + case IsDiscNode of + true -> ordsets:add_element(Node, + DiscNodes); + false -> DiscNodes + end, + ordsets:add_element(Node, RunningNodes)}), case is_only_disc_node(Node) of true -> rabbit_log:info("cluster contains disc nodes again~n"); false -> ok @@ -715,7 +725,15 @@ on_node_down(Node) -> true -> rabbit_log:info("only running disc node went down~n"); false -> ok end, - update_cluster_nodes_status(). + {AllNodes, DiscNodes, RunningNodes} = read_cluster_nodes_status(), + write_cluster_nodes_status({AllNodes, DiscNodes, + ordsets:del_element(Node, RunningNodes)}). + +on_node_leave(Node) -> + {AllNodes, DiscNodes, RunningNodes} = read_cluster_nodes_status(), + write_cluster_nodes_status({ordsets:del_element(Node, AllNodes), + ordsets:del_element(Node, DiscNodes), + ordsets:del_element(Node, RunningNodes)}). %%-------------------------------------------------------------------- %% Internal helpers @@ -1009,11 +1027,7 @@ remove_node_if_mnesia_running(Node) -> %% propagated to all nodes case mnesia:del_table_copy(schema, Node) of {atomic, ok} -> - update_cluster_nodes_status(), - io:format("nodes: ~p~n", [running_clustered_disc_nodes()]), - {_, []} = rpc:multicall(running_clustered_nodes(), - rabbit_mnesia, - update_cluster_nodes_status, []), + rabbit_node_monitor:notify_leave_cluster(Node), ok; {aborted, Reason} -> {error, {failed_to_remove_node, Node, Reason}} @@ -1027,7 +1041,7 @@ leave_cluster() -> remove_node_remotely(Removee) -> case running_clustered_nodes() -- [Removee] of [] -> - ok; + {error, no_running_cluster_nodes}; RunningNodes -> case lists:any( fun (Node) -> diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index 323cf0ce..a25d0fe0 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -22,7 +22,8 @@ -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). --export([notify_cluster/0, rabbit_running_on/1]). +-export([rabbit_running_on/2, rabbit_left_cluster/1, notify_cluster/0, + notify_leave_cluster/1]). -define(SERVER, ?MODULE). -define(RABBIT_UP_RPC_TIMEOUT, 2000). @@ -32,8 +33,10 @@ -ifdef(use_specs). -spec(start_link/0 :: () -> rabbit_types:ok_pid_or_error()). --spec(rabbit_running_on/1 :: (node()) -> 'ok'). +-spec(rabbit_running_on/2 :: (node(), boolean()) -> 'ok'). +-spec(rabbit_left_cluster/1 :: (node()) -> 'ok'). -spec(notify_cluster/0 :: () -> 'ok'). +-spec(notify_leave_cluster/1 :: (node()) -> 'ok'). -endif. @@ -42,15 +45,23 @@ start_link() -> gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). +rabbit_running_on(Node, IsDiscNode) -> + gen_server:cast(rabbit_node_monitor, + {rabbit_running_on, Node, IsDiscNode}). + rabbit_running_on(Node) -> - gen_server:cast(rabbit_node_monitor, {rabbit_running_on, Node}). + rabbit_running_on(Node, rabbit_mnesia:is_disc_node()). + +rabbit_left_cluster(Node) -> + gen_server:cast(rabbit_node_monitor, {rabbit_left_cluster, Node}). notify_cluster() -> Node = node(), Nodes = rabbit_mnesia:running_clustered_nodes() -- [Node], %% notify other rabbits of this rabbit case rpc:multicall(Nodes, rabbit_node_monitor, rabbit_running_on, - [Node], ?RABBIT_UP_RPC_TIMEOUT) of + [Node, rabbit_mnesia:is_disc_node()], + ?RABBIT_UP_RPC_TIMEOUT) of {_, [] } -> ok; {_, Bad} -> rabbit_log:info("failed to contact nodes ~p~n", [Bad]) end, @@ -58,6 +69,16 @@ notify_cluster() -> [ rabbit_running_on(N) || N <- Nodes ], ok. +notify_leave_cluster(Node) -> + Nodes = rabbit_mnesia:running_clustered_nodes() -- [node()], + rabbit_left_cluster(Node), + case rpc:multicall(Nodes, rabbit_node_monitor, rabbit_left_cluster, [Node], + ?RABBIT_UP_RPC_TIMEOUT) of + {_, [] } -> ok; + {_, Bad} -> rabbit_log:info("failed to contact nodes ~p~n", [Bad]) + end, + ok. + %%-------------------------------------------------------------------- init([]) -> @@ -66,14 +87,17 @@ init([]) -> handle_call(_Request, _From, State) -> {noreply, State}. -handle_cast({rabbit_running_on, Node}, Nodes) -> +handle_cast({rabbit_running_on, Node, IsDiscNode}, Nodes) -> case ordsets:is_element(Node, Nodes) of true -> {noreply, Nodes}; false -> rabbit_log:info("rabbit on node ~p up~n", [Node]), erlang:monitor(process, {rabbit, Node}), - ok = handle_live_rabbit(Node), + ok = handle_live_rabbit(Node, IsDiscNode), {noreply, ordsets:add_element(Node, Nodes)} end; +handle_cast({rabbit_left_cluster, Node}, Nodes) -> + ok = rabbit_mnesia:on_node_leave(Node), + {noreply, Nodes}; handle_cast(_Msg, State) -> {noreply, State}. @@ -96,11 +120,12 @@ code_change(_OldVsn, State, _Extra) -> %% of nodes. We really only need to execute some of these statements %% on *one* node, rather than all of them. handle_dead_rabbit(Node) -> + ok = rabbit_mnesia:on_node_down(Node), ok = rabbit_networking:on_node_down(Node), ok = rabbit_amqqueue:on_node_down(Node), - ok = rabbit_alarm:on_node_down(Node), - ok = rabbit_mnesia:on_node_down(Node). + ok = rabbit_alarm:on_node_down(Node). + +handle_live_rabbit(Node, IsDiscNode) -> + ok = rabbit_mnesia:on_node_up(Node, IsDiscNode), + ok = rabbit_alarm:on_node_up(Node). -handle_live_rabbit(Node) -> - ok = rabbit_alarm:on_node_up(Node), - ok = rabbit_mnesia:on_node_up(Node). -- cgit v1.2.1 From 0ecb063239ad0e8ea7150b180b57a4b48a4b1ba0 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 18 Jun 2012 10:25:10 +0100 Subject: make the error/reason for amqp heartbeat timeouts clearer in the logs --- src/rabbit_reader.erl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index c675bc4e..32b66a72 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -229,8 +229,8 @@ start_connection(Parent, ChannelSupSupPid, Collector, StartHeartbeatFun, Deb, log(info, "closing AMQP connection ~p (~s)~n", [self(), ConnStr]) catch Ex -> log(case Ex of + {heartbeat_timeout, _} -> warning; connection_closed_abruptly -> warning; - {timeout, _CState} -> amqp_heartbeat_timeout; _ -> error end, "closing AMQP connection ~p (~s):~n~p~n", [self(), ConnStr, Ex]) @@ -314,8 +314,8 @@ handle_other(handshake_timeout, _Deb, State) -> throw({handshake_timeout, State#v1.callback}); handle_other(timeout, Deb, State = #v1{connection_state = closed}) -> mainloop(Deb, State); -handle_other(timeout, _Deb, #v1{connection_state = S}) -> - throw({timeout, S}); +handle_other(heartbeat_timeout, _Deb, #v1{connection_state = S}) -> + throw({heartbeat_timeout, S}); handle_other({'$gen_call', From, {shutdown, Explanation}}, Deb, State) -> {ForceTermination, NewState} = terminate(Explanation, State), gen_server:reply(From, ok), @@ -684,7 +684,7 @@ handle_method0(#'connection.tune_ok'{frame_max = FrameMax, Frame = rabbit_binary_generator:build_heartbeat_frame(), SendFun = fun() -> catch rabbit_net:send(Sock, Frame) end, Parent = self(), - ReceiveFun = fun() -> Parent ! timeout end, + ReceiveFun = fun() -> Parent ! heartbeat_timeout end, Heartbeater = SHF(Sock, ClientHeartbeat, SendFun, ClientHeartbeat, ReceiveFun), State#v1{connection_state = opening, -- cgit v1.2.1 From 269607e566c743c9e7a701a26e7ed58c252c824b Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Mon, 18 Jun 2012 11:15:42 +0100 Subject: Only one version of append_file is required --- src/rabbit_error_logger_file_h.erl | 2 +- src/rabbit_file.erl | 28 ++-------------------------- src/rabbit_sasl_report_file_h.erl | 2 +- 3 files changed, 4 insertions(+), 28 deletions(-) diff --git a/src/rabbit_error_logger_file_h.erl b/src/rabbit_error_logger_file_h.erl index aa47ed61..042ab23c 100644 --- a/src/rabbit_error_logger_file_h.erl +++ b/src/rabbit_error_logger_file_h.erl @@ -33,7 +33,7 @@ %% Used only when swapping handlers in log rotation init({{File, Suffix}, []}) -> - case rabbit_file:append_file_using_copy(File, Suffix) of + case rabbit_file:append_file(File, Suffix) of ok -> file:delete(File), ok; {error, Error} -> diff --git a/src/rabbit_file.erl b/src/rabbit_file.erl index d8955dee..74c65106 100644 --- a/src/rabbit_file.erl +++ b/src/rabbit_file.erl @@ -20,7 +20,7 @@ -export([is_file/1, is_dir/1, file_size/1, ensure_dir/1, wildcard/2, list_dir/1]). -export([read_term_file/1, write_term_file/2, write_file/2, write_file/3]). --export([append_file_using_copy/2, append_file/2, ensure_parent_dirs_exist/1]). +-export([append_file/2, ensure_parent_dirs_exist/1]). -export([rename/2, delete/1, recursive_delete/1, recursive_copy/2]). -export([lock_file/1]). @@ -42,7 +42,6 @@ -spec(write_term_file/2 :: (file:filename(), [any()]) -> ok_or_error()). -spec(write_file/2 :: (file:filename(), iodata()) -> ok_or_error()). -spec(write_file/3 :: (file:filename(), iodata(), [any()]) -> ok_or_error()). --spec(append_file_using_copy/2 :: (file:filename(), string()) -> ok_or_error()). -spec(append_file/2 :: (file:filename(), string()) -> ok_or_error()). -spec(ensure_parent_dirs_exist/1 :: (string()) -> 'ok'). -spec(rename/2 :: @@ -169,8 +168,7 @@ make_binary(List) -> {error, Reason} end. -%% this version will not read the entire file into memory first -append_file_using_copy(File, Suffix) -> +append_file(File, Suffix) -> case with_fhc_handle(2, fun () -> file:copy(File, {[File, Suffix], [append]}) end) of @@ -178,28 +176,6 @@ append_file_using_copy(File, Suffix) -> Error -> Error end. -append_file(File, Suffix) -> - case read_file_info(File) of - {ok, FInfo} -> append_file(File, FInfo#file_info.size, Suffix); - {error, enoent} -> append_file(File, 0, Suffix); - Error -> Error - end. - -append_file(_, _, "") -> - ok; -append_file(File, 0, Suffix) -> - with_fhc_handle(fun () -> - case prim_file:open([File, Suffix], [append]) of - {ok, Fd} -> prim_file:close(Fd); - Error -> Error - end - end); -append_file(File, _, Suffix) -> - case with_fhc_handle(fun () -> prim_file:read_file(File) end) of - {ok, Data} -> write_file([File, Suffix], Data, [append]); - Error -> Error - end. - ensure_parent_dirs_exist(Filename) -> case ensure_dir(Filename) of ok -> ok; diff --git a/src/rabbit_sasl_report_file_h.erl b/src/rabbit_sasl_report_file_h.erl index 5f4080e9..e8beecfe 100644 --- a/src/rabbit_sasl_report_file_h.erl +++ b/src/rabbit_sasl_report_file_h.erl @@ -34,7 +34,7 @@ %% Used only when swapping handlers and performing %% log rotation init({{File, Suffix}, []}) -> - case rabbit_file:append_file_using_copy(File, Suffix) of + case rabbit_file:append_file(File, Suffix) of ok -> file:delete(File), ok; {error, Error} -> -- cgit v1.2.1 From df25fd23ef52329a3a19d196e151d9f8b83795fe Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Mon, 18 Jun 2012 12:12:40 +0100 Subject: Cosmetic --- src/rabbit_file.erl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/rabbit_file.erl b/src/rabbit_file.erl index 74c65106..5937a335 100644 --- a/src/rabbit_file.erl +++ b/src/rabbit_file.erl @@ -229,7 +229,6 @@ is_symlink_no_handle(File) -> _ -> false end. - recursive_copy(Src, Dest) -> %% Note that this uses the 'file' module and, hence, shouldn't be %% run on many processes at once. -- cgit v1.2.1 From 7a56b4aab2cf21711a8dccd41fd97353d0380ada Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 18 Jun 2012 12:21:22 +0100 Subject: revert the log level for error for heartbeat timeouts --- src/rabbit_reader.erl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 32b66a72..bd5cf588 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -229,7 +229,6 @@ start_connection(Parent, ChannelSupSupPid, Collector, StartHeartbeatFun, Deb, log(info, "closing AMQP connection ~p (~s)~n", [self(), ConnStr]) catch Ex -> log(case Ex of - {heartbeat_timeout, _} -> warning; connection_closed_abruptly -> warning; _ -> error end, "closing AMQP connection ~p (~s):~n~p~n", -- cgit v1.2.1 From 6ab8eeb017bb67fd3d1c705d60ac8c2ccbbe45bd Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 19 Jun 2012 14:35:37 +0100 Subject: Default to 1GB limit, check every 10s. --- ebin/rabbit_app.in | 2 +- src/rabbit_disk_monitor.erl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ebin/rabbit_app.in b/ebin/rabbit_app.in index 3cbb33af..523b54ce 100644 --- a/ebin/rabbit_app.in +++ b/ebin/rabbit_app.in @@ -19,7 +19,7 @@ {ssl_listeners, []}, {ssl_options, []}, {vm_memory_high_watermark, 0.4}, - {disk_free_limit, 0}, + {disk_free_limit, 1000000000}, %% 1GB {msg_store_index_module, rabbit_msg_store_ets_index}, {backing_queue_module, rabbit_variable_queue}, %% 0 ("no limit") would make a better default, but that diff --git a/src/rabbit_disk_monitor.erl b/src/rabbit_disk_monitor.erl index ed29bd80..58375abb 100644 --- a/src/rabbit_disk_monitor.erl +++ b/src/rabbit_disk_monitor.erl @@ -27,7 +27,7 @@ set_check_interval/1, get_disk_free/0]). -define(SERVER, ?MODULE). --define(DEFAULT_DISK_CHECK_INTERVAL, 60000). +-define(DEFAULT_DISK_CHECK_INTERVAL, 10000). -record(state, {dir, limit, -- cgit v1.2.1 From 58351e5fa31ff2267d912913df12d126af886d1d Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Thu, 21 Jun 2012 10:45:34 +0100 Subject: Run additional xref checks in the broker when plugins-src is available. Output full paths and line numbers for analysis. Differentiate between warnings and error where appropriate. --- Makefile | 8 ++ check_xref | 290 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 298 insertions(+) create mode 100755 check_xref diff --git a/Makefile b/Makefile index 49bf926a..b637edc8 100644 --- a/Makefile +++ b/Makefile @@ -111,9 +111,17 @@ plugins: PLUGINS_SRC_DIR="" $(MAKE) -C "$(PLUGINS_SRC_DIR)" plugins-dist PLUGINS_DIST_DIR="$(CURDIR)/$(PLUGINS_DIR)" VERSION=$(VERSION) echo "Put your EZs here and use rabbitmq-plugins to enable them." > $(PLUGINS_DIR)/README rm -f $(PLUGINS_DIR)/rabbit_common*.ez + +# add -q to remove printout of warnings.... +check-xref: $(BEAM_TARGETS) $(PLUGINS_DIR) + rm -rf lib # just in case! + ./check_xref $(PLUGINS_DIR) + else plugins: # Not building plugins +check-xref: +# No xref checks enabled endif $(DEPS_FILE): $(SOURCES) $(INCLUDES) diff --git a/check_xref b/check_xref new file mode 100755 index 00000000..70b922c1 --- /dev/null +++ b/check_xref @@ -0,0 +1,290 @@ +#!/usr/bin/env escript +%% -*- erlang -*- +-mode(compile). + +%% 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 VMware, Inc. +%% Copyright (c) 2010-2012 VMware, Inc. All rights reserved. +%% + +main(["-h"]) -> + io:format("usage: check_xref PluginDirectory (options)~n" + "options:~n" + " -q - quiet mode (only prints errors)~n" + " -X - disables all filters~n"); +main([PluginsDir|Argv]) -> + Quiet = lists:member("-q", Argv), + if Quiet == true -> put({?MODULE, quiet}, true); + true -> ok + end, + + DisableFilters = lists:member("-X", Argv), + if DisableFilters == true -> put({?MODULE, no_filters}, true); + true -> ok + end, + + {ok, Cwd} = file:get_cwd(), + code:add_pathz(filename:join(Cwd, "ebin")), + LibDir = filename:join(Cwd, "lib"), + + try + check(Cwd, PluginsDir, LibDir, checks()) + after + %% TODO: bootstrap file_handle_cache and use + %% rabbit_file:recursive_delete instead of this... + _ = os:cmd("rm -rf " ++ LibDir) + end. + +check(Cwd, PluginsDir, LibDir, Checks) -> + {ok, Plugins} = file:list_dir(PluginsDir), + ok = file:make_dir(LibDir), + [begin + Source = filename:join(PluginsDir, Plugin), + Target = filename:join(LibDir, Plugin), + IsExternal = external_dependency(Plugin), + AppN = case IsExternal of + true -> filename:join(LibDir, unmangle_name(Plugin)); + false -> filename:join(LibDir, + filename:basename(Plugin, ".ez")) + end, + + report(info, "mkdir -p ~s~n", [Target]), + filelib:ensure_dir(Target), + + report(info, "cp ~s ~s~n", [Source, Target]), + {ok, _} = file:copy(Source, Target), + + report(info, "unzip -d ~s ~s~n", [LibDir, Target]), + {ok, _} = zip:unzip(Target, [{cwd, LibDir}]), + + UnpackDir = filename:join(LibDir, filename:basename(Target, ".ez")), + report(info, "mv ~s ~s~n", [UnpackDir, AppN]), + ok = file:rename(UnpackDir, AppN), + + code:add_patha(filename:join(AppN, "ebin")), + if IsExternal =:= true -> + App = list_to_atom(hd(string:tokens(filename:basename(AppN), "-"))), + report(info, "loading ~p~n", [App]), + application:load(App), + store_third_party(App); + true -> + ok + end + end || Plugin <- Plugins, + lists:suffix(".ez", Plugin)], + + RabbitAppEbin = filename:join([LibDir, "rabbit", "ebin"]), + filelib:ensure_dir(filename:join(RabbitAppEbin, "foo")), + {ok, Beams} = file:list_dir("ebin"), + [{ok, _} = file:copy(filename:join("ebin", Beam), + filename:join(RabbitAppEbin, Beam)) || Beam <- Beams], + xref:start(?MODULE), + xref:set_default(?MODULE, [{verbose, false}, {warnings, false}]), + xref:set_library_path(?MODULE, code:get_path()), + xref:add_release(?MODULE, Cwd, {name, rabbit}), + store_unresolved_calls(), + Results = lists:flatten([perform_analysis(Q) || Q <- Checks]), + report(Results). + +%% +%% Analysis +%% + +perform_analysis({Query, Description, Severity}) -> + perform_analysis({Query, Description, Severity, fun(_) -> return end}); +perform_analysis({Query, Description, Severity, Filter}) -> + report_progress("Checking whether any code ~s " + "(~s)~n", [Description, Query]), + case analyse(Query) of + {ok, Analysis} -> + [filter(Result, Filter) || + Result <- process_analysis(Query, Description, + Severity, Analysis)]; + {error, Module, Reason} -> + {analysis_error, {Module, Reason}} + end. + +partition(Results) -> + lists:partition(fun({{_, L}, _}) -> L =:= error end, Results). + +analyse(Query) when is_atom(Query) -> + xref:analyse(?MODULE, Query, [{verbose, false}]); +analyse(Query) when is_list(Query) -> + xref:q(?MODULE, Query). + +process_analysis(Query, Tag, Severity, Analysis) when is_atom(Query) -> + [{{Tag, Severity}, MFA} || MFA <- Analysis]; +process_analysis(Query, Tag, Severity, Analysis) when is_list(Query) -> + [{{Tag, Severity}, Result} || Result <- Analysis]. + +checks() -> + [{"(XXL)(Lin) ((XC - UC) || (XU - X - B))", + "has call to undefined function(s)", + error, filters()}, + {"(Lin) (L - LU)", "has unused local function(s)", + error, filters()}, + {"(Lin) (LU * (X - XU))", + "has exported function(s) only used locally", + warning, filters()}, + {"(Lin) (DF * (XU + LU))", "used deprecated function(s)", + warning, filters()}]. +% {"(Lin) (X - XU)", "possibly unused export", +% warning, fun filter_unused/1}]. + +%% +%% noise filters (can be disabled with -X) - strip uninteresting analyses +%% + +filter(Result, Filter) -> + case Filter(Result) of + false -> Result; + true -> [] %% NB: this gets flattened out later on.... + end. + +filters() -> + case get({?MODULE, no_filters}) of + true -> fun(_) -> false end; + _ -> filter_chain([fun is_unresolved_call/1, fun is_callback/1, + fun is_unused/1, fun is_irrelevant/1]) + end. + +filter_chain(FnChain) -> + fun(AnalysisResult) -> + lists:foldl(fun(F, false) -> F(cleanup(AnalysisResult)); + (_F, true) -> true + end, false, FnChain) + end. + +cleanup({{_, _},{{{{_,_,_}=MFA1,_},{{_,_,_}=MFA2,_}},_}}) -> {MFA1, MFA2}; +cleanup({{_, _},{{{_,_,_}=MFA1,_},{{_,_,_}=MFA2,_}}}) -> {MFA1, MFA2}; +cleanup({{_, _},{{_,_,_}=MFA1,{_,_,_}=MFA2},_}) -> {MFA1, MFA2}; +cleanup({{_, _},{{_,_,_}=MFA1,{_,_,_}=MFA2}}) -> {MFA1, MFA2}; +cleanup({{_, _}, {_,_,_}=MFA}) -> MFA; +cleanup({{_, _}, {{_,_,_}=MFA,_}}) -> MFA; +cleanup({{_,_,_}=MFA, {_,_,_}}) -> MFA; +cleanup({{_,_,_}=MFA, {_,_,_},_}) -> MFA; +cleanup(Other) -> Other. + +is_irrelevant({{M,_,_}, {_,_,_}}) -> + is_irrelevant(M); +is_irrelevant({M,_,_}) -> + is_irrelevant(M); +is_irrelevant(Mod) when is_atom(Mod) -> + lists:member(Mod, get({?MODULE, third_party})). + +is_unused({{_,_,_}=MFA, {_,_,_}}) -> + is_unused(MFA); +is_unused({M,_F,_A}) -> + lists:suffix("_tests", atom_to_list(M)); +is_unused(_) -> + false. + +is_unresolved_call({_, F, A}) -> + UC = get({?MODULE, unresolved_calls}), + sets:is_element({'$M_EXPR', F, A}, UC); +is_unresolved_call(_) -> + false. + +%% TODO: cache this.... +is_callback({M,_,_}=MFA) -> + Attributes = M:module_info(attributes), + Behaviours = proplists:append_values(behaviour, Attributes), + {_, Callbacks} = lists:foldl(fun acc_behaviours/2, {M, []}, Behaviours), + lists:member(MFA, Callbacks); +is_callback(_) -> + false. + +acc_behaviours(B, {M, CB}=Acc) -> + case catch(B:behaviour_info(callbacks)) of + [{_,_}|_]=Callbacks -> + {M, CB ++ [{M, F, A} || {F,A} <- Callbacks]}; + _ -> + Acc + end. + +%% +%% reporting/output +%% + +report(Results) -> + [report_failures(F) || F <- Results], + {Errors, Warnings} = partition(Results), + report(info, "Completed: ~p errors, ~p warnings~n", + [length(Errors), length(Warnings)]), + if length(Errors) > 0 -> halt(1); + true -> halt(0) + end. + +report_failures({analysis_error, {Mod, Reason}}) -> + report(error, "~s:0 Analysis Error: ~p~n", [source_file(Mod), Reason]); +report_failures({{Tag, Level}, {{{{M,_,_},L},{{M2,F2,A2},_}},_}}) -> + report(Level, "~s:~w ~s ~p:~p/~p~n", + [source_file(M), L, Tag, M2, F2, A2]); +report_failures({{Tag, Level}, {{M,F,A},L}}) -> + report(Level, "~s:~w ~s ~p:~p/~p~n", [source_file(M), L, Tag, M, F, A]); +report_failures({{Tag, Level}, {M,F,A}}) -> + report(Level, "~s:unknown ~s ~p:~p/~p~n", [source_file(M), Tag, M, F, A]); +report_failures(Term) -> + report(error, "Ignoring ~p~n", [Term]), + ok. + +report_progress(Fmt, Args) -> + report(info, Fmt, Args). + +report(Level, Fmt, Args) -> + case get({?MODULE, quiet}) of + true -> if Level=:=error -> do_report(lookup_prefix(Level), Fmt, Args); + true -> ok + end; + _ -> do_report(lookup_prefix(Level), Fmt, Args) + end. + +do_report(Prefix, Fmt, Args) -> + io:format(Prefix ++ Fmt, Args). + +lookup_prefix(error) -> "ERROR: "; +lookup_prefix(warning) -> "WARNING: "; +lookup_prefix(info) -> "INFO: ". + +source_file(M) -> + proplists:get_value(source, M:module_info(compile)). + +%% +%% setup/code-path/file-system ops +%% + +store_third_party(App) -> + {ok, AppConfig} = application:get_all_key(App), + case get({?MODULE, third_party}) of + undefined -> + put({?MODULE, third_party}, + proplists:get_value(modules, AppConfig)); + Modules -> + put({?MODULE, third_party}, + proplists:get_value(modules, AppConfig) ++ Modules) + end. + +%% TODO: this ought not to be maintained in such a fashion +external_dependency(Path) -> + lists:any(fun(P) -> lists:prefix(P, Path) end, + ["mochiweb", "webmachine", "rfc4627", "eldap"]). + +unmangle_name(Path) -> + [Name, Vsn|_] = re:split(Path, "-", [{return, list}]), + string:join([Name, Vsn], "-"). + +store_unresolved_calls() -> + {ok, UCFull} = analyse("UC"), + UC = [MFA || {_, {_,_,_}=MFA} <- UCFull], + put({?MODULE, unresolved_calls}, sets:from_list(UC)). -- cgit v1.2.1 From c24700c5d48641efe422ba6cfdf52206213178f8 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Thu, 21 Jun 2012 11:05:19 +0100 Subject: Oops - implement the default filter properly --- check_xref | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/check_xref b/check_xref index 70b922c1..1ef88b01 100755 --- a/check_xref +++ b/check_xref @@ -102,7 +102,7 @@ check(Cwd, PluginsDir, LibDir, Checks) -> %% perform_analysis({Query, Description, Severity}) -> - perform_analysis({Query, Description, Severity, fun(_) -> return end}); + perform_analysis({Query, Description, Severity, fun(_) -> false end}); perform_analysis({Query, Description, Severity, Filter}) -> report_progress("Checking whether any code ~s " "(~s)~n", [Description, Query]), -- cgit v1.2.1 From 770eb7d1956a0fc5a58e39e48fcdb013d954b0ec Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Fri, 22 Jun 2012 12:57:51 +0100 Subject: Remove whitespace --- scripts/rabbitmq-plugins.bat | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/rabbitmq-plugins.bat b/scripts/rabbitmq-plugins.bat index 3c268726..c67a0263 100755 --- a/scripts/rabbitmq-plugins.bat +++ b/scripts/rabbitmq-plugins.bat @@ -43,9 +43,9 @@ if "!RABBITMQ_ENABLED_PLUGINS_FILE!"=="" ( set RABBITMQ_ENABLED_PLUGINS_FILE=!RABBITMQ_BASE!\enabled_plugins ) -if "!RABBITMQ_PLUGINS_DIR!"=="" ( - set RABBITMQ_PLUGINS_DIR=!TDP0!..\plugins -) +if "!RABBITMQ_PLUGINS_DIR!"=="" ( + set RABBITMQ_PLUGINS_DIR=!TDP0!..\plugins +) "!ERLANG_HOME!\bin\erl.exe" -pa "!TDP0!..\ebin" -noinput -hidden -sname rabbitmq-plugins!RANDOM! -s rabbit_plugins_main -enabled_plugins_file "!RABBITMQ_ENABLED_PLUGINS_FILE!" -plugins_dist_dir "!RABBITMQ_PLUGINS_DIR:\=/!" -extra !STAR! -- cgit v1.2.1 From 1a89d54582628a4a42daf89dac3256e3af17099c Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 22 Jun 2012 16:20:04 +0100 Subject: Revert the wrapper around the copy in append_file/2 --- src/rabbit_file.erl | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/rabbit_file.erl b/src/rabbit_file.erl index 5937a335..a95f8f26 100644 --- a/src/rabbit_file.erl +++ b/src/rabbit_file.erl @@ -168,7 +168,24 @@ make_binary(List) -> {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 + {ok, FInfo} -> append_file(File, FInfo#file_info.size, Suffix); + {error, enoent} -> append_file(File, 0, Suffix); + Error -> Error + end. + +append_file(_, _, "") -> + ok; +append_file(File, 0, Suffix) -> + with_fhc_handle(fun () -> + case prim_file:open([File, Suffix], [append]) of + {ok, Fd} -> prim_file:close(Fd); + Error -> Error + end + end); +append_file(File, _, Suffix) -> case with_fhc_handle(2, fun () -> file:copy(File, {[File, Suffix], [append]}) end) of -- cgit v1.2.1 From bab2f50f6d28e31b4d51910f3c24751ec6655b7e Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 22 Jun 2012 18:14:30 +0100 Subject: Scream --- scripts/rabbitmq-server.bat | 4 ++-- scripts/rabbitmq-service.bat | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/scripts/rabbitmq-server.bat b/scripts/rabbitmq-server.bat index b8822739..167f272e 100755 --- a/scripts/rabbitmq-server.bat +++ b/scripts/rabbitmq-server.bat @@ -86,8 +86,8 @@ if "!RABBITMQ_ENABLED_PLUGINS_FILE!"=="" ( set RABBITMQ_ENABLED_PLUGINS_FILE=!RABBITMQ_BASE!\enabled_plugins ) -if "!RABBITMQ_PLUGINS_DIR!"=="" ( - set RABBITMQ_PLUGINS_DIR=!TDP0!..\plugins +if "!RABBITMQ_PLUGINS_DIR!"=="" ( + set RABBITMQ_PLUGINS_DIR=!TDP0!..\plugins ) set RABBITMQ_EBIN_ROOT=!TDP0!..\ebin diff --git a/scripts/rabbitmq-service.bat b/scripts/rabbitmq-service.bat index 849bedcf..4758c861 100755 --- a/scripts/rabbitmq-service.bat +++ b/scripts/rabbitmq-service.bat @@ -154,7 +154,10 @@ if "!RABBITMQ_ENABLED_PLUGINS_FILE!"=="" ( set RABBITMQ_ENABLED_PLUGINS_FILE=!RABBITMQ_BASE!\enabled_plugins ) -set RABBITMQ_PLUGINS_DIR=!TDP0!..\plugins +if "!RABBITMQ_PLUGINS_DIR!"=="" ( + set RABBITMQ_PLUGINS_DIR=!TDP0!..\plugins +) + set RABBITMQ_EBIN_ROOT=!TDP0!..\ebin if "!RABBITMQ_CONFIG_FILE!"=="" ( -- cgit v1.2.1 From 24e9466d265cdbcaf2267fb17e56b273f1db31e7 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 23 Jun 2012 08:48:54 +0100 Subject: consistency: leave the 'ok' matching to the caller ...like we do everywhere else in this API --- src/rabbit_amqqueue.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index eca1017c..e3ed4113 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -511,7 +511,7 @@ basic_consume(#amqqueue{pid = QPid}, NoAck, ChPid, Limiter, Limiter, ConsumerTag, ExclusiveConsume, OkMsg}). basic_cancel(#amqqueue{pid = QPid}, ChPid, ConsumerTag, OkMsg) -> - ok = delegate_call(QPid, {basic_cancel, ChPid, ConsumerTag, OkMsg}). + delegate_call(QPid, {basic_cancel, ChPid, ConsumerTag, OkMsg}). notify_sent(QPid, ChPid) -> Key = {consumer_credit_to, QPid}, -- cgit v1.2.1 From d17b23652ce8d164e7bd86d0185474b2798d5764 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 25 Jun 2012 15:43:27 +0100 Subject: Close down the slave's GM the same way the master's GM does. --- src/rabbit_mirror_queue_slave.erl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index e412fbbc..2109bdd8 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -351,10 +351,15 @@ handle_msg([_SPid], _From, {ensure_monitoring, _Pid}) -> ok; handle_msg([SPid], _From, {process_death, Pid}) -> inform_deaths(SPid, [Pid]); +handle_msg([CPid], _From, {delete_and_terminate, Reason} = Msg) -> + ok = gen_server2:cast(CPid, {gm, Msg}), + {stop, Reason}; handle_msg([SPid], _From, Msg) -> ok = gen_server2:cast(SPid, {gm, Msg}). inform_deaths(SPid, Deaths) -> + %% TODO can we get rid of this with_exit_handler use? See bug + %% 24988 comment 13. rabbit_misc:with_exit_handler( fun () -> {stop, normal} end, fun () -> -- cgit v1.2.1 From 6889a6b02c7820b9bcd0e89c6606d7474c569131 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 25 Jun 2012 15:45:41 +0100 Subject: Ignore normal exits; do not try to change the ring topology as it shuts down. --- src/gm.erl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/gm.erl b/src/gm.erl index 30fcdc5d..a046d3ef 100644 --- a/src/gm.erl +++ b/src/gm.erl @@ -647,7 +647,7 @@ handle_info(flush, State) -> noreply( flush_broadcast_buffer(State #state { broadcast_timer = undefined })); -handle_info({'DOWN', MRef, process, _Pid, _Reason}, +handle_info({'DOWN', MRef, process, _Pid, Reason}, State = #state { self = Self, left = Left, right = Right, @@ -661,8 +661,10 @@ handle_info({'DOWN', MRef, process, _Pid, _Reason}, {_, {Member1, MRef}} -> Member1; _ -> undefined end, - case Member of - undefined -> + case {Member, Reason} of + {undefined, _} -> + noreply(State); + {_, normal} -> noreply(State); _ -> View1 = -- cgit v1.2.1 From c20b3baa4bf74815b767406add4fb61e813f103d Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 25 Jun 2012 15:52:19 +0100 Subject: The spec for gm:group_members/1 says it returns a list of pids - let's do that. --- src/gm.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gm.erl b/src/gm.erl index a046d3ef..abf9b0eb 100644 --- a/src/gm.erl +++ b/src/gm.erl @@ -558,7 +558,7 @@ handle_call(group_members, _From, reply(not_joined, State); handle_call(group_members, _From, State = #state { view = View }) -> - reply(alive_view_members(View), State); + reply(get_pids(alive_view_members(View)), State); handle_call({add_on_right, _NewMember}, _From, State = #state { members_state = undefined }) -> -- cgit v1.2.1 From 9ce01fded365bc954d0dec9c67c2149d1df8ae53 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 25 Jun 2012 15:52:47 +0100 Subject: And finally, have the master wait for all the slaves on deletion. --- src/rabbit_mirror_queue_master.erl | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index 4e71cc43..f7fd0425 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -127,10 +127,21 @@ terminate(Reason, delete_and_terminate(Reason, State = #state { gm = GM, backing_queue = BQ, backing_queue_state = BQS }) -> + Slaves = [Pid || Pid <- gm:group_members(GM), node(Pid) =/= node()], ok = gm:broadcast(GM, {delete_and_terminate, Reason}), + MRefs = [erlang:monitor(process, S) || S <- Slaves], + monitor_wait(MRefs), State #state { backing_queue_state = BQ:delete_and_terminate(Reason, BQS), set_delivered = 0 }. +monitor_wait([]) -> + ok; +monitor_wait([MRef | MRefs]) -> + receive({'DOWN', MRef, process, _Pid, _Info}) -> + ok + end, + monitor_wait(MRefs). + purge(State = #state { gm = GM, backing_queue = BQ, backing_queue_state = BQS }) -> -- cgit v1.2.1 From f954714b4dd70d6f1b2f598773f82724b6153120 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 25 Jun 2012 16:03:30 +0100 Subject: normal -> leaving the ring, change topology shutdown -> ring shutting down, do not change topology gm_tests now pass again. --- src/gm.erl | 2 +- src/rabbit_mirror_queue_coordinator.erl | 4 ++-- src/rabbit_mirror_queue_slave.erl | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/gm.erl b/src/gm.erl index abf9b0eb..b06a99e8 100644 --- a/src/gm.erl +++ b/src/gm.erl @@ -664,7 +664,7 @@ handle_info({'DOWN', MRef, process, _Pid, Reason}, case {Member, Reason} of {undefined, _} -> noreply(State); - {_, normal} -> + {_, shutdown} -> noreply(State); _ -> View1 = diff --git a/src/rabbit_mirror_queue_coordinator.erl b/src/rabbit_mirror_queue_coordinator.erl index 71e0507a..64756719 100644 --- a/src/rabbit_mirror_queue_coordinator.erl +++ b/src/rabbit_mirror_queue_coordinator.erl @@ -405,9 +405,9 @@ handle_msg([CPid], _From, request_length = Msg) -> ok = gen_server2:cast(CPid, Msg); handle_msg([CPid], _From, {ensure_monitoring, _Pids} = Msg) -> ok = gen_server2:cast(CPid, Msg); -handle_msg([CPid], _From, {delete_and_terminate, Reason} = Msg) -> +handle_msg([CPid], _From, {delete_and_terminate, _Reason} = Msg) -> ok = gen_server2:cast(CPid, Msg), - {stop, Reason}; + {stop, shutdown}; handle_msg([_CPid], _From, _Msg) -> ok. diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 2109bdd8..df5f35ad 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -351,9 +351,9 @@ handle_msg([_SPid], _From, {ensure_monitoring, _Pid}) -> ok; handle_msg([SPid], _From, {process_death, Pid}) -> inform_deaths(SPid, [Pid]); -handle_msg([CPid], _From, {delete_and_terminate, Reason} = Msg) -> +handle_msg([CPid], _From, {delete_and_terminate, _Reason} = Msg) -> ok = gen_server2:cast(CPid, {gm, Msg}), - {stop, Reason}; + {stop, shutdown}; handle_msg([SPid], _From, Msg) -> ok = gen_server2:cast(SPid, {gm, Msg}). -- cgit v1.2.1 From 19cb51475cfc9531ae976f62875f8f386b599835 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 25 Jun 2012 16:28:24 +0100 Subject: Get rid of this with_exit_handler use. --- src/rabbit_mirror_queue_slave.erl | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index df5f35ad..481897e6 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -358,18 +358,10 @@ handle_msg([SPid], _From, Msg) -> ok = gen_server2:cast(SPid, {gm, Msg}). inform_deaths(SPid, Deaths) -> - %% TODO can we get rid of this with_exit_handler use? See bug - %% 24988 comment 13. - rabbit_misc:with_exit_handler( - fun () -> {stop, normal} end, - fun () -> - case gen_server2:call(SPid, {gm_deaths, Deaths}, infinity) of - ok -> - ok; - {promote, CPid} -> - {become, rabbit_mirror_queue_coordinator, [CPid]} - end - end). + case gen_server2:call(SPid, {gm_deaths, Deaths}, infinity) of + ok -> ok; + {promote, CPid} -> {become, rabbit_mirror_queue_coordinator, [CPid]} + end. %% --------------------------------------------------------------------------- %% Others -- cgit v1.2.1 From 911853812f4875be87ce1d3c87f24cd40c4e0398 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 25 Jun 2012 17:19:05 +0100 Subject: OK, we do actually need a special way to determine the difference between: leave the group -> normal node shutting down -> shutdown queue deletion -> {shutdown, do_not_heal} --- src/gm.erl | 2 +- src/rabbit_mirror_queue_coordinator.erl | 2 +- src/rabbit_mirror_queue_slave.erl | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/gm.erl b/src/gm.erl index b06a99e8..9d7bed8c 100644 --- a/src/gm.erl +++ b/src/gm.erl @@ -664,7 +664,7 @@ handle_info({'DOWN', MRef, process, _Pid, Reason}, case {Member, Reason} of {undefined, _} -> noreply(State); - {_, shutdown} -> + {_, {shutdown, do_not_heal}} -> noreply(State); _ -> View1 = diff --git a/src/rabbit_mirror_queue_coordinator.erl b/src/rabbit_mirror_queue_coordinator.erl index 64756719..8d2531e6 100644 --- a/src/rabbit_mirror_queue_coordinator.erl +++ b/src/rabbit_mirror_queue_coordinator.erl @@ -407,7 +407,7 @@ handle_msg([CPid], _From, {ensure_monitoring, _Pids} = Msg) -> ok = gen_server2:cast(CPid, Msg); handle_msg([CPid], _From, {delete_and_terminate, _Reason} = Msg) -> ok = gen_server2:cast(CPid, Msg), - {stop, shutdown}; + {stop, {shutdown, do_not_heal}}; handle_msg([_CPid], _From, _Msg) -> ok. diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 481897e6..4bef2b7d 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -353,7 +353,7 @@ handle_msg([SPid], _From, {process_death, Pid}) -> inform_deaths(SPid, [Pid]); handle_msg([CPid], _From, {delete_and_terminate, _Reason} = Msg) -> ok = gen_server2:cast(CPid, {gm, Msg}), - {stop, shutdown}; + {stop, {shutdown, do_not_heal}}; handle_msg([SPid], _From, Msg) -> ok = gen_server2:cast(SPid, {gm, Msg}). -- cgit v1.2.1 From e47bbfb289a388826b35f0adb6ffadfadda5ea20 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 25 Jun 2012 18:05:51 +0100 Subject: Explain this a bit. --- src/rabbit_exchange_decorator.erl | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/rabbit_exchange_decorator.erl b/src/rabbit_exchange_decorator.erl index bfb78201..b40ceda9 100644 --- a/src/rabbit_exchange_decorator.erl +++ b/src/rabbit_exchange_decorator.erl @@ -16,6 +16,16 @@ -module(rabbit_exchange_decorator). +%% This is like an exchange type except that: +%% +%% 1) It applies to all exchanges as soon as it is installed, therefore +%% 2) It is not allowed to affect validation, so no validate/1 or +%% assert_args_equivalence/2 +%% 3) It also can't affect routing +%% +%% It's possible in the future we might relax 3), or even make these +%% able to manipulate messages as they are published. + -ifdef(use_specs). -type(tx() :: 'transaction' | 'none'). -- cgit v1.2.1 From 018b921cfaee21810dfbc158ae72e54289922f29 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 26 Jun 2012 11:40:49 +0100 Subject: Maybe better name? --- src/gm.erl | 2 +- src/rabbit_mirror_queue_coordinator.erl | 2 +- src/rabbit_mirror_queue_slave.erl | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/gm.erl b/src/gm.erl index 9d7bed8c..f88ed18f 100644 --- a/src/gm.erl +++ b/src/gm.erl @@ -664,7 +664,7 @@ handle_info({'DOWN', MRef, process, _Pid, Reason}, case {Member, Reason} of {undefined, _} -> noreply(State); - {_, {shutdown, do_not_heal}} -> + {_, {shutdown, ring_shutdown}} -> noreply(State); _ -> View1 = diff --git a/src/rabbit_mirror_queue_coordinator.erl b/src/rabbit_mirror_queue_coordinator.erl index 8d2531e6..3e058793 100644 --- a/src/rabbit_mirror_queue_coordinator.erl +++ b/src/rabbit_mirror_queue_coordinator.erl @@ -407,7 +407,7 @@ handle_msg([CPid], _From, {ensure_monitoring, _Pids} = Msg) -> ok = gen_server2:cast(CPid, Msg); handle_msg([CPid], _From, {delete_and_terminate, _Reason} = Msg) -> ok = gen_server2:cast(CPid, Msg), - {stop, {shutdown, do_not_heal}}; + {stop, {shutdown, ring_shutdown}}; handle_msg([_CPid], _From, _Msg) -> ok. diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 4bef2b7d..03fafc3e 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -353,7 +353,7 @@ handle_msg([SPid], _From, {process_death, Pid}) -> inform_deaths(SPid, [Pid]); handle_msg([CPid], _From, {delete_and_terminate, _Reason} = Msg) -> ok = gen_server2:cast(CPid, {gm, Msg}), - {stop, {shutdown, do_not_heal}}; + {stop, {shutdown, ring_shutdown}}; handle_msg([SPid], _From, Msg) -> ok = gen_server2:cast(SPid, {gm, Msg}). -- cgit v1.2.1 From 62cb251ecdb27b3a9ce276262b96218de727886d Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Tue, 26 Jun 2012 14:22:21 +0100 Subject: notify cluster of new nodes as soon as they join, and not when they start --- src/rabbit_mnesia.erl | 15 ++++++++++++++- src/rabbit_node_monitor.erl | 37 ++++++++++++++++++++++++------------- 2 files changed, 38 insertions(+), 14 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 679f1c84..2e086445 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -44,6 +44,7 @@ on_node_up/2, on_node_down/1, + on_node_join/2, on_node_leave/1 ]). @@ -209,12 +210,14 @@ join_cluster(DiscoveryNode, WantDiscNode) -> %% user. reset(false), - rabbit_misc:local_info_msg("Clustering with ~p~s~n", [ClusterNodes]), + rabbit_misc:local_info_msg("Clustering with ~p~n", [ClusterNodes]), %% Join the cluster ok = init_db_and_upgrade(DiscNodes, WantDiscNode, false), stop_mnesia(), + rabbit_node_monitor:notify_join_cluster(), + ok. %% return node to its virgin state, where it is not member of any @@ -729,6 +732,16 @@ on_node_down(Node) -> write_cluster_nodes_status({AllNodes, DiscNodes, ordsets:del_element(Node, RunningNodes)}). +on_node_join(Node, IsDiscNode) -> + {AllNodes, DiscNodes, RunningNodes} = read_cluster_nodes_status(), + write_cluster_nodes_status({ordsets:add_element(Node, AllNodes), + case IsDiscNode of + true -> ordsets:add_element(Node, + DiscNodes); + false -> DiscNodes + end, + RunningNodes}). + on_node_leave(Node) -> {AllNodes, DiscNodes, RunningNodes} = read_cluster_nodes_status(), write_cluster_nodes_status({ordsets:del_element(Node, AllNodes), diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index a25d0fe0..342c7443 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -20,10 +20,10 @@ -export([start_link/0]). --export([init/1, handle_call/3, handle_cast/2, handle_info/2, - terminate/2, code_change/3]). --export([rabbit_running_on/2, rabbit_left_cluster/1, notify_cluster/0, - notify_leave_cluster/1]). +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, + code_change/3]). +-export([rabbit_running_on/2, rabbit_left_cluster/1, rabbit_joined_cluster/2, + notify_cluster/0, notify_join_cluster/0, notify_leave_cluster/1]). -define(SERVER, ?MODULE). -define(RABBIT_UP_RPC_TIMEOUT, 2000). @@ -55,28 +55,36 @@ rabbit_running_on(Node) -> rabbit_left_cluster(Node) -> gen_server:cast(rabbit_node_monitor, {rabbit_left_cluster, Node}). -notify_cluster() -> +rabbit_joined_cluster(Node, IsDiscNode) -> + gen_server:cast(rabbit_node_monitor, + {rabbit_joined_cluster, Node, IsDiscNode}). + +cluster_multicall(Fun, Args) -> Node = node(), Nodes = rabbit_mnesia:running_clustered_nodes() -- [Node], %% notify other rabbits of this rabbit - case rpc:multicall(Nodes, rabbit_node_monitor, rabbit_running_on, - [Node, rabbit_mnesia:is_disc_node()], + case rpc:multicall(Nodes, rabbit_node_monitor, Fun, Args, ?RABBIT_UP_RPC_TIMEOUT) of {_, [] } -> ok; {_, Bad} -> rabbit_log:info("failed to contact nodes ~p~n", [Bad]) end, + Nodes. + +notify_cluster() -> + Nodes = cluster_multicall(rabbit_running_on, + [node(), rabbit_mnesia:is_disc_node()]), %% register other active rabbits with this rabbit [ rabbit_running_on(N) || N <- Nodes ], ok. +notify_join_cluster() -> + cluster_multicall(rabbit_joined_cluster, + [node(), rabbit_mnesia:is_disc_node()]), + ok. + notify_leave_cluster(Node) -> - Nodes = rabbit_mnesia:running_clustered_nodes() -- [node()], rabbit_left_cluster(Node), - case rpc:multicall(Nodes, rabbit_node_monitor, rabbit_left_cluster, [Node], - ?RABBIT_UP_RPC_TIMEOUT) of - {_, [] } -> ok; - {_, Bad} -> rabbit_log:info("failed to contact nodes ~p~n", [Bad]) - end, + cluster_multicall(rabbit_left_cluster, [Node]), ok. %%-------------------------------------------------------------------- @@ -95,6 +103,9 @@ handle_cast({rabbit_running_on, Node, IsDiscNode}, Nodes) -> ok = handle_live_rabbit(Node, IsDiscNode), {noreply, ordsets:add_element(Node, Nodes)} end; +handle_cast({rabbit_joined_cluster, Node, IsDiscNode}, Nodes) -> + ok = rabbit_mnesia:on_node_join(Node, IsDiscNode), + {noreply, Nodes}; handle_cast({rabbit_left_cluster, Node}, Nodes) -> ok = rabbit_mnesia:on_node_leave(Node), {noreply, Nodes}; -- cgit v1.2.1 From 299404ea267ff1f38d1e3e797b24d72984cef154 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Tue, 26 Jun 2012 16:01:15 +0100 Subject: remove the node from the running node when needed --- src/rabbit_mnesia.erl | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 2e086445..589fdb47 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -158,7 +158,9 @@ prepare() -> init() -> ensure_mnesia_running(), ensure_mnesia_dir(), - ok = reinit_db(should_be_disc_node(all_clustered_disc_nodes())), + {AllNodes, DiscNodes, _} = read_cluster_nodes_status(), + DiscNode = should_be_disc_node(DiscNodes), + init_db_and_upgrade(AllNodes, DiscNode, DiscNode), %% We intuitively expect the global name server to be synced when %% Mnesia is up. In fact that's not guaranteed to be the case - let's %% make it so. @@ -213,8 +215,7 @@ join_cluster(DiscoveryNode, WantDiscNode) -> rabbit_misc:local_info_msg("Clustering with ~p~n", [ClusterNodes]), %% Join the cluster - ok = init_db_and_upgrade(DiscNodes, WantDiscNode, false), - stop_mnesia(), + ok = init_db_with_mnesia(DiscNodes, WantDiscNode, false), rabbit_node_monitor:notify_join_cluster(), @@ -250,7 +251,10 @@ reset(Force) -> try %% Force=true here so that reset still works when %% clustered with a node which is down - ok = reinit_db(true) + start_mnesia(), + {AllNodes, DiscNodes, _} = read_cluster_nodes_status(), + init_db_and_upgrade( + AllNodes, should_be_disc_node(DiscNodes), true) after stop_mnesia() end, @@ -295,8 +299,7 @@ change_node_type(Type) -> disc -> true end, - ok = init_db_and_upgrade(ClusterNodes, WantDiscNode, false), - stop_mnesia(), + ok = init_db_with_mnesia(ClusterNodes, WantDiscNode, false), ok. @@ -315,13 +318,12 @@ recluster(DiscoveryNode) -> end, case lists:member(node(), ClusterNodes) of - true -> init_db_and_upgrade(ClusterNodes, is_disc_node(), false); + true -> init_db_with_mnesia(ClusterNodes, is_disc_node(), false); false -> throw({error, {inconsistent_cluster, "The nodes provided do not have this node as part of " "the cluster"}}) end, - stop_mnesia(), ok. @@ -435,14 +437,10 @@ table_names() -> %% Operations on the db %%---------------------------------------------------------------------------- -%% Starts mnesia if necessary, adds the provided nodes to the mnesia cluster, -%% creating a new schema if there is the need to and catching up if there are -%% other nodes in the cluster already. It also updates the cluster status file. +%% Adds the provided nodes to the mnesia cluster, creating a new schema if there +%% is the need to and catching up if there are other nodes in the cluster +%% already. It also updates the cluster status file. init_db(ClusterNodes, WantDiscNode, Force) -> - case mnesia:system_info(is_running) of - yes -> ok; - no -> start_mnesia() - end, case change_extra_db_nodes(ClusterNodes, Force) of {error, Reason} -> throw({error, Reason}); @@ -497,9 +495,16 @@ init_db_and_upgrade(ClusterNodes, WantDiscNode, Force) -> end, ok. -reinit_db(Force) -> - {AllNodes, DiscNodes, _} = read_cluster_nodes_status(), - init_db_and_upgrade(AllNodes, should_be_disc_node(DiscNodes), Force). +init_db_with_mnesia(ClusterNodes, WantDiscNode, Force) -> + start_mnesia(), + try + init_db_and_upgrade(ClusterNodes, WantDiscNode, Force) + after + stop_mnesia() + end, + {AllNodes, DiscNodes, RunningNodes} = read_cluster_nodes_status(), + write_cluster_nodes_status( + {AllNodes, DiscNodes, ordsets:del_element(node(), RunningNodes)}). ensure_mnesia_dir() -> MnesiaDir = dir() ++ "/", -- cgit v1.2.1 From 6b3a888b01101c2c969d69ef5244fbcbb176a2ef Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Tue, 26 Jun 2012 16:40:46 +0100 Subject: check that the node is in the cluster in `rabbit_mnesia:remove_node/1' --- src/rabbit_mnesia.erl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 589fdb47..350c7376 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -332,6 +332,11 @@ recluster(DiscoveryNode) -> %% nodes running, then *if the current node is a disk node* we force-load mnesia %% and remove the node. remove_node(Node) -> + case ordsets:is_element(Node, all_clustered_nodes()) of + true -> ok; + false -> throw({error, {not_a_cluster_node, + "The node selected is not in the cluster."}}) + end, case remove_node_if_mnesia_running(Node) of ok -> ok; -- cgit v1.2.1 From 5cff91b22f803cf70c29ff11a1faf7acd604a752 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 26 Jun 2012 16:41:24 +0100 Subject: It probably makes slightly more sense to monitor before we broadcast. --- src/rabbit_mirror_queue_master.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index f7fd0425..750bcd56 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -128,8 +128,8 @@ delete_and_terminate(Reason, State = #state { gm = GM, backing_queue = BQ, backing_queue_state = BQS }) -> Slaves = [Pid || Pid <- gm:group_members(GM), node(Pid) =/= node()], - ok = gm:broadcast(GM, {delete_and_terminate, Reason}), MRefs = [erlang:monitor(process, S) || S <- Slaves], + ok = gm:broadcast(GM, {delete_and_terminate, Reason}), monitor_wait(MRefs), State #state { backing_queue_state = BQ:delete_and_terminate(Reason, BQS), set_delivered = 0 }. -- cgit v1.2.1 From a82ee24122224a9d75f7b795d638145f9f6de49b Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 26 Jun 2012 16:57:53 +0100 Subject: More spec correctness. --- src/rabbit_exchange.erl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index 4f6959ba..57c571f1 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -38,8 +38,9 @@ -spec(recover/0 :: () -> [name()]). -spec(callback/4:: - (rabbit_types:exchange(), fun_name(), non_neg_integer() | atom(), - [any()]) -> 'ok'). + (rabbit_types:exchange(), fun_name(), + fun((boolean()) -> non_neg_integer()) | atom(), + [any()]) -> 'ok'). -spec(policy_changed/2 :: (rabbit_types:exchange(), rabbit_types:exchange()) -> 'ok'). -spec(declare/6 :: @@ -87,7 +88,7 @@ (rabbit_types:exchange()) -> 'not_deleted' | {'deleted', rabbit_binding:deletions()}). -spec(serial/1 :: (rabbit_types:exchange()) -> - fun(() -> 'none' | pos_integer())). + fun((boolean()) -> 'none' | pos_integer())). -spec(peek_serial/1 :: (name()) -> pos_integer() | 'undefined'). -endif. -- cgit v1.2.1 From 4ff6537ba46e602555befd8011134c01177a27da Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Wed, 27 Jun 2012 11:49:06 +0100 Subject: check if only running disc node only in the required places --- src/rabbit_mnesia.erl | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 350c7376..feb9b287 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -728,13 +728,13 @@ on_node_up(Node, IsDiscNode) -> false -> DiscNodes end, ordsets:add_element(Node, RunningNodes)}), - case is_only_disc_node(Node) of + case is_only_running_disc_node(Node) of true -> rabbit_log:info("cluster contains disc nodes again~n"); false -> ok end. on_node_down(Node) -> - case is_only_disc_node(Node) of + case is_only_running_disc_node(Node) of true -> rabbit_log:info("only running disc node went down~n"); false -> ok end, @@ -1094,8 +1094,10 @@ wait_for(Condition) -> timer:sleep(1000). is_only_disc_node(Node) -> - Nodes = running_clustered_disc_nodes(), - [Node] =:= Nodes. + [Node] =:= all_clustered_disc_nodes(). + +is_only_running_disc_node(Node) -> + [Node] =:= running_clustered_disc_nodes(). start_mnesia() -> check_cluster_consistency(), -- cgit v1.2.1 From 6c06773f413079a887c65dbbbd953f1f47307d32 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Wed, 27 Jun 2012 11:55:46 +0100 Subject: change `rabbit_mnesia:cluster_status_if_running' slightly --- src/rabbit_mnesia.erl | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index feb9b287..c20ccf5a 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -105,7 +105,8 @@ -spec(on_node_leave/1 :: (node()) -> 'ok'). %% Functions used in internal rpc calls --spec(cluster_status_if_running/0 :: () -> {'ok', node_status()} | 'error'). +-spec(cluster_status_if_running/0 :: () -> {'ok', node_status()} | + {'error', 'node_not_running'}). -spec(node_info/0 :: () -> {string(), string(), ({'ok', node_status()} | 'error')}). -spec(remove_node_if_mnesia_running/1 :: (node()) -> 'ok' | @@ -421,15 +422,19 @@ running_clustered_disc_nodes() -> %% want online, "working" nodes only. cluster_status_if_running() -> case mnesia:system_info(is_running) of - no -> error; + no -> {error, node_not_running}; yes -> {ok, {ordsets:from_list(mnesia:system_info(db_nodes)), ordsets:from_list(mnesia:table_info(schema, disc_copies)), ordsets:from_list(mnesia:system_info(running_db_nodes))}} end. +cluster_status() -> + {ok, Status} = cluster_status_if_running(), + Status. + node_info() -> {erlang:system_info(otp_release), rabbit_misc:rabbit_version(), - cluster_status_if_running()}. + cluster_status()}. is_disc_node() -> mnesia:system_info(use_dir). @@ -712,8 +717,7 @@ read_cluster_nodes_status() -> %% To update the cluster status when mnesia is running. update_cluster_nodes_status() -> - {ok, Status} = cluster_status_if_running(), - write_cluster_nodes_status(Status). + write_cluster_nodes_status(cluster_status()). %%-------------------------------------------------------------------- %% Hooks for `rabbit_node_monitor' @@ -776,9 +780,9 @@ discover_cluster(Node) -> "You provided the current node as node to cluster with"}}; false -> case rpc:call(Node, rabbit_mnesia, cluster_status_if_running, []) of - {badrpc, _Reason} -> discover_cluster([]); - error -> discover_cluster([]); - {ok, Res} -> {ok, Res} + {badrpc, _Reason} -> discover_cluster([]); + {error, node_not_running} -> discover_cluster([]); + {ok, Res} -> {ok, Res} end end. -- cgit v1.2.1 From 568eed3e214195c3becec38a39c7e525ed079d4d Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Wed, 27 Jun 2012 12:21:19 +0100 Subject: change semantics of the cluster status operations Now they use the file only when offline. Also I change the naming, from a `cluster_nodes_status' suffix to `cluster_status_file'. --- src/rabbit_mnesia.erl | 109 +++++++++++++++++++++++++------------------------- 1 file changed, 54 insertions(+), 55 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index c20ccf5a..5c315a07 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -137,9 +137,9 @@ prepare() -> false -> [] end, - ok = write_cluster_nodes_status({AllNodes, DiscNodes, RunningNodes}) + ok = write_cluster_status_file({AllNodes, DiscNodes, RunningNodes}) end, - case try_read_cluster_nodes_status() of + case try_read_cluster_status_file() of {ok, _} -> %% We check the consistency only when the cluster status exists, %% since when it doesn't exist it means that we just started a fresh @@ -159,7 +159,7 @@ prepare() -> init() -> ensure_mnesia_running(), ensure_mnesia_dir(), - {AllNodes, DiscNodes, _} = read_cluster_nodes_status(), + {AllNodes, DiscNodes, _} = read_cluster_status_file(), DiscNode = should_be_disc_node(DiscNodes), init_db_and_upgrade(AllNodes, DiscNode, DiscNode), %% We intuitively expect the global name server to be synced when @@ -253,7 +253,7 @@ reset(Force) -> %% Force=true here so that reset still works when %% clustered with a node which is down start_mnesia(), - {AllNodes, DiscNodes, _} = read_cluster_nodes_status(), + {AllNodes, DiscNodes, _} = read_cluster_status_file(), init_db_and_upgrade( AllNodes, should_be_disc_node(DiscNodes), true) after @@ -269,7 +269,7 @@ reset(Force) -> [erlang:disconnect_node(N) || N <- all_clustered_nodes()], %% remove persisted messages and any other garbage we find ok = rabbit_file:recursive_delete(filelib:wildcard(dir() ++ "/*")), - ok = write_cluster_nodes_status(initial_cluster_status()), + ok = write_cluster_status_file(initial_cluster_status()), ok. change_node_type(Type) -> @@ -391,35 +391,31 @@ is_clustered() -> is_disc_and_clustered() -> is_disc_node() andalso is_clustered(). -%% Functions that retrieve the nodes in the cluster completely rely on the -%% cluster status file. For obvious reason, if rabbit is down, they might return -%% out of date information. +%% Functions that retrieve the nodes in the cluster completely will rely on the +%% status file if offline. all_clustered_nodes() -> - {AllNodes, _, _} = read_cluster_nodes_status(), + {AllNodes, _, _} = cluster_status(), AllNodes. all_clustered_disc_nodes() -> - {_, DiscNodes, _} = read_cluster_nodes_status(), + {_, DiscNodes, _} = cluster_status(), DiscNodes. all_clustered_ram_nodes() -> - {AllNodes, DiscNodes, _} = read_cluster_nodes_status(), - sets:to_list(sets:subtract(sets:from_list(AllNodes), - sets:from_list(DiscNodes))). + {AllNodes, DiscNodes, _} = cluster_status(), + ordsets:subtract(AllNodes, DiscNodes). running_clustered_nodes() -> - {_, _, RunningNodes} = read_cluster_nodes_status(), + {_, _, RunningNodes} = cluster_status(), RunningNodes. running_clustered_disc_nodes() -> - {_, DiscNodes, RunningNodes} = read_cluster_nodes_status(), - sets:to_list(sets:intersection(sets:from_list(DiscNodes), - sets:from_list(RunningNodes))). + {_, DiscNodes, RunningNodes} = cluster_status(), + ordsets:intersection(DiscNodes, RunningNodes). -%% This function is a bit different, we want it to return correctly only when -%% the node is actually online. This is because when we discover the nodes we -%% want online, "working" nodes only. +%% This function is the actual source of information, since it gets the data +%% from mnesia. Obviously it'll work only when mnesia is running. cluster_status_if_running() -> case mnesia:system_info(is_running) of no -> {error, node_not_running}; @@ -429,12 +425,14 @@ cluster_status_if_running() -> end. cluster_status() -> - {ok, Status} = cluster_status_if_running(), - Status. + case cluster_status_if_running() of + {ok, Status} -> Status; + {error, node_not_running} -> read_cluster_status_file() + end. node_info() -> {erlang:system_info(otp_release), rabbit_misc:rabbit_version(), - cluster_status()}. + cluster_status_if_running()}. is_disc_node() -> mnesia:system_info(use_dir). @@ -484,7 +482,7 @@ init_db(ClusterNodes, WantDiscNode, Force) -> end end, ensure_schema_integrity(), - update_cluster_nodes_status(), + update_cluster_status_file(), ok end. @@ -512,8 +510,8 @@ init_db_with_mnesia(ClusterNodes, WantDiscNode, Force) -> after stop_mnesia() end, - {AllNodes, DiscNodes, RunningNodes} = read_cluster_nodes_status(), - write_cluster_nodes_status( + {AllNodes, DiscNodes, RunningNodes} = read_cluster_status_file(), + write_cluster_status_file( {AllNodes, DiscNodes, ordsets:del_element(node(), RunningNodes)}). ensure_mnesia_dir() -> @@ -654,7 +652,7 @@ check_cluster_consistency() -> case rpc:call(Node, rabbit_mnesia, node_info, []) of {badrpc, _Reason} -> ok; - {OTP, Rabbit, error} -> + {OTP, Rabbit, {error, node_not_running}} -> CheckOTP(OTP), CheckRabbit(Rabbit); {OTP, Rabbit, {ok, {AllNodes, _, _}}} -> @@ -681,23 +679,23 @@ check_cluster_consistency() -> %% various situations. Obviously when mnesia is offline the information we have %% will be outdated, but it can't be otherwise. -cluster_nodes_status_filename() -> +cluster_status_file_filename() -> dir() ++ "/cluster_nodes.config". initial_cluster_status() -> {[node()], [node()], [node()]}. -write_cluster_nodes_status(Status) -> - FileName = cluster_nodes_status_filename(), +write_cluster_status_file(Status) -> + FileName = cluster_status_file_filename(), case rabbit_file:write_term_file(FileName, [Status]) of ok -> ok; {error, Reason} -> - throw({error, {cannot_write_cluster_nodes_status, + throw({error, {cannot_write_cluster_status_file, FileName, Reason}}) end. -try_read_cluster_nodes_status() -> - FileName = cluster_nodes_status_filename(), +try_read_cluster_status_file() -> + FileName = cluster_status_file_filename(), case rabbit_file:read_term_file(FileName) of {ok, [{_, _, _} = Status]} -> {ok, Status}; @@ -707,25 +705,26 @@ try_read_cluster_nodes_status() -> {error, {cannot_read_file, FileName, Reason}} end. -read_cluster_nodes_status() -> - case try_read_cluster_nodes_status() of +read_cluster_status_file() -> + case try_read_cluster_status_file() of {ok, Status} -> Status; {error, Reason} -> - throw({error, {cannot_read_cluster_nodes_status, Reason}}) + throw({error, {cannot_read_cluster_status_file, Reason}}) end. %% To update the cluster status when mnesia is running. -update_cluster_nodes_status() -> - write_cluster_nodes_status(cluster_status()). +update_cluster_status_file() -> + {ok, Status} = cluster_status_if_running(), + write_cluster_status_file(Status). %%-------------------------------------------------------------------- %% Hooks for `rabbit_node_monitor' %%-------------------------------------------------------------------- on_node_up(Node, IsDiscNode) -> - {AllNodes, DiscNodes, RunningNodes} = read_cluster_nodes_status(), - write_cluster_nodes_status({ordsets:add_element(Node, AllNodes), + {AllNodes, DiscNodes, RunningNodes} = read_cluster_status_file(), + write_cluster_status_file({ordsets:add_element(Node, AllNodes), case IsDiscNode of true -> ordsets:add_element(Node, DiscNodes); @@ -742,25 +741,25 @@ on_node_down(Node) -> true -> rabbit_log:info("only running disc node went down~n"); false -> ok end, - {AllNodes, DiscNodes, RunningNodes} = read_cluster_nodes_status(), - write_cluster_nodes_status({AllNodes, DiscNodes, - ordsets:del_element(Node, RunningNodes)}). + {AllNodes, DiscNodes, RunningNodes} = read_cluster_status_file(), + write_cluster_status_file({AllNodes, DiscNodes, + ordsets:del_element(Node, RunningNodes)}). on_node_join(Node, IsDiscNode) -> - {AllNodes, DiscNodes, RunningNodes} = read_cluster_nodes_status(), - write_cluster_nodes_status({ordsets:add_element(Node, AllNodes), - case IsDiscNode of - true -> ordsets:add_element(Node, - DiscNodes); - false -> DiscNodes - end, - RunningNodes}). + {AllNodes, DiscNodes, RunningNodes} = read_cluster_status_file(), + write_cluster_status_file({ordsets:add_element(Node, AllNodes), + case IsDiscNode of + true -> ordsets:add_element(Node, + DiscNodes); + false -> DiscNodes + end, + RunningNodes}). on_node_leave(Node) -> - {AllNodes, DiscNodes, RunningNodes} = read_cluster_nodes_status(), - write_cluster_nodes_status({ordsets:del_element(Node, AllNodes), - ordsets:del_element(Node, DiscNodes), - ordsets:del_element(Node, RunningNodes)}). + {AllNodes, DiscNodes, RunningNodes} = read_cluster_status_file(), + write_cluster_status_file({ordsets:del_element(Node, AllNodes), + ordsets:del_element(Node, DiscNodes), + ordsets:del_element(Node, RunningNodes)}). %%-------------------------------------------------------------------- %% Internal helpers -- cgit v1.2.1 From 9aecd35eefbeddf4461e488a104a736af1bfca2b Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Wed, 27 Jun 2012 17:07:21 +0100 Subject: moved the status file handling to `rabbit_node_monitor', status from mnesia when online I'm much happier with this configuration, with the exception of one problem: `rabbit_mnesia:cluster_status' will happily return the data from mnesia whenever mnesia is online, but that gives wrong results when the node is a RAM node and it just got online. I have some ideas to improve this. --- src/rabbit.erl | 5 +- src/rabbit_mnesia.erl | 281 +++++++++++----------------------------- src/rabbit_node_monitor.erl | 309 +++++++++++++++++++++++++++++++++++--------- 3 files changed, 320 insertions(+), 275 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 0d2e27b9..7bdde569 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -176,7 +176,7 @@ -rabbit_boot_step({notify_cluster, [{description, "notify cluster nodes"}, - {mfa, {rabbit_node_monitor, notify_cluster, []}}, + {mfa, {rabbit_node_monitor, notify_node_up, []}}, {requires, networking}]}). %%--------------------------------------------------------------------------- @@ -332,7 +332,8 @@ start_it(StartFun) -> stop() -> rabbit_log:info("Stopping Rabbit~n"), - ok = app_utils:stop_applications(app_shutdown_order()). + ok = app_utils:stop_applications(app_shutdown_order()), + rabbit_node_monitor:this_node_down(). stop_and_halt() -> try diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 5c315a07..727054ad 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -30,27 +30,25 @@ is_db_empty/0, is_clustered/0, all_clustered_nodes/0, - all_clustered_disc_nodes/0, + clustered_disc_nodes/0, running_clustered_nodes/0, is_disc_node/0, dir/0, table_names/0, wait_for_tables/1, + cluster_status_from_mnesia/0, init_db/3, empty_ram_only_tables/0, copy_db/1, wait_for_tables/0, - on_node_up/2, - on_node_down/1, - on_node_join/2, - on_node_leave/1 + on_node_up/1, + on_node_down/1 ]). %% Used internally in rpc calls --export([cluster_status_if_running/0, - node_info/0, +-export([node_info/0, remove_node_if_mnesia_running/1 ]). @@ -64,11 +62,11 @@ -ifdef(use_specs). --export_type([node_type/0]). +-export_type([node_type/0, cluster_status/0]). -type(node_type() :: disc | ram). --type(node_status() :: {ordsets:ordset(node()), ordsets:ordset(node()), - ordsets:ordset(node())}). +-type(cluster_status() :: {ordsets:ordset(node()), ordsets:ordset(node()), + ordsets:ordset(node())}). %% Main interface -spec(prepare/0 :: () -> 'ok'). @@ -86,11 +84,13 @@ -spec(is_db_empty/0 :: () -> boolean()). -spec(is_clustered/0 :: () -> boolean()). -spec(all_clustered_nodes/0 :: () -> [node()]). --spec(all_clustered_disc_nodes/0 :: () -> [node()]). +-spec(clustered_disc_nodes/0 :: () -> [node()]). -spec(running_clustered_nodes/0 :: () -> [node()]). -spec(is_disc_node/0 :: () -> boolean()). -spec(dir/0 :: () -> file:filename()). -spec(table_names/0 :: () -> [atom()]). +-spec(cluster_status_from_mnesia/0 :: () -> {'ok', cluster_status()} | + {'error', 'mnesia_not_running'}). %% Operations on the db and utils, mainly used in `rabbit_upgrade' and `rabbit' -spec(init_db/3 :: ([node()], boolean(), boolean()) -> 'ok'). @@ -98,17 +98,15 @@ -spec(create_tables/0 :: () -> 'ok'). -spec(copy_db/1 :: (file:filename()) -> rabbit_types:ok_or_error(any())). -spec(wait_for_tables/1 :: ([atom()]) -> 'ok'). +-spec(check_cluster_consistency/0 :: () -> 'ok'). %% Hooks used in `rabbit_node_monitor' --spec(on_node_up/2 :: (node(), boolean()) -> 'ok'). +-spec(on_node_up/1 :: (node()) -> 'ok'). -spec(on_node_down/1 :: (node()) -> 'ok'). --spec(on_node_leave/1 :: (node()) -> 'ok'). %% Functions used in internal rpc calls --spec(cluster_status_if_running/0 :: () -> {'ok', node_status()} | - {'error', 'node_not_running'}). -spec(node_info/0 :: () -> {string(), string(), - ({'ok', node_status()} | 'error')}). + ({'ok', cluster_status()} | 'error')}). -spec(remove_node_if_mnesia_running/1 :: (node()) -> 'ok' | {'error', term()}). @@ -118,49 +116,19 @@ %% Main interface %%---------------------------------------------------------------------------- -%% Sets up the cluster status file when needed, taking care of the legacy -%% files prepare() -> ensure_mnesia_dir(), - NotPresent = - fun (AllNodes0, WantDiscNode) -> - ThisNode = [node()], - - RunningNodes0 = legacy_read_previously_running_nodes(), - legacy_delete_previously_running_nodes(), - - RunningNodes = lists:usort(RunningNodes0 ++ ThisNode), - AllNodes = - lists:usort(AllNodes0 ++ RunningNodes), - DiscNodes = case WantDiscNode of - true -> ThisNode; - false -> [] - end, - - ok = write_cluster_status_file({AllNodes, DiscNodes, RunningNodes}) - end, - case try_read_cluster_status_file() of - {ok, _} -> - %% We check the consistency only when the cluster status exists, - %% since when it doesn't exist it means that we just started a fresh - %% node, and when we have a legacy node with an old - %% "cluster_nodes.config" we can't check the consistency anyway - check_cluster_consistency(), - ok; - {error, {invalid_term, _, [AllNodes]}} -> - %% Legacy file - NotPresent(AllNodes, should_be_disc_node(AllNodes)); - {error, {cannot_read_file, _, enoent}} -> - {ok, {AllNodes, WantDiscNode}} = - application:get_env(rabbit, cluster_nodes), - NotPresent(AllNodes, WantDiscNode) - end. + rabbit_node_monitor:prepare_cluster_status_file(), + check_cluster_consistency(). init() -> ensure_mnesia_running(), ensure_mnesia_dir(), - {AllNodes, DiscNodes, _} = read_cluster_status_file(), - DiscNode = should_be_disc_node(DiscNodes), + %% Here we want the cluster status *file*, not the status from mnesia, + %% because in the case of RAM nodes the status from mnesia doesn't matter. + Status = {AllNodes, _, _} = + rabbit_node_monitor:read_cluster_status_file(), + DiscNode = is_disc_node(Status), init_db_and_upgrade(AllNodes, DiscNode, DiscNode), %% We intuitively expect the global name server to be synced when %% Mnesia is up. In fact that's not guaranteed to be the case - let's @@ -218,7 +186,7 @@ join_cluster(DiscoveryNode, WantDiscNode) -> %% Join the cluster ok = init_db_with_mnesia(DiscNodes, WantDiscNode, false), - rabbit_node_monitor:notify_join_cluster(), + rabbit_node_monitor:notify_joined_cluster(), ok. @@ -246,22 +214,17 @@ reset(Force) -> Node = node(), case Force of true -> - ok; + all_clustered_nodes(); false -> + AllNodes0 = all_clustered_nodes(), %% Reconnecting so that we will get an up to date nodes - try - %% Force=true here so that reset still works when - %% clustered with a node which is down - start_mnesia(), - {AllNodes, DiscNodes, _} = read_cluster_status_file(), - init_db_and_upgrade( - AllNodes, should_be_disc_node(DiscNodes), true) - after - stop_mnesia() - end, + %% Force=true here so that reset still works when + %% clustered with a node which is down + init_db_with_mnesia(AllNodes0, is_disc_node(), true), leave_cluster(), rabbit_misc:ensure_ok(mnesia:delete_schema([Node]), - cannot_delete_schema) + cannot_delete_schema), + all_clustered_nodes() end, %% We need to make sure that we don't end up in a distributed Erlang system %% with nodes while not being in an Mnesia cluster with them. We don't @@ -269,7 +232,7 @@ reset(Force) -> [erlang:disconnect_node(N) || N <- all_clustered_nodes()], %% remove persisted messages and any other garbage we find ok = rabbit_file:recursive_delete(filelib:wildcard(dir() ++ "/*")), - ok = write_cluster_status_file(initial_cluster_status()), + ok = rabbit_node_monitor:reset_cluster_status_file(), ok. change_node_type(Type) -> @@ -376,7 +339,7 @@ status() -> IfNonEmpty = fun (_, []) -> []; (Type, Nodes) -> [{Type, Nodes}] end, - [{nodes, (IfNonEmpty(disc, all_clustered_disc_nodes()) ++ + [{nodes, (IfNonEmpty(disc, clustered_disc_nodes()) ++ IfNonEmpty(ram, all_clustered_ram_nodes()))}, {running_nodes, running_clustered_nodes()}]. @@ -386,7 +349,7 @@ is_db_empty() -> is_clustered() -> Nodes = all_clustered_nodes(), - [node()] /= Nodes andalso [] /= Nodes. + [node()] =/= Nodes andalso [] =/= Nodes. is_disc_and_clustered() -> is_disc_node() andalso is_clustered(). @@ -398,7 +361,7 @@ all_clustered_nodes() -> {AllNodes, _, _} = cluster_status(), AllNodes. -all_clustered_disc_nodes() -> +clustered_disc_nodes() -> {_, DiscNodes, _} = cluster_status(), DiscNodes. @@ -416,25 +379,38 @@ running_clustered_disc_nodes() -> %% This function is the actual source of information, since it gets the data %% from mnesia. Obviously it'll work only when mnesia is running. -cluster_status_if_running() -> +cluster_status_from_mnesia() -> + Check = fun (Nodes) -> + case mnesia:system_info(use_dir) of + true -> ordsets:add_element(node(), Nodes); + false -> Nodes + end + end, case mnesia:system_info(is_running) of - no -> {error, node_not_running}; + no -> {error, mnesia_not_running}; yes -> {ok, {ordsets:from_list(mnesia:system_info(db_nodes)), - ordsets:from_list(mnesia:table_info(schema, disc_copies)), + Check(ordsets:from_list( + mnesia:table_info(schema, disc_copies))), ordsets:from_list(mnesia:system_info(running_db_nodes))}} end. cluster_status() -> - case cluster_status_if_running() of - {ok, Status} -> Status; - {error, node_not_running} -> read_cluster_status_file() + case cluster_status_from_mnesia() of + {ok, Status} -> + Status; + {error, mnesia_not_running} -> + rabbit_node_monitor:read_cluster_status_file() end. node_info() -> {erlang:system_info(otp_release), rabbit_misc:rabbit_version(), - cluster_status_if_running()}. + cluster_status_from_mnesia()}. + +is_disc_node() -> + is_disc_node(cluster_status()). -is_disc_node() -> mnesia:system_info(use_dir). +is_disc_node({_, DiscNodes, _}) -> + DiscNodes =:= [] orelse ordsets:is_element(node(), DiscNodes). dir() -> mnesia:system_info(directory). @@ -453,7 +429,10 @@ init_db(ClusterNodes, WantDiscNode, Force) -> {error, Reason} -> throw({error, Reason}); {ok, Nodes} -> - WasDiscNode = is_disc_node(), + %% Note that we use `system_info' here and not the cluster status + %% since when we start rabbit for the first time the cluster status + %% will say we are a disc node but the tables won't be present yet. + WasDiscNode = mnesia:system_info(use_dir), case {Nodes, WasDiscNode, WantDiscNode} of {[], _, false} -> %% Standalone ram node, we don't want that @@ -482,7 +461,7 @@ init_db(ClusterNodes, WantDiscNode, Force) -> end end, ensure_schema_integrity(), - update_cluster_status_file(), + rabbit_node_monitor:update_cluster_status_file(), ok end. @@ -510,9 +489,7 @@ init_db_with_mnesia(ClusterNodes, WantDiscNode, Force) -> after stop_mnesia() end, - {AllNodes, DiscNodes, RunningNodes} = read_cluster_status_file(), - write_cluster_status_file( - {AllNodes, DiscNodes, ordsets:del_element(node(), RunningNodes)}). + rabbit_node_monitor:this_node_down(). ensure_mnesia_dir() -> MnesiaDir = dir() ++ "/", @@ -610,9 +587,6 @@ wait_for_tables(TableNames) -> throw({error, {failed_waiting_for_tables, Reason}}) end. -should_be_disc_node(DiscNodes) -> - DiscNodes == [] orelse lists:member(node(), DiscNodes). - %% This does not guarantee us much, but it avoids some situations that will %% definitely end up badly check_cluster_consistency() -> @@ -652,7 +626,7 @@ check_cluster_consistency() -> case rpc:call(Node, rabbit_mnesia, node_info, []) of {badrpc, _Reason} -> ok; - {OTP, Rabbit, {error, node_not_running}} -> + {OTP, Rabbit, {error, mnesia_not_running}} -> CheckOTP(OTP), CheckRabbit(Rabbit); {OTP, Rabbit, {ok, {AllNodes, _, _}}} -> @@ -662,104 +636,21 @@ check_cluster_consistency() -> end end, all_clustered_nodes()). -%%---------------------------------------------------------------------------- -%% Cluster status file functions -%%---------------------------------------------------------------------------- - -%% The cluster node status file contains all we need to know about the cluster: -%% -%% * All the clustered nodes -%% * The disc nodes -%% * The running nodes. -%% -%% If the current node is a disc node it will be included in the disc nodes -%% list. -%% -%% We strive to keep the file up to date and we rely on this assumption in -%% various situations. Obviously when mnesia is offline the information we have -%% will be outdated, but it can't be otherwise. - -cluster_status_file_filename() -> - dir() ++ "/cluster_nodes.config". - -initial_cluster_status() -> - {[node()], [node()], [node()]}. - -write_cluster_status_file(Status) -> - FileName = cluster_status_file_filename(), - case rabbit_file:write_term_file(FileName, [Status]) of - ok -> ok; - {error, Reason} -> - throw({error, {cannot_write_cluster_status_file, - FileName, Reason}}) - end. - -try_read_cluster_status_file() -> - FileName = cluster_status_file_filename(), - case rabbit_file:read_term_file(FileName) of - {ok, [{_, _, _} = Status]} -> - {ok, Status}; - {ok, Term} -> - {error, {invalid_term, FileName, Term}}; - {error, Reason} -> - {error, {cannot_read_file, FileName, Reason}} - end. - -read_cluster_status_file() -> - case try_read_cluster_status_file() of - {ok, Status} -> - Status; - {error, Reason} -> - throw({error, {cannot_read_cluster_status_file, Reason}}) - end. - -%% To update the cluster status when mnesia is running. -update_cluster_status_file() -> - {ok, Status} = cluster_status_if_running(), - write_cluster_status_file(Status). - %%-------------------------------------------------------------------- %% Hooks for `rabbit_node_monitor' %%-------------------------------------------------------------------- -on_node_up(Node, IsDiscNode) -> - {AllNodes, DiscNodes, RunningNodes} = read_cluster_status_file(), - write_cluster_status_file({ordsets:add_element(Node, AllNodes), - case IsDiscNode of - true -> ordsets:add_element(Node, - DiscNodes); - false -> DiscNodes - end, - ordsets:add_element(Node, RunningNodes)}), - case is_only_running_disc_node(Node) of +on_node_up(Node) -> + case running_clustered_disc_nodes() =:= [Node] of true -> rabbit_log:info("cluster contains disc nodes again~n"); false -> ok end. -on_node_down(Node) -> - case is_only_running_disc_node(Node) of +on_node_down(_Node) -> + case running_clustered_disc_nodes() =:= [] of true -> rabbit_log:info("only running disc node went down~n"); false -> ok - end, - {AllNodes, DiscNodes, RunningNodes} = read_cluster_status_file(), - write_cluster_status_file({AllNodes, DiscNodes, - ordsets:del_element(Node, RunningNodes)}). - -on_node_join(Node, IsDiscNode) -> - {AllNodes, DiscNodes, RunningNodes} = read_cluster_status_file(), - write_cluster_status_file({ordsets:add_element(Node, AllNodes), - case IsDiscNode of - true -> ordsets:add_element(Node, - DiscNodes); - false -> DiscNodes - end, - RunningNodes}). - -on_node_leave(Node) -> - {AllNodes, DiscNodes, RunningNodes} = read_cluster_status_file(), - write_cluster_status_file({ordsets:del_element(Node, AllNodes), - ordsets:del_element(Node, DiscNodes), - ordsets:del_element(Node, RunningNodes)}). + end. %%-------------------------------------------------------------------- %% Internal helpers @@ -778,10 +669,10 @@ discover_cluster(Node) -> {error, {cannot_discover_cluster, "You provided the current node as node to cluster with"}}; false -> - case rpc:call(Node, rabbit_mnesia, cluster_status_if_running, []) of - {badrpc, _Reason} -> discover_cluster([]); - {error, node_not_running} -> discover_cluster([]); - {ok, Res} -> {ok, Res} + case rpc:call(Node, rabbit_mnesia, cluster_status_from_mnesia, []) of + {badrpc, _Reason} -> discover_cluster([]); + {error, mnesia_not_running} -> discover_cluster([]); + {ok, Res} -> {ok, Res} end end. @@ -1053,7 +944,7 @@ remove_node_if_mnesia_running(Node) -> %% propagated to all nodes case mnesia:del_table_copy(schema, Node) of {atomic, ok} -> - rabbit_node_monitor:notify_leave_cluster(Node), + rabbit_node_monitor:notify_left_cluster(Node), ok; {aborted, Reason} -> {error, {failed_to_remove_node, Node, Reason}} @@ -1097,10 +988,7 @@ wait_for(Condition) -> timer:sleep(1000). is_only_disc_node(Node) -> - [Node] =:= all_clustered_disc_nodes(). - -is_only_running_disc_node(Node) -> - [Node] =:= running_clustered_disc_nodes(). + [Node] =:= clustered_disc_nodes(). start_mnesia() -> check_cluster_consistency(), @@ -1120,28 +1008,3 @@ change_extra_db_nodes(ClusterNodes0, Force) -> {ok, Nodes} -> {ok, Nodes} end. - -%%-------------------------------------------------------------------- -%% Legacy functions related to the "running nodes" file -%%-------------------------------------------------------------------- - -legacy_running_nodes_filename() -> - filename:join(dir(), "nodes_running_at_shutdown"). - -legacy_read_previously_running_nodes() -> - FileName = legacy_running_nodes_filename(), - case rabbit_file:read_term_file(FileName) of - {ok, [Nodes]} -> Nodes; - {error, enoent} -> []; - {error, Reason} -> throw({error, {cannot_read_previous_nodes_file, - FileName, Reason}}) - end. - -legacy_delete_previously_running_nodes() -> - FileName = legacy_running_nodes_filename(), - case file:delete(FileName) of - ok -> ok; - {error, enoent} -> ok; - {error, Reason} -> throw({error, {cannot_delete_previous_nodes_file, - FileName, Reason}}) - end. diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index 342c7443..9e763da0 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -18,12 +18,27 @@ -behaviour(gen_server). --export([start_link/0]). +-export([prepare_cluster_status_file/0, + read_cluster_status_file/0, + update_cluster_status_file/0, + reset_cluster_status_file/0, --export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, - code_change/3]). --export([rabbit_running_on/2, rabbit_left_cluster/1, rabbit_joined_cluster/2, - notify_cluster/0, notify_join_cluster/0, notify_leave_cluster/1]). + joined_cluster/2, + notify_joined_cluster/0, + left_cluster/1, + notify_left_cluster/1, + node_up/2, + notify_node_up/0, + this_node_down/0, + + start_link/0, + init/1, + handle_call/3, + handle_cast/2, + handle_info/2, + terminate/2, + code_change/3 + ]). -define(SERVER, ?MODULE). -define(RABBIT_UP_RPC_TIMEOUT, 2000). @@ -32,90 +47,207 @@ -ifdef(use_specs). --spec(start_link/0 :: () -> rabbit_types:ok_pid_or_error()). --spec(rabbit_running_on/2 :: (node(), boolean()) -> 'ok'). --spec(rabbit_left_cluster/1 :: (node()) -> 'ok'). --spec(notify_cluster/0 :: () -> 'ok'). --spec(notify_leave_cluster/1 :: (node()) -> 'ok'). +-spec(prepare_cluster_status_file/0 :: () -> 'ok'). +-spec(read_cluster_status_file/0 :: () -> rabbit_mnesia:cluster_status()). +-spec(update_cluster_status_file/0 :: () -> 'ok'). +-spec(reset_cluster_status_file/0 :: () -> 'ok'). + +-spec(joined_cluster/2 :: (node(), boolean()) -> 'ok'). +-spec(notify_joined_cluster/0 :: () -> 'ok'). +-spec(left_cluster/1 :: (node()) -> 'ok'). +-spec(notify_left_cluster/1 :: (node()) -> 'ok'). +-spec(node_up/2 :: (node(), boolean()) -> 'ok'). +-spec(notify_node_up/0 :: () -> 'ok'). +-spec(this_node_down/0 :: () -> 'ok'). -endif. -%%-------------------------------------------------------------------- +%%---------------------------------------------------------------------------- +%% Cluster file operations +%%---------------------------------------------------------------------------- -start_link() -> - gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). +%% The cluster node status file contains all we need to know about the cluster: +%% +%% * All the clustered nodes +%% * The disc nodes +%% * The running nodes. +%% +%% If the current node is a disc node it will be included in the disc nodes +%% list. +%% +%% We strive to keep the file up to date and we rely on this assumption in +%% various situations. Obviously when mnesia is offline the information we have +%% will be outdated, but it can't be otherwise. -rabbit_running_on(Node, IsDiscNode) -> - gen_server:cast(rabbit_node_monitor, - {rabbit_running_on, Node, IsDiscNode}). +cluster_status_file_filename() -> + rabbit_mnesia:dir() ++ "/cluster_nodes.config". -rabbit_running_on(Node) -> - rabbit_running_on(Node, rabbit_mnesia:is_disc_node()). +prepare_cluster_status_file() -> + NotPresent = + fun (AllNodes0, WantDiscNode) -> + ThisNode = [node()], -rabbit_left_cluster(Node) -> - gen_server:cast(rabbit_node_monitor, {rabbit_left_cluster, Node}). + RunningNodes0 = legacy_read_previously_running_nodes(), + legacy_delete_previously_running_nodes(), -rabbit_joined_cluster(Node, IsDiscNode) -> - gen_server:cast(rabbit_node_monitor, - {rabbit_joined_cluster, Node, IsDiscNode}). + RunningNodes = lists:usort(RunningNodes0 ++ ThisNode), + AllNodes = + lists:usort(AllNodes0 ++ RunningNodes), + DiscNodes = case WantDiscNode of + true -> ThisNode; + false -> [] + end, -cluster_multicall(Fun, Args) -> - Node = node(), - Nodes = rabbit_mnesia:running_clustered_nodes() -- [Node], - %% notify other rabbits of this rabbit - case rpc:multicall(Nodes, rabbit_node_monitor, Fun, Args, - ?RABBIT_UP_RPC_TIMEOUT) of - {_, [] } -> ok; - {_, Bad} -> rabbit_log:info("failed to contact nodes ~p~n", [Bad]) - end, - Nodes. + ok = write_cluster_status_file({AllNodes, DiscNodes, RunningNodes}) + end, + case try_read_cluster_status_file() of + {ok, _} -> + ok; + {error, {invalid_term, _, [AllNodes]}} -> + %% Legacy file + NotPresent(AllNodes, legacy_should_be_disc_node(AllNodes)); + {error, {cannot_read_file, _, enoent}} -> + {ok, {AllNodes, WantDiscNode}} = + application:get_env(rabbit, cluster_nodes), + NotPresent(AllNodes, WantDiscNode) + end. -notify_cluster() -> - Nodes = cluster_multicall(rabbit_running_on, - [node(), rabbit_mnesia:is_disc_node()]), - %% register other active rabbits with this rabbit - [ rabbit_running_on(N) || N <- Nodes ], + +write_cluster_status_file(Status) -> + FileName = cluster_status_file_filename(), + case rabbit_file:write_term_file(FileName, [Status]) of + ok -> ok; + {error, Reason} -> + throw({error, {cannot_write_cluster_status_file, + FileName, Reason}}) + end. + +try_read_cluster_status_file() -> + FileName = cluster_status_file_filename(), + case rabbit_file:read_term_file(FileName) of + {ok, [{_, _, _} = Status]} -> + {ok, Status}; + {ok, Term} -> + {error, {invalid_term, FileName, Term}}; + {error, Reason} -> + {error, {cannot_read_file, FileName, Reason}} + end. + +read_cluster_status_file() -> + case try_read_cluster_status_file() of + {ok, Status} -> + Status; + {error, Reason} -> + throw({error, {cannot_read_cluster_status_file, Reason}}) + end. + +update_cluster_status_file() -> + {ok, Status} = rabbit_mnesia:cluster_status_from_mnesia(), + write_cluster_status_file(Status). + +reset_cluster_status_file() -> + write_cluster_status_file({[node()], [node()], [node()]}). + +%%---------------------------------------------------------------------------- +%% Cluster notifications +%%---------------------------------------------------------------------------- + +joined_cluster(Node, IsDiscNode) -> + gen_server:cast(rabbit_node_monitor, {rabbit_join, Node, IsDiscNode}). + +notify_joined_cluster() -> + cluster_multicall(joined_cluster, [node(), rabbit_mnesia:is_disc_node()]), ok. -notify_join_cluster() -> - cluster_multicall(rabbit_joined_cluster, - [node(), rabbit_mnesia:is_disc_node()]), +left_cluster(Node) -> + gen_server:cast(rabbit_node_monitor, {left_cluster, Node}). + +notify_left_cluster(Node) -> + left_cluster(Node), + cluster_multicall(left_cluster, [Node]), ok. -notify_leave_cluster(Node) -> - rabbit_left_cluster(Node), - cluster_multicall(rabbit_left_cluster, [Node]), +node_up(Node, IsDiscNode) -> + gen_server:cast(rabbit_node_monitor, {node_up, Node, IsDiscNode}). + +notify_node_up() -> + Nodes = cluster_multicall(node_up, [node(), rabbit_mnesia:is_disc_node()]), + %% register other active rabbits with this rabbit + [ node_up(N, ordsets:is_element(N, rabbit_mnesia:clustered_disc_nodes())) || + N <- Nodes ], ok. -%%-------------------------------------------------------------------- +this_node_down() -> + case mnesia:system_info(is_running) of + yes -> throw({error, node_running}); + no -> {AllNodes, DiscNodes, RunningNodes} = + read_cluster_status_file(), + write_cluster_status_file( + {AllNodes, DiscNodes, + ordsets:del_element(node(), RunningNodes)}) + end. + +%%---------------------------------------------------------------------------- +%% gen_server callbacks +%%---------------------------------------------------------------------------- + +start_link() -> + gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). init([]) -> - {ok, ordsets:new()}. + {ok, no_state}. handle_call(_Request, _From, State) -> {noreply, State}. -handle_cast({rabbit_running_on, Node, IsDiscNode}, Nodes) -> - case ordsets:is_element(Node, Nodes) of - true -> {noreply, Nodes}; +%% Note: when updating the status file, we can't simply write the mnesia +%% information since the message can (and will) overtake the mnesia propagation. +handle_cast({node_up, Node, IsDiscNode}, State) -> + case is_already_monitored({rabbit, Node}) of + true -> {noreply, State}; false -> rabbit_log:info("rabbit on node ~p up~n", [Node]), + {ok, {AllNodes, DiscNodes, RunningNodes}} = + rabbit_mnesia:cluster_status_from_mnesia(), + write_cluster_status_file( + {ordsets:add_element(Node, AllNodes), + case IsDiscNode of + true -> ordsets:add_element(Node, DiscNodes); + false -> DiscNodes + end, + ordsets:add_element(Node, RunningNodes)}), erlang:monitor(process, {rabbit, Node}), - ok = handle_live_rabbit(Node, IsDiscNode), - {noreply, ordsets:add_element(Node, Nodes)} + ok = handle_live_rabbit(Node), + {noreply, State} end; -handle_cast({rabbit_joined_cluster, Node, IsDiscNode}, Nodes) -> - ok = rabbit_mnesia:on_node_join(Node, IsDiscNode), - {noreply, Nodes}; -handle_cast({rabbit_left_cluster, Node}, Nodes) -> - ok = rabbit_mnesia:on_node_leave(Node), - {noreply, Nodes}; +handle_cast({joined_cluster, Node, IsDiscNode}, State) -> + {ok, {AllNodes, DiscNodes, RunningNodes}} = + rabbit_mnesia:cluster_status_from_mnesia(), + write_cluster_status_file({ordsets:add_element(Node, AllNodes), + case IsDiscNode of + true -> ordsets:add_element(Node, + DiscNodes); + false -> DiscNodes + end, + RunningNodes}), + {noreply, State}; +handle_cast({left_cluster, Node}, State) -> + {ok, {AllNodes, DiscNodes, RunningNodes}} = + rabbit_mnesia:cluster_status_from_mnesia(), + write_cluster_status_file({ordsets:del_element(Node, AllNodes), + ordsets:del_element(Node, DiscNodes), + ordsets:del_element(Node, RunningNodes)}), + {noreply, State}; handle_cast(_Msg, State) -> {noreply, State}. -handle_info({'DOWN', _MRef, process, {rabbit, Node}, _Reason}, Nodes) -> +handle_info({'DOWN', _MRef, process, {rabbit, Node}, _Reason}, State) -> rabbit_log:info("rabbit on node ~p down~n", [Node]), + {ok, {AllNodes, DiscNodes, RunningNodes}} = + rabbit_mnesia:cluster_status_from_mnesia(), + write_cluster_status_file({AllNodes, DiscNodes, + ordsets:del_element(Node, RunningNodes)}), ok = handle_dead_rabbit(Node), - {noreply, ordsets:del_element(Node, Nodes)}; + {noreply, State}; handle_info(_Info, State) -> {noreply, State}. @@ -125,18 +257,67 @@ terminate(_Reason, _State) -> code_change(_OldVsn, State, _Extra) -> {ok, State}. -%%-------------------------------------------------------------------- +%%---------------------------------------------------------------------------- +%% Functions that call the module specific hooks when nodes go up/down +%%---------------------------------------------------------------------------- %% TODO: This may turn out to be a performance hog when there are lots %% of nodes. We really only need to execute some of these statements %% on *one* node, rather than all of them. handle_dead_rabbit(Node) -> - ok = rabbit_mnesia:on_node_down(Node), ok = rabbit_networking:on_node_down(Node), ok = rabbit_amqqueue:on_node_down(Node), ok = rabbit_alarm:on_node_down(Node). -handle_live_rabbit(Node, IsDiscNode) -> - ok = rabbit_mnesia:on_node_up(Node, IsDiscNode), - ok = rabbit_alarm:on_node_up(Node). +handle_live_rabbit(Node) -> + ok = rabbit_alarm:on_node_up(Node), + ok = rabbit_mnesia:on_node_down(Node). + +%%-------------------------------------------------------------------- +%% Internal utils +%%-------------------------------------------------------------------- + +cluster_multicall(Fun, Args) -> + Node = node(), + Nodes = rabbit_mnesia:running_clustered_nodes() -- [Node], + %% notify other rabbits of this cluster + case rpc:multicall(Nodes, rabbit_node_monitor, Fun, Args, + ?RABBIT_UP_RPC_TIMEOUT) of + {_, [] } -> ok; + {_, Bad} -> rabbit_log:info("failed to contact nodes ~p~n", [Bad]) + end, + Nodes. + +is_already_monitored(Item) -> + {monitors, Monitors} = process_info(self(), monitors), + lists:any(fun ({_, Item1}) when Item =:= Item1 -> true; + (_) -> false + end, Monitors). + +legacy_should_be_disc_node(DiscNodes) -> + DiscNodes == [] orelse lists:member(node(), DiscNodes). + +%%-------------------------------------------------------------------- +%% Legacy functions related to the "running nodes" file +%%-------------------------------------------------------------------- + +legacy_running_nodes_filename() -> + filename:join(rabbit_mnesia:dir(), "nodes_running_at_shutdown"). + +legacy_read_previously_running_nodes() -> + FileName = legacy_running_nodes_filename(), + case rabbit_file:read_term_file(FileName) of + {ok, [Nodes]} -> Nodes; + {error, enoent} -> []; + {error, Reason} -> throw({error, {cannot_read_previous_nodes_file, + FileName, Reason}}) + end. +legacy_delete_previously_running_nodes() -> + FileName = legacy_running_nodes_filename(), + case file:delete(FileName) of + ok -> ok; + {error, enoent} -> ok; + {error, Reason} -> throw({error, {cannot_delete_previous_nodes_file, + FileName, Reason}}) + end. -- cgit v1.2.1 From f0cd1259d1214630bef0f58a5db249807cd437af Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Thu, 28 Jun 2012 14:06:31 +0100 Subject: instead of manually removing the current node, filter it in `cluster_status' --- src/rabbit.erl | 3 +-- src/rabbit_mnesia.erl | 11 ++++++++--- src/rabbit_node_monitor.erl | 12 ------------ 3 files changed, 9 insertions(+), 17 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 7bdde569..58e1c50e 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -332,8 +332,7 @@ start_it(StartFun) -> stop() -> rabbit_log:info("Stopping Rabbit~n"), - ok = app_utils:stop_applications(app_shutdown_order()), - rabbit_node_monitor:this_node_down(). + ok = app_utils:stop_applications(app_shutdown_order()). stop_and_halt() -> try diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 727054ad..e4f50523 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -398,8 +398,13 @@ cluster_status() -> case cluster_status_from_mnesia() of {ok, Status} -> Status; - {error, mnesia_not_running} -> - rabbit_node_monitor:read_cluster_status_file() + {error, _Reason} -> + {AllNodes, DiscNodes, RunningNodes} = + rabbit_node_monitor:read_cluster_status_file(), + %% The cluster status file records the status when the node is + %% online, but we know for sure that the node is offline now, so we + %% can remove it from the list of running nodes. + {AllNodes, DiscNodes, ordsets:del_element(node(), RunningNodes)} end. node_info() -> @@ -489,7 +494,7 @@ init_db_with_mnesia(ClusterNodes, WantDiscNode, Force) -> after stop_mnesia() end, - rabbit_node_monitor:this_node_down(). + ensure_mnesia_not_running(). ensure_mnesia_dir() -> MnesiaDir = dir() ++ "/", diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index 9e763da0..10ea7a14 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -29,7 +29,6 @@ notify_left_cluster/1, node_up/2, notify_node_up/0, - this_node_down/0, start_link/0, init/1, @@ -58,7 +57,6 @@ -spec(notify_left_cluster/1 :: (node()) -> 'ok'). -spec(node_up/2 :: (node(), boolean()) -> 'ok'). -spec(notify_node_up/0 :: () -> 'ok'). --spec(this_node_down/0 :: () -> 'ok'). -endif. @@ -177,16 +175,6 @@ notify_node_up() -> N <- Nodes ], ok. -this_node_down() -> - case mnesia:system_info(is_running) of - yes -> throw({error, node_running}); - no -> {AllNodes, DiscNodes, RunningNodes} = - read_cluster_status_file(), - write_cluster_status_file( - {AllNodes, DiscNodes, - ordsets:del_element(node(), RunningNodes)}) - end. - %%---------------------------------------------------------------------------- %% gen_server callbacks %%---------------------------------------------------------------------------- -- cgit v1.2.1 From 4673f31defbb48ed363446fd2c421fc206c01e5f Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Thu, 28 Jun 2012 14:51:50 +0100 Subject: don't use `system_info(running_db_nodes)', check for running rabbits instead --- src/rabbit_mnesia.erl | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index e4f50523..2f7d4754 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -49,7 +49,8 @@ %% Used internally in rpc calls -export([node_info/0, - remove_node_if_mnesia_running/1 + remove_node_if_mnesia_running/1, + is_running_remote/0 ]). %% create_tables/0 exported for helping embed RabbitMQ in or alongside @@ -388,10 +389,11 @@ cluster_status_from_mnesia() -> end, case mnesia:system_info(is_running) of no -> {error, mnesia_not_running}; - yes -> {ok, {ordsets:from_list(mnesia:system_info(db_nodes)), + yes -> AllNodes = ordsets:from_list(mnesia:system_info(db_nodes)), + {ok, {AllNodes, Check(ordsets:from_list( mnesia:table_info(schema, disc_copies))), - ordsets:from_list(mnesia:system_info(running_db_nodes))}} + running_nodes(AllNodes)}} end. cluster_status() -> @@ -1013,3 +1015,15 @@ change_extra_db_nodes(ClusterNodes0, Force) -> {ok, Nodes} -> {ok, Nodes} end. + +%% What we really want is nodes running rabbit, not running mnesia. Using +%% `rabbit_mnesia:system_info(running_db_nodes)' will return false positives +%% when we are actually just doing cluster operations (e.g. joining the +%% cluster). +running_nodes(Nodes) -> + {Replies, _BadNodes} = + rpc:multicall(Nodes, rabbit_mnesia, is_running_remote, []), + [Node || {Running, Node} <- Replies, Running]. + +is_running_remote() -> + {proplists:is_defined(rabbit, application:which_applications()), node()}. -- cgit v1.2.1 From 196965329844004567d8d8dbd8ff09d963e05915 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Thu, 28 Jun 2012 15:07:33 +0100 Subject: `cluster_status_from_mnesia/0' fails if tables are not present --- src/rabbit_mnesia.erl | 49 ++++++++++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 2f7d4754..f3cdd6b2 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -125,12 +125,8 @@ prepare() -> init() -> ensure_mnesia_running(), ensure_mnesia_dir(), - %% Here we want the cluster status *file*, not the status from mnesia, - %% because in the case of RAM nodes the status from mnesia doesn't matter. - Status = {AllNodes, _, _} = - rabbit_node_monitor:read_cluster_status_file(), - DiscNode = is_disc_node(Status), - init_db_and_upgrade(AllNodes, DiscNode, DiscNode), + DiscNode = is_disc_node(), + init_db_and_upgrade(all_clustered_nodes(), DiscNode, DiscNode), %% We intuitively expect the global name server to be synced when %% Mnesia is up. In fact that's not guaranteed to be the case - let's %% make it so. @@ -217,11 +213,11 @@ reset(Force) -> true -> all_clustered_nodes(); false -> - AllNodes0 = all_clustered_nodes(), + AllNodes = all_clustered_nodes(), %% Reconnecting so that we will get an up to date nodes %% Force=true here so that reset still works when %% clustered with a node which is down - init_db_with_mnesia(AllNodes0, is_disc_node(), true), + init_db_with_mnesia(AllNodes, is_disc_node(), true), leave_cluster(), rabbit_misc:ensure_ok(mnesia:delete_schema([Node]), cannot_delete_schema), @@ -381,19 +377,34 @@ running_clustered_disc_nodes() -> %% This function is the actual source of information, since it gets the data %% from mnesia. Obviously it'll work only when mnesia is running. cluster_status_from_mnesia() -> - Check = fun (Nodes) -> - case mnesia:system_info(use_dir) of - true -> ordsets:add_element(node(), Nodes); - false -> Nodes - end - end, case mnesia:system_info(is_running) of no -> {error, mnesia_not_running}; - yes -> AllNodes = ordsets:from_list(mnesia:system_info(db_nodes)), - {ok, {AllNodes, - Check(ordsets:from_list( - mnesia:table_info(schema, disc_copies))), - running_nodes(AllNodes)}} + yes -> %% If the tables are not present, it means that `init_db/3' hasn't + %% been run yet. In other words, either we are a virgin node or a + %% restarted RAM node. In both cases we're not interested in what + %% mnesia has to say. + IsDiscNode = mnesia:system_info(use_dir), + Tables = mnesia:system_info(tables), + {Table, _} = case table_definitions(case IsDiscNode of + true -> disc; + false -> ram + end) of [T|_] -> T end, + case lists:member(Table, Tables) of + true -> + AllNodes = + ordsets:from_list(mnesia:system_info(db_nodes)), + DiscCopies = ordsets:from_list( + mnesia:table_info(schema, disc_copies)), + DiscNodes = + case IsDiscNode of + true -> ordsets:add_element(node(), DiscCopies); + false -> DiscCopies + end, + RunningNodes = running_nodes(AllNodes), + {ok, {AllNodes, DiscNodes, RunningNodes}}; + false -> + {error, tables_not_present} + end end. cluster_status() -> -- cgit v1.2.1 From 1147c50b2114a948bf30e6026be1b45bf1462dac Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 2 Jul 2012 11:43:58 +0100 Subject: clarify what 'timeout' stands for --- docs/rabbitmqctl.1.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/rabbitmqctl.1.xml b/docs/rabbitmqctl.1.xml index 68bc87be..2d25edee 100644 --- a/docs/rabbitmqctl.1.xml +++ b/docs/rabbitmqctl.1.xml @@ -1215,7 +1215,7 @@ timeout - Connection timeout. + Connection timeout / negotiated heartbeat interval, in seconds. frame_max -- cgit v1.2.1 From 7337c3065590cb61d088fe12ffd410ff7c6ac4f0 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Tue, 3 Jul 2012 13:24:54 +0100 Subject: use nohup instead of setsid --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 49bf926a..0e3960dc 100644 --- a/Makefile +++ b/Makefile @@ -206,7 +206,7 @@ run-qc: all start-background-node: all -rm -f $(RABBITMQ_MNESIA_DIR).pid mkdir -p $(RABBITMQ_MNESIA_DIR) - setsid sh -c "$(MAKE) run-background-node > $(RABBITMQ_MNESIA_DIR)/startup_log 2> $(RABBITMQ_MNESIA_DIR)/startup_err" & + nohup sh -c "$(MAKE) run-background-node > $(RABBITMQ_MNESIA_DIR)/startup_log 2> $(RABBITMQ_MNESIA_DIR)/startup_err" > /dev/null & ./scripts/rabbitmqctl -n $(RABBITMQ_NODENAME) wait $(RABBITMQ_MNESIA_DIR).pid kernel start-rabbit-on-node: all -- cgit v1.2.1 From 48f9c9e3da7260b2624303415947e07cfbdb252f Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Tue, 3 Jul 2012 15:10:54 +0100 Subject: better `rabbit_mnesia:check_cluster_consistency/0' --- src/rabbit_mnesia.erl | 49 +++++++++++++++++++++---------------------------- 1 file changed, 21 insertions(+), 28 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index f3cdd6b2..04d0d292 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -617,40 +617,33 @@ check_cluster_consistency() -> "~s version mismatch: local node is ~s, " "remote node ~s", [Name, This, Remote])}}) end, - CheckOTP = - fun (OTP) -> CheckVsn(erlang:system_info(otp_release), OTP, "OTP") end, - CheckRabbit = - fun (Rabbit) -> - CheckVsn(rabbit_misc:rabbit_version(), Rabbit, "Rabbit") - end, - - CheckNodes = fun (Node, AllNodes) -> - ThisNode = node(), - case lists:member(ThisNode, AllNodes) of - true -> - ok; - false -> - throw({error, - {inconsistent_cluster, - rabbit_misc:format( - "Node ~p thinks it's clustered " - "with node ~p, but ~p disagrees", - [ThisNode, Node, Node])}}) - end - end, lists:foreach( fun(Node) -> case rpc:call(Node, rabbit_mnesia, node_info, []) of {badrpc, _Reason} -> ok; - {OTP, Rabbit, {error, mnesia_not_running}} -> - CheckOTP(OTP), - CheckRabbit(Rabbit); - {OTP, Rabbit, {ok, {AllNodes, _, _}}} -> - CheckOTP(OTP), - CheckRabbit(Rabbit), - CheckNodes(Node, AllNodes) + {OTP, Rabbit, Res} -> + CheckVsn(erlang:system_info(otp_release), OTP, "OTP"), + CheckVsn(rabbit_misc:rabbit_version(), Rabbit, "Rabbit"), + case Res of + {ok, {AllNodes, _, _}} -> + ThisNode = node(), + case lists:member(ThisNode, AllNodes) of + true -> + ok; + false -> + throw( + {error, + {inconsistent_cluster, + rabbit_misc:format( + "Node ~p thinks it's clustered " + "with node ~p, but ~p disagrees", + [ThisNode, Node, Node])}}) + end; + {error, _Reason} -> + ok + end end end, all_clustered_nodes()). -- cgit v1.2.1 From 53684b7e8378d242fff7ee11726aee027934c308 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Tue, 3 Jul 2012 16:23:28 +0100 Subject: wipe the mnesia data before starting if the cluster nodes are inconsistent --- src/rabbit_misc.erl | 7 +++ src/rabbit_mnesia.erl | 102 +++++++++++++++++++++++++++----------------- src/rabbit_node_monitor.erl | 3 ++ 3 files changed, 74 insertions(+), 38 deletions(-) diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 5bfb7de0..9a3325ca 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -61,6 +61,7 @@ -export([os_cmd/1]). -export([gb_sets_difference/2]). -export([rabbit_version/0]). +-export([sequence_error/1]). %%---------------------------------------------------------------------------- @@ -214,6 +215,8 @@ -spec(os_cmd/1 :: (string()) -> string()). -spec(gb_sets_difference/2 :: (gb_set(), gb_set()) -> gb_set()). -spec(rabbit_version/0 :: () -> string()). +-spec(sequence_error/1 :: ([({'error', any()} | any())]) + -> {'error', any()} | any()). -endif. @@ -945,3 +948,7 @@ gb_sets_difference(S1, S2) -> rabbit_version() -> {ok, VSN} = application:get_key(rabbit, vsn), VSN. + +sequence_error([T]) -> T; +sequence_error([{error, _} = Error | _]) -> Error; +sequence_error([_ | Rest]) -> sequence_error(Rest). diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 04d0d292..d3eac8ae 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -608,44 +608,51 @@ wait_for_tables(TableNames) -> %% This does not guarantee us much, but it avoids some situations that will %% definitely end up badly check_cluster_consistency() -> - CheckVsn = fun (This, This, _) -> - ok; - (This, Remote, Name) -> - throw({error, - {inconsistent_cluster, - rabbit_misc:format( - "~s version mismatch: local node is ~s, " - "remote node ~s", [Name, This, Remote])}}) - end, - - lists:foreach( - fun(Node) -> - case rpc:call(Node, rabbit_mnesia, node_info, []) of - {badrpc, _Reason} -> - ok; - {OTP, Rabbit, Res} -> - CheckVsn(erlang:system_info(otp_release), OTP, "OTP"), - CheckVsn(rabbit_misc:rabbit_version(), Rabbit, "Rabbit"), - case Res of - {ok, {AllNodes, _, _}} -> - ThisNode = node(), - case lists:member(ThisNode, AllNodes) of - true -> - ok; - false -> - throw( - {error, - {inconsistent_cluster, - rabbit_misc:format( - "Node ~p thinks it's clustered " - "with node ~p, but ~p disagrees", - [ThisNode, Node, Node])}}) - end; - {error, _Reason} -> - ok - end - end - end, all_clustered_nodes()). + AllNodes = all_clustered_nodes(), + %% We want to find 0 or 1 consistent nodes. + case + lists:foldl( + fun(Node, {error, Error}) -> + case rpc:call(Node, rabbit_mnesia, node_info, []) of + {badrpc, _Reason} -> + {error, Error}; + {OTP, Rabbit, Res} -> + rabbit_misc:sequence_error( + [check_version_consistency( + erlang:system_info(otp_release), OTP, "OTP"), + check_version_consistency( + rabbit_misc:rabbit_version(), Rabbit, "Rabbit"), + case Res of + {ok, Status} -> + check_nodes_consistency(Node, Status); + {error, _Reason} -> + {error, Error} + end]) + end; + (_Node, {ok, Status}) -> + {ok, Status} + end, {error, no_nodes}, AllNodes) + of + {ok, Status = {RemoteAllNodes, _, _}} -> + case ordsets:is_subset(all_clustered_nodes(), RemoteAllNodes) of + true -> ok; + false -> %% We delete the schema here since we have more nodes + %% than the actually clustered ones, and there is no + %% way to remove those nodes from our schema + %% otherwise. On the other hand, we are sure that there + %% is another online node that we can use to sync the + %% tables with. There is a race here: if between this + %% check and the `init_db' invocation the cluster gets + %% disbanded, we're left with a node with no mnesia + %% data that will try to connect to offline nodes. + mnesia:delete_schema([node()]) + end, + rabbit_node_monitor:write_cluster_status_file(Status); + {error, no_nodes} -> + ok; + {error, Error} -> + throw({error, Error}) + end. %%-------------------------------------------------------------------- %% Hooks for `rabbit_node_monitor' @@ -1031,3 +1038,22 @@ running_nodes(Nodes) -> is_running_remote() -> {proplists:is_defined(rabbit, application:which_applications()), node()}. + +check_nodes_consistency(Node, RemoteStatus = {RemoteAllNodes, _, _}) -> + ThisNode = node(), + case ordsets:is_element(ThisNode, RemoteAllNodes) of + true -> + {ok, RemoteStatus}; + false -> + {error, {inconsistent_cluster, + rabbit_misc:format("Node ~p thinks it's clustered " + "with node ~p, but ~p disagrees", + [ThisNode, Node, Node])}} + end. + +check_version_consistency(This, Remote, _) when This =:= Remote -> + ok; +check_version_consistency(This, Remote, Name) -> + {error, {inconsistent_cluster, + rabbit_misc:format("~s version mismatch: local node is ~s, " + "remote node ~s", [Name, This, Remote])}}. diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index 10ea7a14..788a06c7 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -19,6 +19,7 @@ -behaviour(gen_server). -export([prepare_cluster_status_file/0, + write_cluster_status_file/1, read_cluster_status_file/0, update_cluster_status_file/0, reset_cluster_status_file/0, @@ -47,6 +48,8 @@ -ifdef(use_specs). -spec(prepare_cluster_status_file/0 :: () -> 'ok'). +-spec(write_cluster_status_file/1 :: (rabbit_mnesia:cluster_status()) + -> 'ok'). -spec(read_cluster_status_file/0 :: () -> rabbit_mnesia:cluster_status()). -spec(update_cluster_status_file/0 :: () -> 'ok'). -spec(reset_cluster_status_file/0 :: () -> 'ok'). -- cgit v1.2.1 From 84e49bdec97d860d356c95e5b7a44c5f1f8629ab Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Wed, 4 Jul 2012 11:52:14 +0100 Subject: ensure the temp directories are deleted before proceeding --- Makefile | 10 ++++++---- check_xref | 20 +++++++++++++------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/Makefile b/Makefile index b637edc8..64109312 100644 --- a/Makefile +++ b/Makefile @@ -103,7 +103,7 @@ endif all: $(TARGETS) -.PHONY: plugins +.PHONY: plugins check-xref ifneq "$(PLUGINS_SRC_DIR)" "" plugins: [ -d "$(PLUGINS_SRC_DIR)/rabbitmq-server" ] || ln -s "$(CURDIR)" "$(PLUGINS_SRC_DIR)/rabbitmq-server" @@ -114,14 +114,16 @@ plugins: # add -q to remove printout of warnings.... check-xref: $(BEAM_TARGETS) $(PLUGINS_DIR) - rm -rf lib # just in case! - ./check_xref $(PLUGINS_DIR) + rm -rf lib + ./check_xref $(PLUGINS_DIR) -q else plugins: # Not building plugins + check-xref: -# No xref checks enabled + $(info xref checks are disabled) + endif $(DEPS_FILE): $(SOURCES) $(INCLUDES) diff --git a/check_xref b/check_xref index 1ef88b01..a570b059 100755 --- a/check_xref +++ b/check_xref @@ -28,16 +28,20 @@ main([PluginsDir|Argv]) -> if Quiet == true -> put({?MODULE, quiet}, true); true -> ok end, - + DisableFilters = lists:member("-X", Argv), if DisableFilters == true -> put({?MODULE, no_filters}, true); true -> ok end, - + {ok, Cwd} = file:get_cwd(), code:add_pathz(filename:join(Cwd, "ebin")), LibDir = filename:join(Cwd, "lib"), - + case filelib:is_dir(LibDir) of + false -> ok; + true -> _ = os:cmd("rm -rf " ++ LibDir) + end, + try check(Cwd, PluginsDir, LibDir, checks()) after @@ -58,7 +62,7 @@ check(Cwd, PluginsDir, LibDir, Checks) -> false -> filename:join(LibDir, filename:basename(Plugin, ".ez")) end, - + report(info, "mkdir -p ~s~n", [Target]), filelib:ensure_dir(Target), @@ -74,7 +78,8 @@ check(Cwd, PluginsDir, LibDir, Checks) -> code:add_patha(filename:join(AppN, "ebin")), if IsExternal =:= true -> - App = list_to_atom(hd(string:tokens(filename:basename(AppN), "-"))), + App = list_to_atom(hd(string:tokens(filename:basename(AppN), + "-"))), report(info, "loading ~p~n", [App]), application:load(App), store_third_party(App); @@ -83,7 +88,7 @@ check(Cwd, PluginsDir, LibDir, Checks) -> end end || Plugin <- Plugins, lists:suffix(".ez", Plugin)], - + RabbitAppEbin = filename:join([LibDir, "rabbit", "ebin"]), filelib:ensure_dir(filename:join(RabbitAppEbin, "foo")), {ok, Beams} = file:list_dir("ebin"), @@ -244,7 +249,8 @@ report_progress(Fmt, Args) -> report(Level, Fmt, Args) -> case get({?MODULE, quiet}) of - true -> if Level=:=error -> do_report(lookup_prefix(Level), Fmt, Args); + true -> if Level=:=error -> do_report(lookup_prefix(Level), + Fmt, Args); true -> ok end; _ -> do_report(lookup_prefix(Level), Fmt, Args) -- cgit v1.2.1 From ef4cc576c619c634c64c7bc0a460eecc689d9a4a Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Wed, 4 Jul 2012 12:12:25 +0100 Subject: cosmetic --- check_xref | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/check_xref b/check_xref index a570b059..1e29dbd5 100755 --- a/check_xref +++ b/check_xref @@ -89,11 +89,11 @@ check(Cwd, PluginsDir, LibDir, Checks) -> end || Plugin <- Plugins, lists:suffix(".ez", Plugin)], - RabbitAppEbin = filename:join([LibDir, "rabbit", "ebin"]), - filelib:ensure_dir(filename:join(RabbitAppEbin, "foo")), - {ok, Beams} = file:list_dir("ebin"), - [{ok, _} = file:copy(filename:join("ebin", Beam), - filename:join(RabbitAppEbin, Beam)) || Beam <- Beams], + RabbitAppEbin = filename:join([LibDir, "rabbit", "ebin"]), + filelib:ensure_dir(filename:join(RabbitAppEbin, "foo")), + {ok, Beams} = file:list_dir("ebin"), + [{ok, _} = file:copy(filename:join("ebin", Beam), + filename:join(RabbitAppEbin, Beam)) || Beam <- Beams], xref:start(?MODULE), xref:set_default(?MODULE, [{verbose, false}, {warnings, false}]), xref:set_library_path(?MODULE, code:get_path()), -- cgit v1.2.1 From 748d24ef0931f624ccae1c3a6f215d309bd09cc0 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Wed, 4 Jul 2012 12:21:44 +0100 Subject: all_clustered_ram_nodes() => clustered_ram_nodes() --- src/rabbit_mnesia.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index d3eac8ae..019a5110 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -337,7 +337,7 @@ status() -> (Type, Nodes) -> [{Type, Nodes}] end, [{nodes, (IfNonEmpty(disc, clustered_disc_nodes()) ++ - IfNonEmpty(ram, all_clustered_ram_nodes()))}, + IfNonEmpty(ram, clustered_ram_nodes()))}, {running_nodes, running_clustered_nodes()}]. is_db_empty() -> @@ -362,7 +362,7 @@ clustered_disc_nodes() -> {_, DiscNodes, _} = cluster_status(), DiscNodes. -all_clustered_ram_nodes() -> +clustered_ram_nodes() -> {AllNodes, DiscNodes, _} = cluster_status(), ordsets:subtract(AllNodes, DiscNodes). -- cgit v1.2.1 From 9b8bb6adbe043160bbe19c07d2cc0883292dcd6f Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Wed, 4 Jul 2012 13:43:28 +0100 Subject: reworked functions that get cluster nodes to get only what they need This is mainly do avoid a deadlock caused by running `rabbit:stop/1', which indirectly calls `applications:which_applications/1', from inside `application:stop'. --- src/rabbit_mnesia.erl | 77 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 51 insertions(+), 26 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 019a5110..d26c5d24 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -91,7 +91,7 @@ -spec(dir/0 :: () -> file:filename()). -spec(table_names/0 :: () -> [atom()]). -spec(cluster_status_from_mnesia/0 :: () -> {'ok', cluster_status()} | - {'error', 'mnesia_not_running'}). + {'error', any()}). %% Operations on the db and utils, mainly used in `rabbit_upgrade' and `rabbit' -spec(init_db/3 :: ([node()], boolean(), boolean()) -> 'ok'). @@ -351,32 +351,33 @@ is_clustered() -> is_disc_and_clustered() -> is_disc_node() andalso is_clustered(). -%% Functions that retrieve the nodes in the cluster completely will rely on the -%% status file if offline. +%% Functions that retrieve the nodes in the cluster will rely on the status file +%% if offline. all_clustered_nodes() -> - {AllNodes, _, _} = cluster_status(), + {ok, AllNodes} = cluster_status(all), AllNodes. clustered_disc_nodes() -> - {_, DiscNodes, _} = cluster_status(), + {ok, DiscNodes} =cluster_status(disc), DiscNodes. clustered_ram_nodes() -> - {AllNodes, DiscNodes, _} = cluster_status(), + {ok, AllNodes} = cluster_status(all), + {ok, DiscNodes} = cluster_status(disc), ordsets:subtract(AllNodes, DiscNodes). running_clustered_nodes() -> - {_, _, RunningNodes} = cluster_status(), + {ok, RunningNodes} = cluster_status(running), RunningNodes. running_clustered_disc_nodes() -> - {_, DiscNodes, RunningNodes} = cluster_status(), + {ok, {_, DiscNodes, RunningNodes}} = cluster_status(), ordsets:intersection(DiscNodes, RunningNodes). %% This function is the actual source of information, since it gets the data %% from mnesia. Obviously it'll work only when mnesia is running. -cluster_status_from_mnesia() -> +mnesia_nodes() -> case mnesia:system_info(is_running) of no -> {error, mnesia_not_running}; yes -> %% If the tables are not present, it means that `init_db/3' hasn't @@ -400,34 +401,57 @@ cluster_status_from_mnesia() -> true -> ordsets:add_element(node(), DiscCopies); false -> DiscCopies end, - RunningNodes = running_nodes(AllNodes), - {ok, {AllNodes, DiscNodes, RunningNodes}}; + {ok, {AllNodes, DiscNodes}}; false -> {error, tables_not_present} end end. -cluster_status() -> - case cluster_status_from_mnesia() of - {ok, Status} -> - Status; - {error, _Reason} -> - {AllNodes, DiscNodes, RunningNodes} = - rabbit_node_monitor:read_cluster_status_file(), - %% The cluster status file records the status when the node is - %% online, but we know for sure that the node is offline now, so we - %% can remove it from the list of running nodes. - {AllNodes, DiscNodes, ordsets:del_element(node(), RunningNodes)} +cluster_status(WhichNodes, ForceMnesia) -> + %% I don't want to call `running_nodes/1' unless if necessary, since it can + %% deadlock when stopping applications. + case case mnesia_nodes() of + {ok, {AllNodes, DiscNodes}} -> + {ok, {AllNodes, DiscNodes, + fun() -> running_nodes(AllNodes) end}}; + {error, _Reason} when not ForceMnesia -> + {AllNodes, DiscNodes, RunningNodes} = + rabbit_node_monitor:read_cluster_status_file(), + %% The cluster status file records the status when the node is + %% online, but we know for sure that the node is offline now, so + %% we can remove it from the list of running nodes. + {ok, {AllNodes, DiscNodes, + fun() -> ordsets:del_element(node(), RunningNodes) end}}; + Err = {error, _} -> + Err + end + of + {ok, {AllNodes1, DiscNodes1, RunningNodesThunk}} -> + {ok, case WhichNodes of + status -> {AllNodes1, DiscNodes1, RunningNodesThunk()}; + all -> AllNodes1; + disc -> DiscNodes1; + running -> RunningNodesThunk() + end}; + Err1 = {error, _} -> + Err1 end. +cluster_status(WhichNodes) -> + cluster_status(WhichNodes, false). + +cluster_status() -> + cluster_status(status). + +cluster_status_from_mnesia() -> + cluster_status(status, true). + node_info() -> {erlang:system_info(otp_release), rabbit_misc:rabbit_version(), cluster_status_from_mnesia()}. is_disc_node() -> - is_disc_node(cluster_status()). - -is_disc_node({_, DiscNodes, _}) -> + DiscNodes = clustered_disc_nodes(), DiscNodes =:= [] orelse ordsets:is_element(node(), DiscNodes). dir() -> mnesia:system_info(directory). @@ -1037,7 +1061,8 @@ running_nodes(Nodes) -> [Node || {Running, Node} <- Replies, Running]. is_running_remote() -> - {proplists:is_defined(rabbit, application:which_applications()), node()}. + {proplists:is_defined(rabbit, application:which_applications(infinity)), + node()}. check_nodes_consistency(Node, RemoteStatus = {RemoteAllNodes, _, _}) -> ThisNode = node(), -- cgit v1.2.1 From a47b7dba6201972a293dd3563466ad29f834e799 Mon Sep 17 00:00:00 2001 From: Steve Powell Date: Wed, 4 Jul 2012 14:40:20 +0100 Subject: Make make-port-diff.sh work correctly on MacOs and Linuces --- packaging/macports/make-port-diff.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packaging/macports/make-port-diff.sh b/packaging/macports/make-port-diff.sh index 3eb1b9f5..ac3afa4e 100755 --- a/packaging/macports/make-port-diff.sh +++ b/packaging/macports/make-port-diff.sh @@ -14,8 +14,10 @@ mkdir -p $dir/macports $dir/rabbitmq cd $dir/macports svn checkout http://svn.macports.org/repository/macports/trunk/dports/net/rabbitmq-server/ 2>&1 >/dev/null -# Clear out the svn $id tag -sed -i -e 's|^# \$.*$|# $Id$|' rabbitmq-server/Portfile +# Clear out the svn $id tag from the Portfile (and avoid using -i) +portfile=rabbitmq-server/Portfile +sed -e 's|^# \$.*$|# $Id$|' ${portfile} > ${portfile}.new +mv ${portfile}.new ${portfile} # Get the files from the rabbitmq.com macports repo cd ../rabbitmq -- cgit v1.2.1 From 069b9cdaebb2edf8c54fec99ce88a1f59897221a Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Wed, 4 Jul 2012 15:57:46 +0100 Subject: do not check for consistency when reseting --- src/rabbit_mnesia.erl | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index d26c5d24..97049592 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -214,10 +214,11 @@ reset(Force) -> all_clustered_nodes(); false -> AllNodes = all_clustered_nodes(), - %% Reconnecting so that we will get an up to date nodes - %% Force=true here so that reset still works when - %% clustered with a node which is down - init_db_with_mnesia(AllNodes, is_disc_node(), true), + %% Reconnecting so that we will get an up to date nodes. + %% We don't need to check for consistency because we are resetting. + %% Force=true here so that reset still works when clustered with a + %% node which is down. + init_db_with_mnesia(AllNodes, is_disc_node(), false, true), leave_cluster(), rabbit_misc:ensure_ok(mnesia:delete_schema([Node]), cannot_delete_schema), @@ -524,8 +525,8 @@ init_db_and_upgrade(ClusterNodes, WantDiscNode, Force) -> end, ok. -init_db_with_mnesia(ClusterNodes, WantDiscNode, Force) -> - start_mnesia(), +init_db_with_mnesia(ClusterNodes, WantDiscNode, CheckConsistency, Force) -> + start_mnesia(CheckConsistency), try init_db_and_upgrade(ClusterNodes, WantDiscNode, Force) after @@ -533,6 +534,9 @@ init_db_with_mnesia(ClusterNodes, WantDiscNode, Force) -> end, ensure_mnesia_not_running(). +init_db_with_mnesia(ClusterNodes, WantDiscNode, Force) -> + init_db_with_mnesia(ClusterNodes, WantDiscNode, true, Force). + ensure_mnesia_dir() -> MnesiaDir = dir() ++ "/", case filelib:ensure_dir(MnesiaDir) of @@ -632,13 +636,13 @@ wait_for_tables(TableNames) -> %% This does not guarantee us much, but it avoids some situations that will %% definitely end up badly check_cluster_consistency() -> - AllNodes = all_clustered_nodes(), + AllNodes = ordsets:del_element(node(), all_clustered_nodes()), %% We want to find 0 or 1 consistent nodes. case lists:foldl( fun(Node, {error, Error}) -> case rpc:call(Node, rabbit_mnesia, node_info, []) of - {badrpc, _Reason} -> + {badrpc, Reason} -> {error, Error}; {OTP, Rabbit, Res} -> rabbit_misc:sequence_error( @@ -1032,11 +1036,17 @@ wait_for(Condition) -> is_only_disc_node(Node) -> [Node] =:= clustered_disc_nodes(). -start_mnesia() -> - check_cluster_consistency(), +start_mnesia(CheckConsistency) -> + case CheckConsistency of + true -> check_cluster_consistency(); + false -> ok + end, rabbit_misc:ensure_ok(mnesia:start(), cannot_start_mnesia), ensure_mnesia_running(). +start_mnesia() -> + start_mnesia(true). + stop_mnesia() -> stopped = mnesia:stop(), ensure_mnesia_not_running(). -- cgit v1.2.1 From 11d5035ae21c2cd22d6068335558e26d6fee81fc Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Wed, 4 Jul 2012 16:33:22 +0100 Subject: check if only disc node after `init_db' in `reset/1' --- src/rabbit_mnesia.erl | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 97049592..9ef2f302 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -199,15 +199,6 @@ reset(Force) -> true -> "" end]), ensure_mnesia_not_running(), - case not Force andalso is_disc_and_clustered() andalso - is_only_disc_node(node()) - of - true -> throw({error, {standalone_ram_node, - "You can't reset a node if it's the only disc " - "node in a cluster. Please convert another node" - " of the cluster to a disc node first."}}); - false -> ok - end, Node = node(), case Force of true -> @@ -219,6 +210,14 @@ reset(Force) -> %% Force=true here so that reset still works when clustered with a %% node which is down. init_db_with_mnesia(AllNodes, is_disc_node(), false, true), + case is_disc_and_clustered() andalso is_only_disc_node(node()) of + true -> throw({error, {standalone_ram_node, + "You can't reset a node if it's the " + "only disc node in a cluster. Please " + "convert another node of the cluster " + "to a disc node first."}}); + false -> ok + end, leave_cluster(), rabbit_misc:ensure_ok(mnesia:delete_schema([Node]), cannot_delete_schema), @@ -642,7 +641,7 @@ check_cluster_consistency() -> lists:foldl( fun(Node, {error, Error}) -> case rpc:call(Node, rabbit_mnesia, node_info, []) of - {badrpc, Reason} -> + {badrpc, _Reason} -> {error, Error}; {OTP, Rabbit, Res} -> rabbit_misc:sequence_error( -- cgit v1.2.1 From d8ddf71897b74f9bcdcb159c62a5a5779e0fe005 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 4 Jul 2012 17:35:17 +0100 Subject: Cosmetic: reduce verbosity. --- check_xref | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/check_xref b/check_xref index 1e29dbd5..32d3ce1d 100755 --- a/check_xref +++ b/check_xref @@ -24,15 +24,8 @@ main(["-h"]) -> " -q - quiet mode (only prints errors)~n" " -X - disables all filters~n"); main([PluginsDir|Argv]) -> - Quiet = lists:member("-q", Argv), - if Quiet == true -> put({?MODULE, quiet}, true); - true -> ok - end, - - DisableFilters = lists:member("-X", Argv), - if DisableFilters == true -> put({?MODULE, no_filters}, true); - true -> ok - end, + put({?MODULE, quiet}, lists:member("-q", Argv)), + put({?MODULE, no_filters}, lists:member("-X", Argv)), {ok, Cwd} = file:get_cwd(), code:add_pathz(filename:join(Cwd, "ebin")), @@ -248,12 +241,10 @@ report_progress(Fmt, Args) -> report(info, Fmt, Args). report(Level, Fmt, Args) -> - case get({?MODULE, quiet}) of - true -> if Level=:=error -> do_report(lookup_prefix(Level), - Fmt, Args); - true -> ok - end; - _ -> do_report(lookup_prefix(Level), Fmt, Args) + case {get({?MODULE, quiet}), Level} of + {true, error} -> do_report(lookup_prefix(Level), Fmt, Args); + {false, _} -> do_report(lookup_prefix(Level), Fmt, Args); + _ -> ok end. do_report(Prefix, Fmt, Args) -> -- cgit v1.2.1 From e83681f08e6cd8e93024b1b342daa01102b276f7 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 4 Jul 2012 17:38:13 +0100 Subject: Cosmetic: whitespace. --- check_xref | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/check_xref b/check_xref index 32d3ce1d..382767bb 100755 --- a/check_xref +++ b/check_xref @@ -27,7 +27,7 @@ main([PluginsDir|Argv]) -> put({?MODULE, quiet}, lists:member("-q", Argv)), put({?MODULE, no_filters}, lists:member("-X", Argv)), - {ok, Cwd} = file:get_cwd(), + {ok, Cwd} = file:get_cwd(), code:add_pathz(filename:join(Cwd, "ebin")), LibDir = filename:join(Cwd, "lib"), case filelib:is_dir(LibDir) of @@ -50,11 +50,11 @@ check(Cwd, PluginsDir, LibDir, Checks) -> Source = filename:join(PluginsDir, Plugin), Target = filename:join(LibDir, Plugin), IsExternal = external_dependency(Plugin), - AppN = case IsExternal of - true -> filename:join(LibDir, unmangle_name(Plugin)); - false -> filename:join(LibDir, - filename:basename(Plugin, ".ez")) - end, + AppN = case IsExternal of + true -> filename:join(LibDir, unmangle_name(Plugin)); + false -> filename:join( + LibDir, filename:basename(Plugin, ".ez")) + end, report(info, "mkdir -p ~s~n", [Target]), filelib:ensure_dir(Target), @@ -86,13 +86,13 @@ check(Cwd, PluginsDir, LibDir, Checks) -> filelib:ensure_dir(filename:join(RabbitAppEbin, "foo")), {ok, Beams} = file:list_dir("ebin"), [{ok, _} = file:copy(filename:join("ebin", Beam), - filename:join(RabbitAppEbin, Beam)) || Beam <- Beams], + filename:join(RabbitAppEbin, Beam)) || Beam <- Beams], xref:start(?MODULE), xref:set_default(?MODULE, [{verbose, false}, {warnings, false}]), xref:set_library_path(?MODULE, code:get_path()), xref:add_release(?MODULE, Cwd, {name, rabbit}), store_unresolved_calls(), - Results = lists:flatten([perform_analysis(Q) || Q <- Checks]), + Results = lists:flatten([perform_analysis(Q) || Q <- Checks]), report(Results). %% @@ -205,7 +205,7 @@ is_callback(_) -> acc_behaviours(B, {M, CB}=Acc) -> case catch(B:behaviour_info(callbacks)) of - [{_,_}|_]=Callbacks -> + [{_,_} | _] = Callbacks -> {M, CB ++ [{M, F, A} || {F,A} <- Callbacks]}; _ -> Acc @@ -250,9 +250,9 @@ report(Level, Fmt, Args) -> do_report(Prefix, Fmt, Args) -> io:format(Prefix ++ Fmt, Args). -lookup_prefix(error) -> "ERROR: "; -lookup_prefix(warning) -> "WARNING: "; -lookup_prefix(info) -> "INFO: ". +lookup_prefix(error) -> "ERROR: "; +lookup_prefix(warning) -> "WARNING: "; +lookup_prefix(info) -> "INFO: ". source_file(M) -> proplists:get_value(source, M:module_info(compile)). @@ -278,10 +278,10 @@ external_dependency(Path) -> ["mochiweb", "webmachine", "rfc4627", "eldap"]). unmangle_name(Path) -> - [Name, Vsn|_] = re:split(Path, "-", [{return, list}]), + [Name, Vsn | _] = re:split(Path, "-", [{return, list}]), string:join([Name, Vsn], "-"). store_unresolved_calls() -> {ok, UCFull} = analyse("UC"), - UC = [MFA || {_, {_,_,_}=MFA} <- UCFull], + UC = [MFA || {_, {_,_,_} = MFA} <- UCFull], put({?MODULE, unresolved_calls}, sets:from_list(UC)). -- cgit v1.2.1 From 44a5aa0e9b97e1337a51eebb49aeaf4cd00e768a Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 4 Jul 2012 17:39:57 +0100 Subject: Cosmetic: indulge bias against if statements. --- check_xref | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/check_xref b/check_xref index 382767bb..55458c66 100755 --- a/check_xref +++ b/check_xref @@ -70,14 +70,13 @@ check(Cwd, PluginsDir, LibDir, Checks) -> ok = file:rename(UnpackDir, AppN), code:add_patha(filename:join(AppN, "ebin")), - if IsExternal =:= true -> - App = list_to_atom(hd(string:tokens(filename:basename(AppN), - "-"))), - report(info, "loading ~p~n", [App]), - application:load(App), - store_third_party(App); - true -> - ok + case IsExternal of + true -> App = list_to_atom(hd(string:tokens(filename:basename(AppN), + "-"))), + report(info, "loading ~p~n", [App]), + application:load(App), + store_third_party(App); + _ -> ok end end || Plugin <- Plugins, lists:suffix(".ez", Plugin)], @@ -220,8 +219,9 @@ report(Results) -> {Errors, Warnings} = partition(Results), report(info, "Completed: ~p errors, ~p warnings~n", [length(Errors), length(Warnings)]), - if length(Errors) > 0 -> halt(1); - true -> halt(0) + case length(Errors) > 0 of + true -> halt(1); + false -> halt(0) end. report_failures({analysis_error, {Mod, Reason}}) -> -- cgit v1.2.1 From 82b008482ae58b0f10af773b473f1ebd19e818b5 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 4 Jul 2012 17:41:16 +0100 Subject: Cosmetic: starting a line with "_ =" is meaningless. --- check_xref | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/check_xref b/check_xref index 55458c66..6eb89e15 100755 --- a/check_xref +++ b/check_xref @@ -32,7 +32,7 @@ main([PluginsDir|Argv]) -> LibDir = filename:join(Cwd, "lib"), case filelib:is_dir(LibDir) of false -> ok; - true -> _ = os:cmd("rm -rf " ++ LibDir) + true -> os:cmd("rm -rf " ++ LibDir) end, try @@ -40,7 +40,7 @@ main([PluginsDir|Argv]) -> after %% TODO: bootstrap file_handle_cache and use %% rabbit_file:recursive_delete instead of this... - _ = os:cmd("rm -rf " ++ LibDir) + os:cmd("rm -rf " ++ LibDir) end. check(Cwd, PluginsDir, LibDir, Checks) -> -- cgit v1.2.1 From df7b4ec7fda306a6b48fd37119e9b82bc907664b Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Wed, 4 Jul 2012 18:18:55 +0100 Subject: check if the node is the only disc one in `change_node_type/1' --- src/rabbit_mnesia.erl | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 9ef2f302..18e2edbe 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -240,12 +240,11 @@ change_node_type(Type) -> "Non-clustered nodes can only be disc nodes"}}); true -> ok end, - DiscoveryNodes = all_clustered_nodes(), - ClusterNodes = + {AllNodes, DiscNodes, _} = case discover_cluster(DiscoveryNodes) of - {ok, {ClusterNodes0, _, _}} -> - ClusterNodes0; + {ok, Status} -> + Status; {error, _Reason} -> throw({error, {cannot_connect_to_cluster, @@ -254,15 +253,19 @@ change_node_type(Type) -> "you can use the \"recluster\" command to point to the " "new cluster nodes"}}) end, - WantDiscNode = case Type of ram -> false; disc -> true end, - - ok = init_db_with_mnesia(ClusterNodes, WantDiscNode, false), - - ok. + case not WantDiscNode andalso is_only_disc_node(node(), DiscNodes) of + true -> throw({error, + {standalone_ram_node, + "You can't change the node type to ram if the node is " + "the only disc node in its cluster. Please add more " + "disc nodes to the cluster first."}}); + false -> ok + end, + ok = init_db_with_mnesia(AllNodes, WantDiscNode, false). recluster(DiscoveryNode) -> ensure_mnesia_not_running(), @@ -1032,8 +1035,11 @@ wait_for(Condition) -> error_logger:info_msg("Waiting for ~p...~n", [Condition]), timer:sleep(1000). +is_only_disc_node(Node, DiscNodes) -> + [Node] =:= DiscNodes. + is_only_disc_node(Node) -> - [Node] =:= clustered_disc_nodes(). + is_only_disc_node(Node, clustered_disc_nodes()). start_mnesia(CheckConsistency) -> case CheckConsistency of -- cgit v1.2.1 From 8a79247c82fe22dd307a7ead1fc03c8707b36b0f Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Wed, 4 Jul 2012 19:01:10 +0100 Subject: don't show the running nodes when down --- src/rabbit_mnesia.erl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 18e2edbe..b7e0695e 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -340,8 +340,11 @@ status() -> (Type, Nodes) -> [{Type, Nodes}] end, [{nodes, (IfNonEmpty(disc, clustered_disc_nodes()) ++ - IfNonEmpty(ram, clustered_ram_nodes()))}, - {running_nodes, running_clustered_nodes()}]. + IfNonEmpty(ram, clustered_ram_nodes()))}] ++ + case mnesia:system_info(is_running) of + yes -> [{running_nodes, running_clustered_nodes()}]; + no -> [] + end. is_db_empty() -> lists:all(fun (Tab) -> mnesia:dirty_first(Tab) == '$end_of_table' end, -- cgit v1.2.1 From 6639018db19a4168179426401fb5ca99864c8f4e Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Thu, 5 Jul 2012 12:31:48 +0100 Subject: fix `recluster/1' - delete the schema and write status before `init_db' --- src/rabbit_mnesia.erl | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index b7e0695e..d2851231 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -271,18 +271,21 @@ recluster(DiscoveryNode) -> ensure_mnesia_not_running(), ensure_mnesia_dir(), - ClusterNodes = + Status = {AllNodes, _, _} = case discover_cluster(DiscoveryNode) of - {ok, {ClusterNodes0, _, _}} -> - ClusterNodes0; + {ok, Status0} -> + Status0; {error, _Reason} -> throw({error, {cannot_connect_to_node, "Could not connect to the cluster node provided"}}) end, - - case lists:member(node(), ClusterNodes) of - true -> init_db_with_mnesia(ClusterNodes, is_disc_node(), false); + case ordsets:is_element(node(), AllNodes) of + true -> %% As in `check_consistency/0', we can safely delete the schema + %% here, since it'll be replicated from the other nodes + mnesia:delete_schema([node()]), + rabbit_node_monitor:write_cluster_status_file(Status), + init_db_with_mnesia(AllNodes, is_disc_node(), false); false -> throw({error, {inconsistent_cluster, "The nodes provided do not have this node as part of " @@ -293,7 +296,7 @@ recluster(DiscoveryNode) -> %% We proceed like this: try to remove the node locally. If mnesia is offline %% then we try to remove it remotely on some other node. If there are no other -%% nodes running, then *if the current node is a disk node* we force-load mnesia +%% nodes running, then *if the current node is a disc node* we force-load mnesia %% and remove the node. remove_node(Node) -> case ordsets:is_element(Node, all_clustered_nodes()) of -- cgit v1.2.1 From f4e20306ddd516c40e26f3c8faf1e2316d1cc604 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 5 Jul 2012 16:00:28 +0100 Subject: Rough sketch of dynamic queue HA-ness: * Remove #amqqueue.mirror_nodes, we will always derive this from policy * Remove everything to do with x-ha-* arguments * Abstract a bit more stuff into rabbit_mirror_queue_misc * Add a new "at-least" mode This works! Sort of. Changing policies on the fly, changing HAness altogether, having "at-least" set up a new mirror when one disappears, and probably some other things do not work. --- include/rabbit.hrl | 2 +- src/rabbit_amqqueue.erl | 54 ++++------------------------- src/rabbit_amqqueue_process.erl | 29 ++++++++-------- src/rabbit_mirror_queue_master.erl | 10 ++---- src/rabbit_mirror_queue_misc.erl | 69 +++++++++++++++++++++++++++----------- src/rabbit_types.erl | 3 +- src/rabbit_upgrade_functions.erl | 14 ++++++++ 7 files changed, 89 insertions(+), 92 deletions(-) diff --git a/include/rabbit.hrl b/include/rabbit.hrl index e8b4a623..7be82aef 100644 --- a/include/rabbit.hrl +++ b/include/rabbit.hrl @@ -47,7 +47,7 @@ -record(exchange_serial, {name, next}). -record(amqqueue, {name, durable, auto_delete, exclusive_owner = none, - arguments, pid, slave_pids, mirror_nodes, policy}). + arguments, pid, slave_pids, policy}). %% mnesia doesn't like unary records, so we add a dummy 'value' field -record(route, {binding, value = const}). diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index afbaea65..0565b1a5 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -207,15 +207,15 @@ recover_durable_queues(DurableQueues) -> declare(QueueName, Durable, AutoDelete, Args, Owner) -> ok = check_declare_arguments(QueueName, Args), - {Node, MNodes} = determine_queue_nodes(Args), - Q = start_queue_process(Node, #amqqueue{name = QueueName, + Node = node(), %% TODO utter rubbish + Q = start_queue_process( + Node, rabbit_policy:set(#amqqueue{name = QueueName, durable = Durable, auto_delete = AutoDelete, arguments = Args, exclusive_owner = Owner, pid = none, - slave_pids = [], - mirror_nodes = MNodes}), + slave_pids = []})), case gen_server2:call(Q#amqqueue.pid, {init, false}, infinity) of not_found -> rabbit_misc:not_found(QueueName); Q1 -> Q1 @@ -270,22 +270,6 @@ store_queue(Q = #amqqueue{durable = false}) -> policy_changed(_Q1, _Q2) -> ok. -determine_queue_nodes(Args) -> - Policy = rabbit_misc:table_lookup(Args, <<"x-ha-policy">>), - PolicyParams = rabbit_misc:table_lookup(Args, <<"x-ha-policy-params">>), - case {Policy, PolicyParams} of - {{_Type, <<"nodes">>}, {array, Nodes}} -> - case [list_to_atom(binary_to_list(Node)) || - {longstr, Node} <- Nodes] of - [Node] -> {Node, undefined}; - [First | Rest] -> {First, [First | Rest]} - end; - {{_Type, <<"all">>}, _} -> - {node(), all}; - _ -> - {node(), undefined} - end. - start_queue_process(Node, Q) -> {ok, Pid} = rabbit_amqqueue_sup:start_child(Node, [Q]), Q#amqqueue{pid = Pid}. @@ -351,13 +335,11 @@ 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, - [<<"x-expires">>, <<"x-message-ttl">>, <<"x-ha-policy">>]). + Args, RequiredArgs, QueueName, [<<"x-expires">>, <<"x-message-ttl">>]). check_declare_arguments(QueueName, Args) -> Checks = [{<<"x-expires">>, fun check_positive_int_arg/2}, {<<"x-message-ttl">>, fun check_non_neg_int_arg/2}, - {<<"x-ha-policy">>, fun check_ha_policy_arg/2}, {<<"x-dead-letter-exchange">>, fun check_string_arg/2}, {<<"x-dead-letter-routing-key">>, fun check_dlxrk_arg/2}], [case rabbit_misc:table_lookup(Args, Key) of @@ -406,29 +388,6 @@ check_dlxrk_arg({longstr, _}, Args) -> check_dlxrk_arg({Type, _}, _Args) -> {error, {unacceptable_type, Type}}. -check_ha_policy_arg({longstr, <<"all">>}, _Args) -> - ok; -check_ha_policy_arg({longstr, <<"nodes">>}, Args) -> - case rabbit_misc:table_lookup(Args, <<"x-ha-policy-params">>) of - undefined -> - {error, {require, 'x-ha-policy-params'}}; - {array, []} -> - {error, {require_non_empty_list_of_nodes_for_ha}}; - {array, Ary} -> - case lists:all(fun ({longstr, _Node}) -> true; - (_ ) -> false - end, Ary) of - true -> ok; - false -> {error, {require_node_list_as_longstrs_for_ha, Ary}} - end; - {Type, _} -> - {error, {ha_nodes_policy_params_not_array_of_longstr, Type}} - end; -check_ha_policy_arg({longstr, Policy}, _Args) -> - {error, {invalid_ha_policy, Policy}}; -check_ha_policy_arg({Type, _}, _Args) -> - {error, {unacceptable_type, Type}}. - list() -> mnesia:dirty_match_object(rabbit_queue, #amqqueue{_ = '_'}). @@ -625,8 +584,7 @@ pseudo_queue(QueueName, Pid) -> auto_delete = false, arguments = [], pid = Pid, - slave_pids = [], - mirror_nodes = undefined}. + slave_pids = []}. deliver([], #delivery{mandatory = false, immediate = false}, _Flow) -> %% /dev/null optimisation diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 8933de87..71f8aacd 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -230,8 +230,7 @@ matches(false, Q1, Q2) -> Q1#amqqueue.exclusive_owner =:= Q2#amqqueue.exclusive_owner andalso Q1#amqqueue.arguments =:= Q2#amqqueue.arguments andalso Q1#amqqueue.pid =:= Q2#amqqueue.pid andalso - Q1#amqqueue.slave_pids =:= Q2#amqqueue.slave_pids andalso - Q1#amqqueue.mirror_nodes =:= Q2#amqqueue.mirror_nodes. + Q1#amqqueue.slave_pids =:= Q2#amqqueue.slave_pids. bq_init(BQ, Q, Recover) -> Self = self(), @@ -296,11 +295,11 @@ next_state(State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> timed -> {ensure_sync_timer(State1), 0 } end. -backing_queue_module(#amqqueue{arguments = Args}) -> - case rabbit_misc:table_lookup(Args, <<"x-ha-policy">>) of - undefined -> {ok, BQM} = application:get_env(backing_queue_module), - BQM; - _Policy -> rabbit_mirror_queue_master +backing_queue_module(Q) -> + case rabbit_mirror_queue_misc:is_mirrored(Q) of + false -> {ok, BQM} = application:get_env(backing_queue_module), + BQM; + true -> rabbit_mirror_queue_master end. ensure_sync_timer(State = #q{sync_timer_ref = undefined}) -> @@ -906,11 +905,11 @@ infos(Items, State) -> Prefix ++ [{Item, i(Item, State)} || Item <- (Items1 -- [synchronised_slave_pids])]. -slaves_status(#q{q = #amqqueue{name = Name}}) -> - case rabbit_amqqueue:lookup(Name) of - {ok, #amqqueue{mirror_nodes = undefined}} -> +slaves_status(#q{q = Q}) -> + case rabbit_mirror_queue_misc:slave_pids(Q) of + not_mirrored -> %% TODO do we need this branch? [{slave_pids, ''}, {synchronised_slave_pids, ''}]; - {ok, #amqqueue{slave_pids = SPids}} -> + SPids -> {Results, _Bad} = delegate:invoke(SPids, fun rabbit_mirror_queue_slave:info/1), {SPids1, SSPids} = @@ -955,10 +954,10 @@ i(consumers, _) -> i(memory, _) -> {memory, M} = process_info(self(), memory), M; -i(slave_pids, #q{q = #amqqueue{name = Name}}) -> - case rabbit_amqqueue:lookup(Name) of - {ok, #amqqueue{mirror_nodes = undefined}} -> []; - {ok, #amqqueue{slave_pids = SPids}} -> SPids +i(slave_pids, #q{q = Q}) -> + case rabbit_mirror_queue_misc:slave_pids(Q) of + not_mirrored -> []; + SPids -> SPids end; i(backing_queue_status, #q{backing_queue_state = BQS, backing_queue = BQ}) -> BQ:status(BQS); diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index 750bcd56..899c31b7 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -82,17 +82,13 @@ stop() -> %% Same as start/1. exit({not_valid_for_generic_backing_queue, ?MODULE}). -init(#amqqueue { name = QName, mirror_nodes = MNodes } = Q, Recover, +init(#amqqueue { name = QName } = Q, Recover, AsyncCallback) -> {ok, CPid} = rabbit_mirror_queue_coordinator:start_link( Q, undefined, sender_death_fun(), length_fun()), GM = rabbit_mirror_queue_coordinator:get_gm(CPid), - MNodes1 = - (case MNodes of - all -> rabbit_mnesia:all_clustered_nodes(); - undefined -> []; - _ -> MNodes - end) -- [node()], + {_MNode, MNodes} = rabbit_mirror_queue_misc:determine_queue_nodes(Q), + MNodes1 = MNodes -- [node()], [rabbit_mirror_queue_misc:add_mirror(QName, Node) || Node <- MNodes1], {ok, BQ} = application:get_env(backing_queue_module), BQS = BQ:init(Q, Recover, AsyncCallback), diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 180677fe..876257b8 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -20,6 +20,10 @@ drop_mirror/2, drop_mirror/3, add_mirror/2, add_mirror/3, report_deaths/4]). +%% temp +-export([determine_queue_nodes/1, is_mirrored/1, slave_pids/1]). + + -include("rabbit.hrl"). %%---------------------------------------------------------------------------- @@ -85,24 +89,19 @@ remove_from_queue(QueueName, DeadPids) -> end). on_node_up() -> - Qs = + QNames = rabbit_misc:execute_mnesia_transaction( fun () -> mnesia:foldl( - fun (#amqqueue { mirror_nodes = undefined }, QsN) -> - QsN; - (#amqqueue { name = QName, - mirror_nodes = all }, QsN) -> - [QName | QsN]; - (#amqqueue { name = QName, - mirror_nodes = MNodes }, QsN) -> + fun (Q = #amqqueue{name = QName}, QNames0) -> + {_MNode, MNodes} = determine_queue_nodes(Q), case lists:member(node(), MNodes) of - true -> [QName | QsN]; - false -> QsN + true -> [QName | QNames0]; + false -> QNames0 end end, [], rabbit_queue) end), - [add_mirror(Q, node()) || Q <- Qs], + [add_mirror(QName, node()) || QName <- QNames], ok. drop_mirror(VHostPath, QueueName, MirrorNode) -> @@ -150,14 +149,13 @@ add_mirror(Queue, MirrorNode) -> end end). -if_mirrored_queue(Queue, Fun) -> - rabbit_amqqueue:with( - Queue, fun (#amqqueue { arguments = Args } = Q) -> - case rabbit_misc:table_lookup(Args, <<"x-ha-policy">>) of - undefined -> ok; - _ -> Fun(Q) - end - end). +if_mirrored_queue(QName, Fun) -> + rabbit_amqqueue:with(QName, fun (Q) -> + case is_mirrored(Q) of + false -> ok; + true -> Fun(Q) + end + end). report_deaths(_MirrorPid, _IsMaster, _QueueName, []) -> ok; @@ -172,3 +170,36 @@ report_deaths(MirrorPid, IsMaster, QueueName, DeadPids) -> end, rabbit_misc:pid_to_string(MirrorPid), [[rabbit_misc:pid_to_string(P), $ ] || P <- DeadPids]]). + +%%---------------------------------------------------------------------------- + +determine_queue_nodes(Q) -> + case [rabbit_policy:get(P, Q) || P <- [<<"ha-mode">>, <<"ha-params">>]] of + [{ok, <<"all">>}, _] -> + {node(), rabbit_mnesia:all_clustered_nodes()}; + [{ok, <<"nodes">>}, {ok, Nodes}] -> + case [list_to_atom(binary_to_list(Node)) || Node <- Nodes] of + [Node] -> {Node, []}; + [First | Rest] -> {First, [First | Rest]} + end; + [{ok, <<"at-least">>}, {ok, Count}] -> + {node(), lists:sublist(rabbit_mnesia:all_clustered_nodes(), Count)}; + _ -> + {node(), []} + end. + +is_mirrored(Q) -> + case rabbit_policy:get(<<"ha-mode">>, Q) of + {ok, <<"all">>} -> true; + {ok, <<"nodes">>} -> true; + {ok, <<"at-least">>} -> true; + _ -> false + end. + +slave_pids(Q = #amqqueue{name = Name}) -> + case is_mirrored(Q) of + false -> not_mirrored; + true -> {ok, #amqqueue{slave_pids = SPids}} = + rabbit_amqqueue:lookup(Name), + SPids + end. diff --git a/src/rabbit_types.erl b/src/rabbit_types.erl index 732c29b6..adfb19a0 100644 --- a/src/rabbit_types.erl +++ b/src/rabbit_types.erl @@ -118,8 +118,7 @@ exclusive_owner :: rabbit_types:maybe(pid()), arguments :: rabbit_framing:amqp_table(), pid :: rabbit_types:maybe(pid()), - slave_pids :: [pid()], - mirror_nodes :: [node()] | 'undefined' | 'all'}). + slave_pids :: [pid()]}). -type(exchange() :: #exchange{name :: rabbit_exchange:name(), diff --git a/src/rabbit_upgrade_functions.erl b/src/rabbit_upgrade_functions.erl index 18704807..d024ff63 100644 --- a/src/rabbit_upgrade_functions.erl +++ b/src/rabbit_upgrade_functions.erl @@ -40,6 +40,7 @@ -rabbit_upgrade({exchange_scratches, mnesia, [exchange_scratch]}). -rabbit_upgrade({policy, mnesia, [exchange_scratches, ha_mirrors]}). +-rabbit_upgrade({no_mirror_nodes, mnesia, [policy]}). %% ------------------------------------------------------------------- @@ -62,6 +63,7 @@ -spec(topic_trie_node/0 :: () -> 'ok'). -spec(runtime_parameters/0 :: () -> 'ok'). -spec(policy/0 :: () -> 'ok'). +-spec(no_mirror_nodes/0 :: () -> 'ok'). -endif. @@ -240,6 +242,18 @@ queue_policy(Table) -> [name, durable, auto_delete, exclusive_owner, arguments, pid, slave_pids, mirror_nodes, policy]). +no_mirror_nodes() -> + Tables = [rabbit_queue, rabbit_durable_queue], + RemoveMirrorNodesFun = + fun ({amqqueue, Name, D, AD, O, Args, Pid, SPids, _MNodes, Policy}) -> + {amqqueue, Name, D, AD, O, Args, Pid, SPids, Policy} + end, + [ok = transform(T, RemoveMirrorNodesFun, + [name, durable, auto_delete, exclusive_owner, arguments, + pid, slave_pids, policy]) + || T <- Tables], + ok. + %%-------------------------------------------------------------------- transform(TableName, Fun, FieldList) -> -- cgit v1.2.1 From a08b20906da8509a3fcc0bfb070c023ded24836c Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Thu, 5 Jul 2012 18:02:00 +0100 Subject: fix `remove_node/1' - do not remove nodes from offline nodes, unless we're "safe" We wan to remove nodes from offline nodes only when all the cluster nodes are offline, and when the node we are removing from was either the last to go down or the second after the node we are removing. However we cannot know that for sure. For this reason rabbit will refuse to remove from offline nodes unless the flag `--offline' is passed. There is more information in comments around `rabbit_mnesia:remove_node/2. --- src/rabbit_control_main.erl | 10 ++-- src/rabbit_mnesia.erl | 108 ++++++++++++++++++++++++++++++-------------- 2 files changed, 81 insertions(+), 37 deletions(-) diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index d927206b..10bb0f1b 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -26,11 +26,13 @@ -define(NODE_OPT, "-n"). -define(VHOST_OPT, "-p"). -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(RAM_DEF, {?RAM_OPT, flag}). +-define(OFFLINE_DEF, {?OFFLINE_OPT, flag}). -define(GLOBAL_DEFS(Node), [?QUIET_DEF, ?NODE_DEF(Node)]). @@ -46,7 +48,7 @@ {join_cluster, [?RAM_DEF]}, change_node_type, recluster, - remove_node, + {remove_node, [?OFFLINE_OPT]}, cluster_status, add_user, @@ -256,10 +258,12 @@ action(recluster, Node, [ClusterNodeS], _Opts, Inform) -> Inform("Re-clustering ~p with ~p", [Node, ClusterNode]), rpc_call(Node, rabbit_mnesia, recluster, [ClusterNode]); -action(remove_node, Node, [ClusterNodeS], _Opts, Inform) -> +action(remove_node, Node, [ClusterNodeS], Opts, Inform) -> ClusterNode = list_to_atom(ClusterNodeS), + RemoveWhenOffline = proplists:get_bool(?OFFLINE_OPT, Opts), Inform("Removing node ~p from cluster", [ClusterNode]), - rpc_call(Node, rabbit_mnesia, remove_node, [ClusterNode]); + rpc_call(Node, rabbit_mnesia, remove_node, + [ClusterNode, RemoveWhenOffline]); action(wait, Node, [PidFile], _Opts, Inform) -> Inform("Waiting for ~p", [Node]), diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index d2851231..400c3d7d 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -24,7 +24,7 @@ force_reset/0, recluster/1, change_node_type/1, - remove_node/1, + remove_node/2, status/0, is_db_empty/0, @@ -77,7 +77,7 @@ -spec(force_reset/0 :: () -> 'ok'). -spec(recluster/1 :: (node()) -> 'ok'). -spec(change_node_type/1 :: (node_type()) -> 'ok'). --spec(remove_node/1 :: (node()) -> 'ok'). +-spec(remove_node/2 :: (node(), boolean()) -> 'ok'). %% Various queries to get the status of the db -spec(status/0 :: () -> [{'nodes', [{node_type(), [node()]}]} | @@ -294,44 +294,82 @@ recluster(DiscoveryNode) -> ok. -%% We proceed like this: try to remove the node locally. If mnesia is offline -%% then we try to remove it remotely on some other node. If there are no other -%% nodes running, then *if the current node is a disc node* we force-load mnesia -%% and remove the node. -remove_node(Node) -> - case ordsets:is_element(Node, all_clustered_nodes()) of +%% We proceed like this: try to remove the node locally. If the node if offline, +%% we remove the node if: +%% * This node is a disc node +%% * All other nodes are offline +%% * This node was, at the best of our knowledge (see comment below) the last +%% or second to last after the node we're removing to go down +remove_node(Node, RemoveWhenOffline) -> + case is_clustered() of true -> ok; false -> throw({error, {not_a_cluster_node, "The node selected is not in the cluster."}}) end, + case {mnesia:system_info(is_running), RemoveWhenOffline} of + {yes, true} -> throw({error, {online_node_offline_flag, + "You set the --offline flag, which is " + "used to remove nodes remotely from " + "offline nodes, but this node is " + "online. "}}); + _ -> ok + end, case remove_node_if_mnesia_running(Node) of ok -> ok; {error, mnesia_not_running} -> - case remove_node_remotely(Node) of - ok -> - ok; - {error, no_running_cluster_nodes} -> - case is_disc_node() of - false -> - throw({error, - {removing_node_from_ram_node, - "There are no nodes running and this is a " - "RAM node"}}); - true -> + case {ordsets:del_element(Node, + running_nodes(all_clustered_nodes())), + is_disc_node(), RemoveWhenOffline} + of + {[], true, true} -> + %% Note that while we check if the nodes was the last to go + %% down, apart from the node we're removing from, this is + %% still unsafe. + %% Consider the situation in which A and B are clustered. A + %% goes down, and records B as the running node. Then B gets + %% clustered with C, C goes down and B goes down. In this + %% case, C is the second-to-last, but we don't know that and + %% we'll remove B from A anyway, even if that will lead to + %% bad things. + case ordsets:subtract(running_clustered_nodes(), + ordsets:from_list([node(), Node])) + of + [] -> start_mnesia(), try [mnesia:force_load_table(T) || T <- rabbit_mnesia:table_names()], - remove_node(Node), + remove_node(Node, false), ensure_mnesia_running() after stop_mnesia() - end - end + end; + _ -> + throw({error, + {not_last_node_to_go_down, + "The node you're trying to remove was not " + "the last to go down (excluding the node " + "you are removing). Please use the the " + "last node to go down to remove nodes when " + "the cluster is offline."}}) + end; + {_, _, false} -> + throw({error, + {offline_node_no_offline_flag, + "You are trying to remove a node from an offline " + "node. That's dangerous, but can be done with the " + "--offline flag. Please consult the manual for " + "rabbitmqctl for more informations."}}); + {_, _, _} -> + throw({error, + {removing_node_from_offline_node, + "To remove a node remotely from an offline node, " + "the node you're removing from must be a disc node " + "and all the other nodes must be offline."}}) end; - {error, Reason} -> - throw({error, Reason}) + Err = {error, _} -> + throw(Err) end. %%---------------------------------------------------------------------------- @@ -1007,18 +1045,17 @@ remove_node_if_mnesia_running(Node) -> end. leave_cluster() -> - remove_node_remotely(node()). - -remove_node_remotely(Removee) -> - case running_clustered_nodes() -- [Removee] of - [] -> - {error, no_running_cluster_nodes}; - RunningNodes -> + case {is_clustered(), + running_nodes(ordsets:del_element(node(), all_clustered_nodes()))} + of + {false, []} -> + ok; + {_, AllNodes} -> case lists:any( fun (Node) -> case rpc:call(Node, rabbit_mnesia, remove_node_if_mnesia_running, - [Removee]) + [node()]) of ok -> true; @@ -1030,10 +1067,13 @@ remove_node_remotely(Removee) -> false end end, - RunningNodes) + AllNodes) of true -> ok; - false -> {error, no_running_cluster_nodes} + false -> throw({error, + {no_running_cluster_nodes, + "You cannot leave a cluster if no online " + "nodes are present"}}) end end. -- cgit v1.2.1 From 5167a14626a076904748f1d68cb7be4540c7b75b Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 6 Jul 2012 11:16:37 +0100 Subject: While this didn't actually crash the VM for me, we generally want to HiPE as early as we possibly can. --- src/rabbit.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 58e1c50e..23b422af 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -309,8 +309,8 @@ start() -> boot() -> start_it(fun() -> ok = ensure_application_loaded(), - ok = rabbit_mnesia:prepare(), maybe_hipe_compile(), + ok = rabbit_mnesia:prepare(), ok = ensure_working_log_handlers(), ok = rabbit_upgrade:maybe_upgrade_mnesia(), Plugins = rabbit_plugins:setup(), -- cgit v1.2.1 From a5542de131517694ae09b57e46804670c5050a71 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 6 Jul 2012 13:27:50 +0100 Subject: Rename function, remove utter rubbish. --- src/rabbit_amqqueue.erl | 22 +++++++++++----------- src/rabbit_mirror_queue_master.erl | 2 +- src/rabbit_mirror_queue_misc.erl | 6 +++--- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 0565b1a5..b32aefdb 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -207,18 +207,18 @@ recover_durable_queues(DurableQueues) -> declare(QueueName, Durable, AutoDelete, Args, Owner) -> ok = check_declare_arguments(QueueName, Args), - Node = node(), %% TODO utter rubbish - Q = start_queue_process( - Node, rabbit_policy:set(#amqqueue{name = QueueName, - durable = Durable, - auto_delete = AutoDelete, - arguments = Args, - exclusive_owner = Owner, - pid = none, - slave_pids = []})), - case gen_server2:call(Q#amqqueue.pid, {init, false}, infinity) of + Q0 = rabbit_policy:set(#amqqueue{name = QueueName, + durable = Durable, + auto_delete = AutoDelete, + arguments = Args, + exclusive_owner = Owner, + pid = none, + slave_pids = []}), + {Node, _MNodes} = rabbit_mirror_queue_misc:queue_nodes(Q0), + Q1 = start_queue_process(Node, Q0), + case gen_server2:call(Q1#amqqueue.pid, {init, false}, infinity) of not_found -> rabbit_misc:not_found(QueueName); - Q1 -> Q1 + Q2 -> Q2 end. internal_declare(Q, true) -> diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index 899c31b7..50349204 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -87,7 +87,7 @@ init(#amqqueue { name = QName } = Q, Recover, {ok, CPid} = rabbit_mirror_queue_coordinator:start_link( Q, undefined, sender_death_fun(), length_fun()), GM = rabbit_mirror_queue_coordinator:get_gm(CPid), - {_MNode, MNodes} = rabbit_mirror_queue_misc:determine_queue_nodes(Q), + {_MNode, MNodes} = rabbit_mirror_queue_misc:queue_nodes(Q), MNodes1 = MNodes -- [node()], [rabbit_mirror_queue_misc:add_mirror(QName, Node) || Node <- MNodes1], {ok, BQ} = application:get_env(backing_queue_module), diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 876257b8..a84623f6 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -21,7 +21,7 @@ report_deaths/4]). %% temp --export([determine_queue_nodes/1, is_mirrored/1, slave_pids/1]). +-export([queue_nodes/1, is_mirrored/1, slave_pids/1]). -include("rabbit.hrl"). @@ -94,7 +94,7 @@ on_node_up() -> fun () -> mnesia:foldl( fun (Q = #amqqueue{name = QName}, QNames0) -> - {_MNode, MNodes} = determine_queue_nodes(Q), + {_MNode, MNodes} = queue_nodes(Q), case lists:member(node(), MNodes) of true -> [QName | QNames0]; false -> QNames0 @@ -173,7 +173,7 @@ report_deaths(MirrorPid, IsMaster, QueueName, DeadPids) -> %%---------------------------------------------------------------------------- -determine_queue_nodes(Q) -> +queue_nodes(Q) -> case [rabbit_policy:get(P, Q) || P <- [<<"ha-mode">>, <<"ha-params">>]] of [{ok, <<"all">>}, _] -> {node(), rabbit_mnesia:all_clustered_nodes()}; -- cgit v1.2.1 From d30113e56ae41d7600ff03a07c1b150dd405f1bc Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Fri, 6 Jul 2012 15:01:40 +0100 Subject: correct invocations of `on_node_{up,down}' in `rabbit_node_monitor'. --- src/rabbit_node_monitor.erl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index 788a06c7..f7bc7cfc 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -258,11 +258,12 @@ code_change(_OldVsn, State, _Extra) -> handle_dead_rabbit(Node) -> ok = rabbit_networking:on_node_down(Node), ok = rabbit_amqqueue:on_node_down(Node), - ok = rabbit_alarm:on_node_down(Node). + ok = rabbit_alarm:on_node_down(Node), + ok = rabbit_mnesia:on_node_down(Node). handle_live_rabbit(Node) -> ok = rabbit_alarm:on_node_up(Node), - ok = rabbit_mnesia:on_node_down(Node). + ok = rabbit_mnesia:on_node_up(Node). %%-------------------------------------------------------------------- %% Internal utils -- cgit v1.2.1 From 20fe8c63e46ae6fcc9f6adb7081a7bcd861a2e27 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Fri, 6 Jul 2012 15:44:18 +0100 Subject: fix the flags declaration in `rabbit_control_main' --- src/rabbit_control_main.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index 273f6688..4011709a 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -48,7 +48,7 @@ {join_cluster, [?RAM_DEF]}, change_node_type, recluster, - {remove_node, [?OFFLINE_OPT]}, + {remove_node, [?OFFLINE_DEF]}, cluster_status, add_user, -- cgit v1.2.1 From 40053cb586938328d6c0fcb05de3b0a4da4dd693 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 6 Jul 2012 16:55:54 +0100 Subject: Dynamic change of HA policy. Promotion from non-HA to master, and demotion from master to non-HA as appropriate. There will still be problems if the policy changes such that the master needs to change - I'm not sure we should even allow this. --- src/rabbit_amqqueue.erl | 11 +++++-- src/rabbit_amqqueue_process.erl | 25 +++++++++++++++ src/rabbit_mirror_queue_master.erl | 42 ++++++++++++++++++-------- src/rabbit_mirror_queue_misc.erl | 62 +++++++++++++++++++++++++++++--------- src/rabbit_mirror_queue_slave.erl | 4 ++- 5 files changed, 112 insertions(+), 32 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index b32aefdb..be6613a0 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -30,6 +30,8 @@ -export([on_node_down/1]). -export([update/2, store_queue/1, policy_changed/2]). +%% temp +-export([start_mirroring/1, stop_mirroring/1]). %% internal -export([internal_declare/2, internal_delete/2, run_backing_queue/3, @@ -214,7 +216,7 @@ declare(QueueName, Durable, AutoDelete, Args, Owner) -> exclusive_owner = Owner, pid = none, slave_pids = []}), - {Node, _MNodes} = rabbit_mirror_queue_misc:queue_nodes(Q0), + {Node, _MNodes} = rabbit_mirror_queue_misc:suggested_queue_nodes(Q0), Q1 = start_queue_process(Node, Q0), case gen_server2:call(Q1#amqqueue.pid, {init, false}, infinity) of not_found -> rabbit_misc:not_found(QueueName); @@ -267,8 +269,8 @@ store_queue(Q = #amqqueue{durable = false}) -> ok = mnesia:write(rabbit_queue, Q, write), ok. -policy_changed(_Q1, _Q2) -> - ok. +policy_changed(Q1, Q2) -> + rabbit_mirror_queue_misc:update_mirrors(Q1, Q2). start_queue_process(Node, Q) -> {ok, Pid} = rabbit_amqqueue_sup:start_child(Node, [Q]), @@ -550,6 +552,9 @@ set_ram_duration_target(QPid, Duration) -> set_maximum_since_use(QPid, Age) -> gen_server2:cast(QPid, {set_maximum_since_use, Age}). +start_mirroring(QPid) -> ok = delegate_call(QPid, start_mirroring). +stop_mirroring(QPid) -> ok = delegate_call(QPid, stop_mirroring). + on_node_down(Node) -> rabbit_misc:execute_mnesia_tx_with_tail( fun () -> QsDels = diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 71f8aacd..e4a61cb4 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1199,6 +1199,31 @@ handle_call({requeue, AckTags, ChPid}, From, State) -> ChPid, AckTags, State, fun (State1) -> requeue_and_run(AckTags, State1) end)); +handle_call(start_mirroring, _From, State = #q{backing_queue = BQ, + backing_queue_state = BQS}) -> + %% lookup again to get policy for init_with_existing_bq + {ok, Q} = rabbit_amqqueue:lookup(qname(State)), + true = BQ =/= rabbit_mirror_queue_master, %% assertion + BQ1 = rabbit_mirror_queue_master, + BQS1 = BQ1:init_with_existing_bq(Q, BQ, BQS), + reply(ok, State#q{backing_queue = BQ1, + backing_queue_state = BQS1}); + +handle_call(stop_mirroring, _From, State = #q{backing_queue = BQ, + backing_queue_state = BQS}) -> + BQ = rabbit_mirror_queue_master, %% assertion + {BQ1, BQS1} = BQ:stop_mirroring(BQS), + rabbit_misc:execute_mnesia_transaction( + fun () -> + case mnesia:read({rabbit_queue, qname(State)}) of + [] -> ok; + [Q] -> rabbit_amqqueue:store_queue( + Q#amqqueue{slave_pids = undefined}) + end + end), + reply(ok, State#q{backing_queue = BQ1, + backing_queue_state = BQS1}); + handle_call(force_event_refresh, _From, State = #q{exclusive_consumer = Exclusive}) -> rabbit_event:notify(queue_created, infos(?CREATION_EVENT_KEYS, State)), diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index 50349204..e5ca085d 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -27,6 +27,9 @@ -export([promote_backing_queue_state/6, sender_death_fun/0, length_fun/0]). +%% temp +-export([init_with_existing_bq/3, stop_mirroring/1]). + -behaviour(rabbit_backing_queue). -include("rabbit.hrl"). @@ -82,16 +85,17 @@ stop() -> %% Same as start/1. exit({not_valid_for_generic_backing_queue, ?MODULE}). -init(#amqqueue { name = QName } = Q, Recover, - AsyncCallback) -> +init(Q, Recover, AsyncCallback) -> + {ok, BQ} = application:get_env(backing_queue_module), + BQS = BQ:init(Q, Recover, AsyncCallback), + init_with_existing_bq(Q, BQ, BQS). + +init_with_existing_bq(#amqqueue { name = QName } = Q, BQ, BQS) -> {ok, CPid} = rabbit_mirror_queue_coordinator:start_link( Q, undefined, sender_death_fun(), length_fun()), GM = rabbit_mirror_queue_coordinator:get_gm(CPid), - {_MNode, MNodes} = rabbit_mirror_queue_misc:queue_nodes(Q), - MNodes1 = MNodes -- [node()], - [rabbit_mirror_queue_misc:add_mirror(QName, Node) || Node <- MNodes1], - {ok, BQ} = application:get_env(backing_queue_module), - BQS = BQ:init(Q, Recover, AsyncCallback), + {_MNode, SNodes} = rabbit_mirror_queue_misc:suggested_queue_nodes(Q), + [rabbit_mirror_queue_misc:add_mirror(QName, Node) || Node <- SNodes], ok = gm:broadcast(GM, {length, BQ:len(BQS)}), #state { gm = GM, coordinator = CPid, @@ -103,14 +107,24 @@ init(#amqqueue { name = QName } = Q, Recover, ack_msg_id = dict:new(), known_senders = sets:new() }. +stop_mirroring(State = #state { coordinator = CPid, + backing_queue = BQ, + backing_queue_state = BQS }) -> + unlink(CPid), + stop_all_slaves(unmirroring, State), + {BQ, BQS}. + terminate({shutdown, dropped} = Reason, - State = #state { backing_queue = BQ, backing_queue_state = BQS }) -> + State = #state { gm = GM, + backing_queue = BQ, + backing_queue_state = BQS }) -> %% Backing queue termination - this node has been explicitly %% dropped. Normally, non-durable queues would be tidied up on %% startup, but there's a possibility that we will be added back %% in without this node being restarted. Thus we must do the full %% blown delete_and_terminate now, but only locally: we do not %% broadcast delete_and_terminate. + ok = gm:leave(GM), %% TODO presumably we need this? State #state { backing_queue_state = BQ:delete_and_terminate(Reason, BQS), set_delivered = 0 }; terminate(Reason, @@ -120,15 +134,17 @@ terminate(Reason, %% node. Thus just let some other slave take over. State #state { backing_queue_state = BQ:terminate(Reason, BQS) }. -delete_and_terminate(Reason, State = #state { gm = GM, - backing_queue = BQ, +delete_and_terminate(Reason, State = #state { backing_queue = BQ, backing_queue_state = BQS }) -> + stop_all_slaves(Reason, State), + State #state { backing_queue_state = BQ:delete_and_terminate(Reason, BQS), + set_delivered = 0 }. + +stop_all_slaves(Reason, #state{gm = GM}) -> Slaves = [Pid || Pid <- gm:group_members(GM), node(Pid) =/= node()], MRefs = [erlang:monitor(process, S) || S <- Slaves], ok = gm:broadcast(GM, {delete_and_terminate, Reason}), - monitor_wait(MRefs), - State #state { backing_queue_state = BQ:delete_and_terminate(Reason, BQS), - set_delivered = 0 }. + monitor_wait(MRefs). monitor_wait([]) -> ok; diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index a84623f6..de507afe 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -21,7 +21,8 @@ report_deaths/4]). %% temp --export([queue_nodes/1, is_mirrored/1, slave_pids/1]). +-export([suggested_queue_nodes/1, is_mirrored/1, slave_pids/1, + update_mirrors/2]). -include("rabbit.hrl"). @@ -94,8 +95,8 @@ on_node_up() -> fun () -> mnesia:foldl( fun (Q = #amqqueue{name = QName}, QNames0) -> - {_MNode, MNodes} = queue_nodes(Q), - case lists:member(node(), MNodes) of + {_MNode, SNodes} = suggested_queue_nodes(Q), + case lists:member(node(), SNodes) of true -> [QName | QNames0]; false -> QNames0 end @@ -107,9 +108,9 @@ on_node_up() -> drop_mirror(VHostPath, QueueName, MirrorNode) -> drop_mirror(rabbit_misc:r(VHostPath, queue, QueueName), MirrorNode). -drop_mirror(Queue, MirrorNode) -> +drop_mirror(QName, MirrorNode) -> if_mirrored_queue( - Queue, + QName, fun (#amqqueue { name = Name, pid = QPid, slave_pids = SPids }) -> case [Pid || Pid <- [QPid | SPids], node(Pid) =:= MirrorNode] of [] -> @@ -128,9 +129,9 @@ drop_mirror(Queue, MirrorNode) -> add_mirror(VHostPath, QueueName, MirrorNode) -> add_mirror(rabbit_misc:r(VHostPath, queue, QueueName), MirrorNode). -add_mirror(Queue, MirrorNode) -> +add_mirror(QName, MirrorNode) -> if_mirrored_queue( - Queue, + QName, fun (#amqqueue { name = Name, pid = QPid, slave_pids = SPids } = Q) -> case [Pid || Pid <- [QPid | SPids], node(Pid) =:= MirrorNode] of [] -> case rabbit_mirror_queue_slave_sup:start_child( @@ -173,21 +174,35 @@ report_deaths(MirrorPid, IsMaster, QueueName, DeadPids) -> %%---------------------------------------------------------------------------- -queue_nodes(Q) -> +%% TODO this should take account of current nodes so we don't throw +%% away mirrors or change the master needlessly +suggested_queue_nodes(Q) -> case [rabbit_policy:get(P, Q) || P <- [<<"ha-mode">>, <<"ha-params">>]] of [{ok, <<"all">>}, _] -> - {node(), rabbit_mnesia:all_clustered_nodes()}; + {node(), rabbit_mnesia:all_clustered_nodes() -- [node()]}; [{ok, <<"nodes">>}, {ok, Nodes}] -> case [list_to_atom(binary_to_list(Node)) || Node <- Nodes] of [Node] -> {Node, []}; - [First | Rest] -> {First, [First | Rest]} + [First | Rest] -> {First, Rest} end; [{ok, <<"at-least">>}, {ok, Count}] -> - {node(), lists:sublist(rabbit_mnesia:all_clustered_nodes(), Count)}; + {node(), lists:sublist( + rabbit_mnesia:all_clustered_nodes(), Count) -- [node()]}; _ -> {node(), []} end. +actual_queue_nodes(#amqqueue{pid = MPid, slave_pids = SPids}) -> + MNode = case MPid of + undefined -> undefined; + _ -> node(MPid) + end, + SNodes = case SPids of + undefined -> undefined; + _ -> [node(Pid) || Pid <- SPids] + end, + {MNode, SNodes}. + is_mirrored(Q) -> case rabbit_policy:get(<<"ha-mode">>, Q) of {ok, <<"all">>} -> true; @@ -196,10 +211,27 @@ is_mirrored(Q) -> _ -> false end. -slave_pids(Q = #amqqueue{name = Name}) -> +slave_pids(#amqqueue{name = Name}) -> + {ok, Q = #amqqueue{slave_pids = SPids}} = rabbit_amqqueue:lookup(Name), case is_mirrored(Q) of false -> not_mirrored; - true -> {ok, #amqqueue{slave_pids = SPids}} = - rabbit_amqqueue:lookup(Name), - SPids + true -> SPids + end. + +update_mirrors(OldQ = #amqqueue{name = QName, pid = QPid}, + NewQ = #amqqueue{name = QName, pid = QPid}) -> + case {is_mirrored(OldQ), is_mirrored(NewQ)} of + {false, false} -> ok; + {true, false} -> rabbit_amqqueue:stop_mirroring(QPid); + {false, true} -> rabbit_amqqueue:start_mirroring(QPid); + {true, true} -> {OldMNode, OldSNodes} = actual_queue_nodes(OldQ), + {NewMNode, NewSNodes} = suggested_queue_nodes(NewQ), + case OldMNode of + NewMNode -> ok; + _ -> io:format("TODO: master needs to change for ~p~n", [NewQ]) + end, + Add = NewSNodes -- OldSNodes, + Remove = OldSNodes -- NewSNodes, + [ok = drop_mirror(QName, SNode) || SNode <- Remove], + [ok = add_mirror(QName, SNode) || SNode <- Add] end. diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 03fafc3e..8f57e695 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -275,9 +275,11 @@ terminate(_Reason, #state { backing_queue_state = undefined }) -> %% We've received a delete_and_terminate from gm, thus nothing to %% do here. ok; -terminate({shutdown, dropped} = R, #state { backing_queue = BQ, +terminate({shutdown, dropped} = R, #state { gm = GM, + backing_queue = BQ, backing_queue_state = BQS }) -> %% See rabbit_mirror_queue_master:terminate/2 + ok = gm:leave(GM), %% TODO presumably we need this? BQ:delete_and_terminate(R, BQS); terminate(Reason, #state { q = Q, gm = GM, -- cgit v1.2.1 From 1a36bd727ec792b0a9b75da731f27b2fb8a89a02 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 6 Jul 2012 18:21:28 +0100 Subject: Store sync'ed slaves in Mnesia. This is not quite right yet. --- include/rabbit.hrl | 3 ++- src/rabbit_amqqueue.erl | 1 + src/rabbit_amqqueue_process.erl | 44 ++++++++------------------------------- src/rabbit_mirror_queue_misc.erl | 12 +++++++---- src/rabbit_mirror_queue_slave.erl | 18 +++++++++++----- src/rabbit_upgrade_functions.erl | 16 +++++++++++++- 6 files changed, 48 insertions(+), 46 deletions(-) diff --git a/include/rabbit.hrl b/include/rabbit.hrl index e8b4a623..d6fac46d 100644 --- a/include/rabbit.hrl +++ b/include/rabbit.hrl @@ -47,7 +47,8 @@ -record(exchange_serial, {name, next}). -record(amqqueue, {name, durable, auto_delete, exclusive_owner = none, - arguments, pid, slave_pids, mirror_nodes, policy}). + arguments, pid, slave_pids, sync_slave_pids, mirror_nodes, + policy}). %% mnesia doesn't like unary records, so we add a dummy 'value' field -record(route, {binding, value = const}). diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index afbaea65..32a33812 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -215,6 +215,7 @@ declare(QueueName, Durable, AutoDelete, Args, Owner) -> exclusive_owner = Owner, pid = none, slave_pids = [], + sync_slave_pids = [], mirror_nodes = MNodes}), case gen_server2:call(Q#amqqueue.pid, {init, false}, infinity) of not_found -> rabbit_misc:not_found(QueueName); diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 8933de87..9d03805a 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -93,6 +93,7 @@ consumers, memory, slave_pids, + synchronised_slave_pids, backing_queue_status ]). @@ -102,9 +103,7 @@ durable, auto_delete, arguments, - owner_pid, - slave_pids, - synchronised_slave_pids + owner_pid ]). -define(INFO_KEYS, @@ -893,37 +892,7 @@ make_dead_letter_msg(Reason, now_micros() -> timer:now_diff(now(), {0,0,0}). -infos(Items, State) -> - {Prefix, Items1} = - case lists:member(synchronised_slave_pids, Items) of - true -> Prefix1 = slaves_status(State), - case lists:member(slave_pids, Items) of - true -> {Prefix1, Items -- [slave_pids]}; - false -> {proplists:delete(slave_pids, Prefix1), Items} - end; - false -> {[], Items} - end, - Prefix ++ [{Item, i(Item, State)} - || Item <- (Items1 -- [synchronised_slave_pids])]. - -slaves_status(#q{q = #amqqueue{name = Name}}) -> - case rabbit_amqqueue:lookup(Name) of - {ok, #amqqueue{mirror_nodes = undefined}} -> - [{slave_pids, ''}, {synchronised_slave_pids, ''}]; - {ok, #amqqueue{slave_pids = SPids}} -> - {Results, _Bad} = - delegate:invoke(SPids, fun rabbit_mirror_queue_slave:info/1), - {SPids1, SSPids} = - lists:foldl( - fun ({Pid, Infos}, {SPidsN, SSPidsN}) -> - {[Pid | SPidsN], - case proplists:get_bool(is_synchronised, Infos) of - true -> [Pid | SSPidsN]; - false -> SSPidsN - end} - end, {[], []}, Results), - [{slave_pids, SPids1}, {synchronised_slave_pids, SSPids}] - end. +infos(Items, State) -> [{Item, i(Item, State)} || Item <- Items]. i(name, #q{q = #amqqueue{name = Name}}) -> Name; i(durable, #q{q = #amqqueue{durable = Durable}}) -> Durable; @@ -957,9 +926,14 @@ i(memory, _) -> M; i(slave_pids, #q{q = #amqqueue{name = Name}}) -> case rabbit_amqqueue:lookup(Name) of - {ok, #amqqueue{mirror_nodes = undefined}} -> []; + {ok, #amqqueue{mirror_nodes = undefined}} -> ''; {ok, #amqqueue{slave_pids = SPids}} -> SPids end; +i(synchronised_slave_pids, #q{q = #amqqueue{name = Name}}) -> + case rabbit_amqqueue:lookup(Name) of + {ok, #amqqueue{mirror_nodes = undefined}} -> ''; + {ok, #amqqueue{sync_slave_pids = SSPids}} -> SSPids + end; i(backing_queue_status, #q{backing_queue_state = BQS, backing_queue = BQ}) -> BQ:status(BQS); i(Item, _) -> diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 180677fe..4d26a4d0 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -58,8 +58,9 @@ remove_from_queue(QueueName, DeadPids) -> %% get here. case mnesia:read({rabbit_queue, QueueName}) of [] -> {error, not_found}; - [Q = #amqqueue { pid = QPid, - slave_pids = SPids }] -> + [Q = #amqqueue { pid = QPid, + slave_pids = SPids, + sync_slave_pids = SSPids}] -> [QPid1 | SPids1] = Alive = [Pid || Pid <- [QPid | SPids], not lists:member(node(Pid), DeadNodes)], @@ -70,8 +71,11 @@ remove_from_queue(QueueName, DeadPids) -> %% Either master hasn't changed, so %% we're ok to update mnesia; or we have %% become the master. - Q1 = Q #amqqueue { pid = QPid1, - slave_pids = SPids1 }, + SSPids1 = [SSPid || SSPid <- SSPids, + lists:member(SSPid, SPids1)], + Q1 = Q #amqqueue { pid = QPid1, + slave_pids = SPids1, + sync_slave_pids = SSPids1}, ok = rabbit_amqqueue:store_queue(Q1), {ok, QPid1, [QPid | SPids] -- Alive}; _ -> diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 03fafc3e..ee1d2b07 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -442,8 +442,6 @@ promote_me(From, #state { q = Q = #amqqueue { name = QName }, msg_id_ack = MA, msg_id_status = MS, known_senders = KS }) -> - rabbit_event:notify(queue_slave_promoted, [{pid, self()}, - {name, QName}]), rabbit_log:info("Mirrored-queue (~s): Promoting slave ~s to master~n", [rabbit_misc:rs(QName), rabbit_misc:pid_to_string(self())]), Q1 = Q #amqqueue { pid = self() }, @@ -906,10 +904,20 @@ maybe_store_ack(true, MsgId, AckTag, State = #state { msg_id_ack = MA, %% We intentionally leave out the head where a slave becomes %% unsynchronised: we assert that can never happen. -set_synchronised(true, State = #state { q = #amqqueue { name = QName }, +set_synchronised(true, State = #state { q = Q = #amqqueue { name = QName }, synchronised = false }) -> - rabbit_event:notify(queue_slave_synchronised, [{pid, self()}, - {name, QName}]), + Self = self(), + rabbit_misc:execute_mnesia_transaction( + fun () -> + case mnesia:read({rabbit_queue, QName}) of + [] -> + ok; + [Q1 = #amqqueue{sync_slave_pids = SSPids}] -> + Q2 = Q1#amqqueue{sync_slave_pids = [Self | SSPids]}, + ok = rabbit_amqqueue:store_queue(Q2) + end + end), + rabbit_amqqueue:info(Q, [name]), %% Wake it up TODO this doesn't work State #state { synchronised = true }; set_synchronised(true, State) -> State; diff --git a/src/rabbit_upgrade_functions.erl b/src/rabbit_upgrade_functions.erl index 18704807..8a44e03a 100644 --- a/src/rabbit_upgrade_functions.erl +++ b/src/rabbit_upgrade_functions.erl @@ -40,6 +40,7 @@ -rabbit_upgrade({exchange_scratches, mnesia, [exchange_scratch]}). -rabbit_upgrade({policy, mnesia, [exchange_scratches, ha_mirrors]}). +-rabbit_upgrade({sync_slave_pids, mnesia, [policy]}). %% ------------------------------------------------------------------- @@ -62,7 +63,7 @@ -spec(topic_trie_node/0 :: () -> 'ok'). -spec(runtime_parameters/0 :: () -> 'ok'). -spec(policy/0 :: () -> 'ok'). - +-spec(sync_slave_pids/0 :: () -> 'ok'). -endif. %%-------------------------------------------------------------------- @@ -240,6 +241,19 @@ queue_policy(Table) -> [name, durable, auto_delete, exclusive_owner, arguments, pid, slave_pids, mirror_nodes, policy]). +sync_slave_pids() -> + [ok = sync_slave_pids(T) || T <- [rabbit_queue, rabbit_durable_queue]]. + +sync_slave_pids(Table) -> + transform( + Table, + fun ({amqqueue, N, D, AD, Excl, Args, Pid, SPids, MNodes, Pol}) -> + {amqqueue, N, D, AD, Excl, Args, Pid, SPids, [], MNodes, Pol} + end, + [name, durable, auto_delete, exclusive_owner, arguments, pid, + slave_pids, sync_slave_pids, mirror_nodes, policy]). + + %%-------------------------------------------------------------------- transform(TableName, Fun, FieldList) -> -- cgit v1.2.1 From 9751b5aeeb99e5b329e0fa021a0199b10d8581fb Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Mon, 9 Jul 2012 11:04:25 +0100 Subject: check that the node selected for removal is in the cluster --- src/rabbit_mnesia.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 400c3d7d..67fbfd82 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -301,7 +301,7 @@ recluster(DiscoveryNode) -> %% * This node was, at the best of our knowledge (see comment below) the last %% or second to last after the node we're removing to go down remove_node(Node, RemoveWhenOffline) -> - case is_clustered() of + case ordsets:is_element(Node, all_clustered_nodes()) of true -> ok; false -> throw({error, {not_a_cluster_node, "The node selected is not in the cluster."}}) -- cgit v1.2.1 From 6c7532d5a7bb255f4209f13f6b6246ebd3ce4cf5 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 9 Jul 2012 11:21:57 +0100 Subject: It did work, but we needed to do that in another place too... --- src/rabbit_mirror_queue_misc.erl | 1 + src/rabbit_mirror_queue_slave.erl | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 4d26a4d0..fbffbee1 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -77,6 +77,7 @@ remove_from_queue(QueueName, DeadPids) -> slave_pids = SPids1, sync_slave_pids = SSPids1}, ok = rabbit_amqqueue:store_queue(Q1), + rabbit_amqqueue:info(Q1, [name]), %% Wake it up {ok, QPid1, [QPid | SPids] -- Alive}; _ -> %% Master has changed, and we're not it, diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index ee1d2b07..a858ee4e 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -917,7 +917,7 @@ set_synchronised(true, State = #state { q = Q = #amqqueue { name = QName }, ok = rabbit_amqqueue:store_queue(Q2) end end), - rabbit_amqqueue:info(Q, [name]), %% Wake it up TODO this doesn't work + rabbit_amqqueue:info(Q, [name]), %% Wake it up State #state { synchronised = true }; set_synchronised(true, State) -> State; -- cgit v1.2.1 From efa9328e6f26d034450a917805a702cb4b6a5634 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Mon, 9 Jul 2012 11:32:37 +0100 Subject: ?SERVER instead of explicit name --- src/rabbit_node_monitor.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index f7bc7cfc..f6cbee34 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -154,14 +154,14 @@ reset_cluster_status_file() -> %%---------------------------------------------------------------------------- joined_cluster(Node, IsDiscNode) -> - gen_server:cast(rabbit_node_monitor, {rabbit_join, Node, IsDiscNode}). + gen_server:cast(?SERVER, {rabbit_join, Node, IsDiscNode}). notify_joined_cluster() -> cluster_multicall(joined_cluster, [node(), rabbit_mnesia:is_disc_node()]), ok. left_cluster(Node) -> - gen_server:cast(rabbit_node_monitor, {left_cluster, Node}). + gen_server:cast(?SERVER, {left_cluster, Node}). notify_left_cluster(Node) -> left_cluster(Node), @@ -169,7 +169,7 @@ notify_left_cluster(Node) -> ok. node_up(Node, IsDiscNode) -> - gen_server:cast(rabbit_node_monitor, {node_up, Node, IsDiscNode}). + gen_server:cast(?SERVER, {node_up, Node, IsDiscNode}). notify_node_up() -> Nodes = cluster_multicall(node_up, [node(), rabbit_mnesia:is_disc_node()]), -- cgit v1.2.1 From 0ba3d5e269c3807b48d13536aacfc3e092af7651 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Mon, 9 Jul 2012 11:33:14 +0100 Subject: remove unnecessary `ensure_mnesia_not_running/0' --- src/rabbit_mnesia.erl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 67fbfd82..b5bf7442 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -577,8 +577,7 @@ init_db_with_mnesia(ClusterNodes, WantDiscNode, CheckConsistency, Force) -> init_db_and_upgrade(ClusterNodes, WantDiscNode, Force) after stop_mnesia() - end, - ensure_mnesia_not_running(). + end. init_db_with_mnesia(ClusterNodes, WantDiscNode, Force) -> init_db_with_mnesia(ClusterNodes, WantDiscNode, true, Force). -- cgit v1.2.1 From 720fe8f59a77a266a1922a390484616104401d7b Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Mon, 9 Jul 2012 11:43:14 +0100 Subject: `change_extra_db_nodes/2' throws instead of returning error --- src/rabbit_mnesia.erl | 82 ++++++++++++++++++++++++--------------------------- 1 file changed, 39 insertions(+), 43 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index b5bf7442..a800ac05 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -514,45 +514,41 @@ table_names() -> %% is the need to and catching up if there are other nodes in the cluster %% already. It also updates the cluster status file. init_db(ClusterNodes, WantDiscNode, Force) -> - case change_extra_db_nodes(ClusterNodes, Force) of - {error, Reason} -> - throw({error, Reason}); - {ok, Nodes} -> - %% Note that we use `system_info' here and not the cluster status - %% since when we start rabbit for the first time the cluster status - %% will say we are a disc node but the tables won't be present yet. - WasDiscNode = mnesia:system_info(use_dir), - case {Nodes, WasDiscNode, WantDiscNode} of - {[], _, false} -> - %% Standalone ram node, we don't want that - throw({error, cannot_create_standalone_ram_node}); - {[], false, true} -> - %% RAM -> disc, starting from scratch - ok = create_schema(); - {[], true, true} -> - %% First disc node up - ok; - {[AnotherNode | _], _, _} -> - %% Subsequent node in cluster, catch up - ensure_version_ok( - rpc:call(AnotherNode, rabbit_version, recorded, [])), - ok = wait_for_replicated_tables(), - %% The sequence in which we delete the schema and then the - %% other tables is important: if we delete the schema first - %% when moving to RAM mnesia will loudly complain since it - %% doesn't make much sense to do that. But when moving to - %% disc, we need to move the schema first. - case WantDiscNode of - true -> create_local_table_copy(schema, disc_copies), - create_local_table_copies(disc); - false -> create_local_table_copies(ram), - create_local_table_copy(schema, ram_copies) - end - end, - ensure_schema_integrity(), - rabbit_node_monitor:update_cluster_status_file(), - ok - end. + Nodes = change_extra_db_nodes(ClusterNodes, Force), + %% Note that we use `system_info' here and not the cluster status since when + %% we start rabbit for the first time the cluster status will say we are a + %% disc node but the tables won't be present yet. + WasDiscNode = mnesia:system_info(use_dir), + case {Nodes, WasDiscNode, WantDiscNode} of + {[], _, false} -> + %% Standalone ram node, we don't want that + throw({error, cannot_create_standalone_ram_node}); + {[], false, true} -> + %% RAM -> disc, starting from scratch + ok = create_schema(); + {[], true, true} -> + %% First disc node up + ok; + {[AnotherNode | _], _, _} -> + %% Subsequent node in cluster, catch up + ensure_version_ok( + rpc:call(AnotherNode, rabbit_version, recorded, [])), + ok = wait_for_replicated_tables(), + %% The sequence in which we delete the schema and then the other + %% tables is important: if we delete the schema first when moving to + %% RAM mnesia will loudly complain since it doesn't make much sense + %% to do that. But when moving to disc, we need to move the schema + %% first. + case WantDiscNode of + true -> create_local_table_copy(schema, disc_copies), + create_local_table_copies(disc); + false -> create_local_table_copies(ram), + create_local_table_copy(schema, ram_copies) + end + end, + ensure_schema_integrity(), + rabbit_node_monitor:update_cluster_status_file(), + ok. init_db_and_upgrade(ClusterNodes, WantDiscNode, Force) -> ok = init_db(ClusterNodes, WantDiscNode, Force), @@ -565,7 +561,7 @@ init_db_and_upgrade(ClusterNodes, WantDiscNode, Force) -> %% cluster case WantDiscNode of false -> start_mnesia(), - {ok, _} = change_extra_db_nodes(ClusterNodes, true), + change_extra_db_nodes(ClusterNodes, true), wait_for_replicated_tables(); true -> ok end, @@ -1105,10 +1101,10 @@ change_extra_db_nodes(ClusterNodes0, Force) -> ClusterNodes = lists:usort(ClusterNodes0) -- [node()], case mnesia:change_config(extra_db_nodes, ClusterNodes) of {ok, []} when not Force andalso ClusterNodes =/= [] -> - {error, {failed_to_cluster_with, ClusterNodes, - "Mnesia could not connect to any disc nodes."}}; + throw({error, {failed_to_cluster_with, ClusterNodes, + "Mnesia could not connect to any disc nodes."}}); {ok, Nodes} -> - {ok, Nodes} + Nodes end. %% What we really want is nodes running rabbit, not running mnesia. Using -- cgit v1.2.1 From 66e81b843a3e15302cb1258354c28641cd8acc10 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Mon, 9 Jul 2012 11:51:13 +0100 Subject: split the offline case of `rabbit_mnesia/2' in a separate function --- src/rabbit_mnesia.erl | 100 +++++++++++++++++++++++++------------------------- 1 file changed, 51 insertions(+), 49 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index a800ac05..6795d866 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -318,60 +318,62 @@ remove_node(Node, RemoveWhenOffline) -> ok -> ok; {error, mnesia_not_running} -> - case {ordsets:del_element(Node, - running_nodes(all_clustered_nodes())), - is_disc_node(), RemoveWhenOffline} - of - {[], true, true} -> - %% Note that while we check if the nodes was the last to go - %% down, apart from the node we're removing from, this is - %% still unsafe. - %% Consider the situation in which A and B are clustered. A - %% goes down, and records B as the running node. Then B gets - %% clustered with C, C goes down and B goes down. In this - %% case, C is the second-to-last, but we don't know that and - %% we'll remove B from A anyway, even if that will lead to - %% bad things. - case ordsets:subtract(running_clustered_nodes(), - ordsets:from_list([node(), Node])) - of - [] -> - start_mnesia(), - try - [mnesia:force_load_table(T) || - T <- rabbit_mnesia:table_names()], - remove_node(Node, false), - ensure_mnesia_running() - after - stop_mnesia() - end; - _ -> - throw({error, - {not_last_node_to_go_down, - "The node you're trying to remove was not " - "the last to go down (excluding the node " - "you are removing). Please use the the " - "last node to go down to remove nodes when " - "the cluster is offline."}}) - end; - {_, _, false} -> - throw({error, - {offline_node_no_offline_flag, - "You are trying to remove a node from an offline " - "node. That's dangerous, but can be done with the " - "--offline flag. Please consult the manual for " - "rabbitmqctl for more informations."}}); - {_, _, _} -> - throw({error, - {removing_node_from_offline_node, - "To remove a node remotely from an offline node, " - "the node you're removing from must be a disc node " - "and all the other nodes must be offline."}}) + case RemoveWhenOffline of + true -> remove_node_offline_node(Node); + false -> throw({error, + {offline_node_no_offline_flag, + "You are trying to remove a node from an " + "offline node. That's dangerous, but can be " + "done with the --offline flag. Please consult " + "the manual for rabbitmqctl for more " + "informations."}}) end; Err = {error, _} -> throw(Err) end. +remove_node_offline_node(Node) -> + case {ordsets:del_element(Node, + running_nodes(all_clustered_nodes())), + is_disc_node()} + of + {[], true} -> + %% Note that while we check if the nodes was the last to go down, + %% apart from the node we're removing from, this is still unsafe. + %% Consider the situation in which A and B are clustered. A goes + %% down, and records B as the running node. Then B gets clustered + %% with C, C goes down and B goes down. In this case, C is the + %% second-to-last, but we don't know that and we'll remove B from A + %% anyway, even if that will lead to bad things. + case ordsets:subtract(running_clustered_nodes(), + ordsets:from_list([node(), Node])) + of + [] -> start_mnesia(), + try + [mnesia:force_load_table(T) || + T <- rabbit_mnesia:table_names()], + remove_node(Node, false), + ensure_mnesia_running() + after + stop_mnesia() + end; + _ -> throw({error, + {not_last_node_to_go_down, + "The node you're trying to remove from was not " + "the last to go down (excluding the node you are " + "removing). Please use the the last node to go " + "down to remove nodes when the cluster is " + "offline."}}) + end; + {_, _} -> + throw({error, + {removing_node_from_offline_node, + "To remove a node remotely from an offline node, the node " + "you're removing from must be a disc node and all the " + "other nodes must be offline."}}) + end. + + %%---------------------------------------------------------------------------- %% Queries %%---------------------------------------------------------------------------- -- cgit v1.2.1 From 8100cd3899c1b9105f1b8c7c055f32de5daef910 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Mon, 9 Jul 2012 11:52:21 +0100 Subject: remove useless `all_clustered_nodes/0' in `reset/1' --- src/rabbit_mnesia.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 6795d866..f263c576 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -202,7 +202,7 @@ reset(Force) -> Node = node(), case Force of true -> - all_clustered_nodes(); + ok; false -> AllNodes = all_clustered_nodes(), %% Reconnecting so that we will get an up to date nodes. @@ -221,7 +221,7 @@ reset(Force) -> leave_cluster(), rabbit_misc:ensure_ok(mnesia:delete_schema([Node]), cannot_delete_schema), - all_clustered_nodes() + ok end, %% We need to make sure that we don't end up in a distributed Erlang system %% with nodes while not being in an Mnesia cluster with them. We don't -- cgit v1.2.1 From a79eaecce2c3f28e465c957f8d7c8bc53295aa9d Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Mon, 9 Jul 2012 12:00:03 +0100 Subject: get rid of `is_disc_node' --- src/rabbit_mnesia.erl | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index f263c576..d82fae92 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -147,7 +147,7 @@ init() -> %% same cluster, we simply pick the first online node and we cluster to its %% cluster. join_cluster(DiscoveryNode, WantDiscNode) -> - case is_disc_and_clustered() andalso is_only_disc_node(node()) of + case is_disc_and_clustered() andalso [node()] =:= clustered_disc_nodes() of true -> throw({error, {standalone_ram_node, "You can't cluster a node if it's the only " @@ -210,7 +210,9 @@ reset(Force) -> %% Force=true here so that reset still works when clustered with a %% node which is down. init_db_with_mnesia(AllNodes, is_disc_node(), false, true), - case is_disc_and_clustered() andalso is_only_disc_node(node()) of + case is_disc_and_clustered() andalso + [node()] =:= clustered_disc_nodes() + of true -> throw({error, {standalone_ram_node, "You can't reset a node if it's the " "only disc node in a cluster. Please " @@ -257,7 +259,7 @@ change_node_type(Type) -> ram -> false; disc -> true end, - case not WantDiscNode andalso is_only_disc_node(node(), DiscNodes) of + case not WantDiscNode andalso [node()] =:= DiscNodes of true -> throw({error, {standalone_ram_node, "You can't change the node type to ram if the node is " @@ -1078,12 +1080,6 @@ wait_for(Condition) -> error_logger:info_msg("Waiting for ~p...~n", [Condition]), timer:sleep(1000). -is_only_disc_node(Node, DiscNodes) -> - [Node] =:= DiscNodes. - -is_only_disc_node(Node) -> - is_only_disc_node(Node, clustered_disc_nodes()). - start_mnesia(CheckConsistency) -> case CheckConsistency of true -> check_cluster_consistency(); -- cgit v1.2.1 From 3432b6db0947dbdaab853f980e12aa470154368e Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 9 Jul 2012 12:01:16 +0100 Subject: Be more consistent about always cleaning sync_slave_pids whenever slave_pids is updated, abstract a bit, introduce an async wake_up message. --- src/rabbit_amqqueue.erl | 5 ++++- src/rabbit_amqqueue_process.erl | 5 ++++- src/rabbit_mirror_queue_misc.erl | 27 ++++++++++++++++----------- src/rabbit_mirror_queue_slave.erl | 7 +++---- 4 files changed, 27 insertions(+), 17 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 32a33812..05aad4bd 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -22,7 +22,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]). +-export([force_event_refresh/0, wake_up/1]). -export([consumers/1, consumers_all/1, consumer_info_keys/0]). -export([basic_get/3, basic_consume/7, basic_cancel/4]). -export([notify_sent/2, notify_sent_queue_down/1, unblock/2, flush_all/2]). @@ -102,6 +102,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(consumers/1 :: (rabbit_types:amqqueue()) -> [{pid(), rabbit_types:ctag(), boolean()}]). @@ -476,6 +477,8 @@ force_event_refresh(QNames) -> force_event_refresh(Failed) end. +wake_up(#amqqueue{pid = QPid}) -> gen_server2:cast(QPid, wake_up). + consumers(#amqqueue{ pid = QPid }) -> delegate_call(QPid, consumers). diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 9d03805a..1bf25bb2 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1281,7 +1281,10 @@ handle_cast({set_maximum_since_use, Age}, State) -> noreply(State); handle_cast({dead_letter, {Msg, AckTag}, Reason}, State) -> - dead_letter_msg(Msg, AckTag, Reason, State). + dead_letter_msg(Msg, AckTag, Reason, State); + +handle_cast(wake_up, State) -> + noreply(State). %% We need to not ignore this as we need to remove outstanding %% confirms due to queue death. diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index fbffbee1..83ecd4bf 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -18,7 +18,7 @@ -export([remove_from_queue/2, on_node_up/0, drop_mirror/2, drop_mirror/3, add_mirror/2, add_mirror/3, - report_deaths/4]). + report_deaths/4, store_updated_slaves/1]). -include("rabbit.hrl"). @@ -37,6 +37,7 @@ -spec(add_mirror/3 :: (rabbit_types:vhost(), binary(), atom()) -> rabbit_types:ok_or_error(any())). +-spec(store_updated_slaves/1 :: (rabbit_types:amqqueue()) -> 'ok'). -endif. @@ -58,9 +59,8 @@ remove_from_queue(QueueName, DeadPids) -> %% get here. case mnesia:read({rabbit_queue, QueueName}) of [] -> {error, not_found}; - [Q = #amqqueue { pid = QPid, - slave_pids = SPids, - sync_slave_pids = SSPids}] -> + [Q = #amqqueue { pid = QPid, + slave_pids = SPids}] -> [QPid1 | SPids1] = Alive = [Pid || Pid <- [QPid | SPids], not lists:member(node(Pid), DeadNodes)], @@ -71,13 +71,9 @@ remove_from_queue(QueueName, DeadPids) -> %% Either master hasn't changed, so %% we're ok to update mnesia; or we have %% become the master. - SSPids1 = [SSPid || SSPid <- SSPids, - lists:member(SSPid, SPids1)], - Q1 = Q #amqqueue { pid = QPid1, - slave_pids = SPids1, - sync_slave_pids = SSPids1}, - ok = rabbit_amqqueue:store_queue(Q1), - rabbit_amqqueue:info(Q1, [name]), %% Wake it up + store_updated_slaves( + Q #amqqueue { pid = QPid1, + slave_pids = SPids1 }), {ok, QPid1, [QPid | SPids] -- Alive}; _ -> %% Master has changed, and we're not it, @@ -177,3 +173,12 @@ report_deaths(MirrorPid, IsMaster, QueueName, DeadPids) -> end, rabbit_misc:pid_to_string(MirrorPid), [[rabbit_misc:pid_to_string(P), $ ] || P <- DeadPids]]). + +store_updated_slaves(Q = #amqqueue{slave_pids = SPids, + sync_slave_pids = SSPids}) -> + SSPids1 = [SSPid || SSPid <- SSPids, lists:member(SSPid, 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), + Q1. diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index a858ee4e..7939b48c 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -107,8 +107,8 @@ init(#amqqueue { name = QueueName } = Q) -> mnesia:read({rabbit_queue, QueueName}), case [Pid || Pid <- [QPid | MPids], node(Pid) =:= Node] of [] -> MPids1 = MPids ++ [Self], - ok = rabbit_amqqueue:store_queue( - Q1 #amqqueue { slave_pids = MPids1 }), + rabbit_mirror_queue_misc:store_updated_slaves( + Q1 #amqqueue { slave_pids = MPids1 }), {new, QPid}; [SPid] -> true = rabbit_misc:is_process_alive(SPid), existing @@ -914,10 +914,9 @@ set_synchronised(true, State = #state { q = Q = #amqqueue { name = QName }, ok; [Q1 = #amqqueue{sync_slave_pids = SSPids}] -> Q2 = Q1#amqqueue{sync_slave_pids = [Self | SSPids]}, - ok = rabbit_amqqueue:store_queue(Q2) + rabbit_mirror_queue_misc:store_updated_slaves(Q2) end end), - rabbit_amqqueue:info(Q, [name]), %% Wake it up State #state { synchronised = true }; set_synchronised(true, State) -> State; -- cgit v1.2.1 From 0759645b7b874ba173533a74ed444ce5f1a2e18e Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Mon, 9 Jul 2012 13:16:44 +0100 Subject: `rabbit_misc:rabbit_version/0' => `rabbit_misc:version/0' --- src/rabbit_misc.erl | 6 +++--- src/rabbit_mnesia.erl | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 9a3325ca..7e9eaeb4 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -60,7 +60,7 @@ -export([multi_call/2]). -export([os_cmd/1]). -export([gb_sets_difference/2]). --export([rabbit_version/0]). +-export([version/0]). -export([sequence_error/1]). %%---------------------------------------------------------------------------- @@ -214,7 +214,7 @@ ([pid()], any()) -> {[{pid(), any()}], [{pid(), any()}]}). -spec(os_cmd/1 :: (string()) -> string()). -spec(gb_sets_difference/2 :: (gb_set(), gb_set()) -> gb_set()). --spec(rabbit_version/0 :: () -> string()). +-spec(version/0 :: () -> string()). -spec(sequence_error/1 :: ([({'error', any()} | any())]) -> {'error', any()} | any()). @@ -945,7 +945,7 @@ os_cmd(Command) -> gb_sets_difference(S1, S2) -> gb_sets:fold(fun gb_sets:delete_any/2, S1, S2). -rabbit_version() -> +version() -> {ok, VSN} = application:get_key(rabbit, vsn), VSN. diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index d82fae92..9aec2092 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -498,7 +498,7 @@ cluster_status_from_mnesia() -> cluster_status(status, true). node_info() -> - {erlang:system_info(otp_release), rabbit_misc:rabbit_version(), + {erlang:system_info(otp_release), rabbit_misc:version(), cluster_status_from_mnesia()}. is_disc_node() -> @@ -694,7 +694,7 @@ check_cluster_consistency() -> [check_version_consistency( erlang:system_info(otp_release), OTP, "OTP"), check_version_consistency( - rabbit_misc:rabbit_version(), Rabbit, "Rabbit"), + rabbit_misc:version(), Rabbit, "Rabbit"), case Res of {ok, Status} -> check_nodes_consistency(Node, Status); -- cgit v1.2.1 From debf0ab83b7e5b4d521a575af885e05fd7ddd1a0 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 9 Jul 2012 18:00:08 +0100 Subject: Fix upgrade function. --- src/rabbit_upgrade_functions.erl | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/rabbit_upgrade_functions.erl b/src/rabbit_upgrade_functions.erl index 8a44e03a..d9438d5f 100644 --- a/src/rabbit_upgrade_functions.erl +++ b/src/rabbit_upgrade_functions.erl @@ -242,16 +242,16 @@ queue_policy(Table) -> slave_pids, mirror_nodes, policy]). sync_slave_pids() -> - [ok = sync_slave_pids(T) || T <- [rabbit_queue, rabbit_durable_queue]]. - -sync_slave_pids(Table) -> - transform( - Table, - fun ({amqqueue, N, D, AD, Excl, Args, Pid, SPids, MNodes, Pol}) -> - {amqqueue, N, D, AD, Excl, Args, Pid, SPids, [], MNodes, Pol} - end, - [name, durable, auto_delete, exclusive_owner, arguments, pid, - slave_pids, sync_slave_pids, mirror_nodes, policy]). + Tables = [rabbit_queue, rabbit_durable_queue], + AddSyncSlavesFun = + fun ({amqqueue, N, D, AD, Excl, Args, Pid, SPids, MNodes, Pol}) -> + {amqqueue, N, D, AD, Excl, Args, Pid, SPids, [], MNodes, Pol} + end, + [ok = transform(T, AddSyncSlavesFun, + [name, durable, auto_delete, exclusive_owner, arguments, + pid, slave_pids, sync_slave_pids, mirror_nodes, policy]) + || T <- Tables], + ok. %%-------------------------------------------------------------------- -- cgit v1.2.1 From ab1386ea69e768e0d1829cf800237d7082aac450 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 9 Jul 2012 18:30:52 +0100 Subject: Various fixes. --- src/rabbit_amqqueue_process.erl | 24 ++++++++---------------- src/rabbit_mirror_queue_master.erl | 2 +- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 5a3361ab..9404c53f 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -923,19 +923,19 @@ i(consumers, _) -> i(memory, _) -> {memory, M} = process_info(self(), memory), M; -i(slave_pids, #q{q = Q = #amqqueue{name = Name}}) -> +i(slave_pids, #q{q = #amqqueue{name = Name}}) -> + {ok, Q = #amqqueue{slave_pids = SPids}} = + rabbit_amqqueue:lookup(Name), case rabbit_mirror_queue_misc:is_mirrored(Q) of false -> ''; - true -> {ok, #amqqueue{slave_pids = SPids}} = - rabbit_amqqueue:lookup(Name), - SPids + true -> SPids end; -i(synchronised_slave_pids, #q{q = Q = #amqqueue{name = Name}}) -> +i(synchronised_slave_pids, #q{q = #amqqueue{name = Name}}) -> + {ok, Q = #amqqueue{sync_slave_pids = SSPids}} = + rabbit_amqqueue:lookup(Name), case rabbit_mirror_queue_misc:is_mirrored(Q) of false -> ''; - true -> {ok, #amqqueue{sync_slave_pids = SSPids}} = - rabbit_amqqueue:lookup(Name), - SSPids + true -> SSPids end; i(backing_queue_status, #q{backing_queue_state = BQS, backing_queue = BQ}) -> BQ:status(BQS); @@ -1191,14 +1191,6 @@ handle_call(stop_mirroring, _From, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> BQ = rabbit_mirror_queue_master, %% assertion {BQ1, BQS1} = BQ:stop_mirroring(BQS), - rabbit_misc:execute_mnesia_transaction( - fun () -> - case mnesia:read({rabbit_queue, qname(State)}) of - [] -> ok; - [Q] -> rabbit_amqqueue:store_queue( - Q#amqqueue{slave_pids = undefined}) - end - end), reply(ok, State#q{backing_queue = BQ1, backing_queue_state = BQS1}); diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index e5ca085d..2a04d872 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -111,7 +111,7 @@ stop_mirroring(State = #state { coordinator = CPid, backing_queue = BQ, backing_queue_state = BQS }) -> unlink(CPid), - stop_all_slaves(unmirroring, State), + stop_all_slaves(shutdown, State), {BQ, BQS}. terminate({shutdown, dropped} = Reason, -- cgit v1.2.1 From c24137652800731c77e7b2e0d629f389c9b7a91a Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Mon, 9 Jul 2012 19:39:08 +0100 Subject: add the magic `cluster' word to `remove_node' and `change_node_type' --- src/rabbit_control_main.erl | 16 ++++++++-------- src/rabbit_mnesia.erl | 14 +++++++------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index 4011709a..f3000f17 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -46,9 +46,9 @@ rotate_logs, {join_cluster, [?RAM_DEF]}, - change_node_type, + change_cluster_node_type, recluster, - {remove_node, [?OFFLINE_DEF]}, + {remove_cluster_node, [?OFFLINE_DEF]}, cluster_status, add_user, @@ -251,23 +251,23 @@ action(join_cluster, Node, [ClusterNodeS], Opts, Inform) -> Inform("Clustering node ~p with ~p", [Node, ClusterNode]), rpc_call(Node, rabbit_mnesia, join_cluster, [ClusterNode, DiscNode]); -action(change_node_type, Node, ["ram"], _Opts, Inform) -> +action(change_cluster_node_type, Node, ["ram"], _Opts, Inform) -> Inform("Turning ~p into a ram node", [Node]), - rpc_call(Node, rabbit_mnesia, change_node_type, [ram]); -action(change_node_type, Node, ["disc"], _Opts, Inform) -> + rpc_call(Node, rabbit_mnesia, change_cluster_node_type, [ram]); +action(change_cluster_node_type, Node, ["disc"], _Opts, Inform) -> Inform("Turning ~p into a disc node", [Node]), - rpc_call(Node, rabbit_mnesia, change_node_type, [disc]); + rpc_call(Node, rabbit_mnesia, change_cluster_node_type, [disc]); action(recluster, Node, [ClusterNodeS], _Opts, Inform) -> ClusterNode = list_to_atom(ClusterNodeS), Inform("Re-clustering ~p with ~p", [Node, ClusterNode]), rpc_call(Node, rabbit_mnesia, recluster, [ClusterNode]); -action(remove_node, Node, [ClusterNodeS], Opts, Inform) -> +action(remove_cluster_node, Node, [ClusterNodeS], Opts, Inform) -> ClusterNode = list_to_atom(ClusterNodeS), RemoveWhenOffline = proplists:get_bool(?OFFLINE_OPT, Opts), Inform("Removing node ~p from cluster", [ClusterNode]), - rpc_call(Node, rabbit_mnesia, remove_node, + rpc_call(Node, rabbit_mnesia, remove_cluster_node, [ClusterNode, RemoveWhenOffline]); action(wait, Node, [PidFile], _Opts, Inform) -> diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 9aec2092..3d5af048 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -23,8 +23,8 @@ reset/0, force_reset/0, recluster/1, - change_node_type/1, - remove_node/2, + change_cluster_node_type/1, + remove_cluster_node/2, status/0, is_db_empty/0, @@ -76,8 +76,8 @@ -spec(reset/0 :: () -> 'ok'). -spec(force_reset/0 :: () -> 'ok'). -spec(recluster/1 :: (node()) -> 'ok'). --spec(change_node_type/1 :: (node_type()) -> 'ok'). --spec(remove_node/2 :: (node(), boolean()) -> 'ok'). +-spec(change_cluster_node_type/1 :: (node_type()) -> 'ok'). +-spec(remove_cluster_node/2 :: (node(), boolean()) -> 'ok'). %% Various queries to get the status of the db -spec(status/0 :: () -> [{'nodes', [{node_type(), [node()]}]} | @@ -234,7 +234,7 @@ reset(Force) -> ok = rabbit_node_monitor:reset_cluster_status_file(), ok. -change_node_type(Type) -> +change_cluster_node_type(Type) -> ensure_mnesia_dir(), ensure_mnesia_not_running(), case is_clustered() of @@ -302,7 +302,7 @@ recluster(DiscoveryNode) -> %% * All other nodes are offline %% * This node was, at the best of our knowledge (see comment below) the last %% or second to last after the node we're removing to go down -remove_node(Node, RemoveWhenOffline) -> +remove_cluster_node(Node, RemoveWhenOffline) -> case ordsets:is_element(Node, all_clustered_nodes()) of true -> ok; false -> throw({error, {not_a_cluster_node, @@ -354,7 +354,7 @@ remove_node_offline_node(Node) -> try [mnesia:force_load_table(T) || T <- rabbit_mnesia:table_names()], - remove_node(Node, false), + remove_cluster_node(Node, false), ensure_mnesia_running() after stop_mnesia() -- cgit v1.2.1 From 430f27d70af24eb6e485dc2f656e90882a4b41a0 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Mon, 9 Jul 2012 19:50:35 +0100 Subject: do not update the cluster status file by getting the mnesia status Instead, just update the existing cluster status file. This is because the mnesia propagation time can be slower than the other messages (see other comments in the bug) --- src/rabbit_node_monitor.erl | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index f6cbee34..849f5d31 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -197,8 +197,7 @@ handle_cast({node_up, Node, IsDiscNode}, State) -> case is_already_monitored({rabbit, Node}) of true -> {noreply, State}; false -> rabbit_log:info("rabbit on node ~p up~n", [Node]), - {ok, {AllNodes, DiscNodes, RunningNodes}} = - rabbit_mnesia:cluster_status_from_mnesia(), + {AllNodes, DiscNodes, RunningNodes} = read_cluster_status_file(), write_cluster_status_file( {ordsets:add_element(Node, AllNodes), case IsDiscNode of @@ -211,8 +210,7 @@ handle_cast({node_up, Node, IsDiscNode}, State) -> {noreply, State} end; handle_cast({joined_cluster, Node, IsDiscNode}, State) -> - {ok, {AllNodes, DiscNodes, RunningNodes}} = - rabbit_mnesia:cluster_status_from_mnesia(), + {AllNodes, DiscNodes, RunningNodes} = read_cluster_status_file(), write_cluster_status_file({ordsets:add_element(Node, AllNodes), case IsDiscNode of true -> ordsets:add_element(Node, @@ -222,8 +220,7 @@ handle_cast({joined_cluster, Node, IsDiscNode}, State) -> RunningNodes}), {noreply, State}; handle_cast({left_cluster, Node}, State) -> - {ok, {AllNodes, DiscNodes, RunningNodes}} = - rabbit_mnesia:cluster_status_from_mnesia(), + {AllNodes, DiscNodes, RunningNodes} = read_cluster_status_file(), write_cluster_status_file({ordsets:del_element(Node, AllNodes), ordsets:del_element(Node, DiscNodes), ordsets:del_element(Node, RunningNodes)}), @@ -233,8 +230,7 @@ handle_cast(_Msg, State) -> handle_info({'DOWN', _MRef, process, {rabbit, Node}, _Reason}, State) -> rabbit_log:info("rabbit on node ~p down~n", [Node]), - {ok, {AllNodes, DiscNodes, RunningNodes}} = - rabbit_mnesia:cluster_status_from_mnesia(), + {AllNodes, DiscNodes, RunningNodes} = read_cluster_status_file(), write_cluster_status_file({AllNodes, DiscNodes, ordsets:del_element(Node, RunningNodes)}), ok = handle_dead_rabbit(Node), -- cgit v1.2.1 From 9502c2bdede1aaf2bc73426d1c8dfb3c570d725e Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Mon, 9 Jul 2012 19:59:36 +0100 Subject: cosmetic --- src/rabbit_mnesia.erl | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 3d5af048..b1a4759a 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -461,22 +461,23 @@ mnesia_nodes() -> cluster_status(WhichNodes, ForceMnesia) -> %% I don't want to call `running_nodes/1' unless if necessary, since it can %% deadlock when stopping applications. - case case mnesia_nodes() of - {ok, {AllNodes, DiscNodes}} -> - {ok, {AllNodes, DiscNodes, - fun() -> running_nodes(AllNodes) end}}; - {error, _Reason} when not ForceMnesia -> - {AllNodes, DiscNodes, RunningNodes} = - rabbit_node_monitor:read_cluster_status_file(), - %% The cluster status file records the status when the node is - %% online, but we know for sure that the node is offline now, so - %% we can remove it from the list of running nodes. - {ok, {AllNodes, DiscNodes, - fun() -> ordsets:del_element(node(), RunningNodes) end}}; - Err = {error, _} -> - Err - end - of + Nodes = case mnesia_nodes() of + {ok, {AllNodes, DiscNodes}} -> + {ok, {AllNodes, DiscNodes, + fun() -> running_nodes(AllNodes) end}}; + {error, _Reason} when not ForceMnesia -> + {AllNodes, DiscNodes, RunningNodes} = + rabbit_node_monitor:read_cluster_status_file(), + %% The cluster status file records the status when the node + %% is online, but we know for sure that the node is offline + %% now, so we can remove it from the list of running nodes. + {ok, + {AllNodes, DiscNodes, + fun() -> ordsets:del_element(node(), RunningNodes) end}}; + Err = {error, _} -> + Err + end, + case Nodes of {ok, {AllNodes1, DiscNodes1, RunningNodesThunk}} -> {ok, case WhichNodes of status -> {AllNodes1, DiscNodes1, RunningNodesThunk()}; -- cgit v1.2.1 From 34563b5e8ccdf9100ce7144feaf798bc8b77d9f8 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Mon, 9 Jul 2012 20:11:44 +0100 Subject: `cluster_status/{0,1}' now returns the status directly, fails on error --- src/rabbit_mnesia.erl | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index b1a4759a..6f966f4f 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -406,24 +406,19 @@ is_disc_and_clustered() -> %% if offline. all_clustered_nodes() -> - {ok, AllNodes} = cluster_status(all), - AllNodes. + cluster_status(all). clustered_disc_nodes() -> - {ok, DiscNodes} =cluster_status(disc), - DiscNodes. + cluster_status(disc). clustered_ram_nodes() -> - {ok, AllNodes} = cluster_status(all), - {ok, DiscNodes} = cluster_status(disc), - ordsets:subtract(AllNodes, DiscNodes). + ordsets:subtract(cluster_status(all), cluster_status(disc)). running_clustered_nodes() -> - {ok, RunningNodes} = cluster_status(running), - RunningNodes. + cluster_status(running). running_clustered_disc_nodes() -> - {ok, {_, DiscNodes, RunningNodes}} = cluster_status(), + {_, DiscNodes, RunningNodes} = cluster_status(), ordsets:intersection(DiscNodes, RunningNodes). %% This function is the actual source of information, since it gets the data @@ -490,7 +485,8 @@ cluster_status(WhichNodes, ForceMnesia) -> end. cluster_status(WhichNodes) -> - cluster_status(WhichNodes, false). + {ok, Status} = cluster_status(WhichNodes, false), + Status. cluster_status() -> cluster_status(status). -- cgit v1.2.1 From 5bc20b995a357d206352a472c612dc686cac3ba2 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Mon, 9 Jul 2012 20:28:43 +0100 Subject: clearer `rabbit_mnesia:discover_cluster/1' --- src/rabbit_mnesia.erl | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 6f966f4f..80acfc5d 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -748,18 +748,20 @@ discover_cluster(Nodes) when is_list(Nodes) -> lists:foldl(fun (_, {ok, Res}) -> {ok, Res}; (Node, {error, _}) -> discover_cluster(Node) end, - {error, {cannot_discover_cluster, - "The nodes provided is either offline or not running"}}, + {error, no_nodes_provided}, Nodes); discover_cluster(Node) -> + OfflineError = + {error, {cannot_discover_cluster, + "The nodes provided is either offline or not running"}}, case Node =:= node() of true -> {error, {cannot_discover_cluster, "You provided the current node as node to cluster with"}}; false -> case rpc:call(Node, rabbit_mnesia, cluster_status_from_mnesia, []) of - {badrpc, _Reason} -> discover_cluster([]); - {error, mnesia_not_running} -> discover_cluster([]); + {badrpc, _Reason} -> OfflineError; + {error, mnesia_not_running} -> OfflineError; {ok, Res} -> {ok, Res} end end. -- cgit v1.2.1 From 107f9a484e809b86c798265127f10da540324fdd Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Tue, 10 Jul 2012 11:49:55 +0100 Subject: Remove unnecessary HA heartbeats --- src/rabbit_mirror_queue_coordinator.erl | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/rabbit_mirror_queue_coordinator.erl b/src/rabbit_mirror_queue_coordinator.erl index 3e058793..e55a3938 100644 --- a/src/rabbit_mirror_queue_coordinator.erl +++ b/src/rabbit_mirror_queue_coordinator.erl @@ -36,8 +36,6 @@ length_fun }). --define(ONE_SECOND, 1000). - -ifdef(use_specs). -spec(start_link/4 :: (rabbit_types:amqqueue(), pid() | 'undefined', @@ -325,7 +323,6 @@ init([#amqqueue { name = QueueName } = Q, GM, DeathFun, LengthFun]) -> true = link(GM), GM end, - ensure_gm_heartbeat(), {ok, #state { q = Q, gm = GM1, monitors = pmon:new(), @@ -359,11 +356,6 @@ handle_cast({ensure_monitoring, Pids}, State = #state { monitors = Mons }) -> handle_cast({delete_and_terminate, Reason}, State) -> {stop, Reason, State}. -handle_info(send_gm_heartbeat, State = #state { gm = GM }) -> - gm:broadcast(GM, heartbeat), - ensure_gm_heartbeat(), - noreply(State); - handle_info({'DOWN', _MonitorRef, process, Pid, _Reason}, State = #state { monitors = Mons, death_fun = DeathFun }) -> @@ -420,6 +412,3 @@ noreply(State) -> reply(Reply, State) -> {reply, Reply, State, hibernate}. - -ensure_gm_heartbeat() -> - erlang:send_after(?ONE_SECOND, self(), send_gm_heartbeat). -- cgit v1.2.1 From 6bc72ed7e4c4017ef21215da6a8cdb908e8a52ca Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Tue, 10 Jul 2012 18:04:24 +0100 Subject: add replacement for the stock alarm_handler - not doing anything yet --- src/rabbit.erl | 8 ++++++- src/rabbit_alarm_handler.erl | 53 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 src/rabbit_alarm_handler.erl diff --git a/src/rabbit.erl b/src/rabbit.erl index fda489fe..61e6f274 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -93,10 +93,16 @@ [{description, "kernel ready"}, {requires, external_infrastructure}]}). +-rabbit_boot_step({rabbit_alarm_handler, + [{description, "stock alarm handler replacement"}, + {mfa, {rabbit_alarm_handler, start, []}}, + {requires, kernel_ready}, + {enables, core_initialized}]}). + -rabbit_boot_step({rabbit_alarm, [{description, "alarm handler"}, {mfa, {rabbit_alarm, start, []}}, - {requires, kernel_ready}, + {requires, rabbit_alarm_handler}, {enables, core_initialized}]}). -rabbit_boot_step({rabbit_memory_monitor, diff --git a/src/rabbit_alarm_handler.erl b/src/rabbit_alarm_handler.erl new file mode 100644 index 00000000..dd5fe641 --- /dev/null +++ b/src/rabbit_alarm_handler.erl @@ -0,0 +1,53 @@ +%% 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 VMware, Inc. +%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% + +-module(rabbit_alarm_handler). + +-behaviour(gen_event). + +-export([start/0]). + +-export([init/1, handle_call/2, handle_event/2, handle_info/2, + terminate/2, code_change/3]). + +start() -> + alarm_handler:delete_alarm_handler(alarm_handler), + alarm_handler:add_alarm_handler(?MODULE). + +init(_) -> + {ok, []}. + +handle_event({set_alarm, Alarm}, Alarms)-> + error_logger:info_report([{alarm_handler, {set, Alarm}}]), + {ok, [Alarm | Alarms]}; +handle_event({clear_alarm, AlarmId}, Alarms)-> + error_logger:info_report([{alarm_handler, {clear, AlarmId}}]), + {ok, lists:keydelete(AlarmId, 1, Alarms)}; +handle_event(_, Alarms)-> + {ok, Alarms}. + +handle_info(_, Alarms) -> {ok, Alarms}. + +handle_call(get_alarms, Alarms) -> {ok, Alarms, Alarms}; +handle_call(_Query, Alarms) -> {ok, {error, bad_query}, Alarms}. + +terminate(swap, Alarms) -> + {alarm_handler, Alarms}; +terminate(_, _) -> + ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. -- cgit v1.2.1 From 7b68ff61c81e3fa488bb8c3a300d3238ef4e5de3 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Wed, 11 Jul 2012 11:11:26 +0100 Subject: Rename HA heartbeat when master changes --- src/rabbit_mirror_queue_coordinator.erl | 2 +- src/rabbit_mirror_queue_slave.erl | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rabbit_mirror_queue_coordinator.erl b/src/rabbit_mirror_queue_coordinator.erl index e55a3938..10debb0b 100644 --- a/src/rabbit_mirror_queue_coordinator.erl +++ b/src/rabbit_mirror_queue_coordinator.erl @@ -391,7 +391,7 @@ members_changed([_CPid], _Births, []) -> members_changed([CPid], _Births, Deaths) -> ok = gen_server2:cast(CPid, {gm_deaths, Deaths}). -handle_msg([_CPid], _From, heartbeat) -> +handle_msg([_CPid], _From, master_changed) -> ok; handle_msg([CPid], _From, request_length = Msg) -> ok = gen_server2:cast(CPid, Msg); diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 03fafc3e..c96e98b6 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -199,7 +199,7 @@ handle_call({gm_deaths, Deaths}, From, %% master has changed to not us. gen_server2:reply(From, ok), erlang:monitor(process, Pid), - ok = gm:broadcast(GM, heartbeat), + ok = gm:broadcast(GM, master_changed), noreply(State #state { master_pid = Pid }) end end; @@ -341,7 +341,7 @@ members_changed([_SPid], _Births, []) -> members_changed([SPid], _Births, Deaths) -> inform_deaths(SPid, Deaths). -handle_msg([_SPid], _From, heartbeat) -> +handle_msg([_SPid], _From, master_changed) -> ok; handle_msg([_SPid], _From, request_length) -> %% This is only of value to the master @@ -452,7 +452,7 @@ promote_me(From, #state { q = Q = #amqqueue { name = QName }, rabbit_mirror_queue_master:length_fun()), true = unlink(GM), gen_server2:reply(From, {promote, CPid}), - ok = gm:confirmed_broadcast(GM, heartbeat), + ok = gm:confirmed_broadcast(GM, master_changed), %% Everything that we're monitoring, we need to ensure our new %% coordinator is monitoring. -- cgit v1.2.1 From ee9f396a11946ea2d47f4e6326eefca4c86d774c Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Wed, 11 Jul 2012 11:29:01 +0100 Subject: we don't need a dedicated handler, rabbit_alarm will do --- src/rabbit.erl | 8 +------ src/rabbit_alarm_handler.erl | 53 -------------------------------------------- 2 files changed, 1 insertion(+), 60 deletions(-) delete mode 100644 src/rabbit_alarm_handler.erl diff --git a/src/rabbit.erl b/src/rabbit.erl index 61e6f274..fda489fe 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -93,16 +93,10 @@ [{description, "kernel ready"}, {requires, external_infrastructure}]}). --rabbit_boot_step({rabbit_alarm_handler, - [{description, "stock alarm handler replacement"}, - {mfa, {rabbit_alarm_handler, start, []}}, - {requires, kernel_ready}, - {enables, core_initialized}]}). - -rabbit_boot_step({rabbit_alarm, [{description, "alarm handler"}, {mfa, {rabbit_alarm, start, []}}, - {requires, rabbit_alarm_handler}, + {requires, kernel_ready}, {enables, core_initialized}]}). -rabbit_boot_step({rabbit_memory_monitor, diff --git a/src/rabbit_alarm_handler.erl b/src/rabbit_alarm_handler.erl deleted file mode 100644 index dd5fe641..00000000 --- a/src/rabbit_alarm_handler.erl +++ /dev/null @@ -1,53 +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 VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. -%% - --module(rabbit_alarm_handler). - --behaviour(gen_event). - --export([start/0]). - --export([init/1, handle_call/2, handle_event/2, handle_info/2, - terminate/2, code_change/3]). - -start() -> - alarm_handler:delete_alarm_handler(alarm_handler), - alarm_handler:add_alarm_handler(?MODULE). - -init(_) -> - {ok, []}. - -handle_event({set_alarm, Alarm}, Alarms)-> - error_logger:info_report([{alarm_handler, {set, Alarm}}]), - {ok, [Alarm | Alarms]}; -handle_event({clear_alarm, AlarmId}, Alarms)-> - error_logger:info_report([{alarm_handler, {clear, AlarmId}}]), - {ok, lists:keydelete(AlarmId, 1, Alarms)}; -handle_event(_, Alarms)-> - {ok, Alarms}. - -handle_info(_, Alarms) -> {ok, Alarms}. - -handle_call(get_alarms, Alarms) -> {ok, Alarms, Alarms}; -handle_call(_Query, Alarms) -> {ok, {error, bad_query}, Alarms}. - -terminate(swap, Alarms) -> - {alarm_handler, Alarms}; -terminate(_, _) -> - ok. - -code_change(_OldVsn, State, _Extra) -> - {ok, State}. -- cgit v1.2.1 From 5637ca640ae519af24b6f92c8a3a08569320e186 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Wed, 11 Jul 2012 13:16:22 +0100 Subject: let `rabbit_alarm' handle everything in place of `alarm_handler' No logging on set/clear alarm yet, me and Simon agree that we should do logging only on the events that rabbit knows it should be receiving (right now the resource limits ones) --- Makefile | 4 ++-- src/file_handle_cache.erl | 4 ++-- src/rabbit_alarm.erl | 39 ++++++++++++++++++++++++++------------- src/rabbit_disk_monitor.erl | 4 ++-- src/vm_memory_monitor.erl | 4 ++-- 5 files changed, 34 insertions(+), 21 deletions(-) diff --git a/Makefile b/Makefile index 0e3960dc..3681a787 100644 --- a/Makefile +++ b/Makefile @@ -217,11 +217,11 @@ stop-rabbit-on-node: all echo "rabbit:stop()." | $(ERL_CALL) set-resource-alarm: all - echo "alarm_handler:set_alarm({{resource_limit, $(SOURCE), node()}, []})." | \ + echo "rabbit_alarm:set_alarm({{resource_limit, $(SOURCE), node()}, []})." | \ $(ERL_CALL) clear-resource-alarm: all - echo "alarm_handler:clear_alarm({resource_limit, $(SOURCE), node()})." | \ + echo "rabbit_alarm:clear_alarm({resource_limit, $(SOURCE), node()})." | \ $(ERL_CALL) stop-node: diff --git a/src/file_handle_cache.erl b/src/file_handle_cache.erl index f3b4dbaf..2546d5c5 100644 --- a/src/file_handle_cache.erl +++ b/src/file_handle_cache.erl @@ -1028,8 +1028,8 @@ obtain_limit_reached(#fhc_state { obtain_limit = Limit, adjust_alarm(OldState, NewState) -> case {obtain_limit_reached(OldState), obtain_limit_reached(NewState)} of - {false, true} -> alarm_handler:set_alarm({file_descriptor_limit, []}); - {true, false} -> alarm_handler:clear_alarm(file_descriptor_limit); + {false, true} -> rabbit_alarm:set_alarm({file_descriptor_limit, []}); + {true, false} -> rabbit_alarm:clear_alarm(file_descriptor_limit); _ -> ok end, NewState. diff --git a/src/rabbit_alarm.erl b/src/rabbit_alarm.erl index d16d90a4..1fb2056e 100644 --- a/src/rabbit_alarm.erl +++ b/src/rabbit_alarm.erl @@ -18,19 +18,23 @@ -behaviour(gen_event). --export([start/0, stop/0, register/2, on_node_up/1, on_node_down/1]). +-export([start_link/0, start/0, stop/0, register/2, set_alarm/1, + clear_alarm/1, on_node_up/1, on_node_down/1]). -export([init/1, handle_call/2, handle_event/2, handle_info/2, terminate/2, code_change/3]). -export([remote_conserve_resources/3]). %% Internal use only +-define(SERVER, ?MODULE). + -record(alarms, {alertees, alarmed_nodes}). %%---------------------------------------------------------------------------- -ifdef(use_specs). +-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()). @@ -41,36 +45,43 @@ %%---------------------------------------------------------------------------- +start_link() -> + gen_event:start_link({local, ?SERVER}). + start() -> - ok = alarm_handler:add_alarm_handler(?MODULE, []), + ok = rabbit_sup:start_restartable_child(?MODULE), + ok = gen_event:add_handler(?SERVER, ?MODULE, []), {ok, MemoryWatermark} = application:get_env(vm_memory_high_watermark), rabbit_sup:start_restartable_child(vm_memory_monitor, [MemoryWatermark]), - {ok, DiskLimit} = application:get_env(disk_free_limit), rabbit_sup:start_restartable_child(rabbit_disk_monitor, [DiskLimit]), ok. stop() -> - ok = alarm_handler:delete_alarm_handler(?MODULE). + ok. register(Pid, HighMemMFA) -> - gen_event:call(alarm_handler, ?MODULE, - {register, Pid, HighMemMFA}, + gen_event:call(?SERVER, ?MODULE, {register, Pid, HighMemMFA}, infinity). -on_node_up(Node) -> gen_event:notify(alarm_handler, {node_up, Node}). +set_alarm(Alarm) -> + gen_event:notify(?SERVER, {set_alarm, Alarm}). -on_node_down(Node) -> gen_event:notify(alarm_handler, {node_down, Node}). +clear_alarm(Alarm) -> + gen_event:notify(?SERVER, {clear_alarm, Alarm}). + +on_node_up(Node) -> gen_event:notify(?SERVER, {node_up, Node}). + +on_node_down(Node) -> gen_event:notify(?SERVER, {node_down, Node}). -%% Can't use alarm_handler:{set,clear}_alarm because that doesn't -%% permit notifying a remote node. remote_conserve_resources(Pid, Source, true) -> - gen_event:notify({alarm_handler, node(Pid)}, + gen_event:notify({?SERVER, node(Pid)}, {set_alarm, {{resource_limit, Source, node()}, []}}); remote_conserve_resources(Pid, Source, false) -> - gen_event:notify({alarm_handler, node(Pid)}, + gen_event:notify({?SERVER, node(Pid)}, {clear_alarm, {resource_limit, Source, node()}}). + %%---------------------------------------------------------------------------- init([]) -> @@ -85,15 +96,17 @@ handle_call(_Request, State) -> {ok, not_understood, State}. handle_event({set_alarm, {{resource_limit, Source, Node}, []}}, State) -> + %% TODO: Do some logging {ok, maybe_alert(fun dict:append/3, Node, Source, State)}; handle_event({clear_alarm, {resource_limit, Source, Node}}, State) -> + %% TODO: Do some logging {ok, maybe_alert(fun dict_unappend/3, Node, Source, State)}; handle_event({node_up, Node}, State) -> %% Must do this via notify and not call to avoid possible deadlock. ok = gen_event:notify( - {alarm_handler, Node}, + {?SERVER, Node}, {register, self(), {?MODULE, remote_conserve_resources, []}}), {ok, State}; diff --git a/src/rabbit_disk_monitor.erl b/src/rabbit_disk_monitor.erl index 58375abb..e72181c0 100644 --- a/src/rabbit_disk_monitor.erl +++ b/src/rabbit_disk_monitor.erl @@ -149,10 +149,10 @@ internal_update(State = #state { limit = Limit, case {Alarmed, NewAlarmed} of {false, true} -> emit_update_info("exceeded", CurrentFreeBytes, LimitBytes), - alarm_handler:set_alarm({{resource_limit, disk, node()}, []}); + rabbit_alarm:set_alarm({{resource_limit, disk, node()}, []}); {true, false} -> emit_update_info("below limit", CurrentFreeBytes, LimitBytes), - alarm_handler:clear_alarm({resource_limit, disk, node()}); + rabbit_alarm:clear_alarm({resource_limit, disk, node()}); _ -> ok end, diff --git a/src/vm_memory_monitor.erl b/src/vm_memory_monitor.erl index fb184d1a..3f34c2e9 100644 --- a/src/vm_memory_monitor.erl +++ b/src/vm_memory_monitor.erl @@ -181,10 +181,10 @@ internal_update(State = #state { memory_limit = MemLimit, case {Alarmed, NewAlarmed} of {false, true} -> emit_update_info(set, MemUsed, MemLimit), - alarm_handler:set_alarm({{resource_limit, memory, node()}, []}); + rabbit_alarm:set_alarm({{resource_limit, memory, node()}, []}); {true, false} -> emit_update_info(clear, MemUsed, MemLimit), - alarm_handler:clear_alarm({resource_limit, memory, node()}); + rabbit_alarm:clear_alarm({resource_limit, memory, node()}); _ -> ok end, -- cgit v1.2.1 From c910f4d822ddbb8d7247606a49fb5a54e27306a4 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Wed, 11 Jul 2012 13:32:28 +0100 Subject: log the resource limit alarms --- src/rabbit_alarm.erl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/rabbit_alarm.erl b/src/rabbit_alarm.erl index 1fb2056e..9d166717 100644 --- a/src/rabbit_alarm.erl +++ b/src/rabbit_alarm.erl @@ -96,11 +96,13 @@ handle_call(_Request, State) -> {ok, not_understood, State}. handle_event({set_alarm, {{resource_limit, Source, Node}, []}}, State) -> - %% TODO: Do some logging + rabbit_log:warning("'~p' resource limit alarm set on node ~p", + [Source, Node]), {ok, maybe_alert(fun dict:append/3, Node, Source, State)}; handle_event({clear_alarm, {resource_limit, Source, Node}}, State) -> - %% TODO: Do some logging + rabbit_log:warning("'~p' resource limit alarm cleared on node ~p", + [Source, Node]), {ok, maybe_alert(fun dict_unappend/3, Node, Source, State)}; handle_event({node_up, Node}, State) -> -- cgit v1.2.1 From 09f40e46d9bb98730223e5adfb6342ffc9a7177c Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Wed, 11 Jul 2012 15:28:25 +0100 Subject: filter benign messages in `rabbit_amqqueue:safe_delegate_call_ok/2' Sadly I can't use `rabbit_misc:is_benign_exit/1' in the guards in `with_exit_handler/2'. --- src/rabbit_amqqueue.erl | 18 +++++++++++------- src/rabbit_misc.erl | 12 +++++++++++- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index afbaea65..c51acbdf 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -672,13 +672,17 @@ qpids(Qs) -> lists:append([[QPid | SPids] || #amqqueue{pid = QPid, slave_pids = SPids} <- Qs]). safe_delegate_call_ok(F, Pids) -> - case delegate:invoke(Pids, fun (Pid) -> - rabbit_misc:with_exit_handler( - fun () -> ok end, - fun () -> F(Pid) end) - end) of - {_, []} -> ok; - {_, Bad} -> {error, Bad} + {_, Bads} = delegate:invoke(Pids, fun (Pid) -> + rabbit_misc:with_exit_handler( + fun () -> ok end, + fun () -> F(Pid) end) + end), + case lists:filter( + fun ({_Pid, {exit, R, _}}) -> not rabbit_misc:is_benign_exit(R); + (_) -> false + end, Bads) of + [] -> ok; + Bads1 -> {error, Bads1} end. delegate_call(Pid, Msg) -> diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index d41aa09b..49b46716 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -29,7 +29,8 @@ -export([enable_cover/1, report_cover/1]). -export([start_cover/1]). -export([confirm_to_sender/2]). --export([throw_on_error/2, with_exit_handler/2, filter_exit_map/2]). +-export([throw_on_error/2, is_benign_exit/1, with_exit_handler/2, + filter_exit_map/2]). -export([is_abnormal_termination/1]). -export([with_user/2, with_user_and_vhost/3]). -export([execute_mnesia_transaction/1]). @@ -136,6 +137,7 @@ -spec(report_cover/1 :: ([file:filename() | atom()]) -> 'ok'). -spec(throw_on_error/2 :: (atom(), thunk(rabbit_types:error(any()) | {ok, A} | A)) -> A). +-spec(is_benign_exit/1 :: (any()) -> boolean()). -spec(with_exit_handler/2 :: (thunk(A), thunk(A)) -> A). -spec(filter_exit_map/2 :: (fun ((A) -> B), [A]) -> [B]). -spec(is_abnormal_termination/1 :: (any()) -> boolean()). @@ -419,6 +421,14 @@ throw_on_error(E, Thunk) -> Res -> Res end. +is_benign_exit({R, _}) when R =:= noproc; R =:= nodedown; R =:= normal; + R =:= shutdown -> + true; +is_benign_exit({{R, _}, _}) when R =:= nodedown; R =:= shutdown -> + true; +is_benign_exit(_) -> + false. + with_exit_handler(Handler, Thunk) -> try Thunk() -- cgit v1.2.1 From 77d284a2a17a7fbb9497ea30c3b1fc5ccb92979a Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Thu, 12 Jul 2012 12:31:33 +0100 Subject: do not take place of `alarm_handler', just pretty print what we're interested in --- Makefile | 4 ++-- src/file_handle_cache.erl | 4 ++-- src/rabbit_alarm.erl | 45 +++++++++++++++++++++------------------------ src/rabbit_disk_monitor.erl | 4 ++-- src/vm_memory_monitor.erl | 4 ++-- 5 files changed, 29 insertions(+), 32 deletions(-) diff --git a/Makefile b/Makefile index 3681a787..0e3960dc 100644 --- a/Makefile +++ b/Makefile @@ -217,11 +217,11 @@ stop-rabbit-on-node: all echo "rabbit:stop()." | $(ERL_CALL) set-resource-alarm: all - echo "rabbit_alarm:set_alarm({{resource_limit, $(SOURCE), node()}, []})." | \ + echo "alarm_handler:set_alarm({{resource_limit, $(SOURCE), node()}, []})." | \ $(ERL_CALL) clear-resource-alarm: all - echo "rabbit_alarm:clear_alarm({resource_limit, $(SOURCE), node()})." | \ + echo "alarm_handler:clear_alarm({resource_limit, $(SOURCE), node()})." | \ $(ERL_CALL) stop-node: diff --git a/src/file_handle_cache.erl b/src/file_handle_cache.erl index 2546d5c5..f3b4dbaf 100644 --- a/src/file_handle_cache.erl +++ b/src/file_handle_cache.erl @@ -1028,8 +1028,8 @@ obtain_limit_reached(#fhc_state { obtain_limit = Limit, adjust_alarm(OldState, NewState) -> case {obtain_limit_reached(OldState), obtain_limit_reached(NewState)} of - {false, true} -> rabbit_alarm:set_alarm({file_descriptor_limit, []}); - {true, false} -> rabbit_alarm:clear_alarm(file_descriptor_limit); + {false, true} -> alarm_handler:set_alarm({file_descriptor_limit, []}); + {true, false} -> alarm_handler:clear_alarm(file_descriptor_limit); _ -> ok end, NewState. diff --git a/src/rabbit_alarm.erl b/src/rabbit_alarm.erl index 9d166717..665e2cb9 100644 --- a/src/rabbit_alarm.erl +++ b/src/rabbit_alarm.erl @@ -18,23 +18,19 @@ -behaviour(gen_event). --export([start_link/0, start/0, stop/0, register/2, set_alarm/1, - clear_alarm/1, on_node_up/1, on_node_down/1]). +-export([start/0, stop/0, register/2, on_node_up/1, on_node_down/1]). -export([init/1, handle_call/2, handle_event/2, handle_info/2, terminate/2, code_change/3]). -export([remote_conserve_resources/3]). %% Internal use only --define(SERVER, ?MODULE). - -record(alarms, {alertees, alarmed_nodes}). %%---------------------------------------------------------------------------- -ifdef(use_specs). --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()). @@ -45,43 +41,36 @@ %%---------------------------------------------------------------------------- -start_link() -> - gen_event:start_link({local, ?SERVER}). - start() -> - ok = rabbit_sup:start_restartable_child(?MODULE), - ok = gen_event:add_handler(?SERVER, ?MODULE, []), + ok = alarm_handler:add_alarm_handler(?MODULE, []), {ok, MemoryWatermark} = application:get_env(vm_memory_high_watermark), rabbit_sup:start_restartable_child(vm_memory_monitor, [MemoryWatermark]), + {ok, DiskLimit} = application:get_env(disk_free_limit), rabbit_sup:start_restartable_child(rabbit_disk_monitor, [DiskLimit]), ok. stop() -> - ok. + ok = alarm_handler:delete_alarm_handler(?MODULE). register(Pid, HighMemMFA) -> - gen_event:call(?SERVER, ?MODULE, {register, Pid, HighMemMFA}, + gen_event:call(alarm_handler, ?MODULE, + {register, Pid, HighMemMFA}, infinity). -set_alarm(Alarm) -> - gen_event:notify(?SERVER, {set_alarm, Alarm}). - -clear_alarm(Alarm) -> - gen_event:notify(?SERVER, {clear_alarm, Alarm}). - -on_node_up(Node) -> gen_event:notify(?SERVER, {node_up, Node}). +on_node_up(Node) -> gen_event:notify(alarm_handler, {node_up, Node}). -on_node_down(Node) -> gen_event:notify(?SERVER, {node_down, Node}). +on_node_down(Node) -> gen_event:notify(alarm_handler, {node_down, Node}). +%% Can't use alarm_handler:{set,clear}_alarm because that doesn't +%% permit notifying a remote node. remote_conserve_resources(Pid, Source, true) -> - gen_event:notify({?SERVER, node(Pid)}, + gen_event:notify({alarm_handler, node(Pid)}, {set_alarm, {{resource_limit, Source, node()}, []}}); remote_conserve_resources(Pid, Source, false) -> - gen_event:notify({?SERVER, node(Pid)}, + gen_event:notify({alarm_handler, node(Pid)}, {clear_alarm, {resource_limit, Source, node()}}). - %%---------------------------------------------------------------------------- init([]) -> @@ -105,10 +94,18 @@ handle_event({clear_alarm, {resource_limit, Source, Node}}, State) -> [Source, Node]), {ok, maybe_alert(fun dict_unappend/3, Node, Source, State)}; +handle_event({set_alarm, {file_descriptor_limit, []}}, State) -> + rabbit_log:warning("file descriptor limit alarm set"), + {ok, State}; + +handle_event({clear_alarm, file_descriptor_limit}, State) -> + rabbit_log:warning("file descriptor limit alarm cleared"), + {ok, State}; + handle_event({node_up, Node}, State) -> %% Must do this via notify and not call to avoid possible deadlock. ok = gen_event:notify( - {?SERVER, Node}, + {alarm_handler, Node}, {register, self(), {?MODULE, remote_conserve_resources, []}}), {ok, State}; diff --git a/src/rabbit_disk_monitor.erl b/src/rabbit_disk_monitor.erl index e72181c0..58375abb 100644 --- a/src/rabbit_disk_monitor.erl +++ b/src/rabbit_disk_monitor.erl @@ -149,10 +149,10 @@ internal_update(State = #state { limit = Limit, case {Alarmed, NewAlarmed} of {false, true} -> emit_update_info("exceeded", CurrentFreeBytes, LimitBytes), - rabbit_alarm:set_alarm({{resource_limit, disk, node()}, []}); + alarm_handler:set_alarm({{resource_limit, disk, node()}, []}); {true, false} -> emit_update_info("below limit", CurrentFreeBytes, LimitBytes), - rabbit_alarm:clear_alarm({resource_limit, disk, node()}); + alarm_handler:clear_alarm({resource_limit, disk, node()}); _ -> ok end, diff --git a/src/vm_memory_monitor.erl b/src/vm_memory_monitor.erl index 3f34c2e9..fb184d1a 100644 --- a/src/vm_memory_monitor.erl +++ b/src/vm_memory_monitor.erl @@ -181,10 +181,10 @@ internal_update(State = #state { memory_limit = MemLimit, case {Alarmed, NewAlarmed} of {false, true} -> emit_update_info(set, MemUsed, MemLimit), - rabbit_alarm:set_alarm({{resource_limit, memory, node()}, []}); + alarm_handler:set_alarm({{resource_limit, memory, node()}, []}); {true, false} -> emit_update_info(clear, MemUsed, MemLimit), - rabbit_alarm:clear_alarm({resource_limit, memory, node()}); + alarm_handler:clear_alarm({resource_limit, memory, node()}); _ -> ok end, -- cgit v1.2.1 From 36d3cfdeb75a4c740637d572cbc250f54e9911de Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Thu, 12 Jul 2012 12:44:31 +0100 Subject: add comment clarifying the need to pattern match in `with_exit_handler/2' --- src/rabbit_misc.erl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 49b46716..6ce1d068 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -421,6 +421,9 @@ throw_on_error(E, Thunk) -> Res -> Res end. +%% Note the code duplication between this and `with_exit_handler/2' below - we +%% can't use arbitrary functions in guards, and we can't re-throw runtime +%% errors. is_benign_exit({R, _}) when R =:= noproc; R =:= nodedown; R =:= normal; R =:= shutdown -> true; -- cgit v1.2.1 From e42be7acc25b27ba49888bae0801db0dc79d1404 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Thu, 12 Jul 2012 15:14:31 +0100 Subject: merge `is_abnormal_termination' and `is_benign_exit' --- src/rabbit_amqqueue.erl | 9 +++++---- src/rabbit_misc.erl | 35 ++++++++++++++--------------------- 2 files changed, 19 insertions(+), 25 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index c51acbdf..f601efb1 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -677,10 +677,11 @@ safe_delegate_call_ok(F, Pids) -> fun () -> ok end, fun () -> F(Pid) end) end), - case lists:filter( - fun ({_Pid, {exit, R, _}}) -> not rabbit_misc:is_benign_exit(R); - (_) -> false - end, Bads) of + case lists:filter(fun ({_Pid, {exit, {R, _}, _}}) -> + rabbit_misc:is_abnormal_termination(R); + ({_Pid, _}) -> + false + end, Bads) of [] -> ok; Bads1 -> {error, Bads1} end. diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 6ce1d068..f0ea4e13 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -29,9 +29,8 @@ -export([enable_cover/1, report_cover/1]). -export([start_cover/1]). -export([confirm_to_sender/2]). --export([throw_on_error/2, is_benign_exit/1, with_exit_handler/2, +-export([throw_on_error/2, with_exit_handler/2, is_abnormal_termination/1, filter_exit_map/2]). --export([is_abnormal_termination/1]). -export([with_user/2, with_user_and_vhost/3]). -export([execute_mnesia_transaction/1]). -export([execute_mnesia_transaction/2]). @@ -137,10 +136,9 @@ -spec(report_cover/1 :: ([file:filename() | atom()]) -> 'ok'). -spec(throw_on_error/2 :: (atom(), thunk(rabbit_types:error(any()) | {ok, A} | A)) -> A). --spec(is_benign_exit/1 :: (any()) -> boolean()). -spec(with_exit_handler/2 :: (thunk(A), thunk(A)) -> A). --spec(filter_exit_map/2 :: (fun ((A) -> B), [A]) -> [B]). -spec(is_abnormal_termination/1 :: (any()) -> boolean()). +-spec(filter_exit_map/2 :: (fun ((A) -> B), [A]) -> [B]). -spec(with_user/2 :: (rabbit_types:username(), thunk(A)) -> A). -spec(with_user_and_vhost/3 :: (rabbit_types:username(), rabbit_types:vhost(), thunk(A)) @@ -421,28 +419,28 @@ throw_on_error(E, Thunk) -> Res -> Res end. -%% Note the code duplication between this and `with_exit_handler/2' below - we -%% can't use arbitrary functions in guards, and we can't re-throw runtime -%% errors. -is_benign_exit({R, _}) when R =:= noproc; R =:= nodedown; R =:= normal; - R =:= shutdown -> - true; -is_benign_exit({{R, _}, _}) when R =:= nodedown; R =:= shutdown -> - true; -is_benign_exit(_) -> - false. - with_exit_handler(Handler, Thunk) -> try Thunk() catch - exit:{R, _} when R =:= noproc; R =:= nodedown; + exit:{R, _} when R =:= noproc; R =:= noconnection; R =:= nodedown; R =:= normal; R =:= shutdown -> Handler(); exit:{{R, _}, _} when R =:= nodedown; R =:= shutdown -> Handler() end. +%% Note the code duplication between this and `with_exit_handler/2' above - we +%% can't use arbitrary functions in guards, and we can't re-throw runtime +%% errors. +is_abnormal_termination(R) when R =:= noproc; R =:= noconnection; + R =:= nodedown; R =:= normal; R =:= shutdown -> + false; +is_abnormal_termination({R, _}) when R =:= nodedown; R =:= shutdown -> + false; +is_abnormal_termination(_) -> + true. + filter_exit_map(F, L) -> Ref = make_ref(), lists:filter(fun (R) -> R =/= Ref end, @@ -450,11 +448,6 @@ filter_exit_map(F, L) -> fun () -> Ref end, fun () -> F(I) end) || I <- L]). -is_abnormal_termination(Reason) - when Reason =:= noproc; Reason =:= noconnection; - Reason =:= normal; Reason =:= shutdown -> false; -is_abnormal_termination({shutdown, _}) -> false; -is_abnormal_termination(_) -> true. with_user(Username, Thunk) -> fun () -> -- cgit v1.2.1 From cefed611a7c45d943e98b5dc2490a97f0bbd4636 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 12 Jul 2012 15:31:00 +0100 Subject: Don't reopen file handles in fhc:needs_sync/1, avoid infinite loop. --- src/file_handle_cache.erl | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/file_handle_cache.erl b/src/file_handle_cache.erl index f3b4dbaf..e8788c97 100644 --- a/src/file_handle_cache.erl +++ b/src/file_handle_cache.erl @@ -374,11 +374,12 @@ sync(Ref) -> end). needs_sync(Ref) -> - with_handles( - [Ref], - fun ([#handle { is_dirty = false, write_buffer = [] }]) -> false; - ([_Handle]) -> true - end). + %% This needs *not* to use with_handles/2; see bug 25052 + Handle = get({Ref, fhc_handle}), + case Handle of + #handle { is_dirty = false, write_buffer = [] } -> false; + _ -> true + end. position(Ref, NewOffset) -> with_flushed_handles( -- cgit v1.2.1 From 1fa05b23e961f76fc090b61aa682f15062d97630 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Thu, 12 Jul 2012 15:35:03 +0100 Subject: macro instead of duplicating the guards --- src/rabbit_misc.erl | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index f0ea4e13..804d587b 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -61,6 +61,11 @@ -export([os_cmd/1]). -export([gb_sets_difference/2]). +%% Horrible macro to use in guards +-define(BENIGN_TERMINATION(R), + R =:= noproc; R =:= noconnection; R =:= nodedown; R =:= normal; + R =:= shutdown). + %%---------------------------------------------------------------------------- -ifdef(use_specs). @@ -423,23 +428,13 @@ with_exit_handler(Handler, Thunk) -> try Thunk() catch - exit:{R, _} when R =:= noproc; R =:= noconnection; R =:= nodedown; - R =:= normal; R =:= shutdown -> - Handler(); - exit:{{R, _}, _} when R =:= nodedown; R =:= shutdown -> - Handler() + exit:{R, _} when ?BENIGN_TERMINATION(R) -> Handler(); + exit:{{R, _}, _} when ?BENIGN_TERMINATION(R) -> Handler() end. -%% Note the code duplication between this and `with_exit_handler/2' above - we -%% can't use arbitrary functions in guards, and we can't re-throw runtime -%% errors. -is_abnormal_termination(R) when R =:= noproc; R =:= noconnection; - R =:= nodedown; R =:= normal; R =:= shutdown -> - false; -is_abnormal_termination({R, _}) when R =:= nodedown; R =:= shutdown -> - false; -is_abnormal_termination(_) -> - true. +is_abnormal_termination(R) when ?BENIGN_TERMINATION(R) -> false; +is_abnormal_termination({R, _}) when ?BENIGN_TERMINATION(R) -> false; +is_abnormal_termination(_) -> true. filter_exit_map(F, L) -> Ref = make_ref(), -- cgit v1.2.1 From 063bd3dd5ad5512ab237a9b5e4aa4df615c602a7 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 12 Jul 2012 15:39:55 +0100 Subject: Duh, cosmetic --- src/file_handle_cache.erl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/file_handle_cache.erl b/src/file_handle_cache.erl index e8788c97..c39f7557 100644 --- a/src/file_handle_cache.erl +++ b/src/file_handle_cache.erl @@ -375,8 +375,7 @@ sync(Ref) -> needs_sync(Ref) -> %% This needs *not* to use with_handles/2; see bug 25052 - Handle = get({Ref, fhc_handle}), - case Handle of + case get({Ref, fhc_handle}) of #handle { is_dirty = false, write_buffer = [] } -> false; _ -> true end. -- cgit v1.2.1 From f9ff9e28a9249f1633995e4c63bccf9d8009da25 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 12 Jul 2012 16:55:38 +0100 Subject: grammar --- src/file_handle_cache.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/file_handle_cache.erl b/src/file_handle_cache.erl index c39f7557..fb20ce93 100644 --- a/src/file_handle_cache.erl +++ b/src/file_handle_cache.erl @@ -374,7 +374,7 @@ sync(Ref) -> end). needs_sync(Ref) -> - %% This needs *not* to use with_handles/2; see bug 25052 + %% This must *not* use with_handles/2; see bug 25052 case get({Ref, fhc_handle}) of #handle { is_dirty = false, write_buffer = [] } -> false; _ -> true -- cgit v1.2.1 From ccd409f9522378fed49bf9a56c474734e8a85b7d Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 12 Jul 2012 16:55:52 +0100 Subject: When handling emit_stats we should do nearly everything we normally do in noreply/1, just not create another stats timer. --- src/rabbit_amqqueue_process.erl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 8933de87..b42d1aea 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1328,11 +1328,11 @@ handle_info(drop_expired, State) -> noreply(drop_expired_messages(State#q{ttl_timer_ref = undefined})); handle_info(emit_stats, State) -> - %% Do not invoke noreply as it would see no timer and create a new one. emit_stats(State), - State1 = rabbit_event:reset_stats_timer(State, #q.stats_timer), - assert_invariant(State1), - {noreply, State1, hibernate}; + {noreply, State1, Timeout} = noreply(State), + %% Need to reset *after* we've been through noreply/1 so we do not + %% just create another timer always and therefore never hibernate + {noreply, rabbit_event:reset_stats_timer(State1, #q.stats_timer), Timeout}; handle_info({'DOWN', _MonitorRef, process, DownPid, _Reason}, State = #q{q = #amqqueue{exclusive_owner = DownPid}}) -> -- cgit v1.2.1 From cad12c0f75cd1b6c5566fff203bfb9965214dbd4 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 12 Jul 2012 16:56:15 +0100 Subject: blow up if Ref is unknown just like the old version did --- src/file_handle_cache.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/file_handle_cache.erl b/src/file_handle_cache.erl index fb20ce93..13ee4249 100644 --- a/src/file_handle_cache.erl +++ b/src/file_handle_cache.erl @@ -377,7 +377,7 @@ needs_sync(Ref) -> %% This must *not* use with_handles/2; see bug 25052 case get({Ref, fhc_handle}) of #handle { is_dirty = false, write_buffer = [] } -> false; - _ -> true + #handle {} -> true end. position(Ref, NewOffset) -> -- cgit v1.2.1 From 99927387382064f92603c3255f9ab5c9f8de10f8 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 13 Jul 2012 11:06:05 +0100 Subject: Explain this message. --- src/rabbit_mirror_queue_slave.erl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index c96e98b6..2d7bd9a1 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -199,6 +199,11 @@ handle_call({gm_deaths, Deaths}, From, %% master has changed to not us. gen_server2:reply(From, ok), erlang:monitor(process, Pid), + %% GM is lazy. So we know of the death of the + %% slave since it is a neighbour of ours, but + %% until a message is sent, not all members will + %% know. That might include the new master. So + %% broadcast a no-op message to wake everyone up. ok = gm:broadcast(GM, master_changed), noreply(State #state { master_pid = Pid }) end -- cgit v1.2.1 From 16d4ec43b80b8d2e919840e9c2cb8fe3fd95250f Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 13 Jul 2012 11:55:24 +0100 Subject: I am more dubious about this one. But not enough to take it out. --- src/rabbit_mirror_queue_slave.erl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 2d7bd9a1..60d3e027 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -457,6 +457,8 @@ promote_me(From, #state { q = Q = #amqqueue { name = QName }, rabbit_mirror_queue_master:length_fun()), true = unlink(GM), gen_server2:reply(From, {promote, CPid}), + %% TODO this has been in here since the beginning, but it's not + %% obvious if it is needed. Investigate... ok = gm:confirmed_broadcast(GM, master_changed), %% Everything that we're monitoring, we need to ensure our new -- cgit v1.2.1 From e5b55dc25fe7bfbb923a163d1cbfbccb9870a9da Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 13 Jul 2012 15:01:16 +0100 Subject: tiny simplifying refactor --- src/rabbit_reader.erl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index bd5cf588..7972141c 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -938,8 +938,7 @@ send_exception(State = #v1{connection = #connection{protocol = Protocol}}, rabbit_binary_generator:map_exception(Channel, Reason, Protocol), terminate_channels(), State1 = close_connection(State), - ok = rabbit_writer:internal_send_command( - State1#v1.sock, 0, CloseMethod, Protocol), + ok = send_on_channel0(State1#v1.sock, CloseMethod, Protocol), State1. emit_stats(State) -> -- cgit v1.2.1 From e94e58278892304470d729a06704d07e49a57687 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Fri, 13 Jul 2012 16:36:23 +0100 Subject: `rabbit_alarm' on its own again, added callbacks to fhc and `vm_memory_monitor' --- Makefile | 4 ++-- src/file_handle_cache.erl | 25 ++++++++++++++++++------- src/rabbit.erl | 13 ++++++++++--- src/rabbit_alarm.erl | 43 +++++++++++++++++++++++++++++-------------- src/rabbit_disk_monitor.erl | 4 ++-- src/vm_memory_monitor.erl | 31 +++++++++++++++++++++++-------- 6 files changed, 84 insertions(+), 36 deletions(-) diff --git a/Makefile b/Makefile index 0e3960dc..3681a787 100644 --- a/Makefile +++ b/Makefile @@ -217,11 +217,11 @@ stop-rabbit-on-node: all echo "rabbit:stop()." | $(ERL_CALL) set-resource-alarm: all - echo "alarm_handler:set_alarm({{resource_limit, $(SOURCE), node()}, []})." | \ + echo "rabbit_alarm:set_alarm({{resource_limit, $(SOURCE), node()}, []})." | \ $(ERL_CALL) clear-resource-alarm: all - echo "alarm_handler:clear_alarm({resource_limit, $(SOURCE), node()})." | \ + echo "rabbit_alarm:clear_alarm({resource_limit, $(SOURCE), node()})." | \ $(ERL_CALL) stop-node: diff --git a/src/file_handle_cache.erl b/src/file_handle_cache.erl index f3b4dbaf..9a49cc6a 100644 --- a/src/file_handle_cache.erl +++ b/src/file_handle_cache.erl @@ -150,8 +150,8 @@ info/0, info/1]). -export([ulimit/0]). --export([start_link/0, init/1, handle_call/3, handle_cast/2, handle_info/2, - terminate/2, code_change/3, prioritise_cast/2]). +-export([start_link/0, start_link/2, init/1, handle_call/3, handle_cast/2, + handle_info/2, terminate/2, code_change/3, prioritise_cast/2]). -define(SERVER, ?MODULE). -define(RESERVED_FOR_OTHERS, 100). @@ -195,7 +195,9 @@ obtain_count, obtain_pending, clients, - timer_ref + timer_ref, + alarm_set, + alarm_clear }). -record(cstate, @@ -270,6 +272,10 @@ start_link() -> gen_server2:start_link({local, ?SERVER}, ?MODULE, [], [{timeout, infinity}]). +start_link(AlarmSet, AlarmClear) -> + gen_server2:start_link({local, ?SERVER}, ?MODULE, [AlarmSet, AlarmClear], + [{timeout, infinity}]). + register_callback(M, F, A) when is_atom(M) andalso is_atom(F) andalso is_list(A) -> gen_server2:cast(?SERVER, {register_callback, self(), {M, F, A}}). @@ -807,6 +813,8 @@ i(Item, _) -> throw({bad_argument, Item}). %%---------------------------------------------------------------------------- init([]) -> + init([fun alarm_handler:set_alarm/1, fun alarm_handler:clear_alarm/1]); +init([AlarmSet, AlarmClear]) -> Limit = case application:get_env(file_handles_high_watermark) of {ok, Watermark} when (is_integer(Watermark) andalso Watermark > 0) -> @@ -830,7 +838,9 @@ init([]) -> obtain_count = 0, obtain_pending = pending_new(), clients = Clients, - timer_ref = undefined }}. + timer_ref = undefined, + alarm_set = AlarmSet, + alarm_clear = AlarmClear }}. prioritise_cast(Msg, _State) -> case Msg of @@ -1026,10 +1036,11 @@ obtain_limit_reached(#fhc_state { obtain_limit = Limit, obtain_count = Count}) -> Limit =/= infinity andalso Count >= Limit. -adjust_alarm(OldState, NewState) -> +adjust_alarm(OldState = #fhc_state { alarm_set = AlarmSet, + alarm_clear = AlarmClear }, NewState) -> case {obtain_limit_reached(OldState), obtain_limit_reached(NewState)} of - {false, true} -> alarm_handler:set_alarm({file_descriptor_limit, []}); - {true, false} -> alarm_handler:clear_alarm(file_descriptor_limit); + {false, true} -> AlarmSet({file_descriptor_limit, []}); + {true, false} -> AlarmClear(file_descriptor_limit); _ -> ok end, NewState. diff --git a/src/rabbit.erl b/src/rabbit.erl index fda489fe..ed258c71 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -20,7 +20,8 @@ -export([start/0, boot/0, stop/0, stop_and_halt/0, await_startup/0, status/0, is_running/0, - is_running/1, environment/0, rotate_logs/1, force_event_refresh/0]). + is_running/1, environment/0, rotate_logs/1, force_event_refresh/0, + start_fhc/0]). -export([start/2, stop/1]). @@ -53,8 +54,7 @@ -rabbit_boot_step({file_handle_cache, [{description, "file handle cache server"}, - {mfa, {rabbit_sup, start_restartable_child, - [file_handle_cache]}}, + {mfa, {rabbit, start_fhc, []}}, {requires, pre_boot}, {enables, worker_pool}]}). @@ -730,3 +730,10 @@ config_files() -> [File] <- Files]; error -> [] end. + +%% We don't want this in fhc since it references rabbit stuff. And we can't put +%% this in the bootstep directly. +start_fhc() -> + rabbit_sup:start_restartable_child( + file_handle_cache, + [fun rabbit_alarm:set_alarm/1, fun rabbit_alarm:clear_alarm/1]). diff --git a/src/rabbit_alarm.erl b/src/rabbit_alarm.erl index 665e2cb9..69256b4d 100644 --- a/src/rabbit_alarm.erl +++ b/src/rabbit_alarm.erl @@ -18,22 +18,28 @@ -behaviour(gen_event). --export([start/0, stop/0, register/2, on_node_up/1, on_node_down/1]). +-export([start_link/0, start/0, stop/0, register/2, set_alarm/1, + clear_alarm/1, on_node_up/1, on_node_down/1]). -export([init/1, handle_call/2, handle_event/2, handle_info/2, terminate/2, code_change/3]). -export([remote_conserve_resources/3]). %% Internal use only +-define(SERVER, ?MODULE). + -record(alarms, {alertees, alarmed_nodes}). %%---------------------------------------------------------------------------- -ifdef(use_specs). +-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(set_alarm/1 :: (any()) -> 'ok'). +-spec(clear_alarm/1 :: (any()) -> 'ok'). -spec(on_node_up/1 :: (node()) -> 'ok'). -spec(on_node_down/1 :: (node()) -> 'ok'). @@ -41,36 +47,45 @@ %%---------------------------------------------------------------------------- +start_link() -> + gen_event:start_link({local, ?SERVER}). + start() -> - ok = alarm_handler:add_alarm_handler(?MODULE, []), + ok = rabbit_sup:start_restartable_child(?MODULE), + ok = gen_event:add_handler(?SERVER, ?MODULE, []), {ok, MemoryWatermark} = application:get_env(vm_memory_high_watermark), - rabbit_sup:start_restartable_child(vm_memory_monitor, [MemoryWatermark]), - + rabbit_sup:start_restartable_child( + vm_memory_monitor, [MemoryWatermark, fun rabbit_alarm:set_alarm/1, + fun rabbit_alarm:clear_alarm/1]), {ok, DiskLimit} = application:get_env(disk_free_limit), rabbit_sup:start_restartable_child(rabbit_disk_monitor, [DiskLimit]), ok. stop() -> - ok = alarm_handler:delete_alarm_handler(?MODULE). + ok. register(Pid, HighMemMFA) -> - gen_event:call(alarm_handler, ?MODULE, - {register, Pid, HighMemMFA}, + gen_event:call(?SERVER, ?MODULE, {register, Pid, HighMemMFA}, infinity). -on_node_up(Node) -> gen_event:notify(alarm_handler, {node_up, Node}). +set_alarm(Alarm) -> + gen_event:notify(?SERVER, {set_alarm, Alarm}). -on_node_down(Node) -> gen_event:notify(alarm_handler, {node_down, Node}). +clear_alarm(Alarm) -> + gen_event:notify(?SERVER, {clear_alarm, Alarm}). + +on_node_up(Node) -> gen_event:notify(?SERVER, {node_up, Node}). + +on_node_down(Node) -> gen_event:notify(?SERVER, {node_down, Node}). -%% Can't use alarm_handler:{set,clear}_alarm because that doesn't -%% permit notifying a remote node. remote_conserve_resources(Pid, Source, true) -> - gen_event:notify({alarm_handler, node(Pid)}, + gen_event:notify({?SERVER, node(Pid)}, {set_alarm, {{resource_limit, Source, node()}, []}}); remote_conserve_resources(Pid, Source, false) -> - gen_event:notify({alarm_handler, node(Pid)}, + gen_event:notify({?SERVER, node(Pid)}, {clear_alarm, {resource_limit, Source, node()}}). + %%---------------------------------------------------------------------------- init([]) -> @@ -105,7 +120,7 @@ handle_event({clear_alarm, file_descriptor_limit}, State) -> handle_event({node_up, Node}, State) -> %% Must do this via notify and not call to avoid possible deadlock. ok = gen_event:notify( - {alarm_handler, Node}, + {?SERVER, Node}, {register, self(), {?MODULE, remote_conserve_resources, []}}), {ok, State}; diff --git a/src/rabbit_disk_monitor.erl b/src/rabbit_disk_monitor.erl index 58375abb..e72181c0 100644 --- a/src/rabbit_disk_monitor.erl +++ b/src/rabbit_disk_monitor.erl @@ -149,10 +149,10 @@ internal_update(State = #state { limit = Limit, case {Alarmed, NewAlarmed} of {false, true} -> emit_update_info("exceeded", CurrentFreeBytes, LimitBytes), - alarm_handler:set_alarm({{resource_limit, disk, node()}, []}); + rabbit_alarm:set_alarm({{resource_limit, disk, node()}, []}); {true, false} -> emit_update_info("below limit", CurrentFreeBytes, LimitBytes), - alarm_handler:clear_alarm({resource_limit, disk, node()}); + rabbit_alarm:clear_alarm({resource_limit, disk, node()}); _ -> ok end, diff --git a/src/vm_memory_monitor.erl b/src/vm_memory_monitor.erl index fb184d1a..535208af 100644 --- a/src/vm_memory_monitor.erl +++ b/src/vm_memory_monitor.erl @@ -27,7 +27,7 @@ -behaviour(gen_server). --export([start_link/1]). +-export([start_link/1, start_link/3]). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). @@ -51,7 +51,9 @@ memory_limit, timeout, timer, - alarmed + alarmed, + alarm_set, + alarm_clear }). %%---------------------------------------------------------------------------- @@ -59,6 +61,8 @@ -ifdef(use_specs). -spec(start_link/1 :: (float()) -> rabbit_types:ok_pid_or_error()). +-spec(start_link/3 :: (float(), fun ((any()) -> 'ok'), + fun ((any()) -> 'ok')) -> rabbit_types:ok_pid_or_error()). -spec(get_total_memory/0 :: () -> (non_neg_integer() | 'unknown')). -spec(get_vm_limit/0 :: () -> non_neg_integer()). -spec(get_check_interval/0 :: () -> non_neg_integer()). @@ -99,14 +103,23 @@ get_memory_limit() -> %% gen_server callbacks %%---------------------------------------------------------------------------- -start_link(Args) -> - gen_server:start_link({local, ?SERVER}, ?MODULE, [Args], []). +start_link(MemFraction) -> + gen_server:start_link({local, ?SERVER}, ?MODULE, [MemFraction], []). + +start_link(MemFraction, AlarmSet, AlarmClear) -> + gen_server:start_link({local, ?SERVER}, ?MODULE, + [MemFraction, AlarmSet, AlarmClear], []). init([MemFraction]) -> + init([MemFraction, fun alarm_handler:set_alarm/1, + fun alarm_handler:clear_alarm/1]); +init([MemFraction, AlarmSet, AlarmClear]) -> TRef = start_timer(?DEFAULT_MEMORY_CHECK_INTERVAL), State = #state { timeout = ?DEFAULT_MEMORY_CHECK_INTERVAL, timer = TRef, - alarmed = false}, + alarmed = false, + alarm_set = AlarmSet, + alarm_clear = AlarmClear }, {ok, set_mem_limits(State, MemFraction)}. handle_call(get_vm_memory_high_watermark, _From, State) -> @@ -175,16 +188,18 @@ set_mem_limits(State, MemFraction) -> memory_limit = MemLim }). internal_update(State = #state { memory_limit = MemLimit, - alarmed = Alarmed}) -> + alarmed = Alarmed, + alarm_set = AlarmSet, + alarm_clear = AlarmClear }) -> MemUsed = erlang:memory(total), NewAlarmed = MemUsed > MemLimit, case {Alarmed, NewAlarmed} of {false, true} -> emit_update_info(set, MemUsed, MemLimit), - alarm_handler:set_alarm({{resource_limit, memory, node()}, []}); + AlarmSet({{resource_limit, memory, node()}, []}); {true, false} -> emit_update_info(clear, MemUsed, MemLimit), - alarm_handler:clear_alarm({resource_limit, memory, node()}); + AlarmClear({resource_limit, memory, node()}); _ -> ok end, -- cgit v1.2.1 From 7dc834ccd20a96b146d7ef63a6bfe6fe0864acc7 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Fri, 13 Jul 2012 17:02:46 +0100 Subject: add a `get_alarms/0' to `rabbit_alarm', equivalent to `alarm_handler:get_alarms/0' --- src/rabbit_alarm.erl | 50 +++++++++++++++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/src/rabbit_alarm.erl b/src/rabbit_alarm.erl index 69256b4d..9eeb61b6 100644 --- a/src/rabbit_alarm.erl +++ b/src/rabbit_alarm.erl @@ -19,7 +19,7 @@ -behaviour(gen_event). -export([start_link/0, start/0, stop/0, register/2, set_alarm/1, - clear_alarm/1, on_node_up/1, on_node_down/1]). + clear_alarm/1, get_alarms/0, on_node_up/1, on_node_down/1]). -export([init/1, handle_call/2, handle_event/2, handle_info/2, terminate/2, code_change/3]). @@ -28,7 +28,7 @@ -define(SERVER, ?MODULE). --record(alarms, {alertees, alarmed_nodes}). +-record(alarms, {alertees, alarmed_nodes, alarms}). %%---------------------------------------------------------------------------- @@ -74,6 +74,9 @@ set_alarm(Alarm) -> clear_alarm(Alarm) -> gen_event:notify(?SERVER, {clear_alarm, Alarm}). +get_alarms() -> + gen_event:call(?SERVER, ?MODULE, get_alarms, infinity). + on_node_up(Node) -> gen_event:notify(?SERVER, {node_up, Node}). on_node_down(Node) -> gen_event:notify(?SERVER, {node_down, Node}). @@ -90,32 +93,25 @@ remote_conserve_resources(Pid, Source, false) -> init([]) -> {ok, #alarms{alertees = dict:new(), - alarmed_nodes = dict:new()}}. + alarmed_nodes = dict:new(), + alarms = []}}. handle_call({register, Pid, HighMemMFA}, State) -> {ok, 0 < dict:size(State#alarms.alarmed_nodes), internal_register(Pid, HighMemMFA, State)}; +handle_call(get_alarms, State = #alarms{alarms = Alarms}) -> + {ok, Alarms, State}; + handle_call(_Request, State) -> {ok, not_understood, State}. -handle_event({set_alarm, {{resource_limit, Source, Node}, []}}, State) -> - rabbit_log:warning("'~p' resource limit alarm set on node ~p", - [Source, Node]), - {ok, maybe_alert(fun dict:append/3, Node, Source, State)}; +handle_event({set_alarm, Alarm}, State = #alarms{alarms = Alarms}) -> + handle_set_alarm(Alarm, State#alarms{alarms = [Alarm|Alarms]}); -handle_event({clear_alarm, {resource_limit, Source, Node}}, State) -> - rabbit_log:warning("'~p' resource limit alarm cleared on node ~p", - [Source, Node]), - {ok, maybe_alert(fun dict_unappend/3, Node, Source, State)}; - -handle_event({set_alarm, {file_descriptor_limit, []}}, State) -> - rabbit_log:warning("file descriptor limit alarm set"), - {ok, State}; - -handle_event({clear_alarm, file_descriptor_limit}, State) -> - rabbit_log:warning("file descriptor limit alarm cleared"), - {ok, State}; +handle_event({clear_alarm, Alarm}, State = #alarms{alarms = Alarms}) -> + handle_clear_alarm(Alarm, State#alarms{alarms = lists:keydelete(Alarm, 1, + Alarms)}); handle_event({node_up, Node}, State) -> %% Must do this via notify and not call to avoid possible deadlock. @@ -213,3 +209,19 @@ internal_register(Pid, {M, F, A} = HighMemMFA, end, NewAlertees = dict:store(Pid, HighMemMFA, Alertees), State#alarms{alertees = NewAlertees}. + +handle_set_alarm({{resource_limit, Source, Node}, []}, State) -> + rabbit_log:warning("'~p' resource limit alarm set on node ~p", + [Source, Node]), + {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"), + {ok, State}. + +handle_clear_alarm({resource_limit, Source, Node}, State) -> + rabbit_log:warning("'~p' resource limit alarm cleared on node ~p", + [Source, Node]), + {ok, maybe_alert(fun dict_unappend/3, Node, Source, State)}; +handle_clear_alarm(file_descriptor_limit, State) -> + rabbit_log:warning("file descriptor limit alarm cleared"), + {ok, State}. -- cgit v1.2.1 From f6241d2da086ccbf011f83176d04d64c512042b7 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Fri, 13 Jul 2012 17:25:08 +0100 Subject: cosmetics --- src/rabbit_alarm.erl | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/rabbit_alarm.erl b/src/rabbit_alarm.erl index 9eeb61b6..0d5248a1 100644 --- a/src/rabbit_alarm.erl +++ b/src/rabbit_alarm.erl @@ -61,21 +61,17 @@ start() -> rabbit_sup:start_restartable_child(rabbit_disk_monitor, [DiskLimit]), ok. -stop() -> - ok. +stop() -> ok. register(Pid, HighMemMFA) -> gen_event:call(?SERVER, ?MODULE, {register, Pid, HighMemMFA}, infinity). -set_alarm(Alarm) -> - gen_event:notify(?SERVER, {set_alarm, Alarm}). +set_alarm(Alarm) -> gen_event:notify(?SERVER, {set_alarm, Alarm}). -clear_alarm(Alarm) -> - gen_event:notify(?SERVER, {clear_alarm, Alarm}). +clear_alarm(Alarm) -> gen_event:notify(?SERVER, {clear_alarm, Alarm}). -get_alarms() -> - gen_event:call(?SERVER, ?MODULE, get_alarms, infinity). +get_alarms() -> gen_event:call(?SERVER, ?MODULE, get_alarms, infinity). on_node_up(Node) -> gen_event:notify(?SERVER, {node_up, Node}). -- cgit v1.2.1 From ecb45f3c1ef699f8664c0c26299b7490eab74eb2 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Fri, 13 Jul 2012 17:27:41 +0100 Subject: because we all love aligned arrows (especially Matthias) --- src/rabbit_alarm.erl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rabbit_alarm.erl b/src/rabbit_alarm.erl index 0d5248a1..2eb4255b 100644 --- a/src/rabbit_alarm.erl +++ b/src/rabbit_alarm.erl @@ -73,8 +73,7 @@ clear_alarm(Alarm) -> gen_event:notify(?SERVER, {clear_alarm, Alarm}). get_alarms() -> gen_event:call(?SERVER, ?MODULE, get_alarms, infinity). -on_node_up(Node) -> gen_event:notify(?SERVER, {node_up, Node}). - +on_node_up(Node) -> gen_event:notify(?SERVER, {node_up, Node}). on_node_down(Node) -> gen_event:notify(?SERVER, {node_down, Node}). remote_conserve_resources(Pid, Source, true) -> -- cgit v1.2.1 From 4fb139fba42b34e27f01c2584eb781d60bc6ca72 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Fri, 13 Jul 2012 17:41:54 +0100 Subject: aligning things, part two --- src/rabbit_alarm.erl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/rabbit_alarm.erl b/src/rabbit_alarm.erl index 2eb4255b..94d53e8d 100644 --- a/src/rabbit_alarm.erl +++ b/src/rabbit_alarm.erl @@ -67,13 +67,12 @@ register(Pid, HighMemMFA) -> gen_event:call(?SERVER, ?MODULE, {register, Pid, HighMemMFA}, infinity). -set_alarm(Alarm) -> gen_event:notify(?SERVER, {set_alarm, Alarm}). - +set_alarm(Alarm) -> gen_event:notify(?SERVER, {set_alarm, Alarm}). clear_alarm(Alarm) -> gen_event:notify(?SERVER, {clear_alarm, Alarm}). get_alarms() -> gen_event:call(?SERVER, ?MODULE, get_alarms, infinity). -on_node_up(Node) -> gen_event:notify(?SERVER, {node_up, Node}). +on_node_up(Node) -> gen_event:notify(?SERVER, {node_up, Node}). on_node_down(Node) -> gen_event:notify(?SERVER, {node_down, Node}). remote_conserve_resources(Pid, Source, true) -> -- cgit v1.2.1 From 179c814ef124d6b43bbbd80a7960d748f352ddb4 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Fri, 13 Jul 2012 18:13:22 +0100 Subject: do not blowup on unrecognised alarms --- src/rabbit_alarm.erl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/rabbit_alarm.erl b/src/rabbit_alarm.erl index 94d53e8d..37d5f3f1 100644 --- a/src/rabbit_alarm.erl +++ b/src/rabbit_alarm.erl @@ -210,6 +210,9 @@ handle_set_alarm({{resource_limit, Source, Node}, []}, 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"), + {ok, State}; +handle_set_alarm(Alarm, State) -> + rabbit_log:warning("alarm '~p' set", [Alarm]), {ok, State}. handle_clear_alarm({resource_limit, Source, Node}, State) -> @@ -218,4 +221,7 @@ handle_clear_alarm({resource_limit, Source, Node}, State) -> {ok, maybe_alert(fun dict_unappend/3, Node, Source, State)}; handle_clear_alarm(file_descriptor_limit, State) -> rabbit_log:warning("file descriptor limit alarm cleared"), + {ok, State}; +handle_clear_alarm(Alarm, State) -> + rabbit_log:warning("alarm '~p' cleared", [Alarm]), {ok, State}. -- cgit v1.2.1 From 66752225e4c7417449a32a4e94e658dc8b6b79b5 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 14 Jul 2012 19:27:44 +0100 Subject: cosmetic --- src/rabbit_reader.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 7972141c..86255790 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -541,8 +541,8 @@ handle_input(frame_header, <>, State) -> switch_callback(State, {frame_payload, Type, Channel, PayloadSize}, PayloadSize + 1)); -handle_input({frame_payload, Type, Channel, PayloadSize}, - PayloadAndMarker, State) -> +handle_input({frame_payload, Type, Channel, PayloadSize}, PayloadAndMarker, + State) -> case PayloadAndMarker of <> -> switch_callback(handle_frame(Type, Channel, Payload, State), -- cgit v1.2.1 From ff258ee58e429221ce1163604c418b57619221f7 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sun, 15 Jul 2012 16:02:19 +0100 Subject: renaming ... for consistency with reader, and to better describe what the fun does --- src/rabbit_channel.erl | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 22c6a223..ad39001c 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -267,7 +267,7 @@ handle_cast({method, Method, Content, Flow}, catch exit:Reason = #amqp_error{} -> MethodName = rabbit_misc:method_record_type(Method), - send_exception(Reason#amqp_error{method = MethodName}, State); + handle_exception(Reason#amqp_error{method = MethodName}, State); _:Reason -> {stop, {Reason, erlang:get_stacktrace()}, State} end; @@ -400,15 +400,11 @@ return_ok(State, false, Msg) -> {reply, Msg, State}. ok_msg(true, _Msg) -> undefined; ok_msg(false, Msg) -> Msg. -send_exception(Reason, State = #ch{protocol = Protocol, - channel = Channel, - writer_pid = WriterPid, - reader_pid = ReaderPid, - conn_pid = ConnPid}) -> - {CloseChannel, CloseMethod} = - rabbit_binary_generator:map_exception(Channel, Reason, Protocol), - rabbit_log:error("connection ~p, channel ~p - error:~n~p~n", - [ConnPid, Channel, Reason]), +handle_exception(Reason, State = #ch{protocol = Protocol, + channel = Channel, + writer_pid = WriterPid, + reader_pid = ReaderPid, + conn_pid = ConnPid}) -> %% something bad's happened: notify_queues may not be 'ok' {_Result, State1} = notify_queues(State), case CloseChannel of @@ -1423,7 +1419,7 @@ complete_tx(State = #ch{tx_status = committing}) -> ok = rabbit_writer:send_command(State#ch.writer_pid, #'tx.commit_ok'{}), State#ch{tx_status = in_progress}; complete_tx(State = #ch{tx_status = failed}) -> - {noreply, State1} = send_exception( + {noreply, State1} = handle_exception( rabbit_misc:amqp_error( precondition_failed, "partial tx completion", [], 'tx.commit'), -- cgit v1.2.1 From fc3f3fab60d8afff49ffaffe16d4d34c69112a86 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sun, 15 Jul 2012 16:15:03 +0100 Subject: oops - 'hg record' diff boundary fail --- src/rabbit_channel.erl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index ad39001c..66b4e79a 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -405,6 +405,10 @@ handle_exception(Reason, State = #ch{protocol = Protocol, writer_pid = WriterPid, reader_pid = ReaderPid, conn_pid = ConnPid}) -> + {CloseChannel, CloseMethod} = + rabbit_binary_generator:map_exception(Channel, Reason, Protocol), + rabbit_log:error("connection ~p, channel ~p - error:~n~p~n", + [ConnPid, Channel, Reason]), %% something bad's happened: notify_queues may not be 'ok' {_Result, State1} = notify_queues(State), case CloseChannel of -- cgit v1.2.1 From 3a2a7fde4677265af313b3070ce8159915d71808 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sun, 15 Jul 2012 16:17:21 +0100 Subject: refactor: precondition_failed helper function --- src/rabbit_channel.erl | 38 +++++++++++++++++--------------------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 66b4e79a..1bcdccee 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -418,6 +418,11 @@ handle_exception(Reason, State = #ch{protocol = Protocol, {stop, normal, State1} end. +precondition_failed(Format) -> precondition_failed(Format, []). + +precondition_failed(Format, Params) -> + rabbit_misc:amqp_error(precondition_failed, Format, Params). + return_queue_declare_ok(#resource{name = ActualName}, NoWait, MessageCount, ConsumerCount, State) -> return_ok(State#ch{most_recently_declared_queue = ActualName}, NoWait, @@ -461,9 +466,9 @@ check_user_id_header(#'P_basic'{user_id = Username}, ok; check_user_id_header(#'P_basic'{user_id = Claimed}, #ch{user = #user{username = Actual}}) -> - rabbit_misc:protocol_error( - precondition_failed, "user_id property set to '~s' but " - "authenticated user was '~s'", [Claimed, Actual]). + precondition_failed( + "user_id property set to '~s' but authenticated user was '~s'", + [Claimed, Actual]). check_internal_exchange(#exchange{name = Name, internal = true}) -> rabbit_misc:protocol_error(access_refused, @@ -625,8 +630,7 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, State1#ch{uncommitted_message_q = NewTMQ} end}; {error, Reason} -> - rabbit_misc:protocol_error(precondition_failed, - "invalid message: ~p", [Reason]) + precondition_failed("invalid message: ~p", [Reason]) end; handle_method(#'basic.nack'{delivery_tag = DeliveryTag, @@ -881,8 +885,7 @@ handle_method(#'exchange.delete'{exchange = ExchangeNameBin, {error, not_found} -> rabbit_misc:not_found(ExchangeName); {error, in_use} -> - rabbit_misc:protocol_error( - precondition_failed, "~s in use", [rabbit_misc:rs(ExchangeName)]); + precondition_failed("~s in use", [rabbit_misc:rs(ExchangeName)]); ok -> return_ok(State, NoWait, #'exchange.delete_ok'{}) end; @@ -980,11 +983,9 @@ handle_method(#'queue.delete'{queue = QueueNameBin, QueueName, ConnPid, fun (Q) -> rabbit_amqqueue:delete(Q, IfUnused, IfEmpty) end) of {error, in_use} -> - rabbit_misc:protocol_error( - precondition_failed, "~s in use", [rabbit_misc:rs(QueueName)]); + precondition_failed("~s in use", [rabbit_misc:rs(QueueName)]); {error, not_empty} -> - rabbit_misc:protocol_error( - precondition_failed, "~s not empty", [rabbit_misc:rs(QueueName)]); + precondition_failed("~s not empty", [rabbit_misc:rs(QueueName)]); {ok, PurgedMessageCount} -> return_ok(State, NoWait, #'queue.delete_ok'{message_count = PurgedMessageCount}) @@ -1019,15 +1020,13 @@ handle_method(#'queue.purge'{queue = QueueNameBin, #'queue.purge_ok'{message_count = PurgedMessageCount}); handle_method(#'tx.select'{}, _, #ch{confirm_enabled = true}) -> - rabbit_misc:protocol_error( - precondition_failed, "cannot switch from confirm to tx mode", []); + precondition_failed("cannot switch from confirm to tx mode"); handle_method(#'tx.select'{}, _, State) -> {reply, #'tx.select_ok'{}, State#ch{tx_status = in_progress}}; handle_method(#'tx.commit'{}, _, #ch{tx_status = none}) -> - rabbit_misc:protocol_error( - precondition_failed, "channel is not transactional", []); + precondition_failed("channel is not transactional"); handle_method(#'tx.commit'{}, _, State = #ch{uncommitted_message_q = TMQ, @@ -1041,8 +1040,7 @@ handle_method(#'tx.commit'{}, _, {noreply, maybe_complete_tx(new_tx(State1#ch{tx_status = committing}))}; handle_method(#'tx.rollback'{}, _, #ch{tx_status = none}) -> - rabbit_misc:protocol_error( - precondition_failed, "channel is not transactional", []); + precondition_failed("channel is not transactional"); handle_method(#'tx.rollback'{}, _, State = #ch{unacked_message_q = UAMQ, uncommitted_acks = TAL, @@ -1052,8 +1050,7 @@ handle_method(#'tx.rollback'{}, _, State = #ch{unacked_message_q = UAMQ, {reply, #'tx.rollback_ok'{}, new_tx(State#ch{unacked_message_q = UAMQ1})}; handle_method(#'confirm.select'{}, _, #ch{tx_status = in_progress}) -> - rabbit_misc:protocol_error( - precondition_failed, "cannot switch from tx to confirm mode", []); + precondition_failed("cannot switch from tx to confirm mode"); handle_method(#'confirm.select'{nowait = NoWait}, _, State) -> return_ok(State#ch{confirm_enabled = true}, @@ -1263,8 +1260,7 @@ collect_acks(ToAcc, PrefixAcc, Q, DeliveryTag, Multiple) -> QTail, DeliveryTag, Multiple) end; {empty, _} -> - rabbit_misc:protocol_error( - precondition_failed, "unknown delivery tag ~w", [DeliveryTag]) + precondition_failed("unknown delivery tag ~w", [DeliveryTag]) end. ack(Acked, State) -> -- cgit v1.2.1 From 3206dd9d996afa4e728e80beba6bd926b309bf21 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sun, 15 Jul 2012 16:20:34 +0100 Subject: call the correct function oops --- src/rabbit_channel.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 1bcdccee..864e100a 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -421,7 +421,7 @@ handle_exception(Reason, State = #ch{protocol = Protocol, precondition_failed(Format) -> precondition_failed(Format, []). precondition_failed(Format, Params) -> - rabbit_misc:amqp_error(precondition_failed, Format, Params). + rabbit_misc:protocol_error(precondition_failed, Format, Params). return_queue_declare_ok(#resource{name = ActualName}, NoWait, MessageCount, ConsumerCount, State) -> -- cgit v1.2.1 From 1bbe4398e5c3ead6c0fc585129040fc724e58701 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sun, 15 Jul 2012 16:46:23 +0100 Subject: more consistent error handling in reader --- src/rabbit_channel.erl | 18 ++--- src/rabbit_reader.erl | 215 +++++++++++++++++++++++++++---------------------- 2 files changed, 128 insertions(+), 105 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 864e100a..cdacc19e 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -405,17 +405,17 @@ handle_exception(Reason, State = #ch{protocol = Protocol, writer_pid = WriterPid, reader_pid = ReaderPid, conn_pid = ConnPid}) -> - {CloseChannel, CloseMethod} = - rabbit_binary_generator:map_exception(Channel, Reason, Protocol), - rabbit_log:error("connection ~p, channel ~p - error:~n~p~n", - [ConnPid, Channel, Reason]), %% something bad's happened: notify_queues may not be 'ok' {_Result, State1} = notify_queues(State), - case CloseChannel of - Channel -> ok = rabbit_writer:send_command(WriterPid, CloseMethod), - {noreply, State1}; - _ -> ReaderPid ! {channel_exit, Channel, Reason}, - {stop, normal, State1} + case rabbit_binary_generator:map_exception(Channel, Reason, Protocol) of + {Channel, CloseMethod} -> + rabbit_log:error("connection ~p, channel ~p - error:~n~p~n", + [ConnPid, Channel, Reason]), + ok = rabbit_writer:send_command(WriterPid, CloseMethod), + {noreply, State1}; + {0, _} -> + ReaderPid ! {channel_exit, Channel, Reason}, + {stop, normal, State1} end. precondition_failed(Format) -> precondition_failed(Format, []). diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 86255790..cacca151 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -173,6 +173,8 @@ server_capabilities(rabbit_framing_amqp_0_9_1) -> server_capabilities(_) -> []. +%%-------------------------------------------------------------------------- + log(Level, Fmt, Args) -> rabbit_log:log(connection, Level, Fmt, Args). inet_op(F) -> rabbit_misc:throw_on_error(inet_error, F). @@ -353,9 +355,9 @@ switch_callback(State, Callback, Length) -> State#v1{callback = Callback, recv_len = Length}. terminate(Explanation, State) when ?IS_RUNNING(State) -> - {normal, send_exception(State, 0, - rabbit_misc:amqp_error( - connection_forced, Explanation, [], none))}; + {normal, handle_exception(State, 0, + rabbit_misc:amqp_error( + connection_forced, Explanation, [], none))}; terminate(_Explanation, State) -> {force, State}. @@ -383,6 +385,9 @@ update_last_blocked_by(State = #v1{conserve_resources = true}) -> update_last_blocked_by(State = #v1{conserve_resources = false}) -> State#v1{last_blocked_by = flow}. +%%-------------------------------------------------------------------------- +%% error handling / termination + close_connection(State = #v1{queue_collector = Collector, connection = #connection{ timeout_sec = TimeoutSec}}) -> @@ -406,24 +411,10 @@ handle_dependent_exit(ChPid, Reason, State) -> {_Channel, controlled} -> maybe_close(control_throttle(State)); {Channel, uncontrolled} -> - log(error, "AMQP connection ~p, channel ~p - error:~n~p~n", - [self(), Channel, Reason]), maybe_close(handle_exception(control_throttle(State), Channel, Reason)) end. -channel_cleanup(ChPid) -> - case get({ch_pid, ChPid}) of - undefined -> undefined; - {Channel, MRef} -> credit_flow:peer_down(ChPid), - erase({channel, Channel}), - erase({ch_pid, ChPid}), - erlang:demonitor(MRef, [flush]), - Channel - end. - -all_channels() -> [ChPid || {{ch_pid, ChPid}, _ChannelMRef} <- get()]. - terminate_channels() -> NChannels = length([rabbit_channel:shutdown(ChPid) || ChPid <- all_channels()]), @@ -477,6 +468,80 @@ maybe_close(State) -> termination_kind(normal) -> controlled; termination_kind(_) -> uncontrolled. +handle_exception(State = #v1{connection_state = closed}, Channel, Reason) -> + log(error, "AMQP connection ~p (~p), channel ~p - error:~n~p~n", + [self(), closed, Channel, Reason]), + State; +handle_exception(State = #v1{connection = #connection{protocol = Protocol}, + connection_state = CS}, + Channel, Reason) + when ?IS_RUNNING(State) orelse CS =:= closing -> + log(error, "AMQP connection ~p (~p), channel ~p - error:~n~p~n", + [self(), CS, Channel, Reason]), + {0, CloseMethod} = + rabbit_binary_generator:map_exception(Channel, Reason, Protocol), + terminate_channels(), + State1 = close_connection(State), + ok = send_on_channel0(State1#v1.sock, CloseMethod, Protocol), + State1; +handle_exception(State, Channel, Reason) -> + %% We don't trust the client at this point - force them to wait + %% for a bit so they can't DOS us with repeated failed logins etc. + timer:sleep(?SILENT_CLOSE_DELAY * 1000), + throw({handshake_error, State#v1.connection_state, Channel, Reason}). + +frame_error(Error, Type, Channel, Payload, State) -> + {Str, Bin} = payload_snippet(Payload), + handle_exception(State, Channel, + rabbit_misc:amqp_error(frame_error, + "type ~p, ~s octets = ~p: ~p", + [Type, Str, Bin, Error], none)). + +unexpected_frame(Type, Channel, Payload, State) -> + {Str, Bin} = payload_snippet(Payload), + handle_exception(State, Channel, + rabbit_misc:amqp_error(unexpected_frame, + "type ~p, ~s octets = ~p", + [Type, Str, Bin], none)). + +payload_snippet(Payload) when size(Payload) =< 16 -> + {"all", Payload}; +payload_snippet(<>) -> + {"first 16", Snippet}. + +%%-------------------------------------------------------------------------- + +create_channel(Channel, State) -> + #v1{sock = Sock, queue_collector = Collector, + channel_sup_sup_pid = ChanSupSup, + connection = #connection{protocol = Protocol, + frame_max = FrameMax, + user = User, + vhost = VHost, + capabilities = Capabilities}} = State, + {ok, _ChSupPid, {ChPid, AState}} = + rabbit_channel_sup_sup:start_channel( + ChanSupSup, {tcp, Sock, Channel, FrameMax, self(), name(Sock), + Protocol, User, VHost, Capabilities, Collector}), + MRef = erlang:monitor(process, ChPid), + put({ch_pid, ChPid}, {Channel, MRef}), + put({channel, Channel}, {ChPid, AState}), + {ChPid, AState}. + +channel_cleanup(ChPid) -> + case get({ch_pid, ChPid}) of + undefined -> undefined; + {Channel, MRef} -> credit_flow:peer_down(ChPid), + erase({channel, Channel}), + erase({ch_pid, ChPid}), + erlang:demonitor(MRef, [flush]), + Channel + end. + +all_channels() -> [ChPid || {{ch_pid, ChPid}, _ChannelMRef} <- get()]. + +%%-------------------------------------------------------------------------- + handle_frame(Type, 0, Payload, State = #v1{connection_state = CS, connection = #connection{protocol = Protocol}}) @@ -492,34 +557,43 @@ handle_frame(_Type, _Channel, _Payload, State = #v1{connection_state = CS}) handle_frame(Type, 0, Payload, State = #v1{connection = #connection{protocol = Protocol}}) -> case rabbit_command_assembler:analyze_frame(Type, Payload, Protocol) of - error -> throw({unknown_frame, 0, Type, Payload}); + error -> frame_error(unknown_frame, Type, 0, Payload, State); heartbeat -> State; {method, MethodName, FieldsBin} -> handle_method0(MethodName, FieldsBin, State); - Other -> throw({unexpected_frame_on_channel0, Other}) + _Other -> unexpected_frame(Type, 0, Payload, State) end; handle_frame(Type, Channel, Payload, - State = #v1{connection = #connection{protocol = Protocol}}) -> + State = #v1{connection = #connection{protocol = Protocol}}) + when ?IS_RUNNING(State) -> case rabbit_command_assembler:analyze_frame(Type, Payload, Protocol) of - error -> throw({unknown_frame, Channel, Type, Payload}); - heartbeat -> throw({unexpected_heartbeat_frame, Channel}); - AnalyzedFrame -> process_frame(AnalyzedFrame, Channel, State) - end. + error -> frame_error(unknown_frame, Type, Channel, Payload, State); + heartbeat -> unexpected_frame(Type, Channel, Payload, State); + Frame -> process_frame(Frame, Channel, State) + end; +handle_frame(Type, Channel, Payload, State) -> + unexpected_frame(Type, Channel, Payload, State). process_frame(Frame, Channel, State) -> - case get({channel, Channel}) of - {ChPid, AState} -> - case process_channel_frame(Frame, ChPid, AState) of - {ok, NewAState} -> put({channel, Channel}, {ChPid, NewAState}), - post_process_frame(Frame, ChPid, State); - {error, Reason} -> handle_exception(State, Channel, Reason) - end; - undefined when ?IS_RUNNING(State) -> - ok = create_channel(Channel, State), - process_frame(Frame, Channel, State); - undefined -> - throw({channel_frame_while_starting, - Channel, State#v1.connection_state, Frame}) + {ChPid, AState} = case get({channel, Channel}) of + undefined -> create_channel(Channel, State); + Other -> Other + end, + case process_channel_frame(Frame, ChPid, AState) of + {ok, NewAState} -> put({channel, Channel}, {ChPid, NewAState}), + post_process_frame(Frame, ChPid, State); + {error, Reason} -> handle_exception(State, Channel, Reason) + end. + +process_channel_frame(Frame, ChPid, AState) -> + case rabbit_command_assembler:process(Frame, AState) of + {ok, NewAState} -> {ok, NewAState}; + {ok, Method, NewAState} -> rabbit_channel:do(ChPid, Method), + {ok, NewAState}; + {ok, Method, Content, NewAState} -> rabbit_channel:do_flow( + ChPid, Method, Content), + {ok, NewAState}; + {error, Reason} -> {error, Reason} end. post_process_frame({method, 'channel.close_ok', _}, ChPid, State) -> @@ -536,6 +610,8 @@ post_process_frame({method, MethodName, _}, _ChPid, post_process_frame(_Frame, _ChPid, State) -> control_throttle(State). +%%-------------------------------------------------------------------------- + handle_input(frame_header, <>, State) -> ensure_stats_timer( switch_callback(State, {frame_payload, Type, Channel, PayloadSize}, @@ -547,8 +623,9 @@ handle_input({frame_payload, Type, Channel, PayloadSize}, PayloadAndMarker, <> -> switch_callback(handle_frame(Type, Channel, Payload, State), frame_header, 7); - _ -> - throw({bad_payload, Type, Channel, PayloadSize, PayloadAndMarker}) + <> -> + frame_error({invalid_frame_end_marker, EndMarker}, + Type, Channel, Payload, State) end; %% The two rules pertaining to version negotiation: @@ -619,24 +696,14 @@ ensure_stats_timer(State) -> handle_method0(MethodName, FieldsBin, State = #v1{connection = #connection{protocol = Protocol}}) -> - HandleException = - fun(R) -> - case ?IS_RUNNING(State) of - true -> send_exception(State, 0, R); - %% We don't trust the client at this point - force - %% them to wait for a bit so they can't DOS us with - %% repeated failed logins etc. - false -> timer:sleep(?SILENT_CLOSE_DELAY * 1000), - throw({channel0_error, State#v1.connection_state, R}) - end - end, try handle_method0(Protocol:decode_method_fields(MethodName, FieldsBin), State) catch exit:#amqp_error{method = none} = Reason -> - HandleException(Reason#amqp_error{method = MethodName}); + handle_exception(State, 0, Reason#amqp_error{method = MethodName}); Type:Reason -> - HandleException({Type, Reason, MethodName, erlang:get_stacktrace()}) + Stack = erlang:get_stacktrace(), + handle_exception(State, 0, {Type, Reason, MethodName, Stack}) end. handle_method0(#'connection.start_ok'{mechanism = Mechanism, @@ -897,50 +964,6 @@ cert_info(F, Sock) -> {ok, Cert} -> list_to_binary(F(Cert)) end. -%%-------------------------------------------------------------------------- - -create_channel(Channel, State) -> - #v1{sock = Sock, queue_collector = Collector, - channel_sup_sup_pid = ChanSupSup, - connection = #connection{protocol = Protocol, - frame_max = FrameMax, - user = User, - vhost = VHost, - capabilities = Capabilities}} = State, - {ok, _ChSupPid, {ChPid, AState}} = - rabbit_channel_sup_sup:start_channel( - ChanSupSup, {tcp, Sock, Channel, FrameMax, self(), name(Sock), - Protocol, User, VHost, Capabilities, Collector}), - MRef = erlang:monitor(process, ChPid), - put({ch_pid, ChPid}, {Channel, MRef}), - put({channel, Channel}, {ChPid, AState}), - ok. - -process_channel_frame(Frame, ChPid, AState) -> - case rabbit_command_assembler:process(Frame, AState) of - {ok, NewAState} -> {ok, NewAState}; - {ok, Method, NewAState} -> rabbit_channel:do(ChPid, Method), - {ok, NewAState}; - {ok, Method, Content, NewAState} -> rabbit_channel:do_flow( - ChPid, Method, Content), - {ok, NewAState}; - {error, Reason} -> {error, Reason} - end. - -handle_exception(State = #v1{connection_state = closed}, _Channel, _Reason) -> - State; -handle_exception(State, Channel, Reason) -> - send_exception(State, Channel, Reason). - -send_exception(State = #v1{connection = #connection{protocol = Protocol}}, - Channel, Reason) -> - {0, CloseMethod} = - rabbit_binary_generator:map_exception(Channel, Reason, Protocol), - terminate_channels(), - State1 = close_connection(State), - ok = send_on_channel0(State1#v1.sock, CloseMethod, Protocol), - State1. - emit_stats(State) -> rabbit_event:notify(connection_stats, infos(?STATISTICS_KEYS, State)), rabbit_event:reset_stats_timer(State, #v1.stats_timer). -- cgit v1.2.1 From 5d878a6666046c4ae92efa65d86fd8859c295161 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sun, 15 Jul 2012 16:58:43 +0100 Subject: refactor: move things around in the reader ...for better grouping of functions --- src/rabbit_reader.erl | 121 +++++++++++++++++++++++++++----------------------- 1 file changed, 65 insertions(+), 56 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 86255790..ee3bf29a 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -173,6 +173,8 @@ server_capabilities(rabbit_framing_amqp_0_9_1) -> server_capabilities(_) -> []. +%%-------------------------------------------------------------------------- + log(Level, Fmt, Args) -> rabbit_log:log(connection, Level, Fmt, Args). inet_op(F) -> rabbit_misc:throw_on_error(inet_error, F). @@ -383,6 +385,9 @@ update_last_blocked_by(State = #v1{conserve_resources = true}) -> update_last_blocked_by(State = #v1{conserve_resources = false}) -> State#v1{last_blocked_by = flow}. +%%-------------------------------------------------------------------------- +%% error handling / termination + close_connection(State = #v1{queue_collector = Collector, connection = #connection{ timeout_sec = TimeoutSec}}) -> @@ -412,18 +417,6 @@ handle_dependent_exit(ChPid, Reason, State) -> Channel, Reason)) end. -channel_cleanup(ChPid) -> - case get({ch_pid, ChPid}) of - undefined -> undefined; - {Channel, MRef} -> credit_flow:peer_down(ChPid), - erase({channel, Channel}), - erase({ch_pid, ChPid}), - erlang:demonitor(MRef, [flush]), - Channel - end. - -all_channels() -> [ChPid || {{ch_pid, ChPid}, _ChannelMRef} <- get()]. - terminate_channels() -> NChannels = length([rabbit_channel:shutdown(ChPid) || ChPid <- all_channels()]), @@ -477,6 +470,53 @@ maybe_close(State) -> termination_kind(normal) -> controlled; termination_kind(_) -> uncontrolled. +handle_exception(State = #v1{connection_state = closed}, _Channel, _Reason) -> + State; +handle_exception(State, Channel, Reason) -> + send_exception(State, Channel, Reason). + +send_exception(State = #v1{connection = #connection{protocol = Protocol}}, + Channel, Reason) -> + {0, CloseMethod} = + rabbit_binary_generator:map_exception(Channel, Reason, Protocol), + terminate_channels(), + State1 = close_connection(State), + ok = send_on_channel0(State1#v1.sock, CloseMethod, Protocol), + State1. + +%%-------------------------------------------------------------------------- + +create_channel(Channel, State) -> + #v1{sock = Sock, queue_collector = Collector, + channel_sup_sup_pid = ChanSupSup, + connection = #connection{protocol = Protocol, + frame_max = FrameMax, + user = User, + vhost = VHost, + capabilities = Capabilities}} = State, + {ok, _ChSupPid, {ChPid, AState}} = + rabbit_channel_sup_sup:start_channel( + ChanSupSup, {tcp, Sock, Channel, FrameMax, self(), name(Sock), + Protocol, User, VHost, Capabilities, Collector}), + MRef = erlang:monitor(process, ChPid), + put({ch_pid, ChPid}, {Channel, MRef}), + put({channel, Channel}, {ChPid, AState}), + ok. + +channel_cleanup(ChPid) -> + case get({ch_pid, ChPid}) of + undefined -> undefined; + {Channel, MRef} -> credit_flow:peer_down(ChPid), + erase({channel, Channel}), + erase({ch_pid, ChPid}), + erlang:demonitor(MRef, [flush]), + Channel + end. + +all_channels() -> [ChPid || {{ch_pid, ChPid}, _ChannelMRef} <- get()]. + +%%-------------------------------------------------------------------------- + handle_frame(Type, 0, Payload, State = #v1{connection_state = CS, connection = #connection{protocol = Protocol}}) @@ -522,6 +562,17 @@ process_frame(Frame, Channel, State) -> Channel, State#v1.connection_state, Frame}) end. +process_channel_frame(Frame, ChPid, AState) -> + case rabbit_command_assembler:process(Frame, AState) of + {ok, NewAState} -> {ok, NewAState}; + {ok, Method, NewAState} -> rabbit_channel:do(ChPid, Method), + {ok, NewAState}; + {ok, Method, Content, NewAState} -> rabbit_channel:do_flow( + ChPid, Method, Content), + {ok, NewAState}; + {error, Reason} -> {error, Reason} + end. + post_process_frame({method, 'channel.close_ok', _}, ChPid, State) -> channel_cleanup(ChPid), control_throttle(State); @@ -536,6 +587,8 @@ post_process_frame({method, MethodName, _}, _ChPid, post_process_frame(_Frame, _ChPid, State) -> control_throttle(State). +%%-------------------------------------------------------------------------- + handle_input(frame_header, <>, State) -> ensure_stats_timer( switch_callback(State, {frame_payload, Type, Channel, PayloadSize}, @@ -897,50 +950,6 @@ cert_info(F, Sock) -> {ok, Cert} -> list_to_binary(F(Cert)) end. -%%-------------------------------------------------------------------------- - -create_channel(Channel, State) -> - #v1{sock = Sock, queue_collector = Collector, - channel_sup_sup_pid = ChanSupSup, - connection = #connection{protocol = Protocol, - frame_max = FrameMax, - user = User, - vhost = VHost, - capabilities = Capabilities}} = State, - {ok, _ChSupPid, {ChPid, AState}} = - rabbit_channel_sup_sup:start_channel( - ChanSupSup, {tcp, Sock, Channel, FrameMax, self(), name(Sock), - Protocol, User, VHost, Capabilities, Collector}), - MRef = erlang:monitor(process, ChPid), - put({ch_pid, ChPid}, {Channel, MRef}), - put({channel, Channel}, {ChPid, AState}), - ok. - -process_channel_frame(Frame, ChPid, AState) -> - case rabbit_command_assembler:process(Frame, AState) of - {ok, NewAState} -> {ok, NewAState}; - {ok, Method, NewAState} -> rabbit_channel:do(ChPid, Method), - {ok, NewAState}; - {ok, Method, Content, NewAState} -> rabbit_channel:do_flow( - ChPid, Method, Content), - {ok, NewAState}; - {error, Reason} -> {error, Reason} - end. - -handle_exception(State = #v1{connection_state = closed}, _Channel, _Reason) -> - State; -handle_exception(State, Channel, Reason) -> - send_exception(State, Channel, Reason). - -send_exception(State = #v1{connection = #connection{protocol = Protocol}}, - Channel, Reason) -> - {0, CloseMethod} = - rabbit_binary_generator:map_exception(Channel, Reason, Protocol), - terminate_channels(), - State1 = close_connection(State), - ok = send_on_channel0(State1#v1.sock, CloseMethod, Protocol), - State1. - emit_stats(State) -> rabbit_event:notify(connection_stats, infos(?STATISTICS_KEYS, State)), rabbit_event:reset_stats_timer(State, #v1.stats_timer). -- cgit v1.2.1 From 181dd7e5f5c4e78b0473062627bc8a1c8938fbbc Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sun, 15 Jul 2012 17:41:49 +0100 Subject: better epmd error descriptions in rabbitmqctl --- src/rabbit_misc.erl | 7 ++++++- src/rabbit_nodes.erl | 4 ++-- src/rabbit_prelaunch.erl | 6 +----- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index d41aa09b..1fefa688 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -36,7 +36,7 @@ -export([execute_mnesia_transaction/2]). -export([execute_mnesia_tx_with_tail/1]). -export([ensure_ok/2]). --export([tcp_name/3]). +-export([tcp_name/3, format_inet_error/1]). -export([upmap/2, map_in_order/2]). -export([table_filter/3]). -export([dirty_read_all/1, dirty_foreach_key/2, dirty_dump_log/1]). @@ -152,6 +152,7 @@ -spec(tcp_name/3 :: (atom(), inet:ip_address(), rabbit_networking:ip_port()) -> atom()). +-spec(format_inet_error/1 :: (atom()) -> string()). -spec(upmap/2 :: (fun ((A) -> B), [A]) -> [B]). -spec(map_in_order/2 :: (fun ((A) -> B), [A]) -> [B]). -spec(table_filter/3:: (fun ((A) -> boolean()), fun ((A, boolean()) -> 'ok'), @@ -510,6 +511,10 @@ tcp_name(Prefix, IPAddress, Port) list_to_atom( format("~w_~s:~w", [Prefix, inet_parse:ntoa(IPAddress), Port])). +format_inet_error(address) -> "cannot connect to host/port"; +format_inet_error(timeout) -> "timed out"; +format_inet_error(Error) -> inet:format_error(Error). + %% This is a modified version of Luke Gorrie's pmap - %% http://lukego.livejournal.com/6753.html - that doesn't care about %% the order in which results are received. diff --git a/src/rabbit_nodes.erl b/src/rabbit_nodes.erl index 1c23632d..c8d77b0f 100644 --- a/src/rabbit_nodes.erl +++ b/src/rabbit_nodes.erl @@ -70,8 +70,8 @@ diagnostics0() -> diagnostics_host(Host) -> case names(Host) of {error, EpmdReason} -> - {"- unable to connect to epmd on ~s: ~w", - [Host, EpmdReason]}; + {"- unable to connect to epmd on ~s: ~w (~s)", + [Host, EpmdReason, rabbit_misc:format_inet_error(EpmdReason)]}; {ok, NamePorts} -> {"- ~s: ~p", [Host, [{list_to_atom(Name), Port} || diff --git a/src/rabbit_prelaunch.erl b/src/rabbit_prelaunch.erl index d56211b5..b0454435 100644 --- a/src/rabbit_prelaunch.erl +++ b/src/rabbit_prelaunch.erl @@ -67,9 +67,5 @@ duplicate_node_check(NodeStr) -> {error, EpmdReason} -> rabbit_misc:quit("epmd error for host ~p: ~p (~s)~n", [NodeHost, EpmdReason, - case EpmdReason of - address -> "unable to establish tcp connection"; - timeout -> "timed out establishing tcp connection"; - _ -> inet:format_error(EpmdReason) - end]) + rabbit_misc:format_inet_error(EpmdReason)]) end. -- cgit v1.2.1 From b78427551f4a11ee1e83062711629ced04afdfbc Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 16 Jul 2012 08:28:52 +0100 Subject: refactor: remove gratuitous helper function --- src/rabbit_reader.erl | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index ee3bf29a..75246007 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -887,8 +887,8 @@ i(SockStat, #v1{sock = Sock}) when SockStat =:= recv_oct; SockStat =:= send_oct; SockStat =:= send_cnt; SockStat =:= send_pend -> - socket_info(fun () -> rabbit_net:getstat(Sock, [SockStat]) end, - fun ([{_, I}]) -> I end); + socket_info(fun (S) -> rabbit_net:getstat(S, [SockStat]) end, + fun ([{_, I}]) -> I end, Sock); i(state, #v1{connection_state = S}) -> S; i(last_blocked_by, #v1{last_blocked_by = By}) -> @@ -924,10 +924,7 @@ i(Item, #v1{}) -> throw({bad_argument, Item}). socket_info(Get, Select, Sock) -> - socket_info(fun() -> Get(Sock) end, Select). - -socket_info(Get, Select) -> - case Get() of + case Get(Sock) of {ok, T} -> Select(T); {error, _} -> '' end. -- cgit v1.2.1 From 3eacf1ca4f0497d1b14d3b54538b56525274033a Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 16 Jul 2012 10:38:31 +0100 Subject: oops - handle heartbeat timeouts properly in closed state --- src/rabbit_reader.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index bd5cf588..518021a4 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -311,7 +311,7 @@ handle_other(handshake_timeout, Deb, State) mainloop(Deb, State); handle_other(handshake_timeout, _Deb, State) -> throw({handshake_timeout, State#v1.callback}); -handle_other(timeout, Deb, State = #v1{connection_state = closed}) -> +handle_other(heartbeat_timeout, Deb, State = #v1{connection_state = closed}) -> mainloop(Deb, State); handle_other(heartbeat_timeout, _Deb, #v1{connection_state = S}) -> throw({heartbeat_timeout, S}); -- cgit v1.2.1 From 65c605d2fb4c1856ff6c4b1223bc8f8226450411 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 16 Jul 2012 12:00:33 +0100 Subject: make channel/soft errors easier to distinguish from hard errors --- src/rabbit_channel.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index cdacc19e..428f505c 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -409,7 +409,7 @@ handle_exception(Reason, State = #ch{protocol = Protocol, {_Result, State1} = notify_queues(State), case rabbit_binary_generator:map_exception(Channel, Reason, Protocol) of {Channel, CloseMethod} -> - rabbit_log:error("connection ~p, channel ~p - error:~n~p~n", + rabbit_log:error("connection ~p, channel ~p - soft error:~n~p~n", [ConnPid, Channel, Reason]), ok = rabbit_writer:send_command(WriterPid, CloseMethod), {noreply, State1}; -- cgit v1.2.1 From 28db75eea769575f4312dd1eea737f1b4667589c Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 16 Jul 2012 13:11:18 +0100 Subject: handle shutdown and file system cleanup gracefully --- check_xref | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/check_xref b/check_xref index 6eb89e15..8f65f3b1 100755 --- a/check_xref +++ b/check_xref @@ -34,14 +34,18 @@ main([PluginsDir|Argv]) -> false -> ok; true -> os:cmd("rm -rf " ++ LibDir) end, - - try - check(Cwd, PluginsDir, LibDir, checks()) - after - %% TODO: bootstrap file_handle_cache and use - %% rabbit_file:recursive_delete instead of this... - os:cmd("rm -rf " ++ LibDir) - end. + Rc = try + check(Cwd, PluginsDir, LibDir, checks()) + catch + _:Err -> + io:format(user, "failed: ~p~n", [Err]), + 1 + end, + shutdown(Rc, LibDir). + +shutdown(Rc, LibDir) -> + os:cmd("rm -rf " ++ LibDir), + erlang:halt(Rc). check(Cwd, PluginsDir, LibDir, Checks) -> {ok, Plugins} = file:list_dir(PluginsDir), @@ -220,8 +224,8 @@ report(Results) -> report(info, "Completed: ~p errors, ~p warnings~n", [length(Errors), length(Warnings)]), case length(Errors) > 0 of - true -> halt(1); - false -> halt(0) + true -> 1; + false -> 0 end. report_failures({analysis_error, {Mod, Reason}}) -> -- cgit v1.2.1 From 6c6b7640d096806bdfc940b6631e15367b17c75a Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 16 Jul 2012 14:11:35 +0100 Subject: simplifying refactor --- src/rabbit_reader.erl | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index cacca151..447fed74 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -617,15 +617,13 @@ handle_input(frame_header, <>, State) -> switch_callback(State, {frame_payload, Type, Channel, PayloadSize}, PayloadSize + 1)); -handle_input({frame_payload, Type, Channel, PayloadSize}, PayloadAndMarker, - State) -> - case PayloadAndMarker of - <> -> - switch_callback(handle_frame(Type, Channel, Payload, State), - frame_header, 7); - <> -> - frame_error({invalid_frame_end_marker, EndMarker}, - Type, Channel, Payload, State) +handle_input({frame_payload, Type, Channel, PayloadSize}, Data, State) -> + <> = Data, + case EndMarker of + ?FRAME_END -> State1 = handle_frame(Type, Channel, Payload, State), + switch_callback(State1, frame_header, 7); + _ -> frame_error({invalid_frame_end_marker, EndMarker}, + Type, Channel, Payload, State) end; %% The two rules pertaining to version negotiation: -- cgit v1.2.1 From b20e65292c966802b9d27df6c239731c7573b638 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 17 Jul 2012 11:26:03 +0100 Subject: allow clients to exceed frame size by a little bit --- src/rabbit_reader.erl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index ee9b22c2..477961ae 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -589,9 +589,14 @@ post_process_frame(_Frame, _ChPid, State) -> %%-------------------------------------------------------------------------- +%% We allow clients to exceed the frame size a little bit since quite +%% a few get it wrong - off-by 1 or 8 (empty frame size) are typical. +-define(FRAME_SIZE_FUDGE, ?EMPTY_FRAME_SIZE). + handle_input(frame_header, <>, #v1{connection = #connection{frame_max = FrameMax}}) - when FrameMax /= 0 andalso PayloadSize > FrameMax - ?EMPTY_FRAME_SIZE -> + when FrameMax /= 0 andalso + PayloadSize > FrameMax - ?EMPTY_FRAME_SIZE + ?FRAME_SIZE_FUDGE -> throw({frame_too_large, Type, Channel, PayloadSize, FrameMax - ?EMPTY_FRAME_SIZE}); handle_input(frame_header, <>, State) -> -- cgit v1.2.1 From e94024b08ccc68493f92d07b198bdce11f27de8b Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Tue, 17 Jul 2012 15:06:58 +0100 Subject: change naming from "termination" to "exit". also add `IS_' prefix to macro --- src/rabbit_amqqueue.erl | 2 +- src/rabbit_amqqueue_process.erl | 2 +- src/rabbit_channel.erl | 2 +- src/rabbit_misc.erl | 16 ++++++++-------- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index f601efb1..ff29a846 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -678,7 +678,7 @@ safe_delegate_call_ok(F, Pids) -> fun () -> F(Pid) end) end), case lists:filter(fun ({_Pid, {exit, {R, _}, _}}) -> - rabbit_misc:is_abnormal_termination(R); + rabbit_misc:is_abnormal_exit(R); ({_Pid, _}) -> false end, Bads) of diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 8933de87..388af413 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -788,7 +788,7 @@ handle_queue_down(QPid, Reason, State = #q{queue_monitors = QMons, unconfirmed = UC}) -> case pmon:is_monitored(QPid, QMons) of false -> noreply(State); - true -> case rabbit_misc:is_abnormal_termination(Reason) of + true -> case rabbit_misc:is_abnormal_exit(Reason) of true -> {Lost, _UC1} = dtree:take_all(QPid, UC), QNameS = rabbit_misc:rs(qname(State)), rabbit_log:warning("DLQ ~p for ~s died with " diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 22c6a223..73b461cd 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -1119,7 +1119,7 @@ monitor_delivering_queue(false, QPid, State = #ch{queue_monitors = QMons, delivering_queues = sets:add_element(QPid, DQ)}. handle_publishing_queue_down(QPid, Reason, State = #ch{unconfirmed = UC}) -> - case rabbit_misc:is_abnormal_termination(Reason) of + case rabbit_misc:is_abnormal_exit(Reason) of true -> {MXs, UC1} = dtree:take_all(QPid, UC), send_nacks(MXs, State#ch{unconfirmed = UC1}); false -> {MXs, UC1} = dtree:take(QPid, UC), diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 804d587b..2a51b58b 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -29,7 +29,7 @@ -export([enable_cover/1, report_cover/1]). -export([start_cover/1]). -export([confirm_to_sender/2]). --export([throw_on_error/2, with_exit_handler/2, is_abnormal_termination/1, +-export([throw_on_error/2, with_exit_handler/2, is_abnormal_exit/1, filter_exit_map/2]). -export([with_user/2, with_user_and_vhost/3]). -export([execute_mnesia_transaction/1]). @@ -62,7 +62,7 @@ -export([gb_sets_difference/2]). %% Horrible macro to use in guards --define(BENIGN_TERMINATION(R), +-define(IS_BENIGN_EXIT(R), R =:= noproc; R =:= noconnection; R =:= nodedown; R =:= normal; R =:= shutdown). @@ -142,7 +142,7 @@ -spec(throw_on_error/2 :: (atom(), thunk(rabbit_types:error(any()) | {ok, A} | A)) -> A). -spec(with_exit_handler/2 :: (thunk(A), thunk(A)) -> A). --spec(is_abnormal_termination/1 :: (any()) -> boolean()). +-spec(is_abnormal_exit/1 :: (any()) -> boolean()). -spec(filter_exit_map/2 :: (fun ((A) -> B), [A]) -> [B]). -spec(with_user/2 :: (rabbit_types:username(), thunk(A)) -> A). -spec(with_user_and_vhost/3 :: @@ -428,13 +428,13 @@ with_exit_handler(Handler, Thunk) -> try Thunk() catch - exit:{R, _} when ?BENIGN_TERMINATION(R) -> Handler(); - exit:{{R, _}, _} when ?BENIGN_TERMINATION(R) -> Handler() + exit:{R, _} when ?IS_BENIGN_EXIT(R) -> Handler(); + exit:{{R, _}, _} when ?IS_BENIGN_EXIT(R) -> Handler() end. -is_abnormal_termination(R) when ?BENIGN_TERMINATION(R) -> false; -is_abnormal_termination({R, _}) when ?BENIGN_TERMINATION(R) -> false; -is_abnormal_termination(_) -> true. +is_abnormal_exit(R) when ?IS_BENIGN_EXIT(R) -> false; +is_abnormal_exit({R, _}) when ?IS_BENIGN_EXIT(R) -> false; +is_abnormal_exit(_) -> true. filter_exit_map(F, L) -> Ref = make_ref(), -- cgit v1.2.1 From ee83e9203121a04922d9e14e99bd591bd4ae19aa Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 18 Jul 2012 12:32:40 +0100 Subject: All our log invocations should end in ~n --- src/rabbit_alarm.erl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/rabbit_alarm.erl b/src/rabbit_alarm.erl index 37d5f3f1..d9db0970 100644 --- a/src/rabbit_alarm.erl +++ b/src/rabbit_alarm.erl @@ -205,23 +205,23 @@ internal_register(Pid, {M, F, A} = HighMemMFA, State#alarms{alertees = NewAlertees}. handle_set_alarm({{resource_limit, Source, Node}, []}, State) -> - rabbit_log:warning("'~p' resource limit alarm set on node ~p", + rabbit_log:warning("'~p' resource limit alarm set on node ~p~n", [Source, Node]), {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"), + rabbit_log:warning("file descriptor limit alarm set~n"), {ok, State}; handle_set_alarm(Alarm, State) -> - rabbit_log:warning("alarm '~p' set", [Alarm]), + rabbit_log:warning("alarm '~p' set~n", [Alarm]), {ok, State}. handle_clear_alarm({resource_limit, Source, Node}, State) -> - rabbit_log:warning("'~p' resource limit alarm cleared on node ~p", + rabbit_log:warning("'~p' 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) -> - rabbit_log:warning("file descriptor limit alarm cleared"), + rabbit_log:warning("file descriptor limit alarm cleared~n"), {ok, State}; handle_clear_alarm(Alarm, State) -> - rabbit_log:warning("alarm '~p' cleared", [Alarm]), + rabbit_log:warning("alarm '~p' cleared~n", [Alarm]), {ok, State}. -- cgit v1.2.1 From b30b8382116f5e2643cc1375b8b4393edeaa73fe Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 18 Jul 2012 12:33:40 +0100 Subject: resource_limit alarms are always atoms, and we don't need quotes around them --- src/rabbit_alarm.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_alarm.erl b/src/rabbit_alarm.erl index d9db0970..e6625b2b 100644 --- a/src/rabbit_alarm.erl +++ b/src/rabbit_alarm.erl @@ -205,7 +205,7 @@ internal_register(Pid, {M, F, A} = HighMemMFA, State#alarms{alertees = NewAlertees}. handle_set_alarm({{resource_limit, Source, Node}, []}, State) -> - rabbit_log:warning("'~p' resource limit alarm set on node ~p~n", + rabbit_log:warning("~s resource limit alarm set on node ~p~n", [Source, Node]), {ok, maybe_alert(fun dict:append/3, Node, Source, State)}; handle_set_alarm({file_descriptor_limit, []}, State) -> @@ -216,7 +216,7 @@ handle_set_alarm(Alarm, State) -> {ok, State}. handle_clear_alarm({resource_limit, Source, Node}, State) -> - rabbit_log:warning("'~p' resource limit alarm cleared on node ~p~n", + 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) -> -- cgit v1.2.1 From 4390c4e4b3bd5c3f29300417865774a9a2c8b409 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 18 Jul 2012 12:41:02 +0100 Subject: Minor simplification --- src/file_handle_cache.erl | 4 +--- src/vm_memory_monitor.erl | 6 ++---- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/file_handle_cache.erl b/src/file_handle_cache.erl index 9a49cc6a..b2a036b5 100644 --- a/src/file_handle_cache.erl +++ b/src/file_handle_cache.erl @@ -270,7 +270,7 @@ %%---------------------------------------------------------------------------- start_link() -> - gen_server2:start_link({local, ?SERVER}, ?MODULE, [], [{timeout, infinity}]). + start_link(fun alarm_handler:set_alarm/1, fun alarm_handler:clear_alarm/1). start_link(AlarmSet, AlarmClear) -> gen_server2:start_link({local, ?SERVER}, ?MODULE, [AlarmSet, AlarmClear], @@ -812,8 +812,6 @@ i(Item, _) -> throw({bad_argument, Item}). %% gen_server2 callbacks %%---------------------------------------------------------------------------- -init([]) -> - init([fun alarm_handler:set_alarm/1, fun alarm_handler:clear_alarm/1]); init([AlarmSet, AlarmClear]) -> Limit = case application:get_env(file_handles_high_watermark) of {ok, Watermark} when (is_integer(Watermark) andalso diff --git a/src/vm_memory_monitor.erl b/src/vm_memory_monitor.erl index 535208af..df5f73e7 100644 --- a/src/vm_memory_monitor.erl +++ b/src/vm_memory_monitor.erl @@ -104,15 +104,13 @@ get_memory_limit() -> %%---------------------------------------------------------------------------- start_link(MemFraction) -> - gen_server:start_link({local, ?SERVER}, ?MODULE, [MemFraction], []). + start_link(MemFraction, + fun alarm_handler:set_alarm/1, fun alarm_handler:clear_alarm/1). start_link(MemFraction, AlarmSet, AlarmClear) -> gen_server:start_link({local, ?SERVER}, ?MODULE, [MemFraction, AlarmSet, AlarmClear], []). -init([MemFraction]) -> - init([MemFraction, fun alarm_handler:set_alarm/1, - fun alarm_handler:clear_alarm/1]); init([MemFraction, AlarmSet, AlarmClear]) -> TRef = start_timer(?DEFAULT_MEMORY_CHECK_INTERVAL), State = #state { timeout = ?DEFAULT_MEMORY_CHECK_INTERVAL, -- cgit v1.2.1 From af1c598bb76bced8af10157c34cb914864716129 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Wed, 18 Jul 2012 15:04:10 +0100 Subject: use %HOMEDIR%%HOMEPATH% instead of $PROFILE --- packaging/windows-exe/rabbitmq_nsi.in | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packaging/windows-exe/rabbitmq_nsi.in b/packaging/windows-exe/rabbitmq_nsi.in index 91510991..f5257040 100644 --- a/packaging/windows-exe/rabbitmq_nsi.in +++ b/packaging/windows-exe/rabbitmq_nsi.in @@ -101,7 +101,9 @@ Section "RabbitMQ Service" RabbitService ExpandEnvStrings $0 %COMSPEC% ExecWait '"$0" /C "$INSTDIR\rabbitmq_server-%%VERSION%%\sbin\rabbitmq-service.bat" install' ExecWait '"$0" /C "$INSTDIR\rabbitmq_server-%%VERSION%%\sbin\rabbitmq-service.bat" start' - CopyFiles "$WINDIR\.erlang.cookie" "$PROFILE\.erlang.cookie" + ReadEnvStr $1 "HOMEDRIVE" + ReadEnvStr $2 "HOMEPATH" + CopyFiles "$WINDIR\.erlang.cookie" "$1$2\.erlang.cookie" SectionEnd ;-------------------------------- @@ -234,4 +236,4 @@ Function findErlang System::Call 'Kernel32::SetEnvironmentVariableA(t, t) i("ERLANG_HOME", "$0").r0' ${EndIf} -FunctionEnd \ No newline at end of file +FunctionEnd -- cgit v1.2.1 From aed66d3566058ed5b6d152b538c415fff3241364 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 18 Jul 2012 17:46:15 +0100 Subject: use new frame_error function --- src/rabbit_reader.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 514069bc..ade6122f 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -617,11 +617,11 @@ post_process_frame(_Frame, _ChPid, State) -> -define(FRAME_SIZE_FUDGE, ?EMPTY_FRAME_SIZE). handle_input(frame_header, <>, - #v1{connection = #connection{frame_max = FrameMax}}) + State = #v1{connection = #connection{frame_max = FrameMax}}) when FrameMax /= 0 andalso PayloadSize > FrameMax - ?EMPTY_FRAME_SIZE + ?FRAME_SIZE_FUDGE -> - throw({frame_too_large, Type, Channel, PayloadSize, - FrameMax - ?EMPTY_FRAME_SIZE}); + frame_error({frame_too_large, PayloadSize, FrameMax - ?EMPTY_FRAME_SIZE}, + Type, Channel, <<>>, State); handle_input(frame_header, <>, State) -> ensure_stats_timer( switch_callback(State, {frame_payload, Type, Channel, PayloadSize}, -- cgit v1.2.1 From f117856de1d013363e4d7e9cd006cfd6129f6274 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 19 Jul 2012 10:54:32 +0100 Subject: cosmetic --- src/rabbit_exchange_type.erl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rabbit_exchange_type.erl b/src/rabbit_exchange_type.erl index e6470b72..9a793aab 100644 --- a/src/rabbit_exchange_type.erl +++ b/src/rabbit_exchange_type.erl @@ -54,13 +54,13 @@ %% called when comparing exchanges for equivalence - should return ok or %% exit with #amqp_error{} --callback assert_args_equivalence (rabbit_types:exchange(), - rabbit_framing:amqp_table()) -> +-callback assert_args_equivalence(rabbit_types:exchange(), + rabbit_framing:amqp_table()) -> 'ok' | rabbit_types:connection_exit(). %% called when the policy attached to this exchange changes. --callback policy_changed ( - serial(), rabbit_types:exchange(), rabbit_types:exchange()) -> 'ok'. +-callback policy_changed(serial(), rabbit_types:exchange(), + rabbit_types:exchange()) -> 'ok'. -else. -- cgit v1.2.1 From cdfc1b7b26f6a874ff24cedbc1d733057f33dfd7 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Thu, 19 Jul 2012 16:14:09 +0100 Subject: enable heartbeats with a 10 minutes interval by default I tested with 20k connections and the CPU usage is reasonable on my machine. --- ebin/rabbit_app.in | 1 + src/rabbit_reader.erl | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/ebin/rabbit_app.in b/ebin/rabbit_app.in index 523b54ce..087c62a9 100644 --- a/ebin/rabbit_app.in +++ b/ebin/rabbit_app.in @@ -25,6 +25,7 @@ %% 0 ("no limit") would make a better default, but that %% breaks the QPid Java client {frame_max, 131072}, + {heartbeat, 600}, {msg_store_file_size_limit, 16777216}, {queue_index_max_journal_entries, 262144}, {default_user, <<"guest">>}, diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index bd5cf588..61868cc1 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -740,6 +740,10 @@ server_frame_max() -> {ok, FrameMax} = application:get_env(rabbit, frame_max), FrameMax. +server_heartbeat() -> + {ok, Heartbeat} = application:get_env(rabbit, heartbeat), + Heartbeat. + send_on_channel0(Sock, Method, Protocol) -> ok = rabbit_writer:internal_send_command(Sock, 0, Method, Protocol). @@ -791,7 +795,7 @@ auth_phase(Response, {ok, User} -> Tune = #'connection.tune'{channel_max = 0, frame_max = server_frame_max(), - heartbeat = 0}, + heartbeat = server_heartbeat()}, ok = send_on_channel0(Sock, Tune, Protocol), State#v1{connection_state = tuning, connection = Connection#connection{user = User}} -- cgit v1.2.1 From 86c233de294e88788fd7e2804c337ab0ef6df8d7 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Fri, 20 Jul 2012 00:48:00 +0100 Subject: use rabbit_misc:is_process_alive/1 --- src/rabbit_amqqueue.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index afbaea65..0842dd49 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -599,7 +599,7 @@ on_node_down(Node) -> slave_pids = []} <- mnesia:table(rabbit_queue), node(Pid) == Node andalso - not is_process_alive(Pid)])), + not rabbit_misc:is_process_alive(Pid)])), {Qs, Dels} = lists:unzip(QsDels), T = rabbit_binding:process_deletions( lists:foldl(fun rabbit_binding:combine_deletions/2, -- cgit v1.2.1 From 3602c4a843b89f24c520a3f3d9d48e019c1d7c2d Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 20 Jul 2012 16:45:15 +0100 Subject: remove evil --- src/rabbit_control_main.erl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index b23088cc..cda12492 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -190,11 +190,11 @@ print_report(Node, {Descr, Module, InfoFun, KeysFun}, VHostArg) -> print_report0(Node, {Module, InfoFun, KeysFun}, VHostArg). print_report0(Node, {Module, InfoFun, KeysFun}, VHostArg) -> - case Results = rpc_call(Node, Module, InfoFun, VHostArg) of - [_|_] -> InfoItems = rpc_call(Node, Module, KeysFun, []), - display_row([atom_to_list(I) || I <- InfoItems]), - display_info_list(Results, InfoItems); - _ -> ok + case rpc_call(Node, Module, InfoFun, VHostArg) of + [_|_] = Results -> InfoItems = rpc_call(Node, Module, KeysFun, []), + display_row([atom_to_list(I) || I <- InfoItems]), + display_info_list(Results, InfoItems); + _ -> ok end, io:nl(). -- cgit v1.2.1 From 3647e1e59eda0c2b23e51d7e215bee99aaf876c1 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 20 Jul 2012 16:45:34 +0100 Subject: make 'rabbitmqctl report' respect '-q' sort of --- src/rabbit_control_main.erl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index cda12492..0dda32f1 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -432,14 +432,13 @@ action(list_parameters, Node, Args = [], _Opts, Inform) -> rabbit_runtime_parameters:info_keys()); action(report, Node, _Args, _Opts, Inform) -> - io:format("Reporting server status on ~p~n~n", [erlang:universaltime()]), + Inform("Reporting server status on ~p~n~n", [erlang:universaltime()]), [begin ok = action(Action, N, [], [], Inform), io:nl() end || N <- unsafe_rpc(Node, rabbit_mnesia, running_clustered_nodes, []), Action <- [status, cluster_status, environment]], VHosts = unsafe_rpc(Node, rabbit_vhost, list, []), [print_report(Node, Q) || Q <- ?GLOBAL_QUERIES], [print_report(Node, Q, [V]) || Q <- ?VHOST_QUERIES, V <- VHosts], - io:format("End of server status report~n"), ok; action(eval, Node, [Expr], _Opts, _Inform) -> -- cgit v1.2.1 From 0f6ba029bfef0650dce5625465d1914d47497c3b Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 23 Jul 2012 11:26:32 +0100 Subject: log stale master pids properly, remove duplicate stale pid handling from rmq_misc --- src/rabbit_mirror_queue_misc.erl | 47 +++++++------- src/rabbit_mirror_queue_slave.erl | 131 ++++++++++++++------------------------ 2 files changed, 70 insertions(+), 108 deletions(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index f434f524..10e1b92b 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -138,39 +138,36 @@ add_mirror(Queue, MirrorNode) -> [SPid] -> case rabbit_misc:is_process_alive(SPid) of true -> - %% TODO: this condition is silently ignored - should - %% we really be arriving at this state at all? {error,{queue_already_mirrored_on_node,MirrorNode}}; false -> - %% BUG-24942: we need to strip out this dead pid - %% now, so we do so directly - perhaps we ought - %% to start the txn sooner in order to get a more - %% coarse grained lock though.... - %% - %% BUG-24942: QUESTION - do we need to report that - %% something has changed (either via gm or via - %% the rabbit_event mechanisms) here? - Q1 = Q#amqqueue{ slave_pids = (SPids -- [SPid]) }, - rabbit_misc:execute_mnesia_transaction( - fun() -> - ok = rabbit_amqqueue:store_queue(Q1) - end), - start_child(Name, MirrorNode, Q1) + %% See BUG-24942: we have a stale pid from an old + %% incarnation of this node, because we've come + %% back online faster than the node_down handling + %% logic was able to deal with a death signal. We + %% shall replace the stale pid, and the slave start + %% logic handles this explicitly + start_child(Name, MirrorNode, Q) end end end). start_child(Name, MirrorNode, Q) -> case rabbit_mirror_queue_slave_sup:start_child(MirrorNode, [Q]) of - {ok, undefined} -> %% Already running - ok; - {ok, SPid} -> rabbit_log:info( - "Adding mirror of ~s on node ~p: ~p~n", - [rabbit_misc:rs(Name), MirrorNode, SPid]), - ok; - Other -> %% BUG-24942: should this not be checked for - %% error conditions or something? - Other + {ok, undefined} -> + %% NB: this means the mirror process was + %% already running on the given node. + ok; + {ok, SPid} -> + rabbit_log:info("Adding mirror of ~s on node ~p: ~p~n", + [rabbit_misc:rs(Name), MirrorNode, SPid]), + ok; + {error, {stale_master_pid, StalePid}} -> + rabbit_log:warning("Detected stale HA master while adding " + "mirror of ~s on node ~p: ~p~n", + [rabbit_misc:rs(Name), MirrorNode, StalePid]), + ok; + Other -> + Other end. if_mirrored_queue(Queue, Fun) -> diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 87fbfdfb..336ffa75 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -102,87 +102,52 @@ init(#amqqueue { name = QueueName } = Q) -> Self = self(), Node = node(), case rabbit_misc:execute_mnesia_transaction( - fun () -> - [Q1 = #amqqueue { pid = QPid, slave_pids = MPids }] = - mnesia:read({rabbit_queue, QueueName}), - case [Pid || Pid <- [QPid | MPids], node(Pid) =:= Node] of - [] -> - MPids1 = MPids ++ [Self], - ok = rabbit_amqqueue:store_queue( - Q1 #amqqueue { slave_pids = MPids1 }), - {new, QPid}; - [MPid] when MPid =:= QPid -> - case rabbit_misc:is_process_alive(MPid) of - true -> - %% Well damn, this shouldn't really happen - - %% what this appears to mean is that this - %% node is attempting to start a slave, but - %% a pid already exists for this node and - %% it is *already* the master! This state - %% probably requires a bit more thought. - existing; - false -> - %% We were the master, died, came back - %% online (but not after 3 days!) and - %% our death hasn't been registered by the - %% rest of our unbelieving flock yet! - %% - %% Actually, this is worse than it seems, - %% because the master is now down but the - %% pid in mnesia is from an old incarnation - %% of this node, so messages to it will be - %% silently dropped by Erlang (with some - %% helpful noise in the logs). - %% - %% I'm not sure how we're supposed to - %% recover from this. Won't messages get - %% lost, or at least lose their ordering - %% in this situation? Because a slave that - %% comes back online doesn't contain any - %% state (a slave will start off empty as - %% if they have no mirrored content at all) - %% then don't we fine ourselves in a slightly - %% inconsistent position here? - %% - %% In this scenario, I wonder whether we - %% should call init_with_backing_queue_state - %% to try and recover? - {stale, MPid} - end; - [SPid] -> - case rabbit_misc:is_process_alive(SPid) of - true -> - existing; - false -> - %% we need to replace this pid as it - %% is stale, from an old incarnation - %% of this node. - %% - %% NB: I *do* think we should completely - %% initialise this process before exiting - %% the mnesia txn in this case - once - %% we're *comitted* then I'd expect this - %% slave to become subject to any and all - %% invariants that members of the - %% slave_pids list should enforce, and - %% I'm *not* convinced this is possible - %% immediately after the txn commits - %% as the remaining initialisation code - %% could take arbitrary time to complete. - - MPids1 = (MPids -- [SPid]) ++ [Self], - ok = rabbit_amqqueue:store_queue( + fun () -> + [Q1 = #amqqueue { pid = QPid, slave_pids = MPids }] = + mnesia:read({rabbit_queue, QueueName}), + case [Pid || Pid <- [QPid | MPids], node(Pid) =:= Node] of + [] -> + MPids1 = MPids ++ [Self], + ok = rabbit_amqqueue:store_queue( + Q1 #amqqueue { slave_pids = MPids1 }), + {new, QPid}; + [MPid] when MPid =:= QPid -> + case rabbit_misc:is_process_alive(MPid) of + true -> + %% What this appears to mean is that this + %% node is attempting to start a slave, but + %% a pid already exists for this node and + %% it is *already* the master! + %% This should never happen, so we fail noisily + throw({invariant_failed, + {duplicate_live_master, Node}}); + false -> + %% See bug24942: we have detected a stale + %% master pid (from a previous incarnation + %% of this node) which hasn't been detected + %% via nodedown recovery. We cannot recover + %% it here, so we bail and log the error. + %% This does mean that this node is not a + %% well behaving member of the HA configuration + %% for this cluster and we have opened bug25074 + %% to address this situation explicitly. + {stale, MPid} + end; + [SPid] -> + case rabbit_misc:is_process_alive(SPid) of + true -> existing; + false -> %% See bug24942: we have detected a stale + %% slave pid (from a previous incarnation + %% of this node) which hasn't been detected + %% via nodedown recovery. + MPids1 = (MPids -- [SPid]) ++ [Self], + ok = rabbit_amqqueue:store_queue( Q1#amqqueue{slave_pids=MPids1}), - {new, QPid} - end - end - end) of + {new, QPid} + end + end + end) of {new, MPid} -> - %% BUG-24942: *should* we move the whole initialisation process (bar - %% obviously the trap_exit and erlang:monitor/2 calls) into the - %% mnesia transaction? We could optionally return {ready, State} - %% from it, and in that case immediately return.... - process_flag(trap_exit, true), %% amqqueue_process traps exits too. {ok, GM} = gm:start_link(QueueName, ?MODULE, [self()]), receive {joined, GM} -> @@ -218,10 +183,10 @@ init(#amqqueue { name = QueueName } = Q) -> {ok, State, hibernate, {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}; - {stale, OldPid} -> - %% NB: placeholder for doing something actually useful here... - %% such as {error, {stale_master_pid, OldPid}} - ignore; + {stale, StalePid} -> + %% we cannot proceed if the master is stale, therefore we + %% fail to start and allow the error to be logged + {stop, {stale_master_pid, StalePid}}; existing -> ignore end. -- cgit v1.2.1 From f861229bc15de394d903e036f0ae9afb73e65afe Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 23 Jul 2012 14:08:13 +0100 Subject: throw state invariants in the right place; cosmetic --- src/rabbit_amqqueue.erl | 1 - src/rabbit_mirror_queue_misc.erl | 38 +++++++++---------- src/rabbit_mirror_queue_slave.erl | 77 +++++++++++++++------------------------ 3 files changed, 47 insertions(+), 69 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index aa877b9d..8b82fbae 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -464,7 +464,6 @@ force_event_refresh() -> force_event_refresh(QNames) -> Qs = [Q || Q <- list(), lists:member(Q#amqqueue.name, QNames)], - %% BUG-24942/3: could one of these pids could be stale!? {_, Bad} = rabbit_misc:multi_call( [Q#amqqueue.pid || Q <- Qs], force_event_refresh), FailedPids = [Pid || {Pid, _Reason} <- Bad], diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 10e1b92b..2c1885d4 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -130,26 +130,22 @@ add_mirror(VHostPath, QueueName, MirrorNode) -> add_mirror(rabbit_misc:r(VHostPath, queue, QueueName), MirrorNode). add_mirror(Queue, MirrorNode) -> - if_mirrored_queue(Queue, - fun (#amqqueue { name = Name, pid = QPid, slave_pids = SPids } = Q) -> - case [Pid || Pid <- [QPid | SPids], node(Pid) =:= MirrorNode] of - [] -> - start_child(Name, MirrorNode, Q); - [SPid] -> - case rabbit_misc:is_process_alive(SPid) of - true -> - {error,{queue_already_mirrored_on_node,MirrorNode}}; - false -> - %% See BUG-24942: we have a stale pid from an old - %% incarnation of this node, because we've come - %% back online faster than the node_down handling - %% logic was able to deal with a death signal. We - %% shall replace the stale pid, and the slave start - %% logic handles this explicitly - start_child(Name, MirrorNode, Q) - end - end - end). + if_mirrored_queue( + Queue, + fun (#amqqueue { name = Name, pid = QPid, slave_pids = SPids } = Q) -> + case [Pid || Pid <- [QPid | SPids], node(Pid) =:= MirrorNode] of + [] -> + start_child(Name, MirrorNode, Q); + [SPid] -> + case rabbit_misc:is_process_alive(SPid) of + true -> + {error,{queue_already_mirrored_on_node, + MirrorNode}}; + false -> + start_child(Name, MirrorNode, Q) + end + end + end). start_child(Name, MirrorNode, Q) -> case rabbit_mirror_queue_slave_sup:start_child(MirrorNode, [Q]) of @@ -166,6 +162,8 @@ start_child(Name, MirrorNode, Q) -> "mirror of ~s on node ~p: ~p~n", [rabbit_misc:rs(Name), MirrorNode, StalePid]), ok; + {error, {duplicate_live_master, _}=Err} -> + throw(Err); Other -> Other end. diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 9722c53a..1df7d0dd 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -101,52 +101,10 @@ info(QPid) -> init(#amqqueue { name = QueueName } = Q) -> Self = self(), Node = node(), - case rabbit_misc:execute_mnesia_transaction( - fun () -> - [Q1 = #amqqueue { pid = QPid, slave_pids = MPids }] = - mnesia:read({rabbit_queue, QueueName}), - case [Pid || Pid <- [QPid | MPids], node(Pid) =:= Node] of - [] -> - MPids1 = MPids ++ [Self], - ok = rabbit_amqqueue:store_queue( - Q1 #amqqueue { slave_pids = MPids1 }), - {new, QPid}; - [MPid] when MPid =:= QPid -> - case rabbit_misc:is_process_alive(MPid) of - true -> - %% What this appears to mean is that this - %% node is attempting to start a slave, but - %% a pid already exists for this node and - %% it is *already* the master! - %% This should never happen, so we fail noisily - throw({invariant_failed, - {duplicate_live_master, Node}}); - false -> - %% See bug24942: we have detected a stale - %% master pid (from a previous incarnation - %% of this node) which hasn't been detected - %% via nodedown recovery. We cannot recover - %% it here, so we bail and log the error. - %% This does mean that this node is not a - %% well behaving member of the HA configuration - %% for this cluster and we have opened bug25074 - %% to address this situation explicitly. - {stale, MPid} - end; - [SPid] -> - case rabbit_misc:is_process_alive(SPid) of - true -> existing; - false -> %% See bug24942: we have detected a stale - %% slave pid (from a previous incarnation - %% of this node) which hasn't been detected - %% via nodedown recovery. - MPids1 = (MPids -- [SPid]) ++ [Self], - ok = rabbit_amqqueue:store_queue( - Q1#amqqueue{slave_pids=MPids1}), - {new, QPid} - end - end - end) of + case rabbit_misc:execute_mnesia_transaction(fun() -> + init_it(Self, Node, + QueueName) + end) of {new, MPid} -> process_flag(trap_exit, true), %% amqqueue_process traps exits too. {ok, GM} = gm:start_link(QueueName, ?MODULE, [self()]), @@ -184,13 +142,36 @@ init(#amqqueue { name = QueueName } = Q) -> {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}; {stale, StalePid} -> - %% we cannot proceed if the master is stale, therefore we - %% fail to start and allow the error to be logged {stop, {stale_master_pid, StalePid}}; + duplicate_master -> + {stop, {duplicate_live_master, Node}}; existing -> ignore end. +init_it(Self, Node, QueueName) -> + [Q1 = #amqqueue { pid = QPid, slave_pids = MPids }] = + mnesia:read({rabbit_queue, QueueName}), + case [Pid || Pid <- [QPid | MPids], node(Pid) =:= Node] of + [] -> + MPids1 = MPids ++ [Self], + ok = rabbit_amqqueue:store_queue(Q1#amqqueue{slave_pids=MPids1}), + {new, QPid}; + [MPid] when MPid =:= QPid -> + case rabbit_misc:is_process_alive(MPid) of + true -> duplicate_master; + false -> {stale, MPid} + end; + [SPid] -> + case rabbit_misc:is_process_alive(SPid) of + true -> existing; + false -> MPids1 = (MPids -- [SPid]) ++ [Self], + ok = rabbit_amqqueue:store_queue( + Q1#amqqueue{ slave_pids = MPids1 }), + {new, QPid} + end + end. + handle_call({deliver, Delivery = #delivery { immediate = true }}, From, State) -> %% It is safe to reply 'false' here even if a) we've not seen the -- cgit v1.2.1 From cb667f2eb98030f599f1eb14b2c55761aa940229 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Mon, 23 Jul 2012 16:09:22 +0100 Subject: cosmetics --- src/rabbit_mirror_queue_misc.erl | 2 +- src/rabbit_upgrade_functions.erl | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 83ecd4bf..20026e60 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -60,7 +60,7 @@ remove_from_queue(QueueName, DeadPids) -> case mnesia:read({rabbit_queue, QueueName}) of [] -> {error, not_found}; [Q = #amqqueue { pid = QPid, - slave_pids = SPids}] -> + slave_pids = SPids }] -> [QPid1 | SPids1] = Alive = [Pid || Pid <- [QPid | SPids], not lists:member(node(Pid), DeadNodes)], diff --git a/src/rabbit_upgrade_functions.erl b/src/rabbit_upgrade_functions.erl index d9438d5f..47b22b98 100644 --- a/src/rabbit_upgrade_functions.erl +++ b/src/rabbit_upgrade_functions.erl @@ -64,6 +64,7 @@ -spec(runtime_parameters/0 :: () -> 'ok'). -spec(policy/0 :: () -> 'ok'). -spec(sync_slave_pids/0 :: () -> 'ok'). + -endif. %%-------------------------------------------------------------------- -- cgit v1.2.1 From 598e7e24fbd6529f71e33f48cdbecc5b437c050e Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 23 Jul 2012 16:26:52 +0100 Subject: attempt to handle races between remove_from_queue and rmq_slave:init/1 --- src/rabbit_amqqueue.erl | 1 - src/rabbit_mirror_queue_misc.erl | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 8b82fbae..d82ac266 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -594,7 +594,6 @@ set_maximum_since_use(QPid, Age) -> on_node_down(Node) -> rabbit_misc:execute_mnesia_tx_with_tail( fun () -> QsDels = - %% BUG-24942/3: could one of these pids could be stale!? qlc:e(qlc:q([{{QName, Pid}, delete_queue(QName)} || #amqqueue{name = QName, pid = Pid, slave_pids = []} diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 2c1885d4..31d85561 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -62,7 +62,9 @@ remove_from_queue(QueueName, DeadPids) -> slave_pids = SPids }] -> [QPid1 | SPids1] = Alive = [Pid || Pid <- [QPid | SPids], - not lists:member(node(Pid), DeadNodes)], + not lists:member(node(Pid), + DeadNodes) orelse + rabbit_misc:is_process_alive(Pid)], case {{QPid, SPids}, {QPid1, SPids1}} of {Same, Same} -> {ok, QPid1, []}; -- cgit v1.2.1 From 9bdbbb95b85dd9d3a1a5bf501e96e172ae3899e0 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Wed, 25 Jul 2012 09:49:08 +0100 Subject: match on supervisor:start_child/2 return values properly --- src/rabbit_mirror_queue_misc.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 31d85561..42e1516a 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -159,12 +159,12 @@ start_child(Name, MirrorNode, Q) -> rabbit_log:info("Adding mirror of ~s on node ~p: ~p~n", [rabbit_misc:rs(Name), MirrorNode, SPid]), ok; - {error, {stale_master_pid, StalePid}} -> + {error, {{stale_master_pid, StalePid}, _}} -> rabbit_log:warning("Detected stale HA master while adding " "mirror of ~s on node ~p: ~p~n", [rabbit_misc:rs(Name), MirrorNode, StalePid]), ok; - {error, {duplicate_live_master, _}=Err} -> + {error, {{duplicate_live_master, _}=Err, _}} -> throw(Err); Other -> Other -- cgit v1.2.1 From 01299fadcab0a96356d1c5c0251e63963acfed8b Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Wed, 25 Jul 2012 09:50:48 +0100 Subject: simplify match on master pids --- src/rabbit_mirror_queue_slave.erl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 1df7d0dd..c4ae307c 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -143,7 +143,7 @@ init(#amqqueue { name = QueueName } = Q) -> ?DESIRED_HIBERNATE}}; {stale, StalePid} -> {stop, {stale_master_pid, StalePid}}; - duplicate_master -> + duplicate_live_master -> {stop, {duplicate_live_master, Node}}; existing -> ignore @@ -157,10 +157,10 @@ init_it(Self, Node, QueueName) -> MPids1 = MPids ++ [Self], ok = rabbit_amqqueue:store_queue(Q1#amqqueue{slave_pids=MPids1}), {new, QPid}; - [MPid] when MPid =:= QPid -> - case rabbit_misc:is_process_alive(MPid) of - true -> duplicate_master; - false -> {stale, MPid} + [QPid] -> + case rabbit_misc:is_process_alive(QPid) of + true -> duplicate_live_master; + false -> {stale, QPid} end; [SPid] -> case rabbit_misc:is_process_alive(SPid) of -- cgit v1.2.1 From 7c0fbe7939baa5347f225140d3eb767f140efbf5 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Wed, 25 Jul 2012 09:52:15 +0100 Subject: NB: is superfluous in comments --- src/rabbit_mirror_queue_misc.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 42e1516a..ba62a734 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -152,7 +152,7 @@ add_mirror(Queue, MirrorNode) -> start_child(Name, MirrorNode, Q) -> case rabbit_mirror_queue_slave_sup:start_child(MirrorNode, [Q]) of {ok, undefined} -> - %% NB: this means the mirror process was + %% this means the mirror process was %% already running on the given node. ok; {ok, SPid} -> -- cgit v1.2.1 From ad95be31ee90762658e8867a55d5b1f4b69dfd2c Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Wed, 25 Jul 2012 18:18:08 +0100 Subject: add a first draft for the rabbitmqctl documentation Also, accept disk as well as disc in `change_cluster_node_type'. --- docs/rabbitmqctl.1.xml | 192 +++++++++++++++++++++++++++++++------------- src/rabbit_control_main.erl | 3 +- 2 files changed, 138 insertions(+), 57 deletions(-) diff --git a/docs/rabbitmqctl.1.xml b/docs/rabbitmqctl.1.xml index 2d25edee..065de14c 100644 --- a/docs/rabbitmqctl.1.xml +++ b/docs/rabbitmqctl.1.xml @@ -288,105 +288,185 @@ Cluster management - - cluster clusternode ... + + join_cluster clusternode--ram clusternode - Subset of the nodes of the cluster to which this node should be connected. + Node to cluster with. + + + --ram + + + If provided, the node will join the cluster as a ram node. + + - Instruct the node to become member of a cluster with the - specified nodes. To cluster with currently offline nodes, - use force_cluster. + Instruct the node to become member of the cluster that the + specified node is in. Before clustering, the node is reset, so be + careful when using this command. For this command to succeed the + RabbitMQ application must have been stopped, e.g. with stop_app. Cluster nodes can be of two types: disk or ram. Disk nodes - replicate data in ram and on disk, thus providing - redundancy in the event of node failure and recovery from - global events such as power failure across all nodes. Ram - nodes replicate data in ram only and are mainly used for - scalability. A cluster must always have at least one disk node. + replicate data in ram and on disk, thus providing redundancy in + the event of node failure and recovery from global events such as + power failure across all nodes. Ram nodes replicate data in ram + only and are mainly used for scalability. A cluster must always + have at least one disk node. - If the current node is to become a disk node it needs to - appear in the cluster node list. Otherwise it becomes a - ram node. If the node list is empty or only contains the - current node then the node becomes a standalone, - i.e. non-clustered, (disk) node. + The node will be a disk node by default. If you wish to wish to + create a ram node, provide the --ram flag. After executing the cluster command, whenever - the RabbitMQ application is started on the current node it - will attempt to connect to the specified nodes, thus - becoming an active node in the cluster comprising those - nodes (and possibly others). + the RabbitMQ application is started on the current node it will + attempt to connect to the nodes that were in the cluster when the + node went down. - The list of nodes does not have to contain all the - cluster's nodes; a subset is sufficient. Also, clustering - generally succeeds as long as at least one of the - specified nodes is active. Hence adjustments to the list - are only necessary if the cluster configuration is to be - altered radically. + To leave a cluster, you can simply reset the + node. You can also remove nodes remotely with the + remove_cluster_node command. - For this command to succeed the RabbitMQ application must - have been stopped, e.g. with stop_app. Furthermore, - turning a standalone node into a clustered node requires - the node be reset first, - in order to avoid accidental destruction of data with the - cluster command. + For more details see the clustering + guide. + + For example: + rabbitmqctl cluster hare@elena --ram + + This command instructs the RabbitMQ node to join the cluster that + hare@elena is part of, as a ram node. + + + + cluster_status + - For more details see the clustering guide. + Displays all the nodes in the cluster grouped by node type, + together with the currently running nodes. For example: - rabbitmqctl cluster rabbit@tanto hare@elena + rabbitmqctl cluster_status - This command instructs the RabbitMQ node to join the - cluster with nodes rabbit@tanto and - hare@elena. If the node is one of these then - it becomes a disk node, otherwise a ram node. + This command displays the nodes in the cluster. - - force_cluster clusternode ... + + + + + + change_cluster_node_type + + disk | ram + + + + + + Changes the type of the cluster node. The node must be stopped for + this operation to succeed, and when turning a node into a ram node + the node must not be the only disk node in the cluster. + + For example: + rabbitmqctl change_cluster_node_type disk + + This command displays will turn a ram node into a disk node + (provided that other disk nodes exist in the cluster). + + + + + + + + + remove_cluster_node + --offline + + - clusternode - Subset of the nodes of the cluster to which this node should be connected. + --offline + + + Enables node removal from an offline node. This is only + useful in the situation where all the nodes are offline and + the last node to go down cannot be brought online, thus + preventing the whole cluster to start. It should not be used + in any other circumstances since it can lead to + inconsistencies. + + - Instruct the node to become member of a cluster with the - specified nodes. This will succeed even if the specified nodes - are offline. For a more detailed description, see - cluster. + Removes a cluster node remotely. The node that is being removed + must be online, while the node we are removing from must be + online, except when using the --offline flag. - - Note that this variant of the cluster command just - ignores the current status of the specified nodes. - Clustering may still fail for a variety of other - reasons. + For example: + rabbitmqctl -n hare@mcnulty remove_cluster_node rabbit@stringer + + This command will remove the node + rabbit@stringer from the node + hare@mcnulty. + + - cluster_status + + + recluster + clusternode + + + + + clusternode + + + The node to recluster with. + + + + - Displays all the nodes in the cluster grouped by node type, - together with the currently running nodes. + Instructs an already clustered node to contact + clusternode to cluster when waking up. This is + different from join_cluster since it does not + join any cluster - it checks that the node is already in a cluster + with clusternode. + + + The need for this command is motivated by the fact that clusters + can change while a node is offline. Consider the situation in + which node A and B are clustered. A goes down, C clusters with C, + and then B leaves the cluster. When A wakes up, it'll try to + contact B, but this will fail since B is not in the cluster + anymore. recluster -n A C will solve this + situation. For example: - rabbitmqctl cluster_status + rabbitmqctl change_cluster_node_type disk - This command displays the nodes in the cluster. + This command displays will turn a ram node into a disk node + (provided that other disk nodes exist in the cluster). diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index f3000f17..ebfc5636 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -254,7 +254,8 @@ action(join_cluster, Node, [ClusterNodeS], Opts, Inform) -> action(change_cluster_node_type, Node, ["ram"], _Opts, Inform) -> Inform("Turning ~p into a ram node", [Node]), rpc_call(Node, rabbit_mnesia, change_cluster_node_type, [ram]); -action(change_cluster_node_type, Node, ["disc"], _Opts, Inform) -> +action(change_cluster_node_type, Node, [Type], _Opts, Inform) + when Type =:= "disc" orelse Type =:= "disk" -> Inform("Turning ~p into a disc node", [Node]), rpc_call(Node, rabbit_mnesia, change_cluster_node_type, [disc]); -- cgit v1.2.1 From 81844176be7eaaeb1beb3bdee94b9c507d14d7c6 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Fri, 27 Jul 2012 13:10:11 +0100 Subject: remove unused variable --- src/rabbit_mirror_queue_slave.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 5197dce7..56581b17 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -911,7 +911,7 @@ maybe_store_ack(true, MsgId, AckTag, State = #state { msg_id_ack = MA, %% We intentionally leave out the head where a slave becomes %% unsynchronised: we assert that can never happen. -set_synchronised(true, State = #state { q = Q = #amqqueue { name = QName }, +set_synchronised(true, State = #state { q = #amqqueue { name = QName }, synchronised = false }) -> Self = self(), rabbit_misc:execute_mnesia_transaction( -- cgit v1.2.1 From f620eb3dfe99ee83e24477a5fef1dda490ff286b Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Fri, 27 Jul 2012 15:17:32 +0100 Subject: `store_updated_slaves/1' returns a queue, not `ok' --- src/rabbit_mirror_queue_misc.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 20026e60..3a7df803 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -37,7 +37,8 @@ -spec(add_mirror/3 :: (rabbit_types:vhost(), binary(), atom()) -> rabbit_types:ok_or_error(any())). --spec(store_updated_slaves/1 :: (rabbit_types:amqqueue()) -> 'ok'). +-spec(store_updated_slaves/1 :: (rabbit_types:amqqueue()) -> + rabbit_types:amqqueue()). -endif. -- cgit v1.2.1 From 4b41d7ffc8c08af238cee30483a230918e976dd8 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 30 Jul 2012 11:14:15 +0100 Subject: cosmetic --- src/rabbit_direct.erl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/rabbit_direct.erl b/src/rabbit_direct.erl index c07ad832..c87b1dc1 100644 --- a/src/rabbit_direct.erl +++ b/src/rabbit_direct.erl @@ -40,7 +40,6 @@ (rabbit_channel:channel_number(), pid(), pid(), string(), rabbit_types:protocol(), rabbit_types:user(), rabbit_types:vhost(), rabbit_framing:amqp_table(), pid()) -> {'ok', pid()}). - -spec(disconnect/2 :: (pid(), rabbit_event:event_props()) -> 'ok'). -endif. -- cgit v1.2.1 From 3a4a2f1a6166a8b70c6086716a29a665bf6b4e5e Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 2 Aug 2012 23:36:53 +0100 Subject: inlining refactor --- src/vm_memory_monitor.erl | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/vm_memory_monitor.erl b/src/vm_memory_monitor.erl index df5f73e7..c254c30d 100644 --- a/src/vm_memory_monitor.erl +++ b/src/vm_memory_monitor.erl @@ -179,7 +179,7 @@ set_mem_limits(State, MemFraction) -> ?MEMORY_SIZE_FOR_UNKNOWN_OS; M -> M end, - MemLim = get_mem_limit(MemFraction, TotalMemory), + MemLim = trunc(MemFraction * lists:min([TotalMemory, get_vm_limit()])), error_logger:info_msg("Memory limit set to ~pMB of ~pMB total.~n", [trunc(MemLim/?ONE_MB), trunc(TotalMemory/?ONE_MB)]), internal_update(State #state { total_memory = TotalMemory, @@ -229,10 +229,6 @@ get_vm_limit(_OsType) -> %%http://en.wikipedia.org/wiki/X86-64#Virtual_address_space_details end. -get_mem_limit(MemFraction, TotalMemory) -> - AvMem = lists:min([TotalMemory, get_vm_limit()]), - trunc(AvMem * MemFraction). - %%---------------------------------------------------------------------------- %% Internal Helpers %%---------------------------------------------------------------------------- -- cgit v1.2.1 From c49e614d88b502dcc1d413314f9fbb1f1850dbf1 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 2 Aug 2012 23:53:56 +0100 Subject: trim fat - don't log the setting of the memory hwm twice it already gets logged by set_mem_limits --- src/vm_memory_monitor.erl | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/vm_memory_monitor.erl b/src/vm_memory_monitor.erl index c254c30d..722d66ba 100644 --- a/src/vm_memory_monitor.erl +++ b/src/vm_memory_monitor.erl @@ -124,10 +124,7 @@ handle_call(get_vm_memory_high_watermark, _From, State) -> {reply, State#state.memory_limit / State#state.total_memory, State}; handle_call({set_vm_memory_high_watermark, MemFraction}, _From, State) -> - State1 = set_mem_limits(State, MemFraction), - error_logger:info_msg("Memory alarm changed to ~p, ~p bytes.~n", - [MemFraction, State1#state.memory_limit]), - {reply, ok, State1}; + {reply, ok, set_mem_limits(State, MemFraction)}; handle_call(get_check_interval, _From, State) -> {reply, State#state.timeout, State}; -- cgit v1.2.1 From 6674039eea2371cac28972bea9f920e1aa1580b3 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 3 Aug 2012 00:06:00 +0100 Subject: cosmetic --- src/vm_memory_monitor.erl | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/src/vm_memory_monitor.erl b/src/vm_memory_monitor.erl index 722d66ba..7c191684 100644 --- a/src/vm_memory_monitor.erl +++ b/src/vm_memory_monitor.erl @@ -77,11 +77,9 @@ %% Public API %%---------------------------------------------------------------------------- -get_total_memory() -> - get_total_memory(os:type()). +get_total_memory() -> get_total_memory(os:type()). -get_vm_limit() -> - get_vm_limit(os:type()). +get_vm_limit() -> get_vm_limit(os:type()). get_check_interval() -> gen_server:call(?MODULE, get_check_interval, infinity). @@ -189,21 +187,18 @@ internal_update(State = #state { memory_limit = MemLimit, MemUsed = erlang:memory(total), NewAlarmed = MemUsed > MemLimit, case {Alarmed, NewAlarmed} of - {false, true} -> - emit_update_info(set, MemUsed, MemLimit), - AlarmSet({{resource_limit, memory, node()}, []}); - {true, false} -> - emit_update_info(clear, MemUsed, MemLimit), - AlarmClear({resource_limit, memory, node()}); - _ -> - ok + {false, true} -> emit_update_info(set, MemUsed, MemLimit), + AlarmSet({{resource_limit, memory, node()}, []}); + {true, false} -> emit_update_info(clear, MemUsed, MemLimit), + AlarmClear({resource_limit, memory, node()}); + _ -> ok end, State #state {alarmed = NewAlarmed}. -emit_update_info(State, MemUsed, MemLimit) -> +emit_update_info(AlarmState, MemUsed, MemLimit) -> error_logger:info_msg( "vm_memory_high_watermark ~p. Memory used:~p allowed:~p~n", - [State, MemUsed, MemLimit]). + [AlarmState, MemUsed, MemLimit]). start_timer(Timeout) -> {ok, TRef} = timer:send_interval(Timeout, update), -- cgit v1.2.1 From a2632aeabd14a43051601cdb4b9d2f3bc90e3dd5 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 3 Aug 2012 00:06:40 +0100 Subject: make the comment match the code --- src/vm_memory_monitor.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vm_memory_monitor.erl b/src/vm_memory_monitor.erl index 7c191684..f95a44a0 100644 --- a/src/vm_memory_monitor.erl +++ b/src/vm_memory_monitor.erl @@ -212,7 +212,7 @@ get_vm_limit({win32,_OSname}) -> 8 -> 8*1024*1024*1024*1024 %% 8 TB for 64 bits 2^42 end; -%% On a 32-bit machine, if you're using more than 2 gigs of RAM you're +%% On a 32-bit machine, if you're using more than 4 gigs of RAM you're %% in big trouble anyway. get_vm_limit(_OsType) -> case erlang:system_info(wordsize) of -- cgit v1.2.1 From 31cefc007ba67b4bd5ce4ce9f87aa3d7eecd546d Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 3 Aug 2012 00:09:29 +0100 Subject: tiny refactor --- src/vm_memory_monitor.erl | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/vm_memory_monitor.erl b/src/vm_memory_monitor.erl index f95a44a0..7cba36f8 100644 --- a/src/vm_memory_monitor.erl +++ b/src/vm_memory_monitor.erl @@ -52,8 +52,7 @@ timeout, timer, alarmed, - alarm_set, - alarm_clear + alarm_funs }). %%---------------------------------------------------------------------------- @@ -107,15 +106,14 @@ start_link(MemFraction) -> start_link(MemFraction, AlarmSet, AlarmClear) -> gen_server:start_link({local, ?SERVER}, ?MODULE, - [MemFraction, AlarmSet, AlarmClear], []). + [MemFraction, {AlarmSet, AlarmClear}], []). -init([MemFraction, AlarmSet, AlarmClear]) -> +init([MemFraction, AlarmFuns]) -> TRef = start_timer(?DEFAULT_MEMORY_CHECK_INTERVAL), - State = #state { timeout = ?DEFAULT_MEMORY_CHECK_INTERVAL, - timer = TRef, - alarmed = false, - alarm_set = AlarmSet, - alarm_clear = AlarmClear }, + State = #state { timeout = ?DEFAULT_MEMORY_CHECK_INTERVAL, + timer = TRef, + alarmed = false, + alarm_funs = AlarmFuns }, {ok, set_mem_limits(State, MemFraction)}. handle_call(get_vm_memory_high_watermark, _From, State) -> @@ -181,9 +179,8 @@ set_mem_limits(State, MemFraction) -> memory_limit = MemLim }). internal_update(State = #state { memory_limit = MemLimit, - alarmed = Alarmed, - alarm_set = AlarmSet, - alarm_clear = AlarmClear }) -> + alarmed = Alarmed, + alarm_funs = {AlarmSet, AlarmClear} }) -> MemUsed = erlang:memory(total), NewAlarmed = MemUsed > MemLimit, case {Alarmed, NewAlarmed} of -- cgit v1.2.1 From b2827037303a2d819d91a896c8aac733d808f582 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 6 Aug 2012 11:56:43 +0100 Subject: warn on memory clipping --- src/vm_memory_monitor.erl | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/vm_memory_monitor.erl b/src/vm_memory_monitor.erl index 7cba36f8..85dbf368 100644 --- a/src/vm_memory_monitor.erl +++ b/src/vm_memory_monitor.erl @@ -172,7 +172,17 @@ set_mem_limits(State, MemFraction) -> ?MEMORY_SIZE_FOR_UNKNOWN_OS; M -> M end, - MemLim = trunc(MemFraction * lists:min([TotalMemory, get_vm_limit()])), + UsableMemory = case get_vm_limit() of + Limit when Limit < TotalMemory -> + error_logger:warning_msg( + "Only ~pMB of ~pMB memory usable due to " + "limited address space.~n", + [trunc(V/?ONE_MB) || V <- [Limit, TotalMemory]]), + Limit; + _ -> + TotalMemory + end, + MemLim = trunc(MemFraction * UsableMemory), error_logger:info_msg("Memory limit set to ~pMB of ~pMB total.~n", [trunc(MemLim/?ONE_MB), trunc(TotalMemory/?ONE_MB)]), internal_update(State #state { total_memory = TotalMemory, -- cgit v1.2.1 From d8cb900c46c962ce88428841c6e9ec4f467cbf2d Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 6 Aug 2012 12:12:09 +0100 Subject: track vm_memory_high_watermark explicitly so we can report it back accurately. --- src/vm_memory_monitor.erl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/vm_memory_monitor.erl b/src/vm_memory_monitor.erl index 85dbf368..5ce894a9 100644 --- a/src/vm_memory_monitor.erl +++ b/src/vm_memory_monitor.erl @@ -49,6 +49,7 @@ -record(state, {total_memory, memory_limit, + memory_fraction, timeout, timer, alarmed, @@ -117,7 +118,7 @@ init([MemFraction, AlarmFuns]) -> {ok, set_mem_limits(State, MemFraction)}. handle_call(get_vm_memory_high_watermark, _From, State) -> - {reply, State#state.memory_limit / State#state.total_memory, State}; + {reply, State#state.memory_fraction, State}; handle_call({set_vm_memory_high_watermark, MemFraction}, _From, State) -> {reply, ok, set_mem_limits(State, MemFraction)}; @@ -185,8 +186,9 @@ set_mem_limits(State, MemFraction) -> MemLim = trunc(MemFraction * UsableMemory), error_logger:info_msg("Memory limit set to ~pMB of ~pMB total.~n", [trunc(MemLim/?ONE_MB), trunc(TotalMemory/?ONE_MB)]), - internal_update(State #state { total_memory = TotalMemory, - memory_limit = MemLim }). + internal_update(State #state { total_memory = TotalMemory, + memory_limit = MemLim, + memory_fraction = MemFraction}). internal_update(State = #state { memory_limit = MemLimit, alarmed = Alarmed, -- cgit v1.2.1 From 8aeaf3f89ffff3da206f3a8c7f9a8ef2fc22c8d7 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 7 Aug 2012 12:51:36 +0100 Subject: Take account of the current nodes when selecting new nodes, and some unit tests for the node selection logic. --- src/rabbit_mirror_queue_misc.erl | 70 +++++++++++++++++++++++----------------- src/rabbit_tests.erl | 29 +++++++++++++++++ 2 files changed, 70 insertions(+), 29 deletions(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index fe7c0442..0469f5f2 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -22,6 +22,8 @@ %% temp -export([suggested_queue_nodes/1, is_mirrored/1, update_mirrors/2]). +%% for testing +-export([suggested_queue_nodes/4]). -include("rabbit.hrl"). @@ -204,41 +206,51 @@ store_updated_slaves(Q = #amqqueue{slave_pids = SPids, %%---------------------------------------------------------------------------- -%% TODO this should take account of current nodes so we don't throw -%% away mirrors or change the master needlessly suggested_queue_nodes(Q) -> - case [rabbit_policy:get(P, Q) || P <- [<<"ha-mode">>, <<"ha-params">>]] of - [{ok, <<"all">>}, _] -> - {node(), rabbit_mnesia:all_clustered_nodes() -- [node()]}; - [{ok, <<"nodes">>}, {ok, Nodes}] -> - case [list_to_atom(binary_to_list(Node)) || Node <- Nodes] of - [Node] -> {Node, []}; - [First | Rest] -> {First, Rest} - end; - [{ok, <<"at-least">>}, {ok, Count}] -> - {node(), lists:sublist( - rabbit_mnesia:all_clustered_nodes(), Count) -- [node()]}; - _ -> - {node(), []} + {MNode0, SNodes} = actual_queue_nodes(Q), + MNode = case MNode0 of + none -> node(); + _ -> MNode0 + end, + suggested_queue_nodes(policy(<<"ha-mode">>, Q), policy(<<"ha-params">>, Q), + {MNode, SNodes}, rabbit_mnesia:all_clustered_nodes()). + +policy(Policy, Q) -> + case rabbit_policy:get(Policy, Q) of + {ok, P} -> P; + _ -> none end. +suggested_queue_nodes(<<"all">>, _Params, {MNode, _SNodes}, All) -> + {MNode, All -- [MNode]}; +suggested_queue_nodes(<<"nodes">>, Nodes0, {MNode, _SNodes}, _All) -> + Nodes = [list_to_atom(binary_to_list(Node)) || Node <- Nodes0], + case lists:member(MNode, Nodes) of + true -> {MNode, Nodes -- [MNode]}; + false -> {hd(Nodes), tl(Nodes)} + end; +suggested_queue_nodes(<<"at-least">>, Count, {MNode, SNodes}, All) -> + SCount = Count - 1, + {MNode, case SCount > length(SNodes) of + true -> Cand = (All -- [MNode]) -- SNodes, + SNodes ++ lists:sublist(Cand, SCount - length(SNodes)); + false -> lists:sublist(SNodes, SCount) + end}; +suggested_queue_nodes(_, _, {MNode, _}, _) -> + {MNode, []}. + actual_queue_nodes(#amqqueue{pid = MPid, slave_pids = SPids}) -> - MNode = case MPid of - undefined -> undefined; - _ -> node(MPid) - end, - SNodes = case SPids of - undefined -> undefined; - _ -> [node(Pid) || Pid <- SPids] - end, - {MNode, SNodes}. + {case MPid of + none -> none; + _ -> node(MPid) + end, [node(Pid) || Pid <- SPids]}. is_mirrored(Q) -> - case rabbit_policy:get(<<"ha-mode">>, Q) of - {ok, <<"all">>} -> true; - {ok, <<"nodes">>} -> true; - {ok, <<"at-least">>} -> true; - _ -> false + case policy(<<"ha-mode">>, Q) of + <<"all">> -> true; + <<"nodes">> -> true; + <<"at-least">> -> true; + _ -> false end. update_mirrors(OldQ = #amqqueue{name = QName, pid = QPid}, diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index bb60bd12..bc30fb4c 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -52,6 +52,7 @@ all_tests() -> passed = test_log_management_during_startup(), passed = test_statistics(), passed = test_arguments_parser(), + passed = test_dynamic_mirroring(), passed = test_cluster_management(), passed = test_user_management(), passed = test_runtime_parameters(), @@ -856,6 +857,34 @@ test_arguments_parser() -> passed. +test_dynamic_mirroring() -> + %% Just unit tests of the node selection logic, see multi node + %% tests for the rest... + Test = fun ({NewM, NewSs}, Policy, Params, {OldM, OldSs}, All) -> + {NewM, NewSs0} = + rabbit_mirror_queue_misc:suggested_queue_nodes( + Policy, Params, {OldM, OldSs}, All), + NewSs = lists:sort(NewSs0) + end, + + Test({a,[b,c]},<<"all">>,'_',{a,[]}, [a,b,c]), + Test({a,[b,c]},<<"all">>,'_',{a,[b,c]},[a,b,c]), + Test({a,[b,c]},<<"all">>,'_',{a,[d]}, [a,b,c]), + + Test({a,[b,c]},<<"nodes">>,[<<"a">>,<<"b">>,<<"c">>],{a,[d]},[a,b,c,d]), + Test({a,[b,c]},<<"nodes">>,[<<"a">>,<<"b">>,<<"c">>],{a,[b]},[a,b,c,d]), + Test({b,[a,c]},<<"nodes">>,[<<"a">>,<<"b">>,<<"c">>],{b,[a]},[a,b,c,d]), + Test({a,[b,c]},<<"nodes">>,[<<"a">>,<<"b">>,<<"c">>],{d,[a]},[a,b,c,d]), + + Test({a,[b]}, <<"at-least">>,2,{a,[]}, [a,b,c,d]), + Test({a,[b,c]},<<"at-least">>,3,{a,[]}, [a,b,c,d]), + Test({a,[c]}, <<"at-least">>,2,{a,[c]}, [a,b,c,d]), + Test({a,[b,c]},<<"at-least">>,3,{a,[c]}, [a,b,c,d]), + Test({a,[c]}, <<"at-least">>,2,{a,[c,d]},[a,b,c,d]), + Test({a,[c,d]},<<"at-least">>,3,{a,[c,d]},[a,b,c,d]), + + passed. + test_cluster_management() -> %% 'cluster' and 'reset' should only work if the app is stopped {error, _} = control_action(cluster, []), -- cgit v1.2.1 From 1c5d42cc4f2a309ca4d5230e64ca65477cbb5fc6 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 7 Aug 2012 13:30:10 +0100 Subject: Support changing master (umm, which actually turned out to be a lot easier than expected, thanks Matthew). --- src/rabbit_mirror_queue_misc.erl | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 0469f5f2..87be4df7 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -259,14 +259,11 @@ update_mirrors(OldQ = #amqqueue{name = QName, pid = QPid}, {false, false} -> ok; {true, false} -> rabbit_amqqueue:stop_mirroring(QPid); {false, true} -> rabbit_amqqueue:start_mirroring(QPid); - {true, true} -> {OldMNode, OldSNodes} = actual_queue_nodes(OldQ), - {NewMNode, NewSNodes} = suggested_queue_nodes(NewQ), - case OldMNode of - NewMNode -> ok; - _ -> io:format("TODO: master needs to change for ~p~n", [NewQ]) - end, - Add = NewSNodes -- OldSNodes, - Remove = OldSNodes -- NewSNodes, - [ok = drop_mirror(QName, SNode) || SNode <- Remove], - [ok = add_mirror(QName, SNode) || SNode <- Add] + {true, true} -> All = fun ({A,B}) -> [A|B] end, + OldNodes = All(actual_queue_nodes(OldQ)), + NewNodes = All(suggested_queue_nodes(NewQ)), + Add = NewNodes -- OldNodes, + Remove = OldNodes -- NewNodes, + [ok = drop_mirror(QName, Node) || Node <- Remove], + [ok = add_mirror(QName, Node) || Node <- Add] end. -- cgit v1.2.1 From 28cfa44999c3dce3bde80f8b6e35b3178a3795f1 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 7 Aug 2012 13:40:10 +0100 Subject: Reify something that was previously a little bit magic. --- src/rabbit_mirror_queue_misc.erl | 17 +++++++++++------ src/rabbit_mirror_queue_slave.erl | 2 ++ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 87be4df7..5f36a19f 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -68,11 +68,11 @@ remove_from_queue(QueueName, DeadPids) -> [] -> {error, not_found}; [Q = #amqqueue { pid = QPid, slave_pids = SPids }] -> - [QPid1 | SPids1] = Alive = - [Pid || Pid <- [QPid | SPids], - not lists:member(node(Pid), - DeadNodes) orelse - rabbit_misc:is_process_alive(Pid)], + Alive = [Pid || Pid <- [QPid | SPids], + not lists:member(node(Pid), + DeadNodes) orelse + rabbit_misc:is_process_alive(Pid)], + {QPid1, SPids1} = promote_slave(Alive), case {{QPid, SPids}, {QPid1, SPids1}} of {Same, Same} -> {ok, QPid1, []}; @@ -206,6 +206,11 @@ store_updated_slaves(Q = #amqqueue{slave_pids = SPids, %%---------------------------------------------------------------------------- +promote_slave([SPid | SPids]) -> + %% The slave pids are maintained in descending order of age, so + %% the one to promote is the oldest. + {SPid, SPids}. + suggested_queue_nodes(Q) -> {MNode0, SNodes} = actual_queue_nodes(Q), MNode = case MNode0 of @@ -227,7 +232,7 @@ suggested_queue_nodes(<<"nodes">>, Nodes0, {MNode, _SNodes}, _All) -> Nodes = [list_to_atom(binary_to_list(Node)) || Node <- Nodes0], case lists:member(MNode, Nodes) of true -> {MNode, Nodes -- [MNode]}; - false -> {hd(Nodes), tl(Nodes)} + false -> promote_slave(Nodes) end; suggested_queue_nodes(<<"at-least">>, Count, {MNode, SNodes}, All) -> SCount = Count - 1, diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index f8f83d45..3fc33f72 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -154,6 +154,8 @@ init_it(Self, Node, QueueName) -> mnesia:read({rabbit_queue, QueueName}), case [Pid || Pid <- [QPid | MPids], node(Pid) =:= Node] of [] -> + %% Add to the end, so they are in descending order of age, see + %% rabbit_mirror_queue_misc:promote_slave/1 MPids1 = MPids ++ [Self], rabbit_mirror_queue_misc:store_updated_slaves( Q1#amqqueue{slave_pids = MPids1}), -- cgit v1.2.1 From 02b482798a3b5b8a74d293c6dd7c3b5a9c93bbb6 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 7 Aug 2012 17:36:59 +0100 Subject: Make parameters vhost-specific. --- src/rabbit_control_main.erl | 22 +++--- src/rabbit_policy.erl | 56 ++++++-------- src/rabbit_runtime_parameter.erl | 18 +++-- src/rabbit_runtime_parameters.erl | 133 +++++++++++++++++++-------------- src/rabbit_runtime_parameters_test.erl | 18 ++--- 5 files changed, 131 insertions(+), 116 deletions(-) diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index 0dda32f1..e7948e37 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -60,9 +60,9 @@ {list_permissions, [?VHOST_DEF]}, list_user_permissions, - set_parameter, - clear_parameter, - list_parameters, + {set_parameter, [?VHOST_DEF]}, + {clear_parameter, [?VHOST_DEF]}, + {list_parameters, [?VHOST_DEF]}, {list_queues, [?VHOST_DEF]}, {list_exchanges, [?VHOST_DEF]}, @@ -414,21 +414,25 @@ action(list_permissions, Node, [], Opts, Inform) -> list_vhost_permissions, [VHost]}), rabbit_auth_backend_internal:vhost_perms_info_keys()); -action(set_parameter, Node, [Component, Key, Value], _Opts, Inform) -> +action(set_parameter, Node, [Component, Key, Value], Opts, Inform) -> + VHostArg = list_to_binary(proplists:get_value(?VHOST_OPT, Opts)), Inform("Setting runtime parameter ~p for component ~p to ~p", [Key, Component, Value]), rpc_call(Node, rabbit_runtime_parameters, parse_set, - [list_to_binary(Component), list_to_binary(Key), Value]); + [VHostArg, list_to_binary(Component), list_to_binary(Key), Value]); -action(clear_parameter, Node, [Component, Key], _Opts, Inform) -> +action(clear_parameter, Node, [Component, Key], Opts, Inform) -> + VHostArg = list_to_binary(proplists:get_value(?VHOST_OPT, Opts)), Inform("Clearing runtime parameter ~p for component ~p", [Key, Component]), - rpc_call(Node, rabbit_runtime_parameters, clear, [list_to_binary(Component), + rpc_call(Node, rabbit_runtime_parameters, clear, [VHostArg, + list_to_binary(Component), list_to_binary(Key)]); -action(list_parameters, Node, Args = [], _Opts, Inform) -> +action(list_parameters, Node, [], Opts, Inform) -> + VHostArg = list_to_binary(proplists:get_value(?VHOST_OPT, Opts)), Inform("Listing runtime parameters", []), display_info_list( - rpc_call(Node, rabbit_runtime_parameters, list_formatted, Args), + rpc_call(Node, rabbit_runtime_parameters, list_formatted, [VHostArg]), rabbit_runtime_parameters:info_keys()); action(report, Node, _Args, _Opts, Inform) -> diff --git a/src/rabbit_policy.erl b/src/rabbit_policy.erl index 1551795f..05b43a2e 100644 --- a/src/rabbit_policy.erl +++ b/src/rabbit_policy.erl @@ -26,7 +26,7 @@ -export([register/0]). -export([name/1, get/2, set/1]). --export([validate/3, validate_clear/2, notify/3, notify_clear/2]). +-export([validate/4, validate_clear/3, notify/4, notify_clear/3]). -rabbit_boot_step({?MODULE, [{description, "policy parameters"}, @@ -46,12 +46,13 @@ name0(Policy) -> pget(<<"name">>, Policy). set(Q = #amqqueue{name = Name}) -> Q#amqqueue{policy = set0(Name)}; set(X = #exchange{name = Name}) -> X#exchange{policy = set0(Name)}. -set0(Name) -> match(Name, list()). +set0(Name = #resource{virtual_host = VHost}) -> match(Name, list(VHost)). get(Name, #amqqueue{policy = Policy}) -> get0(Name, Policy); get(Name, #exchange{policy = Policy}) -> get0(Name, Policy); %% Caution - SLOW. -get(Name, EntityName = #resource{}) -> get0(Name, match(EntityName, list())). +get(Name, EntityName = #resource{virtual_host = VHost}) -> + get0(Name, match(EntityName, list(VHost))). get0(_Name, undefined) -> {error, not_found}; get0(Name, List) -> case pget(<<"policy">>, List) of @@ -64,35 +65,33 @@ get0(Name, List) -> case pget(<<"policy">>, List) of %%---------------------------------------------------------------------------- -validate(<<"policy">>, Name, Term) -> +validate(_VHost, <<"policy">>, Name, Term) -> rabbit_parameter_validation:proplist( Name, policy_validation(), Term). -validate_clear(<<"policy">>, _Name) -> +validate_clear(_VHost, <<"policy">>, _Name) -> ok. -notify(<<"policy">>, _Name, _Term) -> - update_policies(). +notify(VHost, <<"policy">>, _Name, _Term) -> + update_policies(VHost). -notify_clear(<<"policy">>, _Name) -> - update_policies(). +notify_clear(VHost, <<"policy">>, _Name) -> + update_policies(VHost). %%---------------------------------------------------------------------------- -list() -> +list(VHost) -> [[{<<"name">>, pget(key, P)} | pget(value, P)] - || P <- rabbit_runtime_parameters:list(<<"policy">>)]. + || P <- rabbit_runtime_parameters:list(VHost, <<"policy">>)]. -update_policies() -> - Policies = list(), +update_policies(VHost) -> + Policies = list(VHost), {Xs, Qs} = rabbit_misc:execute_mnesia_transaction( fun() -> {[update_exchange(X, Policies) || - VHost <- rabbit_vhost:list(), - X <- rabbit_exchange:list(VHost)], + X <- rabbit_exchange:list(VHost)], [update_queue(Q, Policies) || - VHost <- rabbit_vhost:list(), - Q <- rabbit_amqqueue:list(VHost)]} + Q <- rabbit_amqqueue:list(VHost)]} end), [notify(X) || X <- Xs], [notify(Q) || Q <- Qs], @@ -129,28 +128,15 @@ match(Name, Policies) -> [Policy | _Rest] -> Policy end. -matches(#resource{name = Name, virtual_host = VHost}, Policy) -> - Prefix = pget(<<"prefix">>, Policy), - case pget(<<"vhost">>, Policy) of - undefined -> prefix(Prefix, Name); - VHost -> prefix(Prefix, Name); - _ -> false - end. - -prefix(A, B) -> lists:prefix(binary_to_list(A), binary_to_list(B)). +matches(#resource{name = Name}, Policy) -> + lists:prefix(binary_to_list(pget(<<"prefix">>, Policy)), + binary_to_list(Name)). sort_pred(A, B) -> - R = size(pget(<<"prefix">>, A)) >= size(pget(<<"prefix">>, B)), - case {pget(<<"vhost">>, A), pget(<<"vhost">>, B)} of - {undefined, undefined} -> R; - {undefined, _} -> true; - {_, undefined} -> false; - _ -> R - end. + size(pget(<<"prefix">>, A)) >= size(pget(<<"prefix">>, B)). %%---------------------------------------------------------------------------- policy_validation() -> - [{<<"vhost">>, fun rabbit_parameter_validation:binary/2, optional}, - {<<"prefix">>, fun rabbit_parameter_validation:binary/2, mandatory}, + [{<<"prefix">>, fun rabbit_parameter_validation:binary/2, mandatory}, {<<"policy">>, fun rabbit_parameter_validation:list/2, mandatory}]. diff --git a/src/rabbit_runtime_parameter.erl b/src/rabbit_runtime_parameter.erl index c7d30116..18668049 100644 --- a/src/rabbit_runtime_parameter.erl +++ b/src/rabbit_runtime_parameter.erl @@ -21,10 +21,12 @@ -type(validate_results() :: 'ok' | {error, string(), [term()]} | [validate_results()]). --callback validate(binary(), binary(), term()) -> validate_results(). --callback validate_clear(binary(), binary()) -> validate_results(). --callback notify(binary(), binary(), term()) -> 'ok'. --callback notify_clear(binary(), binary()) -> 'ok'. +-callback validate(rabbit_types:vhost(), binary(), binary(), + term()) -> validate_results(). +-callback validate_clear(rabbit_types:vhost(), binary(), + binary()) -> validate_results(). +-callback notify(rabbit_types:vhost(), binary(), binary(), term()) -> 'ok'. +-callback notify_clear(rabbit_types:vhost(), binary(), binary()) -> 'ok'. -else. @@ -32,10 +34,10 @@ behaviour_info(callbacks) -> [ - {validate, 3}, - {validate_clear, 2}, - {notify, 3}, - {notify_clear, 2} + {validate, 4}, + {validate_clear, 3}, + {notify, 4}, + {notify_clear, 3} ]; behaviour_info(_Other) -> undefined. diff --git a/src/rabbit_runtime_parameters.erl b/src/rabbit_runtime_parameters.erl index 3a54e8f6..df026efd 100644 --- a/src/rabbit_runtime_parameters.erl +++ b/src/rabbit_runtime_parameters.erl @@ -18,8 +18,9 @@ -include("rabbit.hrl"). --export([parse_set/3, set/3, clear/2, list/0, list/1, list_strict/1, - list_formatted/0, lookup/2, value/2, value/3, info_keys/0]). +-export([parse_set/4, set/4, clear/3, + list/0, list_strict/1, list/2, list_strict/2, list_formatted/1, + lookup/3, value/3, value/4, info_keys/0]). %%---------------------------------------------------------------------------- @@ -27,16 +28,22 @@ -type(ok_or_error_string() :: 'ok' | {'error_string', string()}). --spec(parse_set/3 :: (binary(), binary(), string()) -> ok_or_error_string()). --spec(set/3 :: (binary(), binary(), term()) -> ok_or_error_string()). --spec(clear/2 :: (binary(), binary()) -> ok_or_error_string()). +-spec(parse_set/4 :: (rabbit_types:vhost(), binary(), binary(), string()) + -> ok_or_error_string()). +-spec(set/4 :: (rabbit_types:vhost(), binary(), binary(), term()) + -> ok_or_error_string()). +-spec(clear/3 :: (rabbit_types:vhost(), binary(), binary()) + -> ok_or_error_string()). -spec(list/0 :: () -> [rabbit_types:infos()]). --spec(list/1 :: (binary()) -> [rabbit_types:infos()]). -spec(list_strict/1 :: (binary()) -> [rabbit_types:infos()] | 'not_found'). --spec(list_formatted/0 :: () -> [rabbit_types:infos()]). --spec(lookup/2 :: (binary(), binary()) -> rabbit_types:infos()). --spec(value/2 :: (binary(), binary()) -> term()). --spec(value/3 :: (binary(), binary(), term()) -> term()). +-spec(list/2 :: (rabbit_types:vhost(), binary()) -> [rabbit_types:infos()]). +-spec(list_strict/2 :: (rabbit_types:vhost(), binary()) + -> [rabbit_types:infos()] | 'not_found'). +-spec(list_formatted/1 :: (rabbit_types:vhost()) -> [rabbit_types:infos()]). +-spec(lookup/3 :: (rabbit_types:vhost(), binary(), binary()) + -> rabbit_types:infos()). +-spec(value/3 :: (rabbit_types:vhost(), binary(), binary()) -> term()). +-spec(value/4 :: (rabbit_types:vhost(), binary(), binary(), term()) -> term()). -spec(info_keys/0 :: () -> rabbit_types:info_keys()). -endif. @@ -49,14 +56,14 @@ %%--------------------------------------------------------------------------- -parse_set(Component, Key, String) -> +parse_set(VHost, Component, Key, String) -> case parse(String) of - {ok, Term} -> set(Component, Key, Term); + {ok, Term} -> set(VHost, Component, Key, Term); {errors, L} -> format_error(L) end. -set(Component, Key, Term) -> - case set0(Component, Key, Term) of +set(VHost, Component, Key, Term) -> + case set0(VHost, Component, Key, Term) of ok -> ok; {errors, L} -> format_error(L) end. @@ -64,16 +71,18 @@ set(Component, Key, Term) -> format_error(L) -> {error_string, rabbit_misc:format_many([{"Validation failed~n", []} | L])}. -set0(Component, Key, Term) -> +set0(VHost, Component, Key, Term) -> case lookup_component(Component) of {ok, Mod} -> case flatten_errors(validate(Term)) of ok -> - case flatten_errors(Mod:validate(Component, Key, Term)) of + case flatten_errors( + Mod:validate(VHost, Component, Key, Term)) of ok -> - case mnesia_update(Component, Key, Term) of + case mnesia_update(VHost, Component, Key, Term) of {old, Term} -> ok; - _ -> Mod:notify(Component, Key, Term) + _ -> Mod:notify( + VHost, Component, Key, Term) end, ok; E -> @@ -86,95 +95,103 @@ set0(Component, Key, Term) -> E end. -mnesia_update(Component, Key, Term) -> +mnesia_update(VHost, Component, Key, Term) -> rabbit_misc:execute_mnesia_transaction( fun () -> - Res = case mnesia:read(?TABLE, {Component, Key}, read) of + Res = case mnesia:read(?TABLE, {VHost, Component, Key}, read) of [] -> new; [Params] -> {old, Params#runtime_parameters.value} end, - ok = mnesia:write(?TABLE, c(Component, Key, Term), write), + ok = mnesia:write(?TABLE, c(VHost, Component, Key, Term), write), Res end). -clear(Component, Key) -> - case clear0(Component, Key) of +clear(VHost, Component, Key) -> + case clear0(VHost, Component, Key) of ok -> ok; {errors, L} -> format_error(L) end. -clear0(Component, Key) -> +clear0(VHost, Component, Key) -> case lookup_component(Component) of - {ok, Mod} -> case flatten_errors(Mod:validate_clear(Component, Key)) of - ok -> mnesia_clear(Component, Key), - Mod:notify_clear(Component, Key), + {ok, Mod} -> case flatten_errors( + Mod:validate_clear(VHost, Component, Key)) of + ok -> mnesia_clear(VHost, Component, Key), + Mod:notify_clear(VHost, Component, Key), ok; E -> E end; E -> E end. -mnesia_clear(Component, Key) -> +mnesia_clear(VHost, Component, Key) -> ok = rabbit_misc:execute_mnesia_transaction( fun () -> - ok = mnesia:delete(?TABLE, {Component, Key}, write) + ok = mnesia:delete(?TABLE, {VHost, Component, Key}, write) end). list() -> [p(P) || P <- rabbit_misc:dirty_read_all(?TABLE)]. -list(Component) -> list(Component, []). -list_strict(Component) -> list(Component, not_found). - -list(Component, Default) -> - case lookup_component(Component) of - {ok, _} -> Match = #runtime_parameters{key = {Component, '_'}, _ = '_'}, - [p(P) || P <- mnesia:dirty_match_object(?TABLE, Match)]; - _ -> Default +list(VHost) -> list(VHost, '_', []). +list_strict(Component) -> list('_', Component, not_found). +list(VHost, Component) -> list(VHost, Component, []). +list_strict(VHost, Component) -> list(VHost, Component, not_found). + +list(VHost, Component, Default) -> + case component_good(Component) of + true -> Match = #runtime_parameters{key = {VHost, Component, '_'}, + _ = '_'}, + [p(P) || P <- mnesia:dirty_match_object(?TABLE, Match)]; + _ -> Default end. -list_formatted() -> - [pset(value, format(pget(value, P)), P) || P <- list()]. +list_formatted(VHost) -> + [pset(value, format(pget(value, P)), P) || P <- list(VHost)]. -lookup(Component, Key) -> - case lookup0(Component, Key, rabbit_misc:const(not_found)) of +lookup(VHost, Component, Key) -> + case lookup0(VHost, Component, Key, rabbit_misc:const(not_found)) of not_found -> not_found; Params -> p(Params) end. -value(Component, Key) -> - case lookup0(Component, Key, rabbit_misc:const(not_found)) of +value(VHost, Component, Key) -> + case lookup0(VHost, Component, Key, rabbit_misc:const(not_found)) of not_found -> not_found; Params -> Params#runtime_parameters.value end. -value(Component, Key, Default) -> - Params = lookup0(Component, Key, - fun () -> lookup_missing(Component, Key, Default) end), +value(VHost, Component, Key, Default) -> + Params = lookup0(VHost, Component, Key, + fun () -> + lookup_missing(VHost, Component, Key, Default) + end), Params#runtime_parameters.value. -lookup0(Component, Key, DefaultFun) -> - case mnesia:dirty_read(?TABLE, {Component, Key}) of +lookup0(VHost, Component, Key, DefaultFun) -> + case mnesia:dirty_read(?TABLE, {VHost, Component, Key}) of [] -> DefaultFun(); [R] -> R end. -lookup_missing(Component, Key, Default) -> +lookup_missing(VHost, Component, Key, Default) -> rabbit_misc:execute_mnesia_transaction( fun () -> - case mnesia:read(?TABLE, {Component, Key}, read) of - [] -> Record = c(Component, Key, Default), + case mnesia:read(?TABLE, {VHost, Component, Key}, read) of + [] -> Record = c(VHost, Component, Key, Default), mnesia:write(?TABLE, Record, write), Record; [R] -> R end end). -c(Component, Key, Default) -> #runtime_parameters{key = {Component, Key}, - value = Default}. +c(VHost, Component, Key, Default) -> + #runtime_parameters{key = {VHost, Component, Key}, + value = Default}. -p(#runtime_parameters{key = {Component, Key}, value = Value}) -> - [{component, Component}, +p(#runtime_parameters{key = {VHost, Component, Key}, value = Value}) -> + [{vhost, VHost}, + {component, Component}, {key, Key}, {value, Value}]. @@ -182,6 +199,12 @@ info_keys() -> [component, key, value]. %%--------------------------------------------------------------------------- +component_good('_') -> true; +component_good(Component) -> case lookup_component(Component) of + {ok, _} -> true; + _ -> false + end. + lookup_component(Component) -> case rabbit_registry:lookup_module( runtime_parameter, list_to_atom(binary_to_list(Component))) of diff --git a/src/rabbit_runtime_parameters_test.erl b/src/rabbit_runtime_parameters_test.erl index f23b3227..5224ccaa 100644 --- a/src/rabbit_runtime_parameters_test.erl +++ b/src/rabbit_runtime_parameters_test.erl @@ -17,7 +17,7 @@ -module(rabbit_runtime_parameters_test). -behaviour(rabbit_runtime_parameter). --export([validate/3, validate_clear/2, notify/3, notify_clear/2]). +-export([validate/4, validate_clear/3, notify/4, notify_clear/3]). -export([register/0, unregister/0]). register() -> @@ -26,13 +26,13 @@ register() -> unregister() -> rabbit_registry:unregister(runtime_parameter, <<"test">>). -validate(<<"test">>, <<"good">>, _Term) -> ok; -validate(<<"test">>, <<"maybe">>, <<"good">>) -> ok; -validate(<<"test">>, _, _) -> {error, "meh", []}. +validate(_, <<"test">>, <<"good">>, _Term) -> ok; +validate(_, <<"test">>, <<"maybe">>, <<"good">>) -> ok; +validate(_, <<"test">>, _, _) -> {error, "meh", []}. -validate_clear(<<"test">>, <<"good">>) -> ok; -validate_clear(<<"test">>, <<"maybe">>) -> ok; -validate_clear(<<"test">>, _) -> {error, "meh", []}. +validate_clear(_, <<"test">>, <<"good">>) -> ok; +validate_clear(_, <<"test">>, <<"maybe">>) -> ok; +validate_clear(_, <<"test">>, _) -> {error, "meh", []}. -notify(_, _, _) -> ok. -notify_clear(_, _) -> ok. +notify(_, _, _, _) -> ok. +notify_clear(_, _, _) -> ok. -- cgit v1.2.1 From 73dfbe12fc532e459921cc1ab523cf0f93eb835e Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 7 Aug 2012 19:42:50 +0100 Subject: remove cruft --- src/rabbit_plugins.erl | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/rabbit_plugins.erl b/src/rabbit_plugins.erl index 7cf6eea9..5cf60d6c 100644 --- a/src/rabbit_plugins.erl +++ b/src/rabbit_plugins.erl @@ -47,9 +47,7 @@ %%---------------------------------------------------------------------------- -%% %% @doc Prepares the file system and installs all enabled plugins. -%% setup() -> {ok, PluginDir} = application:get_env(rabbit, plugins_dir), {ok, ExpandDir} = application:get_env(rabbit, plugins_expand_dir), @@ -98,11 +96,9 @@ read_enabled(PluginsFile) -> PluginsFile, Reason}}) end. -%% %% @doc Calculate the dependency graph from Sources. %% When Reverse =:= true the bottom/leaf level applications are returned in %% the resulting list, otherwise they're skipped. -%% dependencies(Reverse, Sources, AllPlugins) -> {ok, G} = rabbit_misc:build_acyclic_graph( fun (App, _Deps) -> [{App, App}] end, -- cgit v1.2.1 From f67f3666eee79aae80a813919bbf722150359ea6 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 7 Aug 2012 19:47:21 +0100 Subject: remove more cruft --- src/rabbit_plugins.erl | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/src/rabbit_plugins.erl b/src/rabbit_plugins.erl index 5cf60d6c..a7178563 100644 --- a/src/rabbit_plugins.erl +++ b/src/rabbit_plugins.erl @@ -17,8 +17,7 @@ -module(rabbit_plugins). -include("rabbit.hrl"). --export([setup/0, active/0, read_enabled/1, - list/1, dependencies/3]). +-export([setup/0, active/0, read_enabled/1, list/1, dependencies/3]). -define(VERBOSE_DEF, {?VERBOSE_OPT, flag}). -define(MINIMAL_DEF, {?MINIMAL_OPT, flag}). @@ -40,8 +39,7 @@ -spec(active/0 :: () -> [atom()]). -spec(list/1 :: (string()) -> [#plugin{}]). -spec(read_enabled/1 :: (file:filename()) -> [atom()]). --spec(dependencies/3 :: - (boolean(), [atom()], [#plugin{}]) -> [atom()]). +-spec(dependencies/3 :: (boolean(), [atom()], [#plugin{}]) -> [atom()]). -endif. @@ -167,14 +165,12 @@ prepare_plugin(#plugin{type = dir, name = Name, location = Location}, rabbit_file:recursive_copy(Location, filename:join([PluginsDestDir, Name])). -%% Get the #plugin{} from an .ez. get_plugin_info(Base, {ez, EZ0}) -> EZ = filename:join([Base, EZ0]), case read_app_file(EZ) of {application, Name, Props} -> mkplugin(Name, Props, ez, EZ); {error, Reason} -> {error, EZ, Reason} end; -%% Get the #plugin{} from an .app. get_plugin_info(Base, {app, App0}) -> App = filename:join([Base, App0]), case rabbit_file:read_term_file(App) of @@ -194,7 +190,6 @@ mkplugin(Name, Props, Type, Location) -> #plugin{name = Name, version = Version, description = Description, dependencies = Dependencies, location = Location, type = Type}. -%% Read the .app file from an ez. read_app_file(EZ) -> case zip:list_dir(EZ) of {ok, [_|ZippedFiles]} -> @@ -210,13 +205,11 @@ read_app_file(EZ) -> {error, {invalid_ez, Reason}} end. -%% Return the path of the .app files in ebin/. find_app_files(ZippedFiles) -> {ok, RE} = re:compile("^.*/ebin/.*.app$"), [Path || {zip_file, Path, _, _, _, _} <- ZippedFiles, re:run(Path, RE, [{capture, none}]) =:= match]. -%% Parse a binary into a term. parse_binary(Bin) -> try {ok, Ts, _} = erl_scan:string(binary_to_list(Bin)), @@ -226,13 +219,10 @@ parse_binary(Bin) -> Err -> {error, {invalid_app, Err}} end. -%% Filter out applications that can be loaded *right now*. filter_applications(Applications) -> [Application || Application <- Applications, not is_available_app(Application)]. -%% Return whether is application is already available (and hence -%% doesn't need enabling). is_available_app(Application) -> case application:load(Application) of {error, {already_loaded, _}} -> true; @@ -241,10 +231,8 @@ is_available_app(Application) -> _ -> false end. -%% Return the names of the given plugins. plugin_names(Plugins) -> [Name || #plugin{name = Name} <- Plugins]. -%% Find plugins by name in a list of plugins. lookup_plugins(Names, AllPlugins) -> [P || P = #plugin{name = Name} <- AllPlugins, lists:member(Name, Names)]. -- cgit v1.2.1 From e5c00753ace4dfe553aaeb160d7fecb9bc001c98 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 7 Aug 2012 20:05:27 +0100 Subject: Eric's patches --- src/rabbit_plugins.erl | 15 +++++++++++++-- src/rabbit_plugins_main.erl | 40 +++++++++++++++++++++++++--------------- 2 files changed, 38 insertions(+), 17 deletions(-) diff --git a/src/rabbit_plugins.erl b/src/rabbit_plugins.erl index a7178563..514c5ed9 100644 --- a/src/rabbit_plugins.erl +++ b/src/rabbit_plugins.erl @@ -98,11 +98,22 @@ read_enabled(PluginsFile) -> %% When Reverse =:= true the bottom/leaf level applications are returned in %% the resulting list, otherwise they're skipped. dependencies(Reverse, Sources, AllPlugins) -> + %% A dict here is used en lieu of sets to dedup each Name, while + %% still maintaining the Deps list for each whose deps are + %% known. Missing plugins' dependencies cannot be known. + DepMap = lists:foldl( + fun({Name, Deps}, Acc) -> + dict:append_list(Name, Deps, Acc) + end, + dict:new(), + [{Name, Deps} || #plugin{name = Name, + dependencies = Deps} <- AllPlugins] ++ + [{Dep, []} || #plugin{dependencies = Deps} <- AllPlugins, + Dep <- Deps]), {ok, G} = rabbit_misc:build_acyclic_graph( fun (App, _Deps) -> [{App, App}] end, fun (App, Deps) -> [{App, Dep} || Dep <- Deps] end, - [{Name, Deps} - || #plugin{name = Name, dependencies = Deps} <- AllPlugins]), + dict:to_list(DepMap)), Dests = case Reverse of false -> digraph_utils:reachable(Sources, G); true -> digraph_utils:reaching(Sources, G) diff --git a/src/rabbit_plugins_main.erl b/src/rabbit_plugins_main.erl index 572cf150..b5429620 100644 --- a/src/rabbit_plugins_main.erl +++ b/src/rabbit_plugins_main.erl @@ -108,16 +108,20 @@ action(enable, ToEnable0, _Opts, PluginsFile, PluginsDir) -> Enabled, AllPlugins), ToEnable = [list_to_atom(Name) || Name <- ToEnable0], Missing = ToEnable -- plugin_names(AllPlugins), - case Missing of - [] -> ok; - _ -> throw({error_string, - fmt_list("The following plugins could not be found:", - Missing)}) - end, NewEnabled = lists:usort(Enabled ++ ToEnable), - write_enabled_plugins(PluginsFile, NewEnabled), NewImplicitlyEnabled = rabbit_plugins:dependencies(false, NewEnabled, AllPlugins), + MissingDeps = NewImplicitlyEnabled -- plugin_names(AllPlugins), + case {Missing, MissingDeps} of + {[], []} -> ok; + {Miss, []} -> throw({error_string, + fmt_list("The following plugins " + "could not be found:", Miss)}); + {[], Miss} -> throw({error_string, + fmt_list("The following plugin dependencies " + "could not be found:", Miss)}) + end, + write_enabled_plugins(PluginsFile, NewEnabled), maybe_warn_mochiweb(NewImplicitlyEnabled), case NewEnabled -- ImplicitlyEnabled of [] -> io:format("Plugin configuration unchanged.~n"); @@ -183,9 +187,12 @@ format_plugins(Pattern, Opts, PluginsFile, PluginsDir) -> EnabledImplicitly = rabbit_plugins:dependencies(false, EnabledExplicitly, AvailablePlugins) -- EnabledExplicitly, + Missing = [#plugin{name = Name} || + Name <- ((EnabledExplicitly ++ EnabledImplicitly) -- + plugin_names(AvailablePlugins))], {ok, RE} = re:compile(Pattern), Plugins = [ Plugin || - Plugin = #plugin{name = Name} <- AvailablePlugins, + Plugin = #plugin{name = Name} <- AvailablePlugins ++ Missing, re:run(atom_to_list(Name), RE, [{capture, none}]) =:= match, if OnlyEnabled -> lists:member(Name, EnabledExplicitly); OnlyEnabledAll -> (lists:member(Name, @@ -196,18 +203,21 @@ format_plugins(Pattern, Opts, PluginsFile, PluginsDir) -> Plugins1 = usort_plugins(Plugins), MaxWidth = lists:max([length(atom_to_list(Name)) || #plugin{name = Name} <- Plugins1] ++ [0]), - [format_plugin(P, EnabledExplicitly, EnabledImplicitly, Format, - MaxWidth) || P <- Plugins1], + [format_plugin(P, EnabledExplicitly, EnabledImplicitly, + plugin_names(Missing), Format, MaxWidth) || P <- Plugins1], ok. format_plugin(#plugin{name = Name, version = Version, description = Description, dependencies = Deps}, - EnabledExplicitly, EnabledImplicitly, Format, MaxWidth) -> + EnabledExplicitly, EnabledImplicitly, Missing, + Format, MaxWidth) -> Glyph = case {lists:member(Name, EnabledExplicitly), - lists:member(Name, EnabledImplicitly)} of - {true, false} -> "[E]"; - {false, true} -> "[e]"; - _ -> "[ ]" + lists:member(Name, EnabledImplicitly), + lists:member(Name, Missing)} of + {true, false, false} -> "[E]"; + {false, true, false} -> "[e]"; + {_, _, true} -> "[!]"; + _ -> "[ ]" end, case Format of minimal -> io:format("~s~n", [Name]); -- cgit v1.2.1 From 47b74b557c34b1f5b404896ebe4f282134efa19c Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 8 Aug 2012 05:08:32 +0100 Subject: cosmetic --- src/rabbit_plugins.erl | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/rabbit_plugins.erl b/src/rabbit_plugins.erl index a7178563..72d1696c 100644 --- a/src/rabbit_plugins.erl +++ b/src/rabbit_plugins.erl @@ -47,13 +47,12 @@ %% @doc Prepares the file system and installs all enabled plugins. setup() -> - {ok, PluginDir} = application:get_env(rabbit, plugins_dir), - {ok, ExpandDir} = application:get_env(rabbit, plugins_expand_dir), - {ok, EnabledPluginsFile} = application:get_env(rabbit, - enabled_plugins_file), - prepare_plugins(EnabledPluginsFile, PluginDir, ExpandDir), + {ok, PluginDir} = application:get_env(rabbit, plugins_dir), + {ok, ExpandDir} = application:get_env(rabbit, plugins_expand_dir), + {ok, EnabledFile} = application:get_env(rabbit, enabled_plugins_file), + prepare_plugins(EnabledFile, PluginDir, ExpandDir), [prepare_dir_plugin(PluginName) || - PluginName <- filelib:wildcard(ExpandDir ++ "/*/ebin/*.app")]. + PluginName <- filelib:wildcard("*/ebin/*.app", ExpandDir)]. %% @doc Lists the plugins which are currently running. active() -> @@ -112,17 +111,16 @@ dependencies(Reverse, Sources, AllPlugins) -> %%---------------------------------------------------------------------------- -prepare_plugins(EnabledPluginsFile, PluginsDistDir, DestDir) -> +prepare_plugins(EnabledFile, PluginsDistDir, DestDir) -> AllPlugins = list(PluginsDistDir), - Enabled = read_enabled(EnabledPluginsFile), + Enabled = read_enabled(EnabledFile), ToUnpack = dependencies(false, Enabled, AllPlugins), ToUnpackPlugins = lookup_plugins(ToUnpack, AllPlugins), - Missing = Enabled -- plugin_names(ToUnpackPlugins), - case Missing of - [] -> ok; - _ -> io:format("Warning: the following enabled plugins were " - "not found: ~p~n", [Missing]) + case Enabled -- plugin_names(ToUnpackPlugins) of + [] -> ok; + Missing -> io:format("Warning: the following enabled plugins were " + "not found: ~p~n", [Missing]) end, %% Eliminate the contents of the destination directory -- cgit v1.2.1 From 9a72e8980bbabd97cd9ecbc37baa4c799dc64dd9 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 8 Aug 2012 05:12:35 +0100 Subject: cosmetic --- src/rabbit_plugins.erl | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/rabbit_plugins.erl b/src/rabbit_plugins.erl index 72d1696c..3caa13d7 100644 --- a/src/rabbit_plugins.erl +++ b/src/rabbit_plugins.erl @@ -111,7 +111,7 @@ dependencies(Reverse, Sources, AllPlugins) -> %%---------------------------------------------------------------------------- -prepare_plugins(EnabledFile, PluginsDistDir, DestDir) -> +prepare_plugins(EnabledFile, PluginsDistDir, ExpandDir) -> AllPlugins = list(PluginsDistDir), Enabled = read_enabled(EnabledFile), ToUnpack = dependencies(false, Enabled, AllPlugins), @@ -124,18 +124,18 @@ prepare_plugins(EnabledFile, PluginsDistDir, DestDir) -> end, %% Eliminate the contents of the destination directory - case delete_recursively(DestDir) of + case delete_recursively(ExpandDir) of ok -> ok; {error, E} -> rabbit_misc:quit("Could not delete dir ~s (~p)", - [DestDir, E]) + [ExpandDir, E]) end, - case filelib:ensure_dir(DestDir ++ "/") of + case filelib:ensure_dir(ExpandDir ++ "/") of ok -> ok; {error, E2} -> rabbit_misc:quit("Could not create dir ~s (~p)", - [DestDir, E2]) + [ExpandDir, E2]) end, - [prepare_plugin(Plugin, DestDir) || Plugin <- ToUnpackPlugins]. + [prepare_plugin(Plugin, ExpandDir) || Plugin <- ToUnpackPlugins]. prepare_dir_plugin(PluginAppDescFn) -> %% Add the plugin ebin directory to the load path @@ -156,12 +156,11 @@ delete_recursively(Fn) -> Error -> Error end. -prepare_plugin(#plugin{type = ez, location = Location}, PluginDestDir) -> - zip:unzip(Location, [{cwd, PluginDestDir}]); +prepare_plugin(#plugin{type = ez, location = Location}, ExpandDir) -> + zip:unzip(Location, [{cwd, ExpandDir}]); prepare_plugin(#plugin{type = dir, name = Name, location = Location}, - PluginsDestDir) -> - rabbit_file:recursive_copy(Location, - filename:join([PluginsDestDir, Name])). + ExpandDir) -> + rabbit_file:recursive_copy(Location, filename:join([ExpandDir, Name])). get_plugin_info(Base, {ez, EZ0}) -> EZ = filename:join([Base, EZ0]), -- cgit v1.2.1 From f030fef7be26d69e149f47087588d5b2b1869394 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 8 Aug 2012 05:25:40 +0100 Subject: exiting the VM from deep inside a function is uncool --- src/rabbit_plugins.erl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/rabbit_plugins.erl b/src/rabbit_plugins.erl index 3caa13d7..0d3dde8c 100644 --- a/src/rabbit_plugins.erl +++ b/src/rabbit_plugins.erl @@ -126,13 +126,13 @@ prepare_plugins(EnabledFile, PluginsDistDir, ExpandDir) -> %% Eliminate the contents of the destination directory case delete_recursively(ExpandDir) of ok -> ok; - {error, E} -> rabbit_misc:quit("Could not delete dir ~s (~p)", - [ExpandDir, E]) + {error, E} -> throw({error, {cannot_delete_plugins_expand_dir, + [ExpandDir, E]}}) end, case filelib:ensure_dir(ExpandDir ++ "/") of - ok -> ok; - {error, E2} -> rabbit_misc:quit("Could not create dir ~s (~p)", - [ExpandDir, E2]) + ok -> ok; + {error, E} -> throw({error, {cannot_create_plugins_expand_dir, + [ExpandDir, E]}}) end, [prepare_plugin(Plugin, ExpandDir) || Plugin <- ToUnpackPlugins]. -- cgit v1.2.1 From 0b3b4496ad5599a744ebc822a3c84feedc97e7c0 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 8 Aug 2012 05:32:35 +0100 Subject: remove cruft --- src/rabbit_misc.erl | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 25a51d22..31512b67 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -396,19 +396,10 @@ report_coverage_percentage(File, Cov, NotCov, Mod) -> confirm_to_sender(Pid, MsgSeqNos) -> gen_server2:cast(Pid, {confirm, MsgSeqNos, self()}). -%% -%% @doc Halts the emulator after printing out an error message io-formatted with -%% the supplied arguments. The exit status of the beam process will be set to 1. -%% quit(Fmt, Args) -> io:format("ERROR: " ++ Fmt ++ "~n", Args), quit(1). -%% -%% @doc Halts the emulator returning the given status code to the os. -%% On Windows this function will block indefinitely so as to give the io -%% subsystem time to flush stdout completely. -%% quit(Status) -> case os:type() of {unix, _} -> halt(Status); -- cgit v1.2.1 From 986bc8580ab13db5ac7b08ac6431d7dfa1f51d5b Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 8 Aug 2012 05:35:00 +0100 Subject: restore docs (but reformatted) --- src/rabbit_misc.erl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 31512b67..5eb24327 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -396,10 +396,16 @@ report_coverage_percentage(File, Cov, NotCov, Mod) -> confirm_to_sender(Pid, MsgSeqNos) -> gen_server2:cast(Pid, {confirm, MsgSeqNos, self()}). +%% @doc Halts the emulator after printing out an error message +%% io-formatted with the supplied arguments. The exit status of the +%% beam process will be set to 1. quit(Fmt, Args) -> io:format("ERROR: " ++ Fmt ++ "~n", Args), quit(1). +%% @doc Halts the emulator returning the given status code to the os. +%% On Windows this function will block indefinitely so as to give the io +%% subsystem time to flush stdout completely. quit(Status) -> case os:type() of {unix, _} -> halt(Status); -- cgit v1.2.1 From 5e951c561fd35dcfe0a7a8cce1dd2dc98eb0afb6 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 8 Aug 2012 05:41:20 +0100 Subject: cosmetic --- src/rabbit_log.erl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/rabbit_log.erl b/src/rabbit_log.erl index a6b4eeb0..73044e90 100644 --- a/src/rabbit_log.erl +++ b/src/rabbit_log.erl @@ -38,14 +38,14 @@ -spec(start_link/0 :: () -> rabbit_types:ok_pid_or_error()). --spec(log/3 :: (category(), level(), string()) -> 'ok'). --spec(log/4 :: (category(), level(), string(), [any()]) -> 'ok'). --spec(info/1 :: (string()) -> 'ok'). --spec(info/2 :: (string(), [any()]) -> 'ok'). +-spec(log/3 :: (category(), level(), string()) -> 'ok'). +-spec(log/4 :: (category(), level(), string(), [any()]) -> 'ok'). +-spec(info/1 :: (string()) -> 'ok'). +-spec(info/2 :: (string(), [any()]) -> 'ok'). -spec(warning/1 :: (string()) -> 'ok'). -spec(warning/2 :: (string(), [any()]) -> 'ok'). --spec(error/1 :: (string()) -> 'ok'). --spec(error/2 :: (string(), [any()]) -> 'ok'). +-spec(error/1 :: (string()) -> 'ok'). +-spec(error/2 :: (string(), [any()]) -> 'ok'). -endif. -- cgit v1.2.1 From 6c459693325c44d0b5a8a2adfa6d8afdbd3370f0 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 8 Aug 2012 05:42:28 +0100 Subject: cosmetic --- src/rabbit_log.erl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/rabbit_log.erl b/src/rabbit_log.erl index 73044e90..8dfa89d3 100644 --- a/src/rabbit_log.erl +++ b/src/rabbit_log.erl @@ -38,8 +38,9 @@ -spec(start_link/0 :: () -> rabbit_types:ok_pid_or_error()). --spec(log/3 :: (category(), level(), string()) -> 'ok'). --spec(log/4 :: (category(), level(), string(), [any()]) -> 'ok'). +-spec(log/3 :: (category(), level(), string()) -> 'ok'). +-spec(log/4 :: (category(), level(), string(), [any()]) -> 'ok'). + -spec(info/1 :: (string()) -> 'ok'). -spec(info/2 :: (string(), [any()]) -> 'ok'). -spec(warning/1 :: (string()) -> 'ok'). @@ -52,6 +53,7 @@ %%---------------------------------------------------------------------------- start_link() -> gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). + log(Category, Level, Fmt) -> log(Category, Level, Fmt, []). log(Category, Level, Fmt, Args) when is_list(Args) -> -- cgit v1.2.1 From 2a37bc5af6a663724d2e1187e48299142a6798d2 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 8 Aug 2012 09:53:59 +0100 Subject: Stop the clocks! Five and a half years into the RabbitMQ project, and Matthias committed something that broke the build. (Fixed.) --- src/rabbit_plugins.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rabbit_plugins.erl b/src/rabbit_plugins.erl index 0d3dde8c..4b70a2a5 100644 --- a/src/rabbit_plugins.erl +++ b/src/rabbit_plugins.erl @@ -130,9 +130,9 @@ prepare_plugins(EnabledFile, PluginsDistDir, ExpandDir) -> [ExpandDir, E]}}) end, case filelib:ensure_dir(ExpandDir ++ "/") of - ok -> ok; - {error, E} -> throw({error, {cannot_create_plugins_expand_dir, - [ExpandDir, E]}}) + ok -> ok; + {error, E2} -> throw({error, {cannot_create_plugins_expand_dir, + [ExpandDir, E2]}}) end, [prepare_plugin(Plugin, ExpandDir) || Plugin <- ToUnpackPlugins]. -- cgit v1.2.1 From e0911fed71be0e1862aa7a46e46beefeedd1e76b Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 8 Aug 2012 10:12:22 +0100 Subject: Remove vhost parameters when removing vhost. And a tiny driveby refactor. --- src/rabbit_runtime_parameters.erl | 3 ++- src/rabbit_vhost.erl | 13 +++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/rabbit_runtime_parameters.erl b/src/rabbit_runtime_parameters.erl index df026efd..0707193c 100644 --- a/src/rabbit_runtime_parameters.erl +++ b/src/rabbit_runtime_parameters.erl @@ -19,7 +19,7 @@ -include("rabbit.hrl"). -export([parse_set/4, set/4, clear/3, - list/0, list_strict/1, list/2, list_strict/2, list_formatted/1, + list/0, list/1, list_strict/1, list/2, list_strict/2, list_formatted/1, lookup/3, value/3, value/4, info_keys/0]). %%---------------------------------------------------------------------------- @@ -35,6 +35,7 @@ -spec(clear/3 :: (rabbit_types:vhost(), binary(), binary()) -> ok_or_error_string()). -spec(list/0 :: () -> [rabbit_types:infos()]). +-spec(list/1 :: (rabbit_types:vhost()) -> [rabbit_types:infos()]). -spec(list_strict/1 :: (binary()) -> [rabbit_types:infos()] | 'not_found'). -spec(list/2 :: (rabbit_types:vhost(), binary()) -> [rabbit_types:infos()]). -spec(list_strict/2 :: (rabbit_types:vhost(), binary()) diff --git a/src/rabbit_vhost.erl b/src/rabbit_vhost.erl index 5548ef6d..03dfbe24 100644 --- a/src/rabbit_vhost.erl +++ b/src/rabbit_vhost.erl @@ -90,12 +90,13 @@ delete(VHostPath) -> R. internal_delete(VHostPath) -> - lists:foreach( - fun (Info) -> - ok = rabbit_auth_backend_internal:clear_permissions( - proplists:get_value(user, Info), VHostPath) - end, - rabbit_auth_backend_internal:list_vhost_permissions(VHostPath)), + [ok = rabbit_auth_backend_internal:clear_permissions( + proplists:get_value(user, Info), VHostPath) + || Info <- rabbit_auth_backend_internal:list_vhost_permissions(VHostPath)], + [ok = rabbit_runtime_parameters:clear(VHostPath, + proplists:get_value(component, Info), + proplists:get_value(key, Info)) + || Info <- rabbit_runtime_parameters:list(VHostPath)], ok = mnesia:delete({rabbit_vhost, VHostPath}), ok. -- cgit v1.2.1 From aafa49a381e3600ef28927a28bfb27a84eb3004b Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 8 Aug 2012 10:29:50 +0100 Subject: refactor: get rid of rabbit_misc:quit/2 --- src/rabbit_misc.erl | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 5eb24327..8f6a9bcf 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -19,7 +19,7 @@ -include("rabbit_framing.hrl"). -export([method_record_type/1, polite_pause/0, polite_pause/1]). --export([die/1, frame_error/2, amqp_error/4, quit/1, quit/2, +-export([die/1, frame_error/2, amqp_error/4, quit/1, protocol_error/3, protocol_error/4, protocol_error/1]). -export([not_found/1, assert_args_equivalence/4]). -export([dirty_read/1]). @@ -92,7 +92,6 @@ (rabbit_framing:amqp_exception()) -> channel_or_connection_exit()). -spec(quit/1 :: (integer()) -> no_return()). --spec(quit/2 :: (string(), [term()]) -> no_return()). -spec(frame_error/2 :: (rabbit_framing:amqp_method_name(), binary()) -> rabbit_types:connection_exit()). @@ -396,13 +395,6 @@ report_coverage_percentage(File, Cov, NotCov, Mod) -> confirm_to_sender(Pid, MsgSeqNos) -> gen_server2:cast(Pid, {confirm, MsgSeqNos, self()}). -%% @doc Halts the emulator after printing out an error message -%% io-formatted with the supplied arguments. The exit status of the -%% beam process will be set to 1. -quit(Fmt, Args) -> - io:format("ERROR: " ++ Fmt ++ "~n", Args), - quit(1). - %% @doc Halts the emulator returning the given status code to the os. %% On Windows this function will block indefinitely so as to give the io %% subsystem time to flush stdout completely. -- cgit v1.2.1 From 8ae9f440a849ddcc06280a4c359430113508205d Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 8 Aug 2012 10:54:58 +0100 Subject: Update docs --- docs/rabbitmqctl.1.xml | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/docs/rabbitmqctl.1.xml b/docs/rabbitmqctl.1.xml index 2d25edee..1c28ad46 100644 --- a/docs/rabbitmqctl.1.xml +++ b/docs/rabbitmqctl.1.xml @@ -750,15 +750,16 @@ Certain features of RabbitMQ (such as the federation plugin) are controlled by dynamic, cluster-wide parameters. Each parameter - consists of a component name, a key and a value. The - component name and key are strings, and the value is an - Erlang term. Parameters can be set, cleared and listed. In - general you should refer to the documentation for the feature - in question to see how to set parameters. + consists of a component name, a key and a value, and is + associated with a virtual host. The component name and key are + strings, and the value is an Erlang term. Parameters can be + set, cleared and listed. In general you should refer to the + documentation for the feature in question to see how to set + parameters. - set_parameter component_name key value + set_parameter -p vhostpath component_name key value Sets a parameter. @@ -789,12 +790,12 @@ For example: rabbitmqctl set_parameter federation local_username '<<"guest">>' - This command sets the parameter local_username for the federation component to the Erlang term <<"guest">>. + This command sets the parameter local_username for the federation component in the default virtual host to the Erlang term <<"guest">>. - clear_parameter component_name key + clear_parameter -p vhostpath component_name key Clears a parameter. @@ -817,20 +818,20 @@ For example: rabbitmqctl clear_parameter federation local_username - This command clears the parameter local_username for the federation component. + This command clears the parameter local_username for the federation component in the default virtual host. - list_parameters + list_parameters -p vhostpath - Lists all parameters. + Lists all parameters for a virtual host. For example: rabbitmqctl list_parameters - This command lists all parameters. + This command lists all parameters in the default virtual host. -- cgit v1.2.1 From 19a6b8aa175df24c3262b64f040a174fea1839e0 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 8 Aug 2012 11:14:22 +0100 Subject: Revert the part of 942d5ea3c608 which broke all plugins --- src/rabbit_plugins.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_plugins.erl b/src/rabbit_plugins.erl index 4b70a2a5..c3d1ad7c 100644 --- a/src/rabbit_plugins.erl +++ b/src/rabbit_plugins.erl @@ -52,7 +52,7 @@ setup() -> {ok, EnabledFile} = application:get_env(rabbit, enabled_plugins_file), prepare_plugins(EnabledFile, PluginDir, ExpandDir), [prepare_dir_plugin(PluginName) || - PluginName <- filelib:wildcard("*/ebin/*.app", ExpandDir)]. + PluginName <- filelib:wildcard(ExpandDir ++ "/*/ebin/*.app")]. %% @doc Lists the plugins which are currently running. active() -> -- cgit v1.2.1 From 38b820c4014f8a8fbd12092d6df75fc043306729 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Wed, 8 Aug 2012 11:35:25 +0100 Subject: More randomness for temporary node names on windows --- scripts/rabbitmq-plugins.bat | 2 +- scripts/rabbitmq-server.bat | 2 +- scripts/rabbitmqctl.bat | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/rabbitmq-plugins.bat b/scripts/rabbitmq-plugins.bat index c67a0263..341f871a 100755 --- a/scripts/rabbitmq-plugins.bat +++ b/scripts/rabbitmq-plugins.bat @@ -47,7 +47,7 @@ if "!RABBITMQ_PLUGINS_DIR!"=="" ( set RABBITMQ_PLUGINS_DIR=!TDP0!..\plugins ) -"!ERLANG_HOME!\bin\erl.exe" -pa "!TDP0!..\ebin" -noinput -hidden -sname rabbitmq-plugins!RANDOM! -s rabbit_plugins_main -enabled_plugins_file "!RABBITMQ_ENABLED_PLUGINS_FILE!" -plugins_dist_dir "!RABBITMQ_PLUGINS_DIR:\=/!" -extra !STAR! +"!ERLANG_HOME!\bin\erl.exe" -pa "!TDP0!..\ebin" -noinput -hidden -sname rabbitmq-plugins!RANDOM!!TIME:~9! -s rabbit_plugins_main -enabled_plugins_file "!RABBITMQ_ENABLED_PLUGINS_FILE!" -plugins_dist_dir "!RABBITMQ_PLUGINS_DIR:\=/!" -extra !STAR! endlocal endlocal diff --git a/scripts/rabbitmq-server.bat b/scripts/rabbitmq-server.bat index 167f272e..3aea4c07 100755 --- a/scripts/rabbitmq-server.bat +++ b/scripts/rabbitmq-server.bat @@ -96,7 +96,7 @@ set RABBITMQ_EBIN_ROOT=!TDP0!..\ebin -pa "!RABBITMQ_EBIN_ROOT!" ^ -noinput -hidden ^ -s rabbit_prelaunch ^ - -sname rabbitmqprelaunch!RANDOM! ^ + -sname rabbitmqprelaunch!RANDOM!!TIME:~9! ^ -extra "!RABBITMQ_NODENAME!" if ERRORLEVEL 1 ( diff --git a/scripts/rabbitmqctl.bat b/scripts/rabbitmqctl.bat index 9f549f1e..d8b1eaf1 100755 --- a/scripts/rabbitmqctl.bat +++ b/scripts/rabbitmqctl.bat @@ -43,7 +43,7 @@ if not exist "!ERLANG_HOME!\bin\erl.exe" ( exit /B ) -"!ERLANG_HOME!\bin\erl.exe" -pa "!TDP0!..\ebin" -noinput -hidden !RABBITMQ_CTL_ERL_ARGS! -sname rabbitmqctl!RANDOM! -s rabbit_control_main -nodename !RABBITMQ_NODENAME! -extra !STAR! +"!ERLANG_HOME!\bin\erl.exe" -pa "!TDP0!..\ebin" -noinput -hidden !RABBITMQ_CTL_ERL_ARGS! -sname rabbitmqctl!RANDOM!!TIME:~9! -s rabbit_control_main -nodename !RABBITMQ_NODENAME! -extra !STAR! endlocal endlocal -- cgit v1.2.1 From d25d3d59f8dd91b0e1353dbbb0bf04caa9717fe6 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 8 Aug 2012 12:15:13 +0100 Subject: refactor: less confusing variable names --- src/rabbit_plugins.erl | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/rabbit_plugins.erl b/src/rabbit_plugins.erl index c3d1ad7c..0320c69f 100644 --- a/src/rabbit_plugins.erl +++ b/src/rabbit_plugins.erl @@ -51,8 +51,8 @@ setup() -> {ok, ExpandDir} = application:get_env(rabbit, plugins_expand_dir), {ok, EnabledFile} = application:get_env(rabbit, enabled_plugins_file), prepare_plugins(EnabledFile, PluginDir, ExpandDir), - [prepare_dir_plugin(PluginName) || - PluginName <- filelib:wildcard(ExpandDir ++ "/*/ebin/*.app")]. + [prepare_dir_plugin(PluginAppDescPath) || + PluginAppDescPath <- filelib:wildcard(ExpandDir ++ "/*/ebin/*.app")]. %% @doc Lists the plugins which are currently running. active() -> @@ -137,15 +137,11 @@ prepare_plugins(EnabledFile, PluginsDistDir, ExpandDir) -> [prepare_plugin(Plugin, ExpandDir) || Plugin <- ToUnpackPlugins]. -prepare_dir_plugin(PluginAppDescFn) -> - %% Add the plugin ebin directory to the load path - PluginEBinDirN = filename:dirname(PluginAppDescFn), - code:add_path(PluginEBinDirN), - - %% We want the second-last token - NameTokens = string:tokens(PluginAppDescFn,"/."), - PluginNameString = lists:nth(length(NameTokens) - 1, NameTokens), - list_to_atom(PluginNameString). +prepare_dir_plugin(PluginAppDescPath) -> + PluginEBinDir = filename:dirname(PluginAppDescPath), + code:add_path(PluginEBinDir), + NameTokens = string:tokens(PluginAppDescPath, "/."), + list_to_atom(lists:nth(length(NameTokens) - 1, NameTokens)). %%---------------------------------------------------------------------------- -- cgit v1.2.1 From 96751e47efcc2632766ff803ac41614076c0cb46 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 8 Aug 2012 15:17:07 +0100 Subject: the second half of removing rabbit_misc:quit/2 oops --- src/rabbit_prelaunch.erl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/rabbit_prelaunch.erl b/src/rabbit_prelaunch.erl index b0454435..404afe3c 100644 --- a/src/rabbit_prelaunch.erl +++ b/src/rabbit_prelaunch.erl @@ -57,7 +57,7 @@ duplicate_node_check(NodeStr) -> case rabbit_nodes:names(NodeHost) of {ok, NamePorts} -> case proplists:is_defined(NodeName, NamePorts) of - true -> io:format("node with name ~p " + true -> io:format("ERROR: node with name ~p " "already running on ~p~n", [NodeName, NodeHost]), io:format(rabbit_nodes:diagnostics([Node]) ++ "~n"), @@ -65,7 +65,8 @@ duplicate_node_check(NodeStr) -> false -> ok end; {error, EpmdReason} -> - rabbit_misc:quit("epmd error for host ~p: ~p (~s)~n", + io:format("ERROR: epmd error for host ~p: ~p (~s)~n", [NodeHost, EpmdReason, - rabbit_misc:format_inet_error(EpmdReason)]) + rabbit_misc:format_inet_error(EpmdReason)]), + rabbit_misc:quit(?ERROR_CODE) end. -- cgit v1.2.1 From 38541982f8a2b48e3840520ed38cd283b2a60449 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 8 Aug 2012 15:21:08 +0100 Subject: fix dialyzer complaint --- src/rabbit_backing_queue.erl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/rabbit_backing_queue.erl b/src/rabbit_backing_queue.erl index dc144a0e..e81d4a16 100644 --- a/src/rabbit_backing_queue.erl +++ b/src/rabbit_backing_queue.erl @@ -18,6 +18,8 @@ -ifdef(use_specs). +-export_type([async_callback/0]). + %% We can't specify a per-queue ack/state with callback signatures -type(ack() :: any()). -type(state() :: any()). -- cgit v1.2.1 From 4640ccec963f4c1f34aa6ee1fcc428601f3ad344 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 8 Aug 2012 15:21:22 +0100 Subject: cosmetic --- src/rabbit_backing_queue.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rabbit_backing_queue.erl b/src/rabbit_backing_queue.erl index e81d4a16..95523bed 100644 --- a/src/rabbit_backing_queue.erl +++ b/src/rabbit_backing_queue.erl @@ -30,7 +30,8 @@ {rabbit_types:basic_message(), boolean(), Ack, non_neg_integer()})). -type(attempt_recovery() :: boolean()). -type(purged_msg_count() :: non_neg_integer()). --type(async_callback() :: fun ((atom(), fun ((atom(), state()) -> state())) -> 'ok')). +-type(async_callback() :: + fun ((atom(), fun ((atom(), state()) -> state())) -> 'ok')). -type(duration() :: ('undefined' | 'infinity' | number())). -type(msg_fun() :: fun((rabbit_types:basic_message(), ack()) -> 'ok') | -- cgit v1.2.1 From 8ced925b57549e1f05a2e402c548c7876f6cc234 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 8 Aug 2012 15:35:01 +0100 Subject: cosmetic refactor - more informative type signatures - move the final phase of setup() into prepare_plugins - ditch the non-erlangesc get_ prefix of get_plugin_info - vertical alignment - get rid of some intermediate vars --- src/rabbit_plugins.erl | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/src/rabbit_plugins.erl b/src/rabbit_plugins.erl index 0320c69f..076ccebc 100644 --- a/src/rabbit_plugins.erl +++ b/src/rabbit_plugins.erl @@ -35,11 +35,14 @@ -ifdef(use_specs). --spec(setup/0 :: () -> [atom()]). --spec(active/0 :: () -> [atom()]). +-type(plugin_name() :: atom()). + +-spec(setup/0 :: () -> [plugin_name()]). +-spec(active/0 :: () -> [plugin_name()]). -spec(list/1 :: (string()) -> [#plugin{}]). --spec(read_enabled/1 :: (file:filename()) -> [atom()]). --spec(dependencies/3 :: (boolean(), [atom()], [#plugin{}]) -> [atom()]). +-spec(read_enabled/1 :: (file:filename()) -> [plugin_name()]). +-spec(dependencies/3 :: (boolean(), [plugin_name()], [#plugin{}]) -> + [plugin_name()]). -endif. @@ -50,9 +53,7 @@ setup() -> {ok, PluginDir} = application:get_env(rabbit, plugins_dir), {ok, ExpandDir} = application:get_env(rabbit, plugins_expand_dir), {ok, EnabledFile} = application:get_env(rabbit, enabled_plugins_file), - prepare_plugins(EnabledFile, PluginDir, ExpandDir), - [prepare_dir_plugin(PluginAppDescPath) || - PluginAppDescPath <- filelib:wildcard(ExpandDir ++ "/*/ebin/*.app")]. + prepare_plugins(EnabledFile, PluginDir, ExpandDir). %% @doc Lists the plugins which are currently running. active() -> @@ -72,8 +73,7 @@ list(PluginsDir) -> (Plugin = #plugin{}, {Plugins1, Problems1}) -> {[Plugin|Plugins1], Problems1} end, {[], []}, - [get_plugin_info(PluginsDir, Plug) || - Plug <- EZs ++ FreeApps]), + [plugin_info(PluginsDir, Plug) || Plug <- EZs ++ FreeApps]), case Problems of [] -> ok; _ -> io:format("Warning: Problem reading some plugins: ~p~n", @@ -125,9 +125,9 @@ prepare_plugins(EnabledFile, PluginsDistDir, ExpandDir) -> %% Eliminate the contents of the destination directory case delete_recursively(ExpandDir) of - ok -> ok; - {error, E} -> throw({error, {cannot_delete_plugins_expand_dir, - [ExpandDir, E]}}) + ok -> ok; + {error, E1} -> throw({error, {cannot_delete_plugins_expand_dir, + [ExpandDir, E1]}}) end, case filelib:ensure_dir(ExpandDir ++ "/") of ok -> ok; @@ -135,11 +135,13 @@ prepare_plugins(EnabledFile, PluginsDistDir, ExpandDir) -> [ExpandDir, E2]}}) end, - [prepare_plugin(Plugin, ExpandDir) || Plugin <- ToUnpackPlugins]. + [prepare_plugin(Plugin, ExpandDir) || Plugin <- ToUnpackPlugins], + + [prepare_dir_plugin(PluginAppDescPath) || + PluginAppDescPath <- filelib:wildcard(ExpandDir ++ "/*/ebin/*.app")]. prepare_dir_plugin(PluginAppDescPath) -> - PluginEBinDir = filename:dirname(PluginAppDescPath), - code:add_path(PluginEBinDir), + code:add_path(filename:dirname(PluginAppDescPath)), NameTokens = string:tokens(PluginAppDescPath, "/."), list_to_atom(lists:nth(length(NameTokens) - 1, NameTokens)). @@ -158,13 +160,13 @@ prepare_plugin(#plugin{type = dir, name = Name, location = Location}, ExpandDir) -> rabbit_file:recursive_copy(Location, filename:join([ExpandDir, Name])). -get_plugin_info(Base, {ez, EZ0}) -> +plugin_info(Base, {ez, EZ0}) -> EZ = filename:join([Base, EZ0]), case read_app_file(EZ) of {application, Name, Props} -> mkplugin(Name, Props, ez, EZ); {error, Reason} -> {error, EZ, Reason} end; -get_plugin_info(Base, {app, App0}) -> +plugin_info(Base, {app, App0}) -> App = filename:join([Base, App0]), case rabbit_file:read_term_file(App) of {ok, [{application, Name, Props}]} -> -- cgit v1.2.1 From 36c4f0eda914379663b15501f0589a0ae205149c Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 8 Aug 2012 16:44:22 +0100 Subject: refactor: make better use of filename module --- src/rabbit_plugins.erl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rabbit_plugins.erl b/src/rabbit_plugins.erl index 076ccebc..ecb19611 100644 --- a/src/rabbit_plugins.erl +++ b/src/rabbit_plugins.erl @@ -142,8 +142,7 @@ prepare_plugins(EnabledFile, PluginsDistDir, ExpandDir) -> prepare_dir_plugin(PluginAppDescPath) -> code:add_path(filename:dirname(PluginAppDescPath)), - NameTokens = string:tokens(PluginAppDescPath, "/."), - list_to_atom(lists:nth(length(NameTokens) - 1, NameTokens)). + list_to_atom(filename:basename(PluginAppDescPath, ".app")). %%---------------------------------------------------------------------------- -- cgit v1.2.1 From 26461122d432be23aabed9a9b67019ea652d2071 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 10 Aug 2012 12:37:13 +0100 Subject: cosmetic --- src/rabbit_mnesia.erl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 7e9346f9..5a971246 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -726,9 +726,10 @@ wait_for_tables(TableNames) -> end. reset(Force) -> - rabbit_misc:local_info_msg("Resetting Rabbit~s~n", [if Force -> " forcefully"; - true -> "" - end]), + rabbit_misc:local_info_msg("Resetting Rabbit~s~n", + [if Force -> " forcefully"; + true -> "" + end]), ensure_mnesia_not_running(), case not Force andalso is_clustered() andalso is_only_disc_node(node(), false) -- cgit v1.2.1 From c5079b2cfdea029f5d574d2d88a22af795fb924f Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 10 Aug 2012 13:13:15 +0100 Subject: make unclustering work for ram nodes - leave the cluster properly when force=false - disconnect from nodes we know about when force=true --- src/rabbit_mnesia.erl | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 5a971246..a92a92bc 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -738,34 +738,42 @@ reset(Force) -> false -> ok end, Node = node(), - Nodes = all_clustered_nodes() -- [Node], + ConfigNodes = read_cluster_nodes_config(), case Force of - true -> ok; + true -> + %% If Node is a ram node, all_clustered_nodes() returns + %% just that when mnesia isn't running. So we also include + %% the the next best thing - the nodes from the config. + disconnect_nodes( + lists:usort(all_clustered_nodes() ++ ConfigNodes) -- [Node]); false -> ensure_mnesia_dir(), start_mnesia(), - RunningNodes = + {Nodes, RunningNodes} = try %% Force=true here so that reset still works when clustered %% with a node which is down - ok = init_db(read_cluster_nodes_config(), true), - running_clustered_nodes() -- [Node] + ok = init_db(ConfigNodes, true), + {all_clustered_nodes() -- [Node], + running_clustered_nodes() -- [Node]} after stop_mnesia() end, leave_cluster(Nodes, RunningNodes), rabbit_misc:ensure_ok(mnesia:delete_schema([Node]), - cannot_delete_schema) + cannot_delete_schema), + disconnect_nodes(Nodes) end, - %% We need to make sure that we don't end up in a distributed - %% Erlang system with nodes while not being in an Mnesia cluster - %% with them. We don't handle that well. - [erlang:disconnect_node(N) || N <- Nodes], ok = delete_cluster_nodes_config(), %% remove persisted messages and any other garbage we find ok = rabbit_file:recursive_delete(filelib:wildcard(dir() ++ "/*")), ok. +%% We need to make sure that we don't end up in a distributed Erlang +%% system with nodes while not being in an Mnesia cluster with +%% them. We don't handle that well. +disconnect_nodes(Nodes) -> [erlang:disconnect_node(N) || N <- Nodes]. + leave_cluster([], _) -> ok; leave_cluster(Nodes, RunningNodes) -> %% find at least one running cluster node and instruct it to -- cgit v1.2.1 From b3940f5f7849d5dbb0cebd5dff91a69d3c84df7b Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 10 Aug 2012 13:26:37 +0100 Subject: just connect from all (visible) nodes instead That way we don't rely on our current (possibly imperfect) knowledge of what the node was clustered with in mnesia. This is safe because, as per the comment, only mnesia cluster nodes should be part of the erlang distributed system. Note that rabbitmqctl et al run as *hidden* nodes. --- src/rabbit_mnesia.erl | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index a92a92bc..5be23a88 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -738,14 +738,9 @@ reset(Force) -> false -> ok end, Node = node(), - ConfigNodes = read_cluster_nodes_config(), case Force of true -> - %% If Node is a ram node, all_clustered_nodes() returns - %% just that when mnesia isn't running. So we also include - %% the the next best thing - the nodes from the config. - disconnect_nodes( - lists:usort(all_clustered_nodes() ++ ConfigNodes) -- [Node]); + ok; false -> ensure_mnesia_dir(), start_mnesia(), @@ -753,7 +748,7 @@ reset(Force) -> try %% Force=true here so that reset still works when clustered %% with a node which is down - ok = init_db(ConfigNodes, true), + ok = init_db(read_cluster_nodes_config(), true), {all_clustered_nodes() -- [Node], running_clustered_nodes() -- [Node]} after @@ -761,19 +756,17 @@ reset(Force) -> end, leave_cluster(Nodes, RunningNodes), rabbit_misc:ensure_ok(mnesia:delete_schema([Node]), - cannot_delete_schema), - disconnect_nodes(Nodes) + cannot_delete_schema) end, + %% We need to make sure that we don't end up in a distributed + %% Erlang system with nodes while not being in an Mnesia cluster + %% with them. We don't handle that well. + [erlang:disconnect_node(N) || N <- nodes() -- [Node]], ok = delete_cluster_nodes_config(), %% remove persisted messages and any other garbage we find ok = rabbit_file:recursive_delete(filelib:wildcard(dir() ++ "/*")), ok. -%% We need to make sure that we don't end up in a distributed Erlang -%% system with nodes while not being in an Mnesia cluster with -%% them. We don't handle that well. -disconnect_nodes(Nodes) -> [erlang:disconnect_node(N) || N <- Nodes]. - leave_cluster([], _) -> ok; leave_cluster(Nodes, RunningNodes) -> %% find at least one running cluster node and instruct it to -- cgit v1.2.1 From 70612671f27997b9275010c3bfab91fd70cf3156 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Fri, 10 Aug 2012 17:17:34 +0100 Subject: Prevent possible deadlock when obtaining multiple filehandles --- src/file_handle_cache.erl | 59 +++++++++++++++++++++++++++-------------------- src/rabbit_file.erl | 4 ++-- 2 files changed, 36 insertions(+), 27 deletions(-) diff --git a/src/file_handle_cache.erl b/src/file_handle_cache.erl index 68c095d2..c3c5ed8e 100644 --- a/src/file_handle_cache.erl +++ b/src/file_handle_cache.erl @@ -120,12 +120,12 @@ %% do not need to worry about their handles being closed by the server %% - reopening them when necessary is handled transparently. %% -%% The server also supports obtain, release and transfer. obtain/0 +%% The server also supports obtain, release and transfer. obtain %% blocks until a file descriptor is available, at which point the -%% requesting process is considered to 'own' one more -%% descriptor. release/0 is the inverse operation and releases a -%% previously obtained descriptor. transfer/1 transfers ownership of a -%% file descriptor between processes. It is non-blocking. Obtain has a +%% requesting process is considered to 'own' more descriptor(s). +%% release is the inverse operation and releases previously obtained +%% descriptor(s). transfer transfers ownership of file descriptor(s) +%% between processes. It is non-blocking. Obtain has a %% lower limit, set by the ?OBTAIN_LIMIT/1 macro. File handles can use %% the entire limit, but will be evicted by obtain calls up to the %% point at which no more obtain calls can be satisfied by the obtains @@ -136,8 +136,8 @@ %% as sockets can do so in such a way that the overall number of open %% file descriptors is managed. %% -%% The callers of register_callback/3, obtain/0, and the argument of -%% transfer/1 are monitored, reducing the count of handles in use +%% The callers of register_callback/3, obtain, and the argument of +%% transfer are monitored, reducing the count of handles in use %% appropriately when the processes terminate. -behaviour(gen_server2). @@ -146,7 +146,8 @@ -export([open/3, close/1, read/2, append/2, needs_sync/1, sync/1, position/2, truncate/1, current_virtual_offset/1, current_raw_offset/1, flush/1, copy/3, set_maximum_since_use/1, delete/1, clear/1]). --export([obtain/0, release/0, transfer/1, set_limit/1, get_limit/0, info_keys/0, +-export([obtain/0, obtain/1, release/0, release/1, transfer/1, transfer/2, + set_limit/1, get_limit/0, info_keys/0, info/0, info/1]). -export([ulimit/0]). @@ -251,8 +252,11 @@ -spec(clear/1 :: (ref()) -> ok_or_error()). -spec(set_maximum_since_use/1 :: (non_neg_integer()) -> 'ok'). -spec(obtain/0 :: () -> 'ok'). +-spec(obtain/1 :: (non_neg_integer()) -> 'ok'). -spec(release/0 :: () -> 'ok'). +-spec(release/1 :: (non_neg_integer()) -> 'ok'). -spec(transfer/1 :: (pid()) -> 'ok'). +-spec(transfer/2 :: (non_neg_integer(), pid()) -> 'ok'). -spec(set_limit/1 :: (non_neg_integer()) -> 'ok'). -spec(get_limit/0 :: () -> non_neg_integer()). -spec(info_keys/0 :: () -> rabbit_types:info_keys()). @@ -485,18 +489,22 @@ set_maximum_since_use(MaximumAge) -> true -> ok end. -obtain() -> +obtain() -> obtain(1). +release() -> release(1). +transfer(Pid) -> transfer(1, Pid). + +obtain(Count) when Count > 0 -> %% If the FHC isn't running, obtains succeed immediately. case whereis(?SERVER) of undefined -> ok; - _ -> gen_server2:call(?SERVER, {obtain, self()}, infinity) + _ -> gen_server2:call(?SERVER, {obtain, Count, self()}, infinity) end. -release() -> - gen_server2:cast(?SERVER, {release, self()}). +release(Count) when Count > 0 -> + gen_server2:cast(?SERVER, {release, Count, self()}). -transfer(Pid) -> - gen_server2:cast(?SERVER, {transfer, self(), Pid}). +transfer(Count, Pid) when Count > 0 -> + gen_server2:cast(?SERVER, {transfer, Count, self(), Pid}). set_limit(Limit) -> gen_server2:call(?SERVER, {set_limit, Limit}, infinity). @@ -842,7 +850,7 @@ init([AlarmSet, AlarmClear]) -> prioritise_cast(Msg, _State) -> case Msg of - {release, _} -> 5; + {release, _, _} -> 5; _ -> 0 end. @@ -875,11 +883,12 @@ handle_call({open, Pid, Requested, EldestUnusedSince}, From, false -> {noreply, run_pending_item(Item, State)} end; -handle_call({obtain, Pid}, From, State = #fhc_state { obtain_count = Count, - obtain_pending = Pending, - clients = Clients }) -> +handle_call({obtain, N, Pid}, From, State = #fhc_state { + obtain_count = Count, + obtain_pending = Pending, + clients = Clients }) -> ok = track_client(Pid, Clients), - Item = #pending { kind = obtain, pid = Pid, requested = 1, from = From }, + Item = #pending { kind = obtain, pid = Pid, requested = N, from = From }, Enqueue = fun () -> true = ets:update_element(Clients, Pid, {#cstate.blocked, true}), @@ -890,7 +899,7 @@ handle_call({obtain, Pid}, From, State = #fhc_state { obtain_count = Count, case obtain_limit_reached(State) of true -> Enqueue(); false -> case needs_reduce(State #fhc_state { - obtain_count = Count + 1 }) of + obtain_count = Count + N }) of true -> reduce(Enqueue()); false -> adjust_alarm( State, run_pending_item(Item, State)) @@ -925,9 +934,9 @@ handle_cast({update, Pid, EldestUnusedSince}, %% storm of messages {noreply, State}; -handle_cast({release, Pid}, State) -> +handle_cast({release, N, Pid}, State) -> {noreply, adjust_alarm(State, process_pending( - update_counts(obtain, Pid, -1, State)))}; + update_counts(obtain, Pid, -N, State)))}; handle_cast({close, Pid, EldestUnusedSince}, State = #fhc_state { elders = Elders, clients = Clients }) -> @@ -939,11 +948,11 @@ handle_cast({close, Pid, EldestUnusedSince}, {noreply, adjust_alarm(State, process_pending( update_counts(open, Pid, -1, State)))}; -handle_cast({transfer, FromPid, ToPid}, State) -> +handle_cast({transfer, N, FromPid, ToPid}, State) -> ok = track_client(ToPid, State#fhc_state.clients), {noreply, process_pending( - update_counts(obtain, ToPid, +1, - update_counts(obtain, FromPid, -1, State)))}. + update_counts(obtain, ToPid, +N, + update_counts(obtain, FromPid, -N, State)))}. handle_info(check_counts, State) -> {noreply, maybe_reduce(State #fhc_state { timer_ref = undefined })}; diff --git a/src/rabbit_file.erl b/src/rabbit_file.erl index a95f8f26..26f74796 100644 --- a/src/rabbit_file.erl +++ b/src/rabbit_file.erl @@ -105,9 +105,9 @@ with_fhc_handle(Fun) -> with_fhc_handle(1, Fun). with_fhc_handle(N, Fun) -> - [ ok = file_handle_cache:obtain() || _ <- lists:seq(1, N)], + ok = file_handle_cache:obtain(N), try Fun() - after [ ok = file_handle_cache:release() || _ <- lists:seq(1, N)] + after ok = file_handle_cache:release(N) end. read_term_file(File) -> -- cgit v1.2.1 From 10cda2191282649534493f8dc8b80da65bdbf436 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 11 Aug 2012 07:41:00 +0100 Subject: be a bit more selective about which nodes we disconnect from ...in case we one day relax the restriction that all erlang distribution nodes must be part of the same cluster. - force=false -> all nodes we are clustered with - force=true -> all nodes we are connected to Forceful resets are employed to cope with various conditions in which the cluster has gotten into a bad state, so its best not to rely on mnesia's knowledge of the cluster state. Also: - some drive-by refactoring - nodes() never includes node(), so no need to filter the former --- src/rabbit_mnesia.erl | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 5be23a88..61b4054a 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -732,15 +732,14 @@ reset(Force) -> end]), ensure_mnesia_not_running(), case not Force andalso is_clustered() andalso - is_only_disc_node(node(), false) + is_only_disc_node(node(), false) of true -> log_both("no other disc nodes running"); false -> ok end, - Node = node(), case Force of true -> - ok; + disconnect_nodes(nodes()); false -> ensure_mnesia_dir(), start_mnesia(), @@ -749,24 +748,26 @@ reset(Force) -> %% Force=true here so that reset still works when clustered %% with a node which is down ok = init_db(read_cluster_nodes_config(), true), - {all_clustered_nodes() -- [Node], - running_clustered_nodes() -- [Node]} + {all_clustered_nodes() -- [node()], + running_clustered_nodes() -- [node()]} after stop_mnesia() end, leave_cluster(Nodes, RunningNodes), - rabbit_misc:ensure_ok(mnesia:delete_schema([Node]), - cannot_delete_schema) + rabbit_misc:ensure_ok(mnesia:delete_schema([node()]), + cannot_delete_schema), + disconnect_nodes(Nodes) end, - %% We need to make sure that we don't end up in a distributed - %% Erlang system with nodes while not being in an Mnesia cluster - %% with them. We don't handle that well. - [erlang:disconnect_node(N) || N <- nodes() -- [Node]], ok = delete_cluster_nodes_config(), %% remove persisted messages and any other garbage we find ok = rabbit_file:recursive_delete(filelib:wildcard(dir() ++ "/*")), ok. +%% We need to make sure that we don't end up in a distributed Erlang +%% system with nodes while not being in an Mnesia cluster with +%% them. We don't handle that well. +disconnect_nodes(Nodes) -> [erlang:disconnect_node(N) || N <- Nodes]. + leave_cluster([], _) -> ok; leave_cluster(Nodes, RunningNodes) -> %% find at least one running cluster node and instruct it to -- cgit v1.2.1 From af5fd4f44bed3d08afcceaeaa30afc720ea9fee1 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 11 Aug 2012 09:58:09 +0100 Subject: re-assess coverage suspension in tests I took out all the coverage suspensions and then gradually added them back in until the tests passed. The end result is - reduced scope of coverage suspension in some cases - new instances of coverage suspension due to the changes in this bug - some useful helper functions for coverage suspension --- src/rabbit_tests.erl | 58 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 21 deletions(-) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index bb60bd12..e78b979f 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -74,12 +74,10 @@ maybe_run_cluster_dependent_tests() -> run_cluster_dependent_tests(SecondaryNode) -> SecondaryNodeS = atom_to_list(SecondaryNode), - cover:stop(SecondaryNode), ok = control_action(stop_app, []), - ok = control_action(reset, []), + ok = safe_reset(), ok = control_action(cluster, [SecondaryNodeS]), ok = control_action(start_app, []), - cover:start(SecondaryNode), ok = control_action(start_app, SecondaryNode, [], []), io:format("Running cluster dependent tests with node ~p~n", [SecondaryNode]), @@ -940,7 +938,7 @@ test_cluster_management2(SecondaryNode) -> ok = assert_ram_node(), %% join cluster as a ram node - ok = control_action(reset, []), + ok = safe_reset(), ok = control_action(force_cluster, [SecondaryNodeS, "invalid1@invalid"]), ok = control_action(start_app, []), ok = control_action(stop_app, []), @@ -997,29 +995,30 @@ test_cluster_management2(SecondaryNode) -> ok = assert_disc_node(), %% turn a disk node into a ram node - ok = control_action(reset, []), + %% + %% can't use safe_reset here since for some reason nodes()==[] and + %% yet w/o stopping coverage things break + with_suspended_cover( + [SecondaryNode], fun () -> ok = control_action(reset, []) end), ok = control_action(cluster, [SecondaryNodeS]), ok = control_action(start_app, []), ok = control_action(stop_app, []), ok = assert_ram_node(), %% NB: this will log an inconsistent_database error, which is harmless - %% Turning cover on / off is OK even if we're not in general using cover, - %% it just turns the engine on / off, doesn't actually log anything. - cover:stop([SecondaryNode]), - true = disconnect_node(SecondaryNode), - pong = net_adm:ping(SecondaryNode), - cover:start([SecondaryNode]), + with_suspended_cover( + [SecondaryNode], fun () -> + true = disconnect_node(SecondaryNode), + pong = net_adm:ping(SecondaryNode) + end), %% leaving a cluster as a ram node - ok = control_action(reset, []), + ok = safe_reset(), %% ...and as a disk node ok = control_action(cluster, [SecondaryNodeS, NodeS]), ok = control_action(start_app, []), ok = control_action(stop_app, []), - cover:stop(SecondaryNode), - ok = control_action(reset, []), - cover:start(SecondaryNode), + ok = safe_reset(), %% attempt to leave cluster when no other node is alive ok = control_action(cluster, [SecondaryNodeS, NodeS]), @@ -1034,22 +1033,39 @@ test_cluster_management2(SecondaryNode) -> control_action(cluster, [SecondaryNodeS]), %% leave system clustered, with the secondary node as a ram node - ok = control_action(force_reset, []), + with_suspended_cover( + [SecondaryNode], fun () -> ok = control_action(force_reset, []) end), ok = control_action(start_app, []), %% Yes, this is rather ugly. But since we're a clustered Mnesia %% node and we're telling another clustered node to reset itself, %% we will get disconnected half way through causing a %% badrpc. This never happens in real life since rabbitmqctl is - %% not a clustered Mnesia node. - cover:stop(SecondaryNode), - {badrpc, nodedown} = control_action(force_reset, SecondaryNode, [], []), - pong = net_adm:ping(SecondaryNode), - cover:start(SecondaryNode), + %% not a clustered Mnesia node and is a hidden node. + with_suspended_cover( + [SecondaryNode], + fun () -> + {badrpc, nodedown} = + control_action(force_reset, SecondaryNode, [], []), + pong = net_adm:ping(SecondaryNode) + end), ok = control_action(cluster, SecondaryNode, [NodeS], []), ok = control_action(start_app, SecondaryNode, [], []), passed. +%% 'cover' does not cope at all well with nodes disconnecting, which +%% happens as part of reset. So we turn it off temporarily. That is ok +%% even if we're not in general using cover, it just turns the engine +%% on / off and doesn't log anything. +safe_reset() -> with_suspended_cover( + nodes(), fun () -> control_action(reset, []) end). + +with_suspended_cover(Nodes, Fun) -> + cover:stop(Nodes), + Res = Fun(), + cover:start(Nodes), + Res. + test_user_management() -> %% lots if stuff that should fail -- cgit v1.2.1 From f0ca412e77069423a9c493afce010291ef7179f2 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 11 Aug 2012 17:39:24 +0100 Subject: test more branches of 'rabbitmqctl set_vm_memory_high_watermark' and leave the HWM unchanged! --- src/rabbit_tests.erl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index bb60bd12..40ab9e25 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -1216,7 +1216,10 @@ test_server_status() -> ok = control_action(list_consumers, []), %% set vm memory high watermark + HWM = vm_memory_monitor:get_vm_memory_high_watermark(), + ok = control_action(set_vm_memory_high_watermark, ["1"]), ok = control_action(set_vm_memory_high_watermark, ["1.0"]), + ok = control_action(set_vm_memory_high_watermark, [float_to_list(HWM)]), %% cleanup [{ok, _} = rabbit_amqqueue:delete(QR, false, false) || QR <- [Q, Q2]], -- cgit v1.2.1 From 0c8c8518bba6c2019c756e814781ee0c341b17ef Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 11 Aug 2012 17:50:20 +0100 Subject: test 'rabbitmqctl eval' and refactor the error handling for it --- src/rabbit_control_main.erl | 22 +++++++++------------- src/rabbit_tests.erl | 5 +++++ 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index 0dda32f1..27a35142 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -164,8 +164,8 @@ start() -> {error, Reason} -> print_error("~p", [Reason]), rabbit_misc:quit(2); - {error_string, Reason} -> - print_error("~s", [Reason]), + {parse_error, {_Line, Mod, Err}} -> + print_error("~s", [lists:flatten(Mod:format_error(Err))]), rabbit_misc:quit(2); {badrpc, {'EXIT', Reason}} -> print_error("~p", [Reason]), @@ -445,16 +445,15 @@ action(eval, Node, [Expr], _Opts, _Inform) -> case erl_scan:string(Expr) of {ok, Scanned, _} -> case erl_parse:parse_exprs(Scanned) of - {ok, Parsed} -> - {value, Value, _} = unsafe_rpc( - Node, erl_eval, exprs, [Parsed, []]), - io:format("~p~n", [Value]), - ok; - {error, E} -> - {error_string, format_parse_error(E)} + {ok, Parsed} -> {value, Value, _} = + unsafe_rpc( + Node, erl_eval, exprs, [Parsed, []]), + io:format("~p~n", [Value]), + ok; + {error, E} -> {parse_error, E} end; {error, E, _} -> - {error_string, format_parse_error(E)} + {parse_error, E} end. %%---------------------------------------------------------------------------- @@ -543,9 +542,6 @@ exit_loop(Port) -> {Port, _} -> exit_loop(Port) end. -format_parse_error({_Line, Mod, Err}) -> - lists:flatten(Mod:format_error(Err)). - %%---------------------------------------------------------------------------- default_if_empty(List, Default) when is_list(List) -> diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 40ab9e25..56e952d7 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -1221,6 +1221,11 @@ test_server_status() -> ok = control_action(set_vm_memory_high_watermark, ["1.0"]), ok = control_action(set_vm_memory_high_watermark, [float_to_list(HWM)]), + %% eval + {error, {parse_error, _}} = control_action(eval, ["\""]), + {error, {parse_error, _}} = control_action(eval, ["a("]), + ok = control_action(eval, ["a."]), + %% cleanup [{ok, _} = rabbit_amqqueue:delete(QR, false, false) || QR <- [Q, Q2]], -- cgit v1.2.1 From ffcd9f37bda544fdfd03e5283165e8295ff55d6e Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sun, 12 Aug 2012 11:30:33 +0100 Subject: don't rely on evil erlang scoping --- src/rabbit_amqqueue_process.erl | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index b4071627..6bf290de 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -717,14 +717,16 @@ drop_expired_messages(State = #q{backing_queue_state = BQS, Now = now_micros(), DLXFun = dead_letter_fun(expired, State), ExpirePred = fun (#message_properties{expiry = Expiry}) -> Now > Expiry end, - case DLXFun of - undefined -> {undefined, BQS1} = BQ:dropwhile(ExpirePred, false, BQS), - BQS1; - _ -> {Msgs, BQS1} = BQ:dropwhile(ExpirePred, true, BQS), - lists:foreach( - fun({Msg, AckTag}) -> DLXFun(Msg, AckTag) end, Msgs), - BQS1 - end, + BQS1 = case DLXFun of + undefined -> {undefined, BQS2} = + BQ:dropwhile(ExpirePred, false, BQS), + BQS2; + _ -> {Msgs, BQS2} = BQ:dropwhile(ExpirePred, true, BQS), + lists:foreach( + fun({Msg, AckTag}) -> DLXFun(Msg, AckTag) end, + Msgs), + BQS2 + end, ensure_ttl_timer(State#q{backing_queue_state = BQS1}). ensure_ttl_timer(State = #q{backing_queue = BQ, -- cgit v1.2.1 From a026764f8010dd49780cf9ca4b00fd0501a4a7a8 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sun, 12 Aug 2012 12:13:52 +0100 Subject: schedule message expiry timer based on expiry time of queue head --- src/rabbit_amqqueue_process.erl | 42 +++++++++++++++++++++++------------------ src/rabbit_variable_queue.erl | 8 ++++---- 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 6bf290de..85c64fe6 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -576,7 +576,8 @@ deliver_or_enqueue(Delivery = #delivery{message = Message, maybe_record_confirm_message(Confirm, State1), Props = message_properties(Confirm, State2), BQS1 = BQ:publish(Message, Props, SenderPid, BQS), - ensure_ttl_timer(State2#q{backing_queue_state = BQS1}) + ensure_ttl_timer(Props#message_properties.expiry, + State2#q{backing_queue_state = BQS1}) end. requeue_and_run(AckTags, State = #q{backing_queue = BQ}) -> @@ -717,29 +718,34 @@ drop_expired_messages(State = #q{backing_queue_state = BQS, Now = now_micros(), DLXFun = dead_letter_fun(expired, State), ExpirePred = fun (#message_properties{expiry = Expiry}) -> Now > Expiry end, - BQS1 = case DLXFun of - undefined -> {undefined, BQS2} = - BQ:dropwhile(ExpirePred, false, BQS), - BQS2; - _ -> {Msgs, BQS2} = BQ:dropwhile(ExpirePred, true, BQS), - lists:foreach( - fun({Msg, AckTag}) -> DLXFun(Msg, AckTag) end, + {Props, BQS1} = + case DLXFun of + undefined -> + {Next, undefined, BQS2} = BQ:dropwhile(ExpirePred, false, BQS), + {Next, BQS2}; + _ -> + {Next, Msgs, BQS2} = BQ:dropwhile(ExpirePred, true, BQS), + lists:foreach(fun({Msg, AckTag}) -> DLXFun(Msg, AckTag) end, Msgs), - BQS2 - end, - ensure_ttl_timer(State#q{backing_queue_state = BQS1}). - -ensure_ttl_timer(State = #q{backing_queue = BQ, - backing_queue_state = BQS, - ttl = TTL, - ttl_timer_ref = undefined}) + {Next, BQS2} + end, + ensure_ttl_timer(case Props of + undefined -> undefined; + #message_properties{expiry = Next} -> Next + end, State#q{backing_queue_state = BQS1}). + +ensure_ttl_timer(Expiry, State = #q{backing_queue = BQ, + backing_queue_state = BQS, + ttl = TTL, + ttl_timer_ref = undefined}) when TTL =/= undefined -> case BQ:is_empty(BQS) of true -> State; - false -> TRef = erlang:send_after(TTL, self(), drop_expired), + false -> TRef = erlang:send_after((Expiry - now_micros()) div 1000, + self(), drop_expired), State#q{ttl_timer_ref = TRef} end; -ensure_ttl_timer(State) -> +ensure_ttl_timer(_Expiry, State) -> State. ack_if_no_dlx(AckTags, State = #q{dlx = undefined, diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 49213c95..bd606dfb 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -589,12 +589,12 @@ drain_confirmed(State = #vqstate { confirmed = C }) -> dropwhile(Pred, AckRequired, State) -> dropwhile(Pred, AckRequired, State, []). dropwhile(Pred, AckRequired, State, Msgs) -> - End = fun(S) when AckRequired -> {lists:reverse(Msgs), S}; - (S) -> {undefined, S} + End = fun(Next, S) when AckRequired -> {Next, lists:reverse(Msgs), S}; + (Next, S) -> {Next, undefined, S} end, case queue_out(State) of {empty, State1} -> - End(a(State1)); + End(undefined, a(State1)); {{value, MsgStatus = #msg_status { msg_props = MsgProps }}, State1} -> case {Pred(MsgProps), AckRequired} of {true, true} -> @@ -606,7 +606,7 @@ dropwhile(Pred, AckRequired, State, Msgs) -> {_, State2} = internal_fetch(false, MsgStatus, State1), dropwhile(Pred, AckRequired, State2, undefined); {false, _} -> - End(a(in_r(MsgStatus, State1))) + End(MsgProps, a(in_r(MsgStatus, State1))) end end. -- cgit v1.2.1 From 7b42798d86a69a1f77958465fe9e57da136a6347 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sun, 12 Aug 2012 12:34:09 +0100 Subject: oops; caught out by daft Erlang scoping again --- src/rabbit_amqqueue_process.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 85c64fe6..2a8d8d9b 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -731,7 +731,7 @@ drop_expired_messages(State = #q{backing_queue_state = BQS, end, ensure_ttl_timer(case Props of undefined -> undefined; - #message_properties{expiry = Next} -> Next + #message_properties{expiry = Exp} -> Exp end, State#q{backing_queue_state = BQS1}). ensure_ttl_timer(Expiry, State = #q{backing_queue = BQ, -- cgit v1.2.1 From cf81d9326027a2133d74bc557bd382ad9ffb7daa Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sun, 12 Aug 2012 13:23:23 +0100 Subject: more correctness --- src/rabbit_amqqueue_process.erl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 2a8d8d9b..0250902f 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -717,7 +717,7 @@ drop_expired_messages(State = #q{backing_queue_state = BQS, backing_queue = BQ }) -> Now = now_micros(), DLXFun = dead_letter_fun(expired, State), - ExpirePred = fun (#message_properties{expiry = Expiry}) -> Now > Expiry end, + ExpirePred = fun (#message_properties{expiry = Exp}) -> Now >= Exp end, {Props, BQS1} = case DLXFun of undefined -> @@ -741,8 +741,11 @@ ensure_ttl_timer(Expiry, State = #q{backing_queue = BQ, when TTL =/= undefined -> case BQ:is_empty(BQS) of true -> State; - false -> TRef = erlang:send_after((Expiry - now_micros()) div 1000, - self(), drop_expired), + false -> 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), State#q{ttl_timer_ref = TRef} end; ensure_ttl_timer(_Expiry, State) -> -- cgit v1.2.1 From e6a5745390c2612bcd9d9f85de0e8fd6a940cb62 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sun, 12 Aug 2012 13:37:47 +0100 Subject: oops --- src/rabbit_tests.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 56e952d7..21bd02af 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -1222,8 +1222,8 @@ test_server_status() -> ok = control_action(set_vm_memory_high_watermark, [float_to_list(HWM)]), %% eval - {error, {parse_error, _}} = control_action(eval, ["\""]), - {error, {parse_error, _}} = control_action(eval, ["a("]), + {parse_error, _} = control_action(eval, ["\""]), + {parse_error, _} = control_action(eval, ["a("]), ok = control_action(eval, ["a."]), %% cleanup -- cgit v1.2.1 From 010eadbc45a67545573069288d2840bdcd74dbb2 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sun, 12 Aug 2012 13:38:59 +0100 Subject: propagate API change --- src/rabbit_backing_queue.erl | 6 ++++-- src/rabbit_backing_queue_qc.erl | 2 +- src/rabbit_mirror_queue_master.erl | 6 +++--- src/rabbit_tests.erl | 14 +++++++------- 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/rabbit_backing_queue.erl b/src/rabbit_backing_queue.erl index 95523bed..ed5340fe 100644 --- a/src/rabbit_backing_queue.erl +++ b/src/rabbit_backing_queue.erl @@ -124,9 +124,11 @@ %% necessitate an ack or not. If they do, the function returns a list of %% messages with the respective acktags. -callback dropwhile(msg_pred(), true, state()) - -> {[{rabbit_types:basic_message(), ack()}], state()}; + -> {rabbit_types:message_properties() | undefined, + [{rabbit_types:basic_message(), ack()}], state()}; (msg_pred(), false, state()) - -> {undefined, state()}. + -> {rabbit_types:message_properties() | undefined, + undefined, state()}. %% Produce the next message. -callback fetch(true, state()) -> {fetch_result(ack()), state()}; diff --git a/src/rabbit_backing_queue_qc.erl b/src/rabbit_backing_queue_qc.erl index a84800c0..e40d9b29 100644 --- a/src/rabbit_backing_queue_qc.erl +++ b/src/rabbit_backing_queue_qc.erl @@ -268,7 +268,7 @@ next_state(S, Res, {call, ?BQMOD, drain_confirmed, _Args}) -> S#state{bqstate = BQ1}; next_state(S, Res, {call, ?BQMOD, dropwhile, _Args}) -> - BQ = {call, erlang, element, [2, Res]}, + BQ = {call, erlang, element, [3, Res]}, #state{messages = Messages} = S, Msgs1 = drop_messages(Messages), S#state{bqstate = BQ, len = gb_trees:size(Msgs1), messages = Msgs1}; diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index 750bcd56..477449e3 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -185,13 +185,13 @@ dropwhile(Pred, AckRequired, set_delivered = SetDelivered, backing_queue_state = BQS }) -> Len = BQ:len(BQS), - {Msgs, BQS1} = BQ:dropwhile(Pred, AckRequired, BQS), + {Next, Msgs, BQS1} = BQ:dropwhile(Pred, AckRequired, BQS), Len1 = BQ:len(BQS1), ok = gm:broadcast(GM, {set_length, Len1, AckRequired}), Dropped = Len - Len1, SetDelivered1 = lists:max([0, SetDelivered - Dropped]), - {Msgs, State #state { backing_queue_state = BQS1, - set_delivered = SetDelivered1 } }. + {Next, Msgs, State #state { backing_queue_state = BQS1, + set_delivered = SetDelivered1 } }. drain_confirmed(State = #state { backing_queue = BQ, backing_queue_state = BQS, diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 56e952d7..a3e75278 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -2455,10 +2455,10 @@ test_dropwhile(VQ0) -> fun (N, Props) -> Props#message_properties{expiry = N} end, VQ0), %% drop the first 5 messages - {undefined, VQ2} = rabbit_variable_queue:dropwhile( - fun(#message_properties { expiry = Expiry }) -> - Expiry =< 5 - end, false, VQ1), + {_, undefined, VQ2} = rabbit_variable_queue:dropwhile( + fun(#message_properties { expiry = Expiry }) -> + Expiry =< 5 + end, false, VQ1), %% fetch five now VQ3 = lists:foldl(fun (_N, VQN) -> @@ -2475,11 +2475,11 @@ test_dropwhile(VQ0) -> test_dropwhile_varying_ram_duration(VQ0) -> VQ1 = variable_queue_publish(false, 1, VQ0), VQ2 = rabbit_variable_queue:set_ram_duration_target(0, VQ1), - {undefined, VQ3} = rabbit_variable_queue:dropwhile( - fun(_) -> false end, false, VQ2), + {_, undefined, VQ3} = rabbit_variable_queue:dropwhile( + fun(_) -> false end, false, VQ2), VQ4 = rabbit_variable_queue:set_ram_duration_target(infinity, VQ3), VQ5 = variable_queue_publish(false, 1, VQ4), - {undefined, VQ6} = + {_, undefined, VQ6} = rabbit_variable_queue:dropwhile(fun(_) -> false end, false, VQ5), VQ6. -- cgit v1.2.1 From 49e7b78c71e45953698640277b6b86dbe2517d5a Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sun, 12 Aug 2012 15:28:48 +0100 Subject: fix a long-outstanding unresolved type reference this was actually completely wrong; the return value is that of *gen_server2*:start_link --- src/mirrored_supervisor.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mirrored_supervisor.erl b/src/mirrored_supervisor.erl index 4fc488b8..24c3ebd0 100644 --- a/src/mirrored_supervisor.erl +++ b/src/mirrored_supervisor.erl @@ -174,7 +174,7 @@ -spec start_internal(Group, ChildSpecs) -> Result when Group :: group_name(), ChildSpecs :: [supervisor2:child_spec()], - Result :: supervisor2:startlink_ret(). + Result :: {'ok', pid()} | {'error', term()}. -spec create_tables() -> Result when Result :: 'ok'. -- cgit v1.2.1 From ebdb4847e0ddde94e4ec5c649cb8e8d6a81b06f3 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 13 Aug 2012 11:46:27 +0100 Subject: prompt expiry of requeued messages which may require moving the timer expiry forward --- src/rabbit_amqqueue_process.erl | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 0250902f..283eba7c 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -47,6 +47,7 @@ msg_id_to_channel, ttl, ttl_timer_ref, + ttl_timer_expiry, senders, publish_seqno, unconfirmed, @@ -734,19 +735,23 @@ drop_expired_messages(State = #q{backing_queue_state = BQS, #message_properties{expiry = Exp} -> Exp end, State#q{backing_queue_state = BQS1}). -ensure_ttl_timer(Expiry, State = #q{backing_queue = BQ, - backing_queue_state = BQS, - ttl = TTL, - ttl_timer_ref = undefined}) - when TTL =/= undefined -> - case BQ:is_empty(BQS) of - true -> State; - false -> 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), - State#q{ttl_timer_ref = TRef} +ensure_ttl_timer(undefined, State) -> + State; +ensure_ttl_timer(_Expiry, State = #q{ttl = undefined}) -> + State; +ensure_ttl_timer(Expiry, State = #q{ttl_timer_ref = undefined}) -> + 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), + State#q{ttl_timer_ref = TRef, ttl_timer_expiry = Expiry}; +ensure_ttl_timer(Expiry, State = #q{ttl_timer_ref = TRef, + ttl_timer_expiry = TExpiry}) + when Expiry < TExpiry -> + case erlang:cancel_timer(TRef) of + false -> State; + _ -> ensure_ttl_timer(Expiry, State#q{ttl_timer_ref = undefined}) end; ensure_ttl_timer(_Expiry, State) -> State. -- cgit v1.2.1 From 92dca43d2e4159b6a90116721eec80950207cc25 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 13 Aug 2012 15:13:48 +0100 Subject: don't re-schedule timer when the new timer would go off <1ms sooner ...since timer granularity is 1ms --- src/rabbit_amqqueue_process.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 283eba7c..66c90079 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -748,7 +748,7 @@ ensure_ttl_timer(Expiry, State = #q{ttl_timer_ref = undefined}) -> State#q{ttl_timer_ref = TRef, ttl_timer_expiry = Expiry}; ensure_ttl_timer(Expiry, State = #q{ttl_timer_ref = TRef, ttl_timer_expiry = TExpiry}) - when Expiry < TExpiry -> + when Expiry + 1000 < TExpiry -> case erlang:cancel_timer(TRef) of false -> State; _ -> ensure_ttl_timer(Expiry, State#q{ttl_timer_ref = undefined}) -- cgit v1.2.1 From 416cf57c2487e934540f611c2e261e8494d9706f Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 15 Aug 2012 17:46:04 +0100 Subject: Oops, this got missed in bug 25048 - slave_pids is no longer in both lists so there's no need to remove it. --- src/rabbit_amqqueue_process.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 66c90079..f5a3a5f1 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -108,7 +108,7 @@ ]). -define(INFO_KEYS, - ?CREATION_EVENT_KEYS ++ ?STATISTICS_KEYS -- [pid, slave_pids]). + ?CREATION_EVENT_KEYS ++ ?STATISTICS_KEYS -- [pid]). %%---------------------------------------------------------------------------- -- cgit v1.2.1 From 34df392854b305ce7934ac13c9000934c3abfb70 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 16 Aug 2012 18:07:25 +0100 Subject: Fix detection of mirror removal at clean shutdown --- src/rabbit_mirror_queue_misc.erl | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 29e2d29f..51084874 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -20,6 +20,8 @@ drop_mirror/2, drop_mirror/3, add_mirror/2, add_mirror/3, report_deaths/4, store_updated_slaves/1]). +-export([is_really_alive0/1]). %% For RPC + -include("rabbit.hrl"). %%---------------------------------------------------------------------------- @@ -64,9 +66,8 @@ remove_from_queue(QueueName, DeadPids) -> slave_pids = SPids }] -> [QPid1 | SPids1] = Alive = [Pid || Pid <- [QPid | SPids], - not lists:member(node(Pid), - DeadNodes) orelse - rabbit_misc:is_process_alive(Pid)], + not lists:member(node(Pid), DeadNodes) orelse + is_really_alive(Pid)], case {{QPid, SPids}, {QPid1, SPids1}} of {Same, Same} -> {ok, QPid1, []}; @@ -88,6 +89,30 @@ remove_from_queue(QueueName, DeadPids) -> end end). +%% We want to find out if the pid is alive in case a node restarted +%% quickly and we didn't notice until later (in which case we should +%% do nothing). But if we are dealing with a queue going down because +%% it's in a broker which is shutting down cleanly, its process could +%% still be alive momentarily (since we heard about the death of the +%% *GM* process) so ask the application controller whether rabbit is +%% running - in the above case by the time the controller responds the +%% process will have terminated. +%% +%% See bug 25104. + +is_really_alive(Pid) -> + %% Don't bother optimising the local case since we will only ever + %% call for a remote node + case rpc:call(node(Pid), + rabbit_mirror_queue_misc, is_process_alive0, [Pid]) of + true -> true; + _ -> false + end. + +is_really_alive0(Pid) -> + lists:keymember(rabbit, 1, application:which_applications()) + andalso is_process_alive(Pid). + on_node_up() -> Qs = rabbit_misc:execute_mnesia_transaction( -- cgit v1.2.1 From ce79f85248be67244a5fe2970ee415956f84ab1b Mon Sep 17 00:00:00 2001 From: Steve Powell Date: Fri, 17 Aug 2012 10:53:56 +0100 Subject: Disable parallel build (equivalent to supplying build.jobs=1 on the port command). --- packaging/macports/Portfile.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/macports/Portfile.in b/packaging/macports/Portfile.in index e461e49e..82c1fb0c 100644 --- a/packaging/macports/Portfile.in +++ b/packaging/macports/Portfile.in @@ -59,7 +59,7 @@ set mandest ${destroot}${prefix}/share/man use_configure no -use_parallel_build yes +use_parallel_build no build.env-append HOME=${workpath} -- cgit v1.2.1 From 66dfeca31ca13922b71c8d961d48c304508a02c9 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 17 Aug 2012 11:29:22 +0100 Subject: Gotta love those timeouts. --- src/rabbit_mirror_queue_misc.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 51084874..8c120670 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -110,7 +110,7 @@ is_really_alive(Pid) -> end. is_really_alive0(Pid) -> - lists:keymember(rabbit, 1, application:which_applications()) + lists:keymember(rabbit, 1, application:which_applications(infinity)) andalso is_process_alive(Pid). on_node_up() -> -- cgit v1.2.1 From 1ab8e0cbc66e711c3182d7ae734c704d5b0f183b Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 17 Aug 2012 12:16:05 +0100 Subject: at-least mode can imply that we need to start slaves in response to slaves dying elsewhere. So do that. --- src/rabbit_mirror_queue_misc.erl | 40 ++++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 5f36a19f..52846f58 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -58,38 +58,58 @@ %% slave (now master) receives messages it's not ready for (for %% example, new consumers). %% Returns {ok, NewMPid, DeadPids} -remove_from_queue(QueueName, DeadPids) -> - DeadNodes = [node(DeadPid) || DeadPid <- DeadPids], + +remove_from_queue(QueueName, DeadGMPids) -> + case remove_from_queue0(QueueName, DeadGMPids) of + {ok, NewMPid, DeadQPids, ExtraNodes} -> + [ok = add_mirror(QueueName, Node) || Node <- ExtraNodes], + {ok, NewMPid, DeadQPids}; + Other -> + Other + end. + +remove_from_queue0(QueueName, DeadGMPids) -> + DeadNodes = [node(DeadGMPid) || DeadGMPid <- DeadGMPids], rabbit_misc:execute_mnesia_transaction( fun () -> %% Someone else could have deleted the queue before we %% get here. case mnesia:read({rabbit_queue, QueueName}) of [] -> {error, not_found}; - [Q = #amqqueue { pid = QPid, + [Q = #amqqueue { name = QName, + pid = QPid, slave_pids = SPids }] -> Alive = [Pid || Pid <- [QPid | SPids], not lists:member(node(Pid), DeadNodes) orelse - rabbit_misc:is_process_alive(Pid)], + %% TODO when bug 25104 hits default do whatever it does. + false], + %% rabbit_misc:is_process_alive(Pid)], {QPid1, SPids1} = promote_slave(Alive), case {{QPid, SPids}, {QPid1, SPids1}} of {Same, Same} -> - {ok, QPid1, []}; + {ok, QPid1, [], []}; _ when QPid =:= QPid1 orelse node(QPid1) =:= node() -> %% Either master hasn't changed, so %% we're ok to update mnesia; or we have %% become the master. - store_updated_slaves( - Q #amqqueue { pid = QPid1, - slave_pids = SPids1 }), - {ok, QPid1, [QPid | SPids] -- Alive}; + Q1 = store_updated_slaves( + Q #amqqueue { pid = QPid1, + slave_pids = SPids1 }), + %% Sometimes a slave dying means we need + %% to start more on other nodes - + %% "at-least" mode can cause this to + %% happen. + {_, OldNodes} = actual_queue_nodes(Q1), + {_, NewNodes} = suggested_queue_nodes(Q1), + {ok, QPid1, [QPid | SPids] -- Alive, + NewNodes -- OldNodes}; _ -> %% Master has changed, and we're not it, %% so leave alone to allow the promoted %% slave to find it and make its %% promotion atomic. - {ok, QPid1, []} + {ok, QPid1, [], []} end end end). -- cgit v1.2.1 From 2cba5f21768390aa9849b8b9d5b796d840af41a1 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 17 Aug 2012 14:07:37 +0100 Subject: Mention parameters here. Oh, and bindings, and don't talk about permissions in such a funny way. --- docs/rabbitmqctl.1.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/rabbitmqctl.1.xml b/docs/rabbitmqctl.1.xml index 1c28ad46..6d93db4c 100644 --- a/docs/rabbitmqctl.1.xml +++ b/docs/rabbitmqctl.1.xml @@ -581,7 +581,7 @@ Deleting a virtual host deletes all its exchanges, - queues, user mappings and associated permissions. + queues, bindings, user permissions and parameters. For example: rabbitmqctl delete_vhost test -- cgit v1.2.1 From 0712501332fafa59e9ba063500be85f0775b5c84 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 17 Aug 2012 17:02:40 +0100 Subject: I was thinking that which_applications needed to come first so that the process would be gone by the time we got to is_process_alive. But if we swap them round the which_applications check will return false in the shutdown case anyway. And of course is_process_alive/1 is rather faster. --- src/rabbit_mirror_queue_misc.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 8c120670..7d1efd50 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -110,8 +110,8 @@ is_really_alive(Pid) -> end. is_really_alive0(Pid) -> - lists:keymember(rabbit, 1, application:which_applications(infinity)) - andalso is_process_alive(Pid). + is_process_alive(Pid) andalso + lists:keymember(rabbit, 1, application:which_applications(infinity)). on_node_up() -> Qs = -- cgit v1.2.1 From 848d4e358203a9966a6b24b881d89aeda2b7a46f Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 17 Aug 2012 17:45:52 +0100 Subject: So strip all that stuff out. --- src/rabbit_mirror_queue_misc.erl | 29 +---------------------------- 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 7d1efd50..89e334dd 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -20,8 +20,6 @@ drop_mirror/2, drop_mirror/3, add_mirror/2, add_mirror/3, report_deaths/4, store_updated_slaves/1]). --export([is_really_alive0/1]). %% For RPC - -include("rabbit.hrl"). %%---------------------------------------------------------------------------- @@ -66,8 +64,7 @@ remove_from_queue(QueueName, DeadPids) -> slave_pids = SPids }] -> [QPid1 | SPids1] = Alive = [Pid || Pid <- [QPid | SPids], - not lists:member(node(Pid), DeadNodes) orelse - is_really_alive(Pid)], + not lists:member(node(Pid), DeadNodes)], case {{QPid, SPids}, {QPid1, SPids1}} of {Same, Same} -> {ok, QPid1, []}; @@ -89,30 +86,6 @@ remove_from_queue(QueueName, DeadPids) -> end end). -%% We want to find out if the pid is alive in case a node restarted -%% quickly and we didn't notice until later (in which case we should -%% do nothing). But if we are dealing with a queue going down because -%% it's in a broker which is shutting down cleanly, its process could -%% still be alive momentarily (since we heard about the death of the -%% *GM* process) so ask the application controller whether rabbit is -%% running - in the above case by the time the controller responds the -%% process will have terminated. -%% -%% See bug 25104. - -is_really_alive(Pid) -> - %% Don't bother optimising the local case since we will only ever - %% call for a remote node - case rpc:call(node(Pid), - rabbit_mirror_queue_misc, is_process_alive0, [Pid]) of - true -> true; - _ -> false - end. - -is_really_alive0(Pid) -> - is_process_alive(Pid) andalso - lists:keymember(rabbit, 1, application:which_applications(infinity)). - on_node_up() -> Qs = rabbit_misc:execute_mnesia_transaction( -- cgit v1.2.1 From 3637d9825e7e7bf85e0b69b1635ae2c9a16d6baf Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 20 Aug 2012 15:30:41 +0100 Subject: cosmetic - save some lines --- src/rabbit_heartbeat.erl | 45 ++++++++++++++------------------------------- 1 file changed, 14 insertions(+), 31 deletions(-) diff --git a/src/rabbit_heartbeat.erl b/src/rabbit_heartbeat.erl index 80b4e768..05aad8c9 100644 --- a/src/rabbit_heartbeat.erl +++ b/src/rabbit_heartbeat.erl @@ -59,21 +59,15 @@ 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 %% where the last message was sent just after a heartbeat. - heartbeater( - {Sock, TimeoutSec * 1000 div 2, send_oct, 0, - fun () -> - SendFun(), - continue - end}). + heartbeater({Sock, TimeoutSec * 1000 div 2, send_oct, 0, + fun () -> SendFun(), continue end}). start_heartbeat_receiver(Sock, TimeoutSec, ReceiveFun) -> %% we check for incoming data every interval, and time out after %% two checks with no change. As a result we will time out between %% 2 and 3 intervals after the last data has been received. - heartbeater({Sock, TimeoutSec * 1000, recv_oct, 1, fun () -> - ReceiveFun(), - stop - end}). + heartbeater({Sock, TimeoutSec * 1000, recv_oct, 1, + fun () -> ReceiveFun(), stop end}). start_heartbeat_fun(SupPid) -> fun (Sock, SendTimeoutSec, SendFun, ReceiveTimeoutSec, ReceiveFun) -> @@ -88,17 +82,11 @@ start_heartbeat_fun(SupPid) -> {Sender, Receiver} end. -pause_monitor({_Sender, none}) -> - ok; -pause_monitor({_Sender, Receiver}) -> - Receiver ! pause, - ok. +pause_monitor({_Sender, none}) -> ok; +pause_monitor({_Sender, Receiver}) -> Receiver ! pause, ok. -resume_monitor({_Sender, none}) -> - ok; -resume_monitor({_Sender, Receiver}) -> - Receiver ! resume, - ok. +resume_monitor({_Sender, none}) -> ok; +resume_monitor({_Sender, Receiver}) -> Receiver ! resume, ok. %%---------------------------------------------------------------------------- start_heartbeater(0, _SupPid, _Sock, _TimeoutFun, _Name, _Callback) -> @@ -106,8 +94,7 @@ start_heartbeater(0, _SupPid, _Sock, _TimeoutFun, _Name, _Callback) -> start_heartbeater(TimeoutSec, SupPid, Sock, TimeoutFun, Name, Callback) -> supervisor2:start_child( SupPid, {Name, - {rabbit_heartbeat, Callback, - [Sock, TimeoutSec, TimeoutFun]}, + {rabbit_heartbeat, Callback, [Sock, TimeoutSec, TimeoutFun]}, transient, ?MAX_WAIT, worker, [rabbit_heartbeat]}). heartbeater(Params) -> @@ -117,15 +104,11 @@ heartbeater({Sock, TimeoutMillisec, StatName, Threshold, Handler} = Params, {StatVal, SameCount}) -> Recurse = fun (V) -> heartbeater(Params, V) 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}); + Other -> exit({unexpected_message, Other}) + end; + Other -> exit({unexpected_message, Other}) after TimeoutMillisec -> case rabbit_net:getstat(Sock, [StatName]) of {ok, [{StatName, NewStatVal}]} -> -- cgit v1.2.1 From c87149c90403112fbfca842fcc11931c6e3036a4 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Tue, 21 Aug 2012 15:21:01 +0100 Subject: add mochijson2 and license, plus some helper functions The helper functions are there to remove the tags that mochijson2 uses to distinguish json objects. --- LICENSE-MIT-Mochi | 9 + src/mochijson2.erl | 893 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/rabbit_misc.erl | 21 ++ 3 files changed, 923 insertions(+) create mode 100644 LICENSE-MIT-Mochi create mode 100644 src/mochijson2.erl diff --git a/LICENSE-MIT-Mochi b/LICENSE-MIT-Mochi new file mode 100644 index 00000000..c85b65a4 --- /dev/null +++ b/LICENSE-MIT-Mochi @@ -0,0 +1,9 @@ +This is the MIT license. + +Copyright (c) 2007 Mochi Media, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/src/mochijson2.erl b/src/mochijson2.erl new file mode 100644 index 00000000..bddb52cc --- /dev/null +++ b/src/mochijson2.erl @@ -0,0 +1,893 @@ +%% This file is a copy of `mochijson2.erl' from mochiweb, revision +%% d541e9a0f36c00dcadc2e589f20e47fbf46fc76f. For the license, see +%% `LICENSE-MIT-Mochi'. + +%% @author Bob Ippolito +%% @copyright 2007 Mochi Media, Inc. + +%% @doc Yet another JSON (RFC 4627) library for Erlang. mochijson2 works +%% with binaries as strings, arrays as lists (without an {array, _}) +%% wrapper and it only knows how to decode UTF-8 (and ASCII). +%% +%% JSON terms are decoded as follows (javascript -> erlang): +%%
    +%%
  • {"key": "value"} -> +%% {struct, [{<<"key">>, <<"value">>}]}
  • +%%
  • ["array", 123, 12.34, true, false, null] -> +%% [<<"array">>, 123, 12.34, true, false, null] +%%
  • +%%
+%%
    +%%
  • Strings in JSON decode to UTF-8 binaries in Erlang
  • +%%
  • Objects decode to {struct, PropList}
  • +%%
  • Numbers decode to integer or float
  • +%%
  • true, false, null decode to their respective terms.
  • +%%
+%% The encoder will accept the same format that the decoder will produce, +%% but will also allow additional cases for leniency: +%%
    +%%
  • atoms other than true, false, null will be considered UTF-8 +%% strings (even as a proplist key) +%%
  • +%%
  • {json, IoList} will insert IoList directly into the output +%% with no validation +%%
  • +%%
  • {array, Array} will be encoded as Array +%% (legacy mochijson style) +%%
  • +%%
  • A non-empty raw proplist will be encoded as an object as long +%% as the first pair does not have an atom key of json, struct, +%% or array +%%
  • +%%
+ +-module(mochijson2). +-author('bob@mochimedia.com'). +-export([encoder/1, encode/1]). +-export([decoder/1, decode/1, decode/2]). + +%% This is a macro to placate syntax highlighters.. +-define(Q, $\"). +-define(ADV_COL(S, N), S#decoder{offset=N+S#decoder.offset, + column=N+S#decoder.column}). +-define(INC_COL(S), S#decoder{offset=1+S#decoder.offset, + column=1+S#decoder.column}). +-define(INC_LINE(S), S#decoder{offset=1+S#decoder.offset, + column=1, + line=1+S#decoder.line}). +-define(INC_CHAR(S, C), + case C of + $\n -> + S#decoder{column=1, + line=1+S#decoder.line, + offset=1+S#decoder.offset}; + _ -> + S#decoder{column=1+S#decoder.column, + offset=1+S#decoder.offset} + end). +-define(IS_WHITESPACE(C), + (C =:= $\s orelse C =:= $\t orelse C =:= $\r orelse C =:= $\n)). + +%% @type json_string() = atom | binary() +%% @type json_number() = integer() | float() +%% @type json_array() = [json_term()] +%% @type json_object() = {struct, [{json_string(), json_term()}]} +%% @type json_eep18_object() = {[{json_string(), json_term()}]} +%% @type json_iolist() = {json, iolist()} +%% @type json_term() = json_string() | json_number() | json_array() | +%% json_object() | json_eep18_object() | json_iolist() + +-record(encoder, {handler=null, + utf8=false}). + +-record(decoder, {object_hook=null, + offset=0, + line=1, + column=1, + state=null}). + +%% @spec encoder([encoder_option()]) -> function() +%% @doc Create an encoder/1 with the given options. +%% @type encoder_option() = handler_option() | utf8_option() +%% @type utf8_option() = boolean(). Emit unicode as utf8 (default - false) +encoder(Options) -> + State = parse_encoder_options(Options, #encoder{}), + fun (O) -> json_encode(O, State) end. + +%% @spec encode(json_term()) -> iolist() +%% @doc Encode the given as JSON to an iolist. +encode(Any) -> + json_encode(Any, #encoder{}). + +%% @spec decoder([decoder_option()]) -> function() +%% @doc Create a decoder/1 with the given options. +decoder(Options) -> + State = parse_decoder_options(Options, #decoder{}), + fun (O) -> json_decode(O, State) end. + +%% @spec decode(iolist(), [{format, proplist | eep18 | struct}]) -> json_term() +%% @doc Decode the given iolist to Erlang terms using the given object format +%% for decoding, where proplist returns JSON objects as [{binary(), json_term()}] +%% proplists, eep18 returns JSON objects as {[binary(), json_term()]}, and struct +%% returns them as-is. +decode(S, Options) -> + json_decode(S, parse_decoder_options(Options, #decoder{})). + +%% @spec decode(iolist()) -> json_term() +%% @doc Decode the given iolist to Erlang terms. +decode(S) -> + json_decode(S, #decoder{}). + +%% Internal API + +parse_encoder_options([], State) -> + State; +parse_encoder_options([{handler, Handler} | Rest], State) -> + parse_encoder_options(Rest, State#encoder{handler=Handler}); +parse_encoder_options([{utf8, Switch} | Rest], State) -> + parse_encoder_options(Rest, State#encoder{utf8=Switch}). + +parse_decoder_options([], State) -> + State; +parse_decoder_options([{object_hook, Hook} | Rest], State) -> + parse_decoder_options(Rest, State#decoder{object_hook=Hook}); +parse_decoder_options([{format, Format} | Rest], State) + when Format =:= struct orelse Format =:= eep18 orelse Format =:= proplist -> + parse_decoder_options(Rest, State#decoder{object_hook=Format}). + +json_encode(true, _State) -> + <<"true">>; +json_encode(false, _State) -> + <<"false">>; +json_encode(null, _State) -> + <<"null">>; +json_encode(I, _State) when is_integer(I) -> + integer_to_list(I); +json_encode(F, _State) when is_float(F) -> + mochinum:digits(F); +json_encode(S, State) when is_binary(S); is_atom(S) -> + json_encode_string(S, State); +json_encode([{K, _}|_] = Props, State) when (K =/= struct andalso + K =/= array andalso + K =/= json) -> + json_encode_proplist(Props, State); +json_encode({struct, Props}, State) when is_list(Props) -> + json_encode_proplist(Props, State); +json_encode({Props}, State) when is_list(Props) -> + json_encode_proplist(Props, State); +json_encode({}, State) -> + json_encode_proplist([], State); +json_encode(Array, State) when is_list(Array) -> + json_encode_array(Array, State); +json_encode({array, Array}, State) when is_list(Array) -> + json_encode_array(Array, State); +json_encode({json, IoList}, _State) -> + IoList; +json_encode(Bad, #encoder{handler=null}) -> + exit({json_encode, {bad_term, Bad}}); +json_encode(Bad, State=#encoder{handler=Handler}) -> + json_encode(Handler(Bad), State). + +json_encode_array([], _State) -> + <<"[]">>; +json_encode_array(L, State) -> + F = fun (O, Acc) -> + [$,, json_encode(O, State) | Acc] + end, + [$, | Acc1] = lists:foldl(F, "[", L), + lists:reverse([$\] | Acc1]). + +json_encode_proplist([], _State) -> + <<"{}">>; +json_encode_proplist(Props, State) -> + F = fun ({K, V}, Acc) -> + KS = json_encode_string(K, State), + VS = json_encode(V, State), + [$,, VS, $:, KS | Acc] + end, + [$, | Acc1] = lists:foldl(F, "{", Props), + lists:reverse([$\} | Acc1]). + +json_encode_string(A, State) when is_atom(A) -> + L = atom_to_list(A), + case json_string_is_safe(L) of + true -> + [?Q, L, ?Q]; + false -> + json_encode_string_unicode(xmerl_ucs:from_utf8(L), State, [?Q]) + end; +json_encode_string(B, State) when is_binary(B) -> + case json_bin_is_safe(B) of + true -> + [?Q, B, ?Q]; + false -> + json_encode_string_unicode(xmerl_ucs:from_utf8(B), State, [?Q]) + end; +json_encode_string(I, _State) when is_integer(I) -> + [?Q, integer_to_list(I), ?Q]; +json_encode_string(L, State) when is_list(L) -> + case json_string_is_safe(L) of + true -> + [?Q, L, ?Q]; + false -> + json_encode_string_unicode(L, State, [?Q]) + end. + +json_string_is_safe([]) -> + true; +json_string_is_safe([C | Rest]) -> + case C of + ?Q -> + false; + $\\ -> + false; + $\b -> + false; + $\f -> + false; + $\n -> + false; + $\r -> + false; + $\t -> + false; + C when C >= 0, C < $\s; C >= 16#7f, C =< 16#10FFFF -> + false; + C when C < 16#7f -> + json_string_is_safe(Rest); + _ -> + false + end. + +json_bin_is_safe(<<>>) -> + true; +json_bin_is_safe(<>) -> + case C of + ?Q -> + false; + $\\ -> + false; + $\b -> + false; + $\f -> + false; + $\n -> + false; + $\r -> + false; + $\t -> + false; + C when C >= 0, C < $\s; C >= 16#7f -> + false; + C when C < 16#7f -> + json_bin_is_safe(Rest) + end. + +json_encode_string_unicode([], _State, Acc) -> + lists:reverse([$\" | Acc]); +json_encode_string_unicode([C | Cs], State, Acc) -> + Acc1 = case C of + ?Q -> + [?Q, $\\ | Acc]; + %% Escaping solidus is only useful when trying to protect + %% against "" injection attacks which are only + %% possible when JSON is inserted into a HTML document + %% in-line. mochijson2 does not protect you from this, so + %% if you do insert directly into HTML then you need to + %% uncomment the following case or escape the output of encode. + %% + %% $/ -> + %% [$/, $\\ | Acc]; + %% + $\\ -> + [$\\, $\\ | Acc]; + $\b -> + [$b, $\\ | Acc]; + $\f -> + [$f, $\\ | Acc]; + $\n -> + [$n, $\\ | Acc]; + $\r -> + [$r, $\\ | Acc]; + $\t -> + [$t, $\\ | Acc]; + C when C >= 0, C < $\s -> + [unihex(C) | Acc]; + C when C >= 16#7f, C =< 16#10FFFF, State#encoder.utf8 -> + [xmerl_ucs:to_utf8(C) | Acc]; + C when C >= 16#7f, C =< 16#10FFFF, not State#encoder.utf8 -> + [unihex(C) | Acc]; + C when C < 16#7f -> + [C | Acc]; + _ -> + exit({json_encode, {bad_char, C}}) + end, + json_encode_string_unicode(Cs, State, Acc1). + +hexdigit(C) when C >= 0, C =< 9 -> + C + $0; +hexdigit(C) when C =< 15 -> + C + $a - 10. + +unihex(C) when C < 16#10000 -> + <> = <>, + Digits = [hexdigit(D) || D <- [D3, D2, D1, D0]], + [$\\, $u | Digits]; +unihex(C) when C =< 16#10FFFF -> + N = C - 16#10000, + S1 = 16#d800 bor ((N bsr 10) band 16#3ff), + S2 = 16#dc00 bor (N band 16#3ff), + [unihex(S1), unihex(S2)]. + +json_decode(L, S) when is_list(L) -> + json_decode(iolist_to_binary(L), S); +json_decode(B, S) -> + {Res, S1} = decode1(B, S), + {eof, _} = tokenize(B, S1#decoder{state=trim}), + Res. + +decode1(B, S=#decoder{state=null}) -> + case tokenize(B, S#decoder{state=any}) of + {{const, C}, S1} -> + {C, S1}; + {start_array, S1} -> + decode_array(B, S1); + {start_object, S1} -> + decode_object(B, S1) + end. + +make_object(V, #decoder{object_hook=N}) when N =:= null orelse N =:= struct -> + V; +make_object({struct, P}, #decoder{object_hook=eep18}) -> + {P}; +make_object({struct, P}, #decoder{object_hook=proplist}) -> + P; +make_object(V, #decoder{object_hook=Hook}) -> + Hook(V). + +decode_object(B, S) -> + decode_object(B, S#decoder{state=key}, []). + +decode_object(B, S=#decoder{state=key}, Acc) -> + case tokenize(B, S) of + {end_object, S1} -> + V = make_object({struct, lists:reverse(Acc)}, S1), + {V, S1#decoder{state=null}}; + {{const, K}, S1} -> + {colon, S2} = tokenize(B, S1), + {V, S3} = decode1(B, S2#decoder{state=null}), + decode_object(B, S3#decoder{state=comma}, [{K, V} | Acc]) + end; +decode_object(B, S=#decoder{state=comma}, Acc) -> + case tokenize(B, S) of + {end_object, S1} -> + V = make_object({struct, lists:reverse(Acc)}, S1), + {V, S1#decoder{state=null}}; + {comma, S1} -> + decode_object(B, S1#decoder{state=key}, Acc) + end. + +decode_array(B, S) -> + decode_array(B, S#decoder{state=any}, []). + +decode_array(B, S=#decoder{state=any}, Acc) -> + case tokenize(B, S) of + {end_array, S1} -> + {lists:reverse(Acc), S1#decoder{state=null}}; + {start_array, S1} -> + {Array, S2} = decode_array(B, S1), + decode_array(B, S2#decoder{state=comma}, [Array | Acc]); + {start_object, S1} -> + {Array, S2} = decode_object(B, S1), + decode_array(B, S2#decoder{state=comma}, [Array | Acc]); + {{const, Const}, S1} -> + decode_array(B, S1#decoder{state=comma}, [Const | Acc]) + end; +decode_array(B, S=#decoder{state=comma}, Acc) -> + case tokenize(B, S) of + {end_array, S1} -> + {lists:reverse(Acc), S1#decoder{state=null}}; + {comma, S1} -> + decode_array(B, S1#decoder{state=any}, Acc) + end. + +tokenize_string(B, S=#decoder{offset=O}) -> + case tokenize_string_fast(B, O) of + {escape, O1} -> + Length = O1 - O, + S1 = ?ADV_COL(S, Length), + <<_:O/binary, Head:Length/binary, _/binary>> = B, + tokenize_string(B, S1, lists:reverse(binary_to_list(Head))); + O1 -> + Length = O1 - O, + <<_:O/binary, String:Length/binary, ?Q, _/binary>> = B, + {{const, String}, ?ADV_COL(S, Length + 1)} + end. + +tokenize_string_fast(B, O) -> + case B of + <<_:O/binary, ?Q, _/binary>> -> + O; + <<_:O/binary, $\\, _/binary>> -> + {escape, O}; + <<_:O/binary, C1, _/binary>> when C1 < 128 -> + tokenize_string_fast(B, 1 + O); + <<_:O/binary, C1, C2, _/binary>> when C1 >= 194, C1 =< 223, + C2 >= 128, C2 =< 191 -> + tokenize_string_fast(B, 2 + O); + <<_:O/binary, C1, C2, C3, _/binary>> when C1 >= 224, C1 =< 239, + C2 >= 128, C2 =< 191, + C3 >= 128, C3 =< 191 -> + tokenize_string_fast(B, 3 + O); + <<_:O/binary, C1, C2, C3, C4, _/binary>> when C1 >= 240, C1 =< 244, + C2 >= 128, C2 =< 191, + C3 >= 128, C3 =< 191, + C4 >= 128, C4 =< 191 -> + tokenize_string_fast(B, 4 + O); + _ -> + throw(invalid_utf8) + end. + +tokenize_string(B, S=#decoder{offset=O}, Acc) -> + case B of + <<_:O/binary, ?Q, _/binary>> -> + {{const, iolist_to_binary(lists:reverse(Acc))}, ?INC_COL(S)}; + <<_:O/binary, "\\\"", _/binary>> -> + tokenize_string(B, ?ADV_COL(S, 2), [$\" | Acc]); + <<_:O/binary, "\\\\", _/binary>> -> + tokenize_string(B, ?ADV_COL(S, 2), [$\\ | Acc]); + <<_:O/binary, "\\/", _/binary>> -> + tokenize_string(B, ?ADV_COL(S, 2), [$/ | Acc]); + <<_:O/binary, "\\b", _/binary>> -> + tokenize_string(B, ?ADV_COL(S, 2), [$\b | Acc]); + <<_:O/binary, "\\f", _/binary>> -> + tokenize_string(B, ?ADV_COL(S, 2), [$\f | Acc]); + <<_:O/binary, "\\n", _/binary>> -> + tokenize_string(B, ?ADV_COL(S, 2), [$\n | Acc]); + <<_:O/binary, "\\r", _/binary>> -> + tokenize_string(B, ?ADV_COL(S, 2), [$\r | Acc]); + <<_:O/binary, "\\t", _/binary>> -> + tokenize_string(B, ?ADV_COL(S, 2), [$\t | Acc]); + <<_:O/binary, "\\u", C3, C2, C1, C0, Rest/binary>> -> + C = erlang:list_to_integer([C3, C2, C1, C0], 16), + if C > 16#D7FF, C < 16#DC00 -> + %% coalesce UTF-16 surrogate pair + <<"\\u", D3, D2, D1, D0, _/binary>> = Rest, + D = erlang:list_to_integer([D3,D2,D1,D0], 16), + [CodePoint] = xmerl_ucs:from_utf16be(<>), + Acc1 = lists:reverse(xmerl_ucs:to_utf8(CodePoint), Acc), + tokenize_string(B, ?ADV_COL(S, 12), Acc1); + true -> + Acc1 = lists:reverse(xmerl_ucs:to_utf8(C), Acc), + tokenize_string(B, ?ADV_COL(S, 6), Acc1) + end; + <<_:O/binary, C1, _/binary>> when C1 < 128 -> + tokenize_string(B, ?INC_CHAR(S, C1), [C1 | Acc]); + <<_:O/binary, C1, C2, _/binary>> when C1 >= 194, C1 =< 223, + C2 >= 128, C2 =< 191 -> + tokenize_string(B, ?ADV_COL(S, 2), [C2, C1 | Acc]); + <<_:O/binary, C1, C2, C3, _/binary>> when C1 >= 224, C1 =< 239, + C2 >= 128, C2 =< 191, + C3 >= 128, C3 =< 191 -> + tokenize_string(B, ?ADV_COL(S, 3), [C3, C2, C1 | Acc]); + <<_:O/binary, C1, C2, C3, C4, _/binary>> when C1 >= 240, C1 =< 244, + C2 >= 128, C2 =< 191, + C3 >= 128, C3 =< 191, + C4 >= 128, C4 =< 191 -> + tokenize_string(B, ?ADV_COL(S, 4), [C4, C3, C2, C1 | Acc]); + _ -> + throw(invalid_utf8) + end. + +tokenize_number(B, S) -> + case tokenize_number(B, sign, S, []) of + {{int, Int}, S1} -> + {{const, list_to_integer(Int)}, S1}; + {{float, Float}, S1} -> + {{const, list_to_float(Float)}, S1} + end. + +tokenize_number(B, sign, S=#decoder{offset=O}, []) -> + case B of + <<_:O/binary, $-, _/binary>> -> + tokenize_number(B, int, ?INC_COL(S), [$-]); + _ -> + tokenize_number(B, int, S, []) + end; +tokenize_number(B, int, S=#decoder{offset=O}, Acc) -> + case B of + <<_:O/binary, $0, _/binary>> -> + tokenize_number(B, frac, ?INC_COL(S), [$0 | Acc]); + <<_:O/binary, C, _/binary>> when C >= $1 andalso C =< $9 -> + tokenize_number(B, int1, ?INC_COL(S), [C | Acc]) + end; +tokenize_number(B, int1, S=#decoder{offset=O}, Acc) -> + case B of + <<_:O/binary, C, _/binary>> when C >= $0 andalso C =< $9 -> + tokenize_number(B, int1, ?INC_COL(S), [C | Acc]); + _ -> + tokenize_number(B, frac, S, Acc) + end; +tokenize_number(B, frac, S=#decoder{offset=O}, Acc) -> + case B of + <<_:O/binary, $., C, _/binary>> when C >= $0, C =< $9 -> + tokenize_number(B, frac1, ?ADV_COL(S, 2), [C, $. | Acc]); + <<_:O/binary, E, _/binary>> when E =:= $e orelse E =:= $E -> + tokenize_number(B, esign, ?INC_COL(S), [$e, $0, $. | Acc]); + _ -> + {{int, lists:reverse(Acc)}, S} + end; +tokenize_number(B, frac1, S=#decoder{offset=O}, Acc) -> + case B of + <<_:O/binary, C, _/binary>> when C >= $0 andalso C =< $9 -> + tokenize_number(B, frac1, ?INC_COL(S), [C | Acc]); + <<_:O/binary, E, _/binary>> when E =:= $e orelse E =:= $E -> + tokenize_number(B, esign, ?INC_COL(S), [$e | Acc]); + _ -> + {{float, lists:reverse(Acc)}, S} + end; +tokenize_number(B, esign, S=#decoder{offset=O}, Acc) -> + case B of + <<_:O/binary, C, _/binary>> when C =:= $- orelse C=:= $+ -> + tokenize_number(B, eint, ?INC_COL(S), [C | Acc]); + _ -> + tokenize_number(B, eint, S, Acc) + end; +tokenize_number(B, eint, S=#decoder{offset=O}, Acc) -> + case B of + <<_:O/binary, C, _/binary>> when C >= $0 andalso C =< $9 -> + tokenize_number(B, eint1, ?INC_COL(S), [C | Acc]) + end; +tokenize_number(B, eint1, S=#decoder{offset=O}, Acc) -> + case B of + <<_:O/binary, C, _/binary>> when C >= $0 andalso C =< $9 -> + tokenize_number(B, eint1, ?INC_COL(S), [C | Acc]); + _ -> + {{float, lists:reverse(Acc)}, S} + end. + +tokenize(B, S=#decoder{offset=O}) -> + case B of + <<_:O/binary, C, _/binary>> when ?IS_WHITESPACE(C) -> + tokenize(B, ?INC_CHAR(S, C)); + <<_:O/binary, "{", _/binary>> -> + {start_object, ?INC_COL(S)}; + <<_:O/binary, "}", _/binary>> -> + {end_object, ?INC_COL(S)}; + <<_:O/binary, "[", _/binary>> -> + {start_array, ?INC_COL(S)}; + <<_:O/binary, "]", _/binary>> -> + {end_array, ?INC_COL(S)}; + <<_:O/binary, ",", _/binary>> -> + {comma, ?INC_COL(S)}; + <<_:O/binary, ":", _/binary>> -> + {colon, ?INC_COL(S)}; + <<_:O/binary, "null", _/binary>> -> + {{const, null}, ?ADV_COL(S, 4)}; + <<_:O/binary, "true", _/binary>> -> + {{const, true}, ?ADV_COL(S, 4)}; + <<_:O/binary, "false", _/binary>> -> + {{const, false}, ?ADV_COL(S, 5)}; + <<_:O/binary, "\"", _/binary>> -> + tokenize_string(B, ?INC_COL(S)); + <<_:O/binary, C, _/binary>> when (C >= $0 andalso C =< $9) + orelse C =:= $- -> + tokenize_number(B, S); + <<_:O/binary>> -> + trim = S#decoder.state, + {eof, S} + end. +%% +%% Tests +%% +-ifdef(TEST). +-include_lib("eunit/include/eunit.hrl"). + + +%% testing constructs borrowed from the Yaws JSON implementation. + +%% Create an object from a list of Key/Value pairs. + +obj_new() -> + {struct, []}. + +is_obj({struct, Props}) -> + F = fun ({K, _}) when is_binary(K) -> true end, + lists:all(F, Props). + +obj_from_list(Props) -> + Obj = {struct, Props}, + ?assert(is_obj(Obj)), + Obj. + +%% Test for equivalence of Erlang terms. +%% Due to arbitrary order of construction, equivalent objects might +%% compare unequal as erlang terms, so we need to carefully recurse +%% through aggregates (tuples and objects). + +equiv({struct, Props1}, {struct, Props2}) -> + equiv_object(Props1, Props2); +equiv(L1, L2) when is_list(L1), is_list(L2) -> + equiv_list(L1, L2); +equiv(N1, N2) when is_number(N1), is_number(N2) -> N1 == N2; +equiv(B1, B2) when is_binary(B1), is_binary(B2) -> B1 == B2; +equiv(A, A) when A =:= true orelse A =:= false orelse A =:= null -> true. + +%% Object representation and traversal order is unknown. +%% Use the sledgehammer and sort property lists. + +equiv_object(Props1, Props2) -> + L1 = lists:keysort(1, Props1), + L2 = lists:keysort(1, Props2), + Pairs = lists:zip(L1, L2), + true = lists:all(fun({{K1, V1}, {K2, V2}}) -> + equiv(K1, K2) and equiv(V1, V2) + end, Pairs). + +%% Recursively compare tuple elements for equivalence. + +equiv_list([], []) -> + true; +equiv_list([V1 | L1], [V2 | L2]) -> + equiv(V1, V2) andalso equiv_list(L1, L2). + +decode_test() -> + [1199344435545.0, 1] = decode(<<"[1199344435545.0,1]">>), + <<16#F0,16#9D,16#9C,16#95>> = decode([34,"\\ud835","\\udf15",34]). + +e2j_vec_test() -> + test_one(e2j_test_vec(utf8), 1). + +test_one([], _N) -> + %% io:format("~p tests passed~n", [N-1]), + ok; +test_one([{E, J} | Rest], N) -> + %% io:format("[~p] ~p ~p~n", [N, E, J]), + true = equiv(E, decode(J)), + true = equiv(E, decode(encode(E))), + test_one(Rest, 1+N). + +e2j_test_vec(utf8) -> + [ + {1, "1"}, + {3.1416, "3.14160"}, %% text representation may truncate, trail zeroes + {-1, "-1"}, + {-3.1416, "-3.14160"}, + {12.0e10, "1.20000e+11"}, + {1.234E+10, "1.23400e+10"}, + {-1.234E-10, "-1.23400e-10"}, + {10.0, "1.0e+01"}, + {123.456, "1.23456E+2"}, + {10.0, "1e1"}, + {<<"foo">>, "\"foo\""}, + {<<"foo", 5, "bar">>, "\"foo\\u0005bar\""}, + {<<"">>, "\"\""}, + {<<"\n\n\n">>, "\"\\n\\n\\n\""}, + {<<"\" \b\f\r\n\t\"">>, "\"\\\" \\b\\f\\r\\n\\t\\\"\""}, + {obj_new(), "{}"}, + {obj_from_list([{<<"foo">>, <<"bar">>}]), "{\"foo\":\"bar\"}"}, + {obj_from_list([{<<"foo">>, <<"bar">>}, {<<"baz">>, 123}]), + "{\"foo\":\"bar\",\"baz\":123}"}, + {[], "[]"}, + {[[]], "[[]]"}, + {[1, <<"foo">>], "[1,\"foo\"]"}, + + %% json array in a json object + {obj_from_list([{<<"foo">>, [123]}]), + "{\"foo\":[123]}"}, + + %% json object in a json object + {obj_from_list([{<<"foo">>, obj_from_list([{<<"bar">>, true}])}]), + "{\"foo\":{\"bar\":true}}"}, + + %% fold evaluation order + {obj_from_list([{<<"foo">>, []}, + {<<"bar">>, obj_from_list([{<<"baz">>, true}])}, + {<<"alice">>, <<"bob">>}]), + "{\"foo\":[],\"bar\":{\"baz\":true},\"alice\":\"bob\"}"}, + + %% json object in a json array + {[-123, <<"foo">>, obj_from_list([{<<"bar">>, []}]), null], + "[-123,\"foo\",{\"bar\":[]},null]"} + ]. + +%% test utf8 encoding +encoder_utf8_test() -> + %% safe conversion case (default) + [34,"\\u0001","\\u0442","\\u0435","\\u0441","\\u0442",34] = + encode(<<1,"\321\202\320\265\321\201\321\202">>), + + %% raw utf8 output (optional) + Enc = mochijson2:encoder([{utf8, true}]), + [34,"\\u0001",[209,130],[208,181],[209,129],[209,130],34] = + Enc(<<1,"\321\202\320\265\321\201\321\202">>). + +input_validation_test() -> + Good = [ + {16#00A3, <>}, %% pound + {16#20AC, <>}, %% euro + {16#10196, <>} %% denarius + ], + lists:foreach(fun({CodePoint, UTF8}) -> + Expect = list_to_binary(xmerl_ucs:to_utf8(CodePoint)), + Expect = decode(UTF8) + end, Good), + + Bad = [ + %% 2nd, 3rd, or 4th byte of a multi-byte sequence w/o leading byte + <>, + %% missing continuations, last byte in each should be 80-BF + <>, + <>, + <>, + %% we don't support code points > 10FFFF per RFC 3629 + <>, + %% escape characters trigger a different code path + <> + ], + lists:foreach( + fun(X) -> + ok = try decode(X) catch invalid_utf8 -> ok end, + %% could be {ucs,{bad_utf8_character_code}} or + %% {json_encode,{bad_char,_}} + {'EXIT', _} = (catch encode(X)) + end, Bad). + +inline_json_test() -> + ?assertEqual(<<"\"iodata iodata\"">>, + iolist_to_binary( + encode({json, [<<"\"iodata">>, " iodata\""]}))), + ?assertEqual({struct, [{<<"key">>, <<"iodata iodata">>}]}, + decode( + encode({struct, + [{key, {json, [<<"\"iodata">>, " iodata\""]}}]}))), + ok. + +big_unicode_test() -> + UTF8Seq = list_to_binary(xmerl_ucs:to_utf8(16#0001d120)), + ?assertEqual( + <<"\"\\ud834\\udd20\"">>, + iolist_to_binary(encode(UTF8Seq))), + ?assertEqual( + UTF8Seq, + decode(iolist_to_binary(encode(UTF8Seq)))), + ok. + +custom_decoder_test() -> + ?assertEqual( + {struct, [{<<"key">>, <<"value">>}]}, + (decoder([]))("{\"key\": \"value\"}")), + F = fun ({struct, [{<<"key">>, <<"value">>}]}) -> win end, + ?assertEqual( + win, + (decoder([{object_hook, F}]))("{\"key\": \"value\"}")), + ok. + +atom_test() -> + %% JSON native atoms + [begin + ?assertEqual(A, decode(atom_to_list(A))), + ?assertEqual(iolist_to_binary(atom_to_list(A)), + iolist_to_binary(encode(A))) + end || A <- [true, false, null]], + %% Atom to string + ?assertEqual( + <<"\"foo\"">>, + iolist_to_binary(encode(foo))), + ?assertEqual( + <<"\"\\ud834\\udd20\"">>, + iolist_to_binary(encode(list_to_atom(xmerl_ucs:to_utf8(16#0001d120))))), + ok. + +key_encode_test() -> + %% Some forms are accepted as keys that would not be strings in other + %% cases + ?assertEqual( + <<"{\"foo\":1}">>, + iolist_to_binary(encode({struct, [{foo, 1}]}))), + ?assertEqual( + <<"{\"foo\":1}">>, + iolist_to_binary(encode({struct, [{<<"foo">>, 1}]}))), + ?assertEqual( + <<"{\"foo\":1}">>, + iolist_to_binary(encode({struct, [{"foo", 1}]}))), + ?assertEqual( + <<"{\"foo\":1}">>, + iolist_to_binary(encode([{foo, 1}]))), + ?assertEqual( + <<"{\"foo\":1}">>, + iolist_to_binary(encode([{<<"foo">>, 1}]))), + ?assertEqual( + <<"{\"foo\":1}">>, + iolist_to_binary(encode([{"foo", 1}]))), + ?assertEqual( + <<"{\"\\ud834\\udd20\":1}">>, + iolist_to_binary( + encode({struct, [{[16#0001d120], 1}]}))), + ?assertEqual( + <<"{\"1\":1}">>, + iolist_to_binary(encode({struct, [{1, 1}]}))), + ok. + +unsafe_chars_test() -> + Chars = "\"\\\b\f\n\r\t", + [begin + ?assertEqual(false, json_string_is_safe([C])), + ?assertEqual(false, json_bin_is_safe(<>)), + ?assertEqual(<>, decode(encode(<>))) + end || C <- Chars], + ?assertEqual( + false, + json_string_is_safe([16#0001d120])), + ?assertEqual( + false, + json_bin_is_safe(list_to_binary(xmerl_ucs:to_utf8(16#0001d120)))), + ?assertEqual( + [16#0001d120], + xmerl_ucs:from_utf8( + binary_to_list( + decode(encode(list_to_atom(xmerl_ucs:to_utf8(16#0001d120))))))), + ?assertEqual( + false, + json_string_is_safe([16#110000])), + ?assertEqual( + false, + json_bin_is_safe(list_to_binary(xmerl_ucs:to_utf8([16#110000])))), + %% solidus can be escaped but isn't unsafe by default + ?assertEqual( + <<"/">>, + decode(<<"\"\\/\"">>)), + ok. + +int_test() -> + ?assertEqual(0, decode("0")), + ?assertEqual(1, decode("1")), + ?assertEqual(11, decode("11")), + ok. + +large_int_test() -> + ?assertEqual(<<"-2147483649214748364921474836492147483649">>, + iolist_to_binary(encode(-2147483649214748364921474836492147483649))), + ?assertEqual(<<"2147483649214748364921474836492147483649">>, + iolist_to_binary(encode(2147483649214748364921474836492147483649))), + ok. + +float_test() -> + ?assertEqual(<<"-2147483649.0">>, iolist_to_binary(encode(-2147483649.0))), + ?assertEqual(<<"2147483648.0">>, iolist_to_binary(encode(2147483648.0))), + ok. + +handler_test() -> + ?assertEqual( + {'EXIT',{json_encode,{bad_term,{x,y}}}}, + catch encode({x,y})), + F = fun ({x,y}) -> [] end, + ?assertEqual( + <<"[]">>, + iolist_to_binary((encoder([{handler, F}]))({x, y}))), + ok. + +encode_empty_test_() -> + [{A, ?_assertEqual(<<"{}">>, iolist_to_binary(encode(B)))} + || {A, B} <- [{"eep18 {}", {}}, + {"eep18 {[]}", {[]}}, + {"{struct, []}", {struct, []}}]]. + +encode_test_() -> + P = [{<<"k">>, <<"v">>}], + JSON = iolist_to_binary(encode({struct, P})), + [{atom_to_list(F), + ?_assertEqual(JSON, iolist_to_binary(encode(decode(JSON, [{format, F}]))))} + || F <- [struct, eep18, proplist]]. + +format_test_() -> + P = [{<<"k">>, <<"v">>}], + JSON = iolist_to_binary(encode({struct, P})), + [{atom_to_list(F), + ?_assertEqual(A, decode(JSON, [{format, F}]))} + || {F, A} <- [{struct, {struct, P}}, + {eep18, {P}}, + {proplist, P}]]. + +-endif. diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 8f6a9bcf..55048d59 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -60,6 +60,7 @@ -export([multi_call/2]). -export([os_cmd/1]). -export([gb_sets_difference/2]). +-export([json_decode/1, json_encode/1]). %% Horrible macro to use in guards -define(IS_BENIGN_EXIT(R), @@ -934,3 +935,23 @@ os_cmd(Command) -> gb_sets_difference(S1, S2) -> gb_sets:fold(fun gb_sets:delete_any/2, S1, S2). + +json_to_term({struct, Obj}) -> + lists:map(fun ({K, V}) -> {K, json_to_term(V)} end, Obj); +json_to_term(Array) when is_list(Array) -> + lists:map(fun json_to_term/1, Array); +json_to_term(Value) -> + Value. + +json_decode(Body) -> + json_to_term(mochijson2:decode(Body)). + +term_to_json([{_, _}|_] = Obj) -> + {struct, lists:map(fun ({K, V}) -> {K, term_to_json(V)} end, Obj)}; +term_to_json(Array) when is_list(Array) -> + lists:map(fun term_to_json/1, Array); +term_to_json(Value) -> + Value. + +json_encode(Term) -> + mochijson2:encode(term_to_json(Term)). -- cgit v1.2.1 From b6f3a9ae3212e9241cc877c236439bdfc19e625c Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Tue, 21 Aug 2012 17:49:07 +0100 Subject: remove the wrapper functions around mochijson2 In the end it's better to keep the `struct' tag - empty lists are ambiguous. I was mislead by the fact that the management plugin removes the outer `struct'. --- src/rabbit_misc.erl | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 55048d59..8f6a9bcf 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -60,7 +60,6 @@ -export([multi_call/2]). -export([os_cmd/1]). -export([gb_sets_difference/2]). --export([json_decode/1, json_encode/1]). %% Horrible macro to use in guards -define(IS_BENIGN_EXIT(R), @@ -935,23 +934,3 @@ os_cmd(Command) -> gb_sets_difference(S1, S2) -> gb_sets:fold(fun gb_sets:delete_any/2, S1, S2). - -json_to_term({struct, Obj}) -> - lists:map(fun ({K, V}) -> {K, json_to_term(V)} end, Obj); -json_to_term(Array) when is_list(Array) -> - lists:map(fun json_to_term/1, Array); -json_to_term(Value) -> - Value. - -json_decode(Body) -> - json_to_term(mochijson2:decode(Body)). - -term_to_json([{_, _}|_] = Obj) -> - {struct, lists:map(fun ({K, V}) -> {K, term_to_json(V)} end, Obj)}; -term_to_json(Array) when is_list(Array) -> - lists:map(fun term_to_json/1, Array); -term_to_json(Value) -> - Value. - -json_encode(Term) -> - mochijson2:encode(term_to_json(Term)). -- cgit v1.2.1 From 6d8eac9dd963b60b36e2732e25666cf188f9ff0f Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Tue, 21 Aug 2012 18:10:45 +0100 Subject: Simon's comment on the rabbitmqctl manpage --- docs/rabbitmqctl.1.xml | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/docs/rabbitmqctl.1.xml b/docs/rabbitmqctl.1.xml index 065de14c..4703f180 100644 --- a/docs/rabbitmqctl.1.xml +++ b/docs/rabbitmqctl.1.xml @@ -318,7 +318,10 @@ the event of node failure and recovery from global events such as power failure across all nodes. Ram nodes replicate data in ram only and are mainly used for scalability. A cluster must always - have at least one disk node. + have at least one disk node. Note that the queue data will always + be on disc, including on ram nodes. This makes ram nodes more + performant only when managing resources (e.g. adding/removing + queues, exhanges, or bindings). The node will be a disk node by default. If you wish to wish to @@ -331,8 +334,8 @@ node went down. - To leave a cluster, you can simply reset the - node. You can also remove nodes remotely with the + To leave a cluster, reset the node. You can + also remove nodes remotely with the remove_cluster_node command. @@ -341,7 +344,7 @@ guide. For example: - rabbitmqctl cluster hare@elena --ram + rabbitmqctl join_cluster hare@elena --ram This command instructs the RabbitMQ node to join the cluster that hare@elena is part of, as a ram node. @@ -382,8 +385,8 @@ For example: rabbitmqctl change_cluster_node_type disk - This command displays will turn a ram node into a disk node - (provided that other disk nodes exist in the cluster). + This command will turn a ram node into a disk node (provided that + other disk nodes exist in the cluster). @@ -414,7 +417,7 @@
Removes a cluster node remotely. The node that is being removed - must be online, while the node we are removing from must be + must be offline, while the node we are removing from must be online, except when using the --offline flag. For example: -- cgit v1.2.1 From db2cd27b33b092d2b9853d71a2b78b67a843882b Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Tue, 21 Aug 2012 18:13:33 +0100 Subject: forgot about the recluster example --- docs/rabbitmqctl.1.xml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/docs/rabbitmqctl.1.xml b/docs/rabbitmqctl.1.xml index 4703f180..7492eb82 100644 --- a/docs/rabbitmqctl.1.xml +++ b/docs/rabbitmqctl.1.xml @@ -465,12 +465,6 @@ anymore. recluster -n A C will solve this situation. - For example: - rabbitmqctl change_cluster_node_type disk - - This command displays will turn a ram node into a disk node - (provided that other disk nodes exist in the cluster). - -- cgit v1.2.1 From 7a0ca3ef325035d83133ed07234d63d25b3d7ec0 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Tue, 21 Aug 2012 18:18:48 +0100 Subject: recluster => update_cluster_nodes --- docs/rabbitmqctl.1.xml | 6 +++--- src/rabbit_control_main.erl | 6 +++--- src/rabbit_mnesia.erl | 14 +++++++------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/rabbitmqctl.1.xml b/docs/rabbitmqctl.1.xml index 7492eb82..ae39effa 100644 --- a/docs/rabbitmqctl.1.xml +++ b/docs/rabbitmqctl.1.xml @@ -434,7 +434,7 @@ - recluster + update_cluster_nodes clusternode @@ -444,7 +444,7 @@ clusternode - The node to recluster with. + The node to update_cluster_nodes with. @@ -462,7 +462,7 @@ which node A and B are clustered. A goes down, C clusters with C, and then B leaves the cluster. When A wakes up, it'll try to contact B, but this will fail since B is not in the cluster - anymore. recluster -n A C will solve this + anymore. update_cluster_nodes -n A C will solve this situation. diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index b92493e3..c58c8eee 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -47,7 +47,7 @@ {join_cluster, [?RAM_DEF]}, change_cluster_node_type, - recluster, + update_cluster_nodes, {remove_cluster_node, [?OFFLINE_DEF]}, cluster_status, @@ -259,10 +259,10 @@ action(change_cluster_node_type, Node, [Type], _Opts, Inform) Inform("Turning ~p into a disc node", [Node]), rpc_call(Node, rabbit_mnesia, change_cluster_node_type, [disc]); -action(recluster, Node, [ClusterNodeS], _Opts, Inform) -> +action(update_cluster_nodes, Node, [ClusterNodeS], _Opts, Inform) -> ClusterNode = list_to_atom(ClusterNodeS), Inform("Re-clustering ~p with ~p", [Node, ClusterNode]), - rpc_call(Node, rabbit_mnesia, recluster, [ClusterNode]); + rpc_call(Node, rabbit_mnesia, update_cluster_nodes, [ClusterNode]); action(remove_cluster_node, Node, [ClusterNodeS], Opts, Inform) -> ClusterNode = list_to_atom(ClusterNodeS), diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 6aade1da..d839dd7a 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -22,7 +22,7 @@ join_cluster/2, reset/0, force_reset/0, - recluster/1, + update_cluster_nodes/1, change_cluster_node_type/1, remove_cluster_node/2, @@ -75,7 +75,7 @@ -spec(join_cluster/2 :: ([node()], boolean()) -> 'ok'). -spec(reset/0 :: () -> 'ok'). -spec(force_reset/0 :: () -> 'ok'). --spec(recluster/1 :: (node()) -> 'ok'). +-spec(update_cluster_nodes/1 :: (node()) -> 'ok'). -spec(change_cluster_node_type/1 :: (node_type()) -> 'ok'). -spec(remove_cluster_node/2 :: (node(), boolean()) -> 'ok'). @@ -152,8 +152,8 @@ join_cluster(DiscoveryNode, WantDiscNode) -> {standalone_ram_node, "You can't cluster a node if it's the only " "disc node in its existing cluster. If new nodes " - "joined while this node was offline, use \"recluster\" " - "to add them manually"}}); + "joined while this node was offline, use " + "\"update_cluster_nodes\" to add them manually"}}); _ -> ok end, @@ -254,8 +254,8 @@ change_cluster_node_type(Type) -> {cannot_connect_to_cluster, "Could not connect to the cluster nodes present in " "this node status file. If the cluster has changed, " - "you can use the \"recluster\" command to point to the " - "new cluster nodes"}}) + "you can use the \"update_cluster_nodes\" command to " + "point to the new cluster nodes"}}) end, WantDiscNode = case Type of ram -> false; @@ -271,7 +271,7 @@ change_cluster_node_type(Type) -> end, ok = init_db_with_mnesia(AllNodes, WantDiscNode, false). -recluster(DiscoveryNode) -> +update_cluster_nodes(DiscoveryNode) -> ensure_mnesia_not_running(), ensure_mnesia_dir(), -- cgit v1.2.1 From 40a804ceefa01a1931501c9b20b6d1a639af9805 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Tue, 21 Aug 2012 18:23:33 +0100 Subject: more manpage fixes --- docs/rabbitmqctl.1.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/rabbitmqctl.1.xml b/docs/rabbitmqctl.1.xml index ae39effa..720df077 100644 --- a/docs/rabbitmqctl.1.xml +++ b/docs/rabbitmqctl.1.xml @@ -321,7 +321,7 @@ have at least one disk node. Note that the queue data will always be on disc, including on ram nodes. This makes ram nodes more performant only when managing resources (e.g. adding/removing - queues, exhanges, or bindings). + queues, exchanges, or bindings). The node will be a disk node by default. If you wish to wish to @@ -444,7 +444,7 @@ clusternode - The node to update_cluster_nodes with. + The node to consult for up to date information. @@ -462,8 +462,8 @@ which node A and B are clustered. A goes down, C clusters with C, and then B leaves the cluster. When A wakes up, it'll try to contact B, but this will fail since B is not in the cluster - anymore. update_cluster_nodes -n A C will solve this - situation. + anymore. update_cluster_nodes -n A C will solve + this situation. -- cgit v1.2.1 From 237c3a8e99a4dea1b7d297c4298d3b19d655c446 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Tue, 21 Aug 2012 18:54:54 +0100 Subject: manpage fix --- docs/rabbitmqctl.1.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/rabbitmqctl.1.xml b/docs/rabbitmqctl.1.xml index 720df077..ac73e84a 100644 --- a/docs/rabbitmqctl.1.xml +++ b/docs/rabbitmqctl.1.xml @@ -385,8 +385,7 @@ For example: rabbitmqctl change_cluster_node_type disk - This command will turn a ram node into a disk node (provided that - other disk nodes exist in the cluster). + This command will turn a ram node into a disk node. -- cgit v1.2.1 From c9ead9eb132006c67366064d65c789c76a3b355a Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Tue, 21 Aug 2012 19:11:27 +0100 Subject: remove_cluster_node => forget_cluster_node --- docs/rabbitmqctl.1.xml | 6 +++--- src/rabbit_control_main.erl | 6 +++--- src/rabbit_mnesia.erl | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/rabbitmqctl.1.xml b/docs/rabbitmqctl.1.xml index ac73e84a..c74ec785 100644 --- a/docs/rabbitmqctl.1.xml +++ b/docs/rabbitmqctl.1.xml @@ -336,7 +336,7 @@ To leave a cluster, reset the node. You can also remove nodes remotely with the - remove_cluster_node command. + forget_cluster_node command. For more details see the - remove_cluster_node + forget_cluster_node --offline @@ -420,7 +420,7 @@ online, except when using the --offline flag. For example: - rabbitmqctl -n hare@mcnulty remove_cluster_node rabbit@stringer + rabbitmqctl -n hare@mcnulty forget_cluster_node rabbit@stringer This command will remove the node rabbit@stringer from the node diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index c58c8eee..0caa5be6 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -48,7 +48,7 @@ {join_cluster, [?RAM_DEF]}, change_cluster_node_type, update_cluster_nodes, - {remove_cluster_node, [?OFFLINE_DEF]}, + {forget_cluster_node, [?OFFLINE_DEF]}, cluster_status, add_user, @@ -264,11 +264,11 @@ action(update_cluster_nodes, Node, [ClusterNodeS], _Opts, Inform) -> Inform("Re-clustering ~p with ~p", [Node, ClusterNode]), rpc_call(Node, rabbit_mnesia, update_cluster_nodes, [ClusterNode]); -action(remove_cluster_node, Node, [ClusterNodeS], Opts, Inform) -> +action(forget_cluster_node, Node, [ClusterNodeS], Opts, Inform) -> ClusterNode = list_to_atom(ClusterNodeS), RemoveWhenOffline = proplists:get_bool(?OFFLINE_OPT, Opts), Inform("Removing node ~p from cluster", [ClusterNode]), - rpc_call(Node, rabbit_mnesia, remove_cluster_node, + rpc_call(Node, rabbit_mnesia, forget_cluster_node, [ClusterNode, RemoveWhenOffline]); action(wait, Node, [PidFile], _Opts, Inform) -> diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index d839dd7a..7c2a88cb 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -24,7 +24,7 @@ force_reset/0, update_cluster_nodes/1, change_cluster_node_type/1, - remove_cluster_node/2, + forget_cluster_node/2, status/0, is_db_empty/0, @@ -77,7 +77,7 @@ -spec(force_reset/0 :: () -> 'ok'). -spec(update_cluster_nodes/1 :: (node()) -> 'ok'). -spec(change_cluster_node_type/1 :: (node_type()) -> 'ok'). --spec(remove_cluster_node/2 :: (node(), boolean()) -> 'ok'). +-spec(forget_cluster_node/2 :: (node(), boolean()) -> 'ok'). %% Various queries to get the status of the db -spec(status/0 :: () -> [{'nodes', [{node_type(), [node()]}]} | @@ -304,7 +304,7 @@ update_cluster_nodes(DiscoveryNode) -> %% * All other nodes are offline %% * This node was, at the best of our knowledge (see comment below) the last %% or second to last after the node we're removing to go down -remove_cluster_node(Node, RemoveWhenOffline) -> +forget_cluster_node(Node, RemoveWhenOffline) -> case ordsets:is_element(Node, all_clustered_nodes()) of true -> ok; false -> throw({error, {not_a_cluster_node, @@ -356,7 +356,7 @@ remove_node_offline_node(Node) -> try [mnesia:force_load_table(T) || T <- rabbit_mnesia:table_names()], - remove_cluster_node(Node, false), + forget_cluster_node(Node, false), ensure_mnesia_running() after stop_mnesia() -- cgit v1.2.1 From e12d3861941f1c188f4d3838eae26218c83a8c1b Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Wed, 22 Aug 2012 11:57:05 +0100 Subject: remove commented code - we do not use `disc_only' anywhere --- src/rabbit_mnesia.erl | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 7c2a88cb..a67c0fe9 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -1002,13 +1002,6 @@ create_local_table_copies(Type) -> HasDiscOnlyCopies -> disc_only_copies; true -> ram_copies end; -%%% unused code - commented out to keep dialyzer happy -%%% Type =:= disc_only -> -%%% if -%%% HasDiscCopies or HasDiscOnlyCopies -> -%%% disc_only_copies; -%%% true -> ram_copies -%%% end; Type =:= ram -> ram_copies end, -- cgit v1.2.1 From 37267bda71d777c8eb3919085f356819ee678617 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 22 Aug 2012 16:00:38 +0100 Subject: s/at-least/exactly/g --- src/rabbit_mirror_queue_misc.erl | 12 ++++++------ src/rabbit_tests.erl | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 0383a15b..203ad651 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -93,7 +93,7 @@ remove_from_queue0(QueueName, DeadGMPids) -> slave_pids = SPids1 }), %% Sometimes a slave dying means we need %% to start more on other nodes - - %% "at-least" mode can cause this to + %% "exactly" mode can cause this to %% happen. {_, OldNodes} = actual_queue_nodes(Q1), {_, NewNodes} = suggested_queue_nodes(Q1), @@ -249,7 +249,7 @@ suggested_queue_nodes(<<"nodes">>, Nodes0, {MNode, _SNodes}, _All) -> true -> {MNode, Nodes -- [MNode]}; false -> promote_slave(Nodes) end; -suggested_queue_nodes(<<"at-least">>, Count, {MNode, SNodes}, All) -> +suggested_queue_nodes(<<"exactly">>, Count, {MNode, SNodes}, All) -> SCount = Count - 1, {MNode, case SCount > length(SNodes) of true -> Cand = (All -- [MNode]) -- SNodes, @@ -267,10 +267,10 @@ actual_queue_nodes(#amqqueue{pid = MPid, slave_pids = SPids}) -> is_mirrored(Q) -> case policy(<<"ha-mode">>, Q) of - <<"all">> -> true; - <<"nodes">> -> true; - <<"at-least">> -> true; - _ -> false + <<"all">> -> true; + <<"nodes">> -> true; + <<"exactly">> -> true; + _ -> false end. update_mirrors(OldQ = #amqqueue{name = QName, pid = QPid}, diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 84d7e96a..6e185751 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -874,12 +874,12 @@ test_dynamic_mirroring() -> Test({b,[a,c]},<<"nodes">>,[<<"a">>,<<"b">>,<<"c">>],{b,[a]},[a,b,c,d]), Test({a,[b,c]},<<"nodes">>,[<<"a">>,<<"b">>,<<"c">>],{d,[a]},[a,b,c,d]), - Test({a,[b]}, <<"at-least">>,2,{a,[]}, [a,b,c,d]), - Test({a,[b,c]},<<"at-least">>,3,{a,[]}, [a,b,c,d]), - Test({a,[c]}, <<"at-least">>,2,{a,[c]}, [a,b,c,d]), - Test({a,[b,c]},<<"at-least">>,3,{a,[c]}, [a,b,c,d]), - Test({a,[c]}, <<"at-least">>,2,{a,[c,d]},[a,b,c,d]), - Test({a,[c,d]},<<"at-least">>,3,{a,[c,d]},[a,b,c,d]), + Test({a,[b]}, <<"exactly">>,2,{a,[]}, [a,b,c,d]), + Test({a,[b,c]},<<"exactly">>,3,{a,[]}, [a,b,c,d]), + Test({a,[c]}, <<"exactly">>,2,{a,[c]}, [a,b,c,d]), + Test({a,[b,c]},<<"exactly">>,3,{a,[c]}, [a,b,c,d]), + Test({a,[c]}, <<"exactly">>,2,{a,[c,d]},[a,b,c,d]), + Test({a,[c,d]},<<"exactly">>,3,{a,[c,d]},[a,b,c,d]), passed. -- cgit v1.2.1 From f2bac30842c15d270450c0bda67b06e00fc7fbad Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Wed, 22 Aug 2012 17:17:44 +0100 Subject: get nodes and whether the node should be disc or ram from config if virgin node The first suitable node is picked from the list in the configuration, no consistency check is done - we might want to do that? --- src/rabbit_mnesia.erl | 80 ++++++++++++++++++++++++++++++++++++++------- src/rabbit_node_monitor.erl | 36 ++++++++++---------- 2 files changed, 86 insertions(+), 30 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index a67c0fe9..5cfdd5e0 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -125,14 +125,37 @@ prepare() -> init() -> ensure_mnesia_running(), ensure_mnesia_dir(), - DiscNode = is_disc_node(), - init_db_and_upgrade(all_clustered_nodes(), DiscNode, DiscNode), + case is_virgin_node() of + true -> init_from_config(); + false -> normal_init(is_disc_node(), all_clustered_nodes()) + end, %% We intuitively expect the global name server to be synced when %% Mnesia is up. In fact that's not guaranteed to be the case - let's %% make it so. ok = global:sync(), ok. +normal_init(DiscNode, AllNodes) -> + DiscNode = is_disc_node(), + init_db_and_upgrade(AllNodes, DiscNode, DiscNode). + +init_from_config() -> + {ok, {TryNodes, DiscNode}} = + application:get_env(rabbit, cluster_nodes), + case find_good_node(TryNodes -- [node()]) of + {ok, Node} -> + rabbit_log:info("Node '~p' selected for clustering from " + "configuration~n", [Node]), + {ok, {_, DiscNodes, _}} = discover_cluster(Node), + init_db_and_upgrade(DiscNodes, DiscNode, false), + rabbit_node_monitor:notify_joined_cluster(); + none -> + rabbit_log:warning("Could not find any suitable node amongst the " + "ones provided in the configuration: ~p~n", + [TryNodes]), + normal_init(DiscNode, [node()]) + end. + %% Make the node join a cluster. The node will be reset automatically before we %% actually cluster it. The nodes provided will be used to find out about the %% nodes in the cluster. @@ -428,10 +451,10 @@ running_clustered_disc_nodes() -> mnesia_nodes() -> case mnesia:system_info(is_running) of no -> {error, mnesia_not_running}; - yes -> %% If the tables are not present, it means that `init_db/3' hasn't - %% been run yet. In other words, either we are a virgin node or a - %% restarted RAM node. In both cases we're not interested in what - %% mnesia has to say. + yes -> %% If the tables are not present, it means that `init_db/3' + %% hasn't been run yet. In other words, either we are a virgin + %% node or a restarted RAM node. In both cases we're not + %% interested in what mnesia has to say. IsDiscNode = mnesia:system_info(use_dir), Tables = mnesia:system_info(tables), {Table, _} = case table_definitions(case IsDiscNode of @@ -690,10 +713,8 @@ check_cluster_consistency() -> {error, Error}; {OTP, Rabbit, Res} -> rabbit_misc:sequence_error( - [check_version_consistency( - erlang:system_info(otp_release), OTP, "OTP"), - check_version_consistency( - rabbit_misc:version(), Rabbit, "Rabbit"), + [check_otp_consistency(OTP), + check_rabbit_consistency(Rabbit), case Res of {ok, Status} -> check_nodes_consistency(Node, Status); @@ -701,8 +722,7 @@ check_cluster_consistency() -> {error, Error} end]) end; - (_Node, {ok, Status}) -> - {ok, Status} + (_Node, {ok, Status}) -> {ok, Status} end, {error, no_nodes}, AllNodes) of {ok, Status = {RemoteAllNodes, _, _}} -> @@ -1130,3 +1150,39 @@ check_version_consistency(This, Remote, Name) -> {error, {inconsistent_cluster, rabbit_misc:format("~s version mismatch: local node is ~s, " "remote node ~s", [Name, This, Remote])}}. + +check_otp_consistency(Remote) -> + check_version_consistency(erlang:system_info(otp_release), Remote, "OTP"). + +check_rabbit_consistency(Remote) -> + check_version_consistency(rabbit_misc:version(), Remote, "Rabbit"). + +%% 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 tables while still being +%% non-virgin. What we do instead is to check if the mnesia directory is non +%% existant or empty, with the exception of the cluster status file, which will +%% be there thanks to `rabbit_node_monitor:prepare_cluster_status_file/0'. +is_virgin_node() -> + case rabbit_file:list_dir(dir()) of + {error, enoent} -> true; + {ok, []} -> true; + {ok, [File]} -> (dir() ++ "/" ++ File) =:= + rabbit_node_monitor:cluster_status_file_name(); + {ok, Files} -> false + end. + +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, _} -> + case rabbit_misc:sequence_error([check_otp_consistency(OTP), + check_rabbit_consistency(Rabbit)]) + of + {error, _} -> find_good_node(Nodes); + ok -> {ok, Node} + end + end. diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index 849f5d31..b2d1c11c 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -18,7 +18,8 @@ -behaviour(gen_server). --export([prepare_cluster_status_file/0, +-export([cluster_status_file_name/0, + prepare_cluster_status_file/0, write_cluster_status_file/1, read_cluster_status_file/0, update_cluster_status_file/0, @@ -80,26 +81,27 @@ %% various situations. Obviously when mnesia is offline the information we have %% will be outdated, but it can't be otherwise. -cluster_status_file_filename() -> +cluster_status_file_name() -> rabbit_mnesia:dir() ++ "/cluster_nodes.config". prepare_cluster_status_file() -> NotPresent = fun (AllNodes0, WantDiscNode) -> - ThisNode = [node()], + ThisNode = [node()], - RunningNodes0 = legacy_read_previously_running_nodes(), - legacy_delete_previously_running_nodes(), + RunningNodes0 = legacy_read_previously_running_nodes(), + legacy_delete_previously_running_nodes(), - RunningNodes = lists:usort(RunningNodes0 ++ ThisNode), - AllNodes = - lists:usort(AllNodes0 ++ RunningNodes), - DiscNodes = case WantDiscNode of - true -> ThisNode; - false -> [] - end, + RunningNodes = lists:usort(RunningNodes0 ++ ThisNode), + AllNodes = + lists:usort(AllNodes0 ++ RunningNodes), + DiscNodes = case WantDiscNode of + true -> ThisNode; + false -> [] + end, - ok = write_cluster_status_file({AllNodes, DiscNodes, RunningNodes}) + ok = write_cluster_status_file({AllNodes, DiscNodes, + RunningNodes}) end, case try_read_cluster_status_file() of {ok, _} -> @@ -108,14 +110,12 @@ prepare_cluster_status_file() -> %% Legacy file NotPresent(AllNodes, legacy_should_be_disc_node(AllNodes)); {error, {cannot_read_file, _, enoent}} -> - {ok, {AllNodes, WantDiscNode}} = - application:get_env(rabbit, cluster_nodes), - NotPresent(AllNodes, WantDiscNode) + NotPresent([], true) end. write_cluster_status_file(Status) -> - FileName = cluster_status_file_filename(), + FileName = cluster_status_file_name(), case rabbit_file:write_term_file(FileName, [Status]) of ok -> ok; {error, Reason} -> @@ -124,7 +124,7 @@ write_cluster_status_file(Status) -> end. try_read_cluster_status_file() -> - FileName = cluster_status_file_filename(), + FileName = cluster_status_file_name(), case rabbit_file:read_term_file(FileName) of {ok, [{_, _, _} = Status]} -> {ok, Status}; -- cgit v1.2.1 From 602437bd972b207ae4bc97358104f60dc4248238 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 23 Aug 2012 04:55:58 +0100 Subject: cosmetic --- src/rabbit_mirror_queue_slave.erl | 43 ++++++++++++++++----------------------- 1 file changed, 18 insertions(+), 25 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index e4d78c45..4e0eb2a6 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -101,16 +101,12 @@ info(QPid) -> init(#amqqueue { name = QueueName } = Q) -> Self = self(), Node = node(), - case rabbit_misc:execute_mnesia_transaction(fun() -> - init_it(Self, Node, - QueueName) - end) of + case rabbit_misc:execute_mnesia_transaction( + fun() -> init_it(Self, Node, QueueName) end) of {new, MPid} -> process_flag(trap_exit, true), %% amqqueue_process traps exits too. {ok, GM} = gm:start_link(QueueName, ?MODULE, [self()]), - receive {joined, GM} -> - ok - end, + receive {joined, GM} -> ok end, erlang:monitor(process, MPid), ok = file_handle_cache:register_callback( rabbit_amqqueue, set_maximum_since_use, [Self]), @@ -153,24 +149,21 @@ init_it(Self, Node, QueueName) -> [Q1 = #amqqueue { pid = QPid, slave_pids = MPids }] = mnesia:read({rabbit_queue, QueueName}), case [Pid || Pid <- [QPid | MPids], node(Pid) =:= Node] of - [] -> - MPids1 = MPids ++ [Self], - rabbit_mirror_queue_misc:store_updated_slaves( - Q1#amqqueue{slave_pids = MPids1}), - {new, QPid}; - [QPid] -> - case rabbit_misc:is_process_alive(QPid) of - true -> duplicate_live_master; - false -> {stale, QPid} - end; - [SPid] -> - case rabbit_misc:is_process_alive(SPid) of - true -> existing; - false -> MPids1 = (MPids -- [SPid]) ++ [Self], - rabbit_mirror_queue_misc:store_updated_slaves( - Q1#amqqueue{slave_pids = MPids1}), - {new, QPid} - end + [] -> MPids1 = MPids ++ [Self], + rabbit_mirror_queue_misc:store_updated_slaves( + Q1#amqqueue{slave_pids = MPids1}), + {new, QPid}; + [QPid] -> case rabbit_misc:is_process_alive(QPid) of + true -> duplicate_live_master; + false -> {stale, QPid} + end; + [SPid] -> case rabbit_misc:is_process_alive(SPid) of + true -> existing; + false -> MPids1 = (MPids -- [SPid]) ++ [Self], + rabbit_mirror_queue_misc:store_updated_slaves( + Q1#amqqueue{slave_pids = MPids1}), + {new, QPid} + end end. handle_call({deliver, Delivery = #delivery { immediate = true }}, -- cgit v1.2.1 From ad8f703bc83f7cc4f79d2275ab71adcd8e27b021 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Thu, 23 Aug 2012 11:24:54 +0100 Subject: little fixes to the automatic clustering config code --- src/rabbit_mnesia.erl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 5cfdd5e0..dc27e58b 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -136,7 +136,6 @@ init() -> ok. normal_init(DiscNode, AllNodes) -> - DiscNode = is_disc_node(), init_db_and_upgrade(AllNodes, DiscNode, DiscNode). init_from_config() -> @@ -153,7 +152,7 @@ init_from_config() -> rabbit_log:warning("Could not find any suitable node amongst the " "ones provided in the configuration: ~p~n", [TryNodes]), - normal_init(DiscNode, [node()]) + normal_init(true, [node()]) end. %% Make the node join a cluster. The node will be reset automatically before we @@ -1169,7 +1168,7 @@ is_virgin_node() -> {ok, []} -> true; {ok, [File]} -> (dir() ++ "/" ++ File) =:= rabbit_node_monitor:cluster_status_file_name(); - {ok, Files} -> false + {ok, _} -> false end. find_good_node([]) -> -- cgit v1.2.1 From 2b7a86181289078a8a3b407d8df876f111051fec Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Thu, 23 Aug 2012 11:28:59 +0100 Subject: Match policies by regex instead of prefix --- src/rabbit_parameter_validation.erl | 9 ++++++++- src/rabbit_policy.erl | 13 +++++++++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/rabbit_parameter_validation.erl b/src/rabbit_parameter_validation.erl index af940dde..0247643d 100644 --- a/src/rabbit_parameter_validation.erl +++ b/src/rabbit_parameter_validation.erl @@ -16,7 +16,7 @@ -module(rabbit_parameter_validation). --export([number/2, binary/2, list/2, proplist/3]). +-export([number/2, binary/2, list/2, regex/2, proplist/3]). number(_Name, Term) when is_number(Term) -> ok; @@ -36,6 +36,13 @@ list(_Name, Term) when is_list(Term) -> list(Name, Term) -> {error, "~s should be list, actually was ~p", [Name, Term]}. +regex(Name, Term) -> + case re:compile(Term) of + {ok, _} -> ok; + {error, Reason} -> {error, "~s should be regular expression " + "but is invalid: ~p", [Name, Reason]} + end. + proplist(Name, Constraints, Term) when is_list(Term) -> {Results, Remainder} = lists:foldl( diff --git a/src/rabbit_policy.erl b/src/rabbit_policy.erl index 05b43a2e..3ed0734b 100644 --- a/src/rabbit_policy.erl +++ b/src/rabbit_policy.erl @@ -129,14 +129,19 @@ match(Name, Policies) -> end. matches(#resource{name = Name}, Policy) -> - lists:prefix(binary_to_list(pget(<<"prefix">>, Policy)), - binary_to_list(Name)). + case re:run(binary_to_list(Name), + binary_to_list(pget(<<"pattern">>, Policy)), + [{capture, none}]) of + nomatch -> false; + match -> true + end. sort_pred(A, B) -> - size(pget(<<"prefix">>, A)) >= size(pget(<<"prefix">>, B)). + pget(<<"priority">>, A) >= pget(<<"priority">>, B). %%---------------------------------------------------------------------------- policy_validation() -> - [{<<"prefix">>, fun rabbit_parameter_validation:binary/2, mandatory}, + [{<<"priority">>, fun rabbit_parameter_validation:number/2, mandatory}, + {<<"pattern">>, fun rabbit_parameter_validation:regex/2, mandatory}, {<<"policy">>, fun rabbit_parameter_validation:list/2, mandatory}]. -- cgit v1.2.1 From 6bc19d6d7e6ccd7305389335f88736719be3eb02 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 23 Aug 2012 11:36:06 +0100 Subject: Fix broken merge --- src/rabbit_mirror_queue_misc.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 203ad651..e4591cca 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -76,11 +76,11 @@ remove_from_queue0(QueueName, DeadGMPids) -> %% get here. case mnesia:read({rabbit_queue, QueueName}) of [] -> {error, not_found}; - [Q = #amqqueue { name = QName, - pid = QPid, + [Q = #amqqueue { pid = QPid, slave_pids = SPids }] -> Alive = [Pid || Pid <- [QPid | SPids], not lists:member(node(Pid), DeadNodes)], + {QPid1, SPids1} = promote_slave(Alive), case {{QPid, SPids}, {QPid1, SPids1}} of {Same, Same} -> {ok, QPid1, [], []}; -- cgit v1.2.1 From 491c6014e6a5ded6a96ed5a0f4b01a1644b668ef Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Thu, 23 Aug 2012 12:30:13 +0100 Subject: non-zero timeout when idle for the slave as well --- src/rabbit_mirror_queue_slave.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 4e0eb2a6..d82e6ef3 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -577,9 +577,9 @@ next_state(State = #state{backing_queue = BQ, backing_queue_state = BQS}) -> confirm_messages(MsgIds, State #state { backing_queue_state = BQS1 })), case BQ:needs_timeout(BQS1) of - false -> {stop_sync_timer(State1), hibernate}; - idle -> {stop_sync_timer(State1), 0 }; - timed -> {ensure_sync_timer(State1), 0 } + false -> {stop_sync_timer(State1), hibernate }; + idle -> {stop_sync_timer(State1), ?SYNC_INTERVAL}; + timed -> {ensure_sync_timer(State1), 0 } end. backing_queue_timeout(State = #state { backing_queue = BQ }) -> -- cgit v1.2.1 From ac77b5c2deff7e94232385d40ed8bda054839037 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Thu, 23 Aug 2012 14:10:47 +0100 Subject: wrappers around mochijson2 to catch exceptions --- src/rabbit_misc.erl | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 8f6a9bcf..69d96ad4 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -60,6 +60,7 @@ -export([multi_call/2]). -export([os_cmd/1]). -export([gb_sets_difference/2]). +-export([json_encode/1, json_decode/1]). %% Horrible macro to use in guards -define(IS_BENIGN_EXIT(R), @@ -217,6 +218,8 @@ ([pid()], any()) -> {[{pid(), any()}], [{pid(), any()}]}). -spec(os_cmd/1 :: (string()) -> string()). -spec(gb_sets_difference/2 :: (gb_set(), gb_set()) -> gb_set()). +-spec(json_encode/1 :: (any()) -> string() | {'error', any()}). +-spec(json_decode/1 :: (string()) -> any() | 'error'). -endif. @@ -934,3 +937,20 @@ os_cmd(Command) -> gb_sets_difference(S1, S2) -> gb_sets:fold(fun gb_sets:delete_any/2, S1, S2). + +json_encode(Term) -> + try + mochijson2:encode(Term) + catch + exit:{json_encode, E} -> + {error, E} + end. + +json_decode(Term) -> + try + mochijson2:decode(Term) + catch + %% Sadly `mochijson2:decode/1' does not offer a nice way to catch + %% decoding errors... + error:_ -> error + end. -- cgit v1.2.1 From 444cc742c9c67c06c488ee46ac0974ae81ebb847 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Thu, 23 Aug 2012 14:21:48 +0100 Subject: put result in `ok' --- src/rabbit_misc.erl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 69d96ad4..9b54958d 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -218,8 +218,8 @@ ([pid()], any()) -> {[{pid(), any()}], [{pid(), any()}]}). -spec(os_cmd/1 :: (string()) -> string()). -spec(gb_sets_difference/2 :: (gb_set(), gb_set()) -> gb_set()). --spec(json_encode/1 :: (any()) -> string() | {'error', any()}). --spec(json_decode/1 :: (string()) -> any() | 'error'). +-spec(json_encode/1 :: (any()) -> {'ok', string()} | {'error', any()}). +-spec(json_decode/1 :: (string()) -> {'ok', any()} | 'error'). -endif. @@ -940,7 +940,7 @@ gb_sets_difference(S1, S2) -> json_encode(Term) -> try - mochijson2:encode(Term) + {ok, mochijson2:encode(Term)} catch exit:{json_encode, E} -> {error, E} @@ -948,7 +948,7 @@ json_encode(Term) -> json_decode(Term) -> try - mochijson2:decode(Term) + {ok, mochijson2:decode(Term)} catch %% Sadly `mochijson2:decode/1' does not offer a nice way to catch %% decoding errors... -- cgit v1.2.1 From 4eadf72452fd7813413fe2d576164a00140490a2 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Thu, 23 Aug 2012 14:53:13 +0100 Subject: re-introduced wrapper functions to strip and add the `struct' tags We can live with the ambiguity of the empty list, since we only use the term->json function to display the JSON back to the users --- src/rabbit_misc.erl | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 9b54958d..20f541e5 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -60,7 +60,7 @@ -export([multi_call/2]). -export([os_cmd/1]). -export([gb_sets_difference/2]). --export([json_encode/1, json_decode/1]). +-export([json_encode/1, json_decode/1, json_to_term/1, term_to_json/1]). %% Horrible macro to use in guards -define(IS_BENIGN_EXIT(R), @@ -220,6 +220,8 @@ -spec(gb_sets_difference/2 :: (gb_set(), gb_set()) -> gb_set()). -spec(json_encode/1 :: (any()) -> {'ok', string()} | {'error', any()}). -spec(json_decode/1 :: (string()) -> {'ok', any()} | 'error'). +-spec(json_to_term/1 :: (any()) -> any()). +-spec(term_to_json/1 :: (any()) -> any()). -endif. @@ -954,3 +956,21 @@ json_decode(Term) -> %% decoding errors... error:_ -> error end. + +json_to_term({struct, L}) -> + [{K, json_to_term(V)} || {K, V} <- L]; +json_to_term(L) when is_list(L) -> + [json_to_term(I) || I <- L]; +json_to_term(V) when is_binary(V) orelse is_number(V) orelse V =:= null orelse + V =:= true orelse V =:= false -> + V. + +%% This has the flaw that empty lists will never be JSON objects, so use with +%% care. +term_to_json([{_, _}|_] = L) -> + {struct, [{K, term_to_json(V)} || {K, V} <- L]}; +term_to_json(L) when is_list(L) -> + [term_to_json(I) || I <- L]; +term_to_json(V) when is_binary(V) orelse is_number(V) orelse V =:= null orelse + V =:= true orelse V =:= false -> + V. -- cgit v1.2.1 From bf31e4e01ff5036720bb2445f057674012300c43 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 23 Aug 2012 15:24:37 +0100 Subject: Suggest queue nodes based on running nodes, otherwise (at the very least) we can immediately try to start a mirror on a node that has just gone down. --- src/rabbit_mirror_queue_misc.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index e4591cca..7caa96b5 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -233,7 +233,8 @@ suggested_queue_nodes(Q) -> _ -> MNode0 end, suggested_queue_nodes(policy(<<"ha-mode">>, Q), policy(<<"ha-params">>, Q), - {MNode, SNodes}, rabbit_mnesia:all_clustered_nodes()). + {MNode, SNodes}, + rabbit_mnesia:running_clustered_nodes()). policy(Policy, Q) -> case rabbit_policy:get(Policy, Q) of -- cgit v1.2.1 From 5bfceace4064f4aa29d2da995a922f43233d543d Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Thu, 23 Aug 2012 15:29:00 +0100 Subject: accept parameters in JSON format --- src/rabbit_runtime_parameters.erl | 70 ++++++--------------------------------- 1 file changed, 10 insertions(+), 60 deletions(-) diff --git a/src/rabbit_runtime_parameters.erl b/src/rabbit_runtime_parameters.erl index 0707193c..b932f122 100644 --- a/src/rabbit_runtime_parameters.erl +++ b/src/rabbit_runtime_parameters.erl @@ -58,9 +58,9 @@ %%--------------------------------------------------------------------------- parse_set(VHost, Component, Key, String) -> - case parse(String) of - {ok, Term} -> set(VHost, Component, Key, Term); - {errors, L} -> format_error(L) + case rabbit_misc:json_decode(String) of + {ok, JSON} -> set(VHost, Component, Key, rabbit_misc:json_to_term(JSON)); + error -> {error_string, "JSON decoding error"} end. set(VHost, Component, Key, Term) -> @@ -75,20 +75,13 @@ format_error(L) -> set0(VHost, Component, Key, Term) -> case lookup_component(Component) of {ok, Mod} -> - case flatten_errors(validate(Term)) of + case flatten_errors(Mod:validate(VHost, Component, Key, Term)) of ok -> - case flatten_errors( - Mod:validate(VHost, Component, Key, Term)) of - ok -> - case mnesia_update(VHost, Component, Key, Term) of - {old, Term} -> ok; - _ -> Mod:notify( - VHost, Component, Key, Term) - end, - ok; - E -> - E - end; + case mnesia_update(VHost, Component, Key, Term) of + {old, Term} -> ok; + _ -> Mod:notify(VHost, Component, Key, Term) + end, + ok; E -> E end; @@ -214,51 +207,8 @@ lookup_component(Component) -> {ok, Module} -> {ok, Module} end. -parse(Src0) -> - Src1 = string:strip(Src0), - Src = case lists:reverse(Src1) of - [$. |_] -> Src1; - _ -> Src1 ++ "." - end, - case erl_scan:string(Src) of - {ok, Scanned, _} -> - case erl_parse:parse_term(Scanned) of - {ok, Parsed} -> - {ok, Parsed}; - {error, E} -> - {errors, - [{"Could not parse value: ~s", [format_parse_error(E)]}]} - end; - {error, E, _} -> - {errors, [{"Could not scan value: ~s", [format_parse_error(E)]}]} - end. - -format_parse_error({_Line, Mod, Err}) -> - lists:flatten(Mod:format_error(Err)). - format(Term) -> - list_to_binary(rabbit_misc:format("~p", [Term])). - -%%--------------------------------------------------------------------------- - -%% We will want to be able to biject these to JSON. So we have some -%% generic restrictions on what we consider acceptable. -validate(Proplist = [T | _]) when is_tuple(T) -> validate_proplist(Proplist); -validate(L) when is_list(L) -> validate_list(L); -validate(T) when is_tuple(T) -> {error, "tuple: ~p", [T]}; -validate(B) when is_boolean(B) -> ok; -validate(null) -> ok; -validate(A) when is_atom(A) -> {error, "atom: ~p", [A]}; -validate(N) when is_number(N) -> ok; -validate(B) when is_binary(B) -> ok; -validate(B) when is_bitstring(B) -> {error, "bitstring: ~p", [B]}. - -validate_list(L) -> [validate(I) || I <- L]. -validate_proplist(L) -> [vp(I) || I <- L]. - -vp({K, V}) when is_binary(K) -> validate(V); -vp({K, _V}) -> {error, "bad key: ~p", [K]}; -vp(H) -> {error, "not two tuple: ~p", [H]}. + list_to_binary(rabbit_misc:json_encode(rabbit_misc:term_to_json(Term))). flatten_errors(L) -> case [{F, A} || I <- lists:flatten([L]), {error, F, A} <- [I]] of -- cgit v1.2.1 From defdb24b336394e6806149be5580b381143cff3e Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Thu, 23 Aug 2012 15:59:45 +0100 Subject: Compile regular expressions that will be matched multiple times --- src/rabbit_policy.erl | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/src/rabbit_policy.erl b/src/rabbit_policy.erl index 3ed0734b..3400a7d5 100644 --- a/src/rabbit_policy.erl +++ b/src/rabbit_policy.erl @@ -81,11 +81,12 @@ notify_clear(VHost, <<"policy">>, _Name) -> %%---------------------------------------------------------------------------- list(VHost) -> - [[{<<"name">>, pget(key, P)} | pget(value, P)] - || P <- rabbit_runtime_parameters:list(VHost, <<"policy">>)]. + lists:sort(fun sort_pred/2, + [[{<<"name">>, pget(key, P)} | pget(value, P)] + || P <- rabbit_runtime_parameters:list(VHost, <<"policy">>)]). update_policies(VHost) -> - Policies = list(VHost), + Policies = add_compile(list(VHost)), {Xs, Qs} = rabbit_misc:execute_mnesia_transaction( fun() -> {[update_exchange(X, Policies) || @@ -98,7 +99,7 @@ update_policies(VHost) -> ok. update_exchange(X = #exchange{name = XName, policy = OldPolicy}, Policies) -> - NewPolicy = match(XName, Policies), + NewPolicy = strip_compile(match(XName, Policies)), case NewPolicy of OldPolicy -> no_change; _ -> rabbit_exchange:update( @@ -107,7 +108,7 @@ update_exchange(X = #exchange{name = XName, policy = OldPolicy}, Policies) -> end. update_queue(Q = #amqqueue{name = QName, policy = OldPolicy}, Policies) -> - NewPolicy = match(QName, Policies), + NewPolicy = strip_compile(match(QName, Policies)), case NewPolicy of OldPolicy -> no_change; _ -> rabbit_amqqueue:update( @@ -123,19 +124,34 @@ notify({Q1 = #amqqueue{}, Q2 = #amqqueue{}}) -> rabbit_amqqueue:policy_changed(Q1, Q2). match(Name, Policies) -> - case lists:sort(fun sort_pred/2, [P || P <- Policies, matches(Name, P)]) of + case lists:filter(fun (P) -> matches(Name, P) end, Policies) of [] -> undefined; [Policy | _Rest] -> Policy end. matches(#resource{name = Name}, Policy) -> case re:run(binary_to_list(Name), - binary_to_list(pget(<<"pattern">>, Policy)), + pattern_pref(Policy), [{capture, none}]) of nomatch -> false; match -> true end. +add_compile(Policies) -> + [ begin + {ok, MP} = re:compile(binary_to_list(pget(<<"pattern">>, Policy))), + [{<<"compiled">>, MP} | Policy] + end || Policy <- Policies ]. + +strip_compile(undefined) -> undefined; +strip_compile(Policy) -> proplists:delete(<<"compiled">>, Policy). + +pattern_pref(Policy) -> + case pget(<<"compiled">>, Policy) of + undefined -> binary_to_list(pget(<<"pattern">>, Policy)); + Compiled -> Compiled + end. + sort_pred(A, B) -> pget(<<"priority">>, A) >= pget(<<"priority">>, B). -- cgit v1.2.1 From 0079936dae2c931670b857586ee69e94060df03d Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 23 Aug 2012 16:19:35 +0100 Subject: Try to be a bit more defensive before looping. --- src/rabbit_amqqueue.erl | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index a5f227bc..461b25eb 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -311,8 +311,17 @@ with(Name, F, E) -> case lookup(Name) of {ok, Q = #amqqueue{slave_pids = []}} -> rabbit_misc:with_exit_handler(E, fun () -> F(Q) end); - {ok, Q} -> - E1 = fun () -> timer:sleep(25), with(Name, F, E) end, + {ok, Q = #amqqueue{pid = QPid}} -> + %% We check is_process_alive(QPid) in case we receive a + %% nodedown (for example) in F() that has nothing to do + %% with the QPid. + E1 = fun () -> + case rabbit_misc:is_process_alive(QPid) of + true -> E(); + false -> timer:sleep(25), + with(Name, F, E) + end + end, rabbit_misc:with_exit_handler(E1, fun () -> F(Q) end); {error, not_found} -> E() -- cgit v1.2.1 From 6756fc90edf4f65a24bd9ec1b517fcd27cee2130 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 24 Aug 2012 08:56:38 +0100 Subject: tiny refactor: rename fun and clarify comment --- src/rabbit_net.erl | 8 ++++---- src/rabbit_reader.erl | 18 +++++++++--------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/rabbit_net.erl b/src/rabbit_net.erl index bedf5142..698a7c9a 100644 --- a/src/rabbit_net.erl +++ b/src/rabbit_net.erl @@ -19,7 +19,7 @@ -export([is_ssl/1, ssl_info/1, controlling_process/2, getstat/2, recv/1, async_recv/3, port_command/2, getopts/2, setopts/2, send/2, - close/1, maybe_fast_close/1, sockname/1, peername/1, peercert/1, + close/1, fast_close/1, sockname/1, peername/1, peercert/1, tune_buffer_size/1, connection_string/2]). %%--------------------------------------------------------------------------- @@ -59,7 +59,7 @@ -spec(setopts/2 :: (socket(), opts()) -> ok_or_any_error()). -spec(send/2 :: (socket(), binary() | iolist()) -> ok_or_any_error()). -spec(close/1 :: (socket()) -> ok_or_any_error()). --spec(maybe_fast_close/1 :: (socket()) -> ok_or_any_error()). +-spec(fast_close/1 :: (socket()) -> ok_or_any_error()). -spec(sockname/1 :: (socket()) -> ok_val_or_error({inet:ip_address(), rabbit_networking:ip_port()})). @@ -148,8 +148,8 @@ send(Sock, Data) when is_port(Sock) -> gen_tcp:send(Sock, Data). close(Sock) when ?IS_SSL(Sock) -> ssl:close(Sock#ssl_socket.ssl); close(Sock) when is_port(Sock) -> gen_tcp:close(Sock). -maybe_fast_close(Sock) when ?IS_SSL(Sock) -> ok; -maybe_fast_close(Sock) when is_port(Sock) -> erlang:port_close(Sock), ok. +fast_close(Sock) when ?IS_SSL(Sock) -> ok; +fast_close(Sock) when is_port(Sock) -> erlang:port_close(Sock), ok. sockname(Sock) when ?IS_SSL(Sock) -> ssl:sockname(Sock#ssl_socket.ssl); sockname(Sock) when is_port(Sock) -> inet:sockname(Sock). diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 19dac70c..bd20deb2 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -236,15 +236,15 @@ start_connection(Parent, ChannelSupSupPid, Collector, StartHeartbeatFun, Deb, end, "closing AMQP connection ~p (~s):~n~p~n", [self(), ConnStr, Ex]) after - %% The reader is the controlling process and hence its - %% termination will close the socket. Furthermore, - %% gen_tcp:close/1 waits for pending output to be sent, which - %% results in unnecessary delays. However, to keep the - %% file_handle_cache accounting as accurate as possible it - %% would be good to close the socket immediately if we - %% can. But we can only do this for non-ssl sockets. - %% - rabbit_net:maybe_fast_close(ClientSock), + %% We don't call gen_tcp:close/1 here since it waits for + %% pending output to be sent, which results in unnecessary + %% delays. We could just terminate - the reader is the + %% controlling process and hence its termination will close + %% the socket. However, to keep the file_handle_cache + %% accounting as accurate as possible we ought to close the + %% socket w/o delay before termination. fast_close does that, + %% though only for non-ssl sockets. + rabbit_net:fast_close(ClientSock), rabbit_event:notify(connection_closed, [{pid, self()}]) end, done. -- cgit v1.2.1 From bb8bf19e9c4ffff295d69935e6eee606e791df50 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Fri, 24 Aug 2012 12:05:34 +0100 Subject: Disallow empty patterns --- src/rabbit_parameter_validation.erl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/rabbit_parameter_validation.erl b/src/rabbit_parameter_validation.erl index 0247643d..04ddcc13 100644 --- a/src/rabbit_parameter_validation.erl +++ b/src/rabbit_parameter_validation.erl @@ -36,6 +36,8 @@ list(_Name, Term) when is_list(Term) -> list(Name, Term) -> {error, "~s should be list, actually was ~p", [Name, Term]}. +regex(Name, Empty) when Empty == <<>> orelse Empty == [] -> + {error, "~s should be a regular expression but was empty.", [Name]}; regex(Name, Term) -> case re:compile(Term) of {ok, _} -> ok; -- cgit v1.2.1 From 1093ba0a920163d3549c1d7c887887d5de69fbe4 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Fri, 24 Aug 2012 15:15:49 +0100 Subject: document the fact that -detached causes the wrong pid to be recorded --- docs/rabbitmq-server.1.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/rabbitmq-server.1.xml b/docs/rabbitmq-server.1.xml index ca63927c..620b1dd3 100644 --- a/docs/rabbitmq-server.1.xml +++ b/docs/rabbitmq-server.1.xml @@ -109,7 +109,9 @@ Defaults to 5672. -detached - start the server process in the background + Start the server process in the background. Note that this will + cause the wrong pid to be recorded in the pid file, since the pid + of the shell that launches the detached process is captured. For example: rabbitmq-server -detached -- cgit v1.2.1 -- cgit v1.2.1 From 9834f36ae4276f3d7f912bcc4e94c36653dae77f Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Wed, 29 Aug 2012 09:49:14 +0100 Subject: Slaves must join gm before updating amqqueue records, otherwise messages received from publishers will become stuck due to diverging sender queue state in the slave failing to correlate with incoming messages --- src/rabbit_mirror_queue_slave.erl | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index d82e6ef3..28afe64b 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -19,17 +19,8 @@ %% For general documentation of HA design, see %% rabbit_mirror_queue_coordinator %% -%% We join the GM group before we add ourselves to the amqqueue -%% record. As a result: -%% 1. We can receive msgs from GM that correspond to messages we will -%% never receive from publishers. -%% 2. When we receive a message from publishers, we must receive a -%% message from the GM group for it. -%% 3. However, that instruction from the GM group can arrive either -%% before or after the actual message. We need to be able to -%% distinguish between GM instructions arriving early, and case (1) -%% above. -%% +%% We receive messages from GM and from publishers, and the gm +%% messages can arrive either before or after the 'actual' message. %% All instructions from the GM group must be processed in the order %% in which they're received. @@ -99,14 +90,25 @@ info(QPid) -> gen_server2:call(QPid, info, infinity). init(#amqqueue { name = QueueName } = Q) -> + %% We join the GM group before we add ourselves to the amqqueue + %% record. As a result: + %% 1. We can receive msgs from GM that correspond to messages we will + %% never receive from publishers. + %% 2. When we receive a message from publishers, we must receive a + %% message from the GM group for it. + %% 3. However, that instruction from the GM group can arrive either + %% before or after the actual message. We need to be able to + %% distinguish between GM instructions arriving early, and case (1) + %% above. + %% + process_flag(trap_exit, true), %% amqqueue_process traps exits too. + {ok, GM} = gm:start_link(QueueName, ?MODULE, [self()]), + receive {joined, GM} -> ok end, Self = self(), - Node = node(), + Node = node(), case rabbit_misc:execute_mnesia_transaction( fun() -> init_it(Self, Node, QueueName) end) of {new, MPid} -> - process_flag(trap_exit, true), %% amqqueue_process traps exits too. - {ok, GM} = gm:start_link(QueueName, ?MODULE, [self()]), - receive {joined, GM} -> ok end, erlang:monitor(process, MPid), ok = file_handle_cache:register_callback( rabbit_amqqueue, set_maximum_since_use, [Self]), -- cgit v1.2.1 From b8462cc66357d32c2ff35706b0a34824749d9423 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Wed, 29 Aug 2012 11:18:11 +0100 Subject: cosmetic --- src/rabbit_mirror_queue_slave.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 28afe64b..ef43d96e 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -105,7 +105,7 @@ init(#amqqueue { name = QueueName } = Q) -> {ok, GM} = gm:start_link(QueueName, ?MODULE, [self()]), receive {joined, GM} -> ok end, Self = self(), - Node = node(), + Node = node(), case rabbit_misc:execute_mnesia_transaction( fun() -> init_it(Self, Node, QueueName) end) of {new, MPid} -> -- cgit v1.2.1 From 4aa312b49ba655ea6e3370432711839407369d5f Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Wed, 29 Aug 2012 15:10:52 +0100 Subject: reset when turning disc into ram node --- src/rabbit_mnesia.erl | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index dc27e58b..4d731137 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -291,6 +291,15 @@ change_cluster_node_type(Type) -> "disc nodes to the cluster first."}}); false -> ok end, + %% This is an horrible hack due to + %% . + %% This is safe if the cluster situation remains the same between the + %% cluster discovery and joining the cluster, but if for example the other + %% cluster nodes disappear in that timeframe we are in trouble. + ok = case is_disc_node() andalso not WantDiscNode of + true -> reset(false); + false -> ok + end, ok = init_db_with_mnesia(AllNodes, WantDiscNode, false). update_cluster_nodes(DiscoveryNode) -> -- cgit v1.2.1 From a02e65fd6d9bfda74d125525f6bdf46fcfd29eab Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Wed, 29 Aug 2012 16:47:05 +0100 Subject: revert last change (reset when changing node from disc to ram) mnesia, mnesia.... For some reason, this approach does not in some cases. For example, starting with nodes A, B, and C clustered and all disc, the following sequence of commands causes an error: $ rabbitmqctl -n A stop_app $ rabbitmqctl -n B stop_app $ rabbitmqctl -n B change_cluster_node_type ram # reset happens here $ rabbitmqctl -n B start_app $ rabbitmqctl -n A start_app error happens here: {error, {cannot_start_application,mnesia, {shutdown,{mnesia_sup,start,[normal,[]]}}}} I have no idea why, but it's probably connected to the fact that we can't do those changes when disc replicas are offline, and probably also related that operations like leaving the cluster require all the cluster node to be online. At this point I'd give up and put this limitation in `change_cluster_node_type', but I want to make sure that these limitations do not have repercussions in other commands. We should already take care of the "leave cluster when node offline" case, but there are probably others... --- src/rabbit_mnesia.erl | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 4d731137..dc27e58b 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -291,15 +291,6 @@ change_cluster_node_type(Type) -> "disc nodes to the cluster first."}}); false -> ok end, - %% This is an horrible hack due to - %% . - %% This is safe if the cluster situation remains the same between the - %% cluster discovery and joining the cluster, but if for example the other - %% cluster nodes disappear in that timeframe we are in trouble. - ok = case is_disc_node() andalso not WantDiscNode of - true -> reset(false); - false -> ok - end, ok = init_db_with_mnesia(AllNodes, WantDiscNode, false). update_cluster_nodes(DiscoveryNode) -> -- cgit v1.2.1 From e7051a1a9eb68e6180e340c4f0c5740ba83d2162 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Thu, 30 Aug 2012 13:04:27 +0100 Subject: missing spec --- src/rabbit_node_monitor.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index b2d1c11c..d29408de 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -48,6 +48,7 @@ -ifdef(use_specs). +-spec(cluster_status_file_name/0 :: () -> string()). -spec(prepare_cluster_status_file/0 :: () -> 'ok'). -spec(write_cluster_status_file/1 :: (rabbit_mnesia:cluster_status()) -> 'ok'). -- cgit v1.2.1 From 6389be116625872fa6f8bde8d071ac2ccd06e81f Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Thu, 30 Aug 2012 13:34:17 +0100 Subject: when terminating multiple simple children, we shouldn't rely on the order of 'EXIT' vs 'DOWN' messages --- src/supervisor2.erl | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/src/supervisor2.erl b/src/supervisor2.erl index 3d3623d7..dba9d4b5 100644 --- a/src/supervisor2.erl +++ b/src/supervisor2.erl @@ -816,29 +816,37 @@ terminate_simple_children(Child, Dynamics, SupName) -> {Replies, Timedout} = lists:foldl( fun (_Pid, {Replies, Timedout}) -> - {Reply, Timedout1} = + {Pid, Reply, Timedout1} = receive TimeoutMsg -> Remaining = Pids -- [P || {P, _} <- Replies], [exit(P, kill) || P <- Remaining], receive {'DOWN', _MRef, process, Pid, Reason} -> - {{error, Reason}, true} + Res = child_res(Child, Reason, Timedout), + {Pid, Res, true} end; {'DOWN', _MRef, process, Pid, Reason} -> - {child_res(Child, Reason, Timedout), Timedout}; - {'EXIT', Pid, Reason} -> - receive {'DOWN', _MRef, process, Pid, _} -> - {{error, Reason}, Timedout} - end + Res = child_res(Child, Reason, Timedout), + {Pid, Res, Timedout} end, {[{Pid, Reply} | Replies], Timedout1} end, {[], false}, Pids), timeout_stop(Child, TRef, TimeoutMsg, Timedout), ReportError = shutdown_error_reporter(SupName), - [case Reply of - {_Pid, ok} -> ok; - {Pid, {error, R}} -> ReportError(R, Child#child{pid = Pid}) - end || Reply <- Replies], + Report = fun(_, ok) -> ok; + (Pid, {error, R}) -> ReportError(R, Child#child{pid = Pid}) + end, + [begin + receive + {'EXIT', Pid, Reason} -> + case Reply of + {error, noproc} -> Report(Pid, Reason); + _ -> Report(Pid, Reply) + end + after + 0 -> Report(Pid, Reply) + end + end || {Pid, Reply} <- Replies], ok. child_exit_reason(#child{shutdown = brutal_kill}) -> kill; -- cgit v1.2.1 From 6934fac0e30d9d9c4f179c38598aaa78a76dced4 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 30 Aug 2012 13:56:59 +0100 Subject: refactor and always prefer the EXIT reason --- src/supervisor2.erl | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/src/supervisor2.erl b/src/supervisor2.erl index dba9d4b5..89a8fd92 100644 --- a/src/supervisor2.erl +++ b/src/supervisor2.erl @@ -816,36 +816,30 @@ terminate_simple_children(Child, Dynamics, SupName) -> {Replies, Timedout} = lists:foldl( fun (_Pid, {Replies, Timedout}) -> - {Pid, Reply, Timedout1} = + {Pid1, Reason1, Timedout1} = receive TimeoutMsg -> Remaining = Pids -- [P || {P, _} <- Replies], [exit(P, kill) || P <- Remaining], - receive {'DOWN', _MRef, process, Pid, Reason} -> - Res = child_res(Child, Reason, Timedout), - {Pid, Res, true} + receive + {'DOWN', _MRef, process, Pid, Reason} -> + {Pid, Reason, true} end; {'DOWN', _MRef, process, Pid, Reason} -> - Res = child_res(Child, Reason, Timedout), - {Pid, Res, Timedout} + {Pid, Reason, Timedout} end, - {[{Pid, Reply} | Replies], Timedout1} + {[{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, - [begin - receive - {'EXIT', Pid, Reason} -> - case Reply of - {error, noproc} -> Report(Pid, Reason); - _ -> Report(Pid, Reply) - end - after - 0 -> Report(Pid, Reply) - end + [receive + {'EXIT', Pid, Reason} -> Report(Pid, Reason) + after + 0 -> Report(Pid, Reply) end || {Pid, Reply} <- Replies], ok. -- cgit v1.2.1 From d22b4dd1b543bc969a370178bd0d2e44c616ff51 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Thu, 30 Aug 2012 17:05:54 +0100 Subject: take into account requeues when setting synch state for slaves To do this, keep count all the fetches we've seen that require an ack for messages we don't have (e.g. when the queue we have is shorter than on the master). We then decrease this counter appropriately when requeueing, acking, and set_length'ing. Given this, we can deem the slave synced only when the length is the same *and* the counter described above is 9 - there are no pending acks on the master for messages we don't have. I might have missed something (I barely tested this) but it seems to do the trick. --- src/rabbit_mirror_queue_master.erl | 8 +-- src/rabbit_mirror_queue_slave.erl | 115 ++++++++++++++++++++----------------- 2 files changed, 65 insertions(+), 58 deletions(-) diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index 477449e3..094b83c9 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -145,7 +145,7 @@ monitor_wait([MRef | MRefs]) -> purge(State = #state { gm = GM, backing_queue = BQ, backing_queue_state = BQS }) -> - ok = gm:broadcast(GM, {set_length, 0, false}), + ok = gm:broadcast(GM, {set_length, 0, BQ:len(BQS), false}), {Count, BQS1} = BQ:purge(BQS), {Count, State #state { backing_queue_state = BQS1, set_delivered = 0 }}. @@ -187,8 +187,8 @@ dropwhile(Pred, AckRequired, Len = BQ:len(BQS), {Next, Msgs, BQS1} = BQ:dropwhile(Pred, AckRequired, BQS), Len1 = BQ:len(BQS1), - ok = gm:broadcast(GM, {set_length, Len1, AckRequired}), Dropped = Len - Len1, + ok = gm:broadcast(GM, {set_length, Len1, Dropped, AckRequired}), SetDelivered1 = lists:max([0, SetDelivered - Dropped]), {Next, Msgs, State #state { backing_queue_state = BQS1, set_delivered = SetDelivered1 } }. @@ -251,7 +251,7 @@ ack(AckTags, State = #state { gm = GM, {MsgIds, BQS1} = BQ:ack(AckTags, BQS), case MsgIds of [] -> ok; - _ -> ok = gm:broadcast(GM, {ack, MsgIds}) + _ -> ok = gm:broadcast(GM, {ack, MsgIds, BQ:len(BQS1)}) end, AM1 = lists:foldl(fun dict:erase/2, AM, AckTags), {MsgIds, State #state { backing_queue_state = BQS1, @@ -265,7 +265,7 @@ requeue(AckTags, State = #state { gm = GM, backing_queue = BQ, backing_queue_state = BQS }) -> {MsgIds, BQS1} = BQ:requeue(AckTags, BQS), - ok = gm:broadcast(GM, {requeue, MsgIds}), + ok = gm:broadcast(GM, {requeue, MsgIds, BQ:len(BQS1)}), {MsgIds, State #state { backing_queue_state = BQS1 }}. len(#state { backing_queue = BQ, backing_queue_state = BQS }) -> diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index ef43d96e..2c60acf0 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -77,7 +77,8 @@ msg_id_status, known_senders, - synchronised + synchronised, + external_pending }). start_link(Q) -> @@ -131,7 +132,8 @@ init(#amqqueue { name = QueueName } = Q) -> msg_id_status = dict:new(), known_senders = pmon:new(), - synchronised = false + synchronised = false, + external_pending = 0 }, rabbit_event:notify(queue_slave_created, infos(?CREATION_EVENT_KEYS, State)), @@ -809,71 +811,67 @@ process_instruction({discard, ChPid, Msg = #basic_message { id = MsgId }}, {ok, State1 #state { sender_queues = SQ1, msg_id_status = MS1, backing_queue_state = BQS1 }}; -process_instruction({set_length, Length, AckRequired}, +process_instruction({set_length, Length, Dropped, AckRequired}, State = #state { backing_queue = BQ, backing_queue_state = BQS }) -> QLen = BQ:len(BQS), ToDrop = QLen - Length, {ok, - case ToDrop >= 0 of - true -> - State1 = - lists:foldl( - fun (const, StateN = #state {backing_queue_state = BQSN}) -> - {{#basic_message{id = MsgId}, _IsDelivered, AckTag, - _Remaining}, BQSN1} = BQ:fetch(AckRequired, BQSN), - maybe_store_ack( - AckRequired, MsgId, AckTag, - StateN #state { backing_queue_state = BQSN1 }) - end, State, lists:duplicate(ToDrop, const)), - set_synchronised(true, State1); - false -> - State - end}; + set_synchronised( + Length, + case ToDrop >= 0 of + true -> + State1 = + lists:foldl( + fun (const, StateN = #state{backing_queue_state = BQSN}) -> + {{#basic_message{id = MsgId}, _, AckTag, _}, + BQSN1} = BQ:fetch(AckRequired, BQSN), + maybe_store_ack( + AckRequired, MsgId, AckTag, + StateN #state { backing_queue_state = BQSN1 }) + end, State, lists:duplicate(ToDrop, const)), + case AckRequired of + true -> set_synchronised(ToDrop, Dropped, Length, State1); + false -> State1 + end; + false -> + State + end)}; process_instruction({fetch, AckRequired, MsgId, Remaining}, State = #state { backing_queue = BQ, - backing_queue_state = BQS }) -> + backing_queue_state = BQS, + external_pending = ExtPending }) -> QLen = BQ:len(BQS), - {ok, case QLen - 1 of - Remaining -> + {ok, case {QLen - 1, AckRequired} of + {Remaining, _} -> {{#basic_message{id = MsgId}, _IsDelivered, AckTag, Remaining}, BQS1} = BQ:fetch(AckRequired, BQS), maybe_store_ack(AckRequired, MsgId, AckTag, State #state { backing_queue_state = BQS1 }); - Other when Other + 1 =:= Remaining -> - set_synchronised(true, State); - Other when Other < Remaining -> - %% we must be shorter than the master - State + {_, false} when QLen =< Remaining -> + set_synchronised(Remaining, State); + {_, true} when QLen =< Remaining -> + State #state { external_pending = ExtPending + 1} end}; -process_instruction({ack, MsgIds}, +process_instruction({ack, MsgIds, Length}, State = #state { backing_queue = BQ, backing_queue_state = BQS, msg_id_ack = MA }) -> {AckTags, MA1} = msg_ids_to_acktags(MsgIds, MA), {MsgIds1, BQS1} = BQ:ack(AckTags, BQS), [] = MsgIds1 -- MsgIds, %% ASSERTION - {ok, State #state { msg_id_ack = MA1, - backing_queue_state = BQS1 }}; -process_instruction({requeue, MsgIds}, + {ok, set_synchronised(length(AckTags), length(MsgIds), Length, + State #state { msg_id_ack = MA1, + backing_queue_state = BQS1 })}; +process_instruction({requeue, MsgIds, Length}, State = #state { backing_queue = BQ, backing_queue_state = BQS, msg_id_ack = MA }) -> {AckTags, MA1} = msg_ids_to_acktags(MsgIds, MA), - {ok, case length(AckTags) =:= length(MsgIds) of - true -> - {MsgIds, BQS1} = BQ:requeue(AckTags, BQS), - State #state { msg_id_ack = MA1, - backing_queue_state = BQS1 }; - false -> - %% The only thing we can safely do is nuke out our BQ - %% and MA. The interaction between this and confirms - %% doesn't really bear thinking about... - {_Count, BQS1} = BQ:purge(BQS), - {_MsgIds, BQS2} = ack_all(BQ, MA, BQS1), - State #state { msg_id_ack = dict:new(), - backing_queue_state = BQS2 } - end}; + {_MsgIds, BQS1} = BQ:requeue(AckTags, BQS), + {ok, set_synchronised(length(AckTags), length(MsgIds), Length, + State #state { msg_id_ack = MA1, + backing_queue_state = BQS1 })}; process_instruction({sender_death, ChPid}, State = #state { sender_queues = SQ, msg_id_status = MS, @@ -891,10 +889,8 @@ process_instruction({sender_death, ChPid}, msg_id_status = MS1, known_senders = pmon:demonitor(ChPid, KS) } end}; -process_instruction({length, Length}, - State = #state { backing_queue = BQ, - backing_queue_state = BQS }) -> - {ok, set_synchronised(Length =:= BQ:len(BQS), State)}; +process_instruction({length, Length}, State) -> + {ok, set_synchronised(Length, State)}; process_instruction({delete_and_terminate, Reason}, State = #state { backing_queue = BQ, backing_queue_state = BQS }) -> @@ -913,9 +909,6 @@ msg_ids_to_acktags(MsgIds, MA) -> end, {[], MA}, MsgIds), {lists:reverse(AckTags), MA1}. -ack_all(BQ, MA, BQS) -> - BQ:ack([AckTag || {_MsgId, {_Num, AckTag}} <- dict:to_list(MA)], BQS). - maybe_store_ack(false, _MsgId, _AckTag, State) -> State; maybe_store_ack(true, MsgId, AckTag, State = #state { msg_id_ack = MA, @@ -923,9 +916,23 @@ maybe_store_ack(true, MsgId, AckTag, State = #state { msg_id_ack = MA, State #state { msg_id_ack = dict:store(MsgId, {Num, AckTag}, MA), ack_num = Num + 1 }. +set_synchronised(LocalPending, RemotePending, Length, + State = #state { backing_queue = BQ, + backing_queue_state = BQS, + external_pending = ExtPending }) -> + ExtPending1 = ExtPending - (RemotePending - LocalPending), + State1 = State #state { external_pending = ExtPending1 }, + case ExtPending1 =:= 0 andalso Length =:= BQ:len(BQS) of + true -> set_synchronised1(true, State1); + false when ExtPending1 >= 0 -> set_synchronised1(false, State1) + end. + +set_synchronised(Length, State) -> + set_synchronised(0, 0, Length, State). + %% We intentionally leave out the head where a slave becomes %% unsynchronised: we assert that can never happen. -set_synchronised(true, State = #state { q = #amqqueue { name = QName }, +set_synchronised1(true, State = #state { q = #amqqueue { name = QName }, synchronised = false }) -> Self = self(), rabbit_misc:execute_mnesia_transaction( @@ -939,7 +946,7 @@ set_synchronised(true, State = #state { q = #amqqueue { name = QName }, end end), State #state { synchronised = true }; -set_synchronised(true, State) -> +set_synchronised1(true, State) -> State; -set_synchronised(false, State = #state { synchronised = false }) -> +set_synchronised1(false, State = #state { synchronised = false }) -> State. -- cgit v1.2.1 From ec9c54a99566adbc905152a19e2179902fd52a1d Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Thu, 30 Aug 2012 17:38:34 +0100 Subject: matthias doesn't like the nested `set_synchronised' :( --- src/rabbit_mirror_queue_slave.erl | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 2c60acf0..58b9b644 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -817,26 +817,24 @@ process_instruction({set_length, Length, Dropped, AckRequired}, QLen = BQ:len(BQS), ToDrop = QLen - Length, {ok, - set_synchronised( - Length, - case ToDrop >= 0 of - true -> - State1 = - lists:foldl( - fun (const, StateN = #state{backing_queue_state = BQSN}) -> - {{#basic_message{id = MsgId}, _, AckTag, _}, - BQSN1} = BQ:fetch(AckRequired, BQSN), - maybe_store_ack( - AckRequired, MsgId, AckTag, - StateN #state { backing_queue_state = BQSN1 }) - end, State, lists:duplicate(ToDrop, const)), - case AckRequired of - true -> set_synchronised(ToDrop, Dropped, Length, State1); - false -> State1 - end; - false -> - State - end)}; + case ToDrop >= 0 of + true -> + State1 = + lists:foldl( + fun (const, StateN = #state{backing_queue_state = BQSN}) -> + {{#basic_message{id = MsgId}, _, AckTag, _}, BQSN1} = + BQ:fetch(AckRequired, BQSN), + maybe_store_ack( + AckRequired, MsgId, AckTag, + StateN #state { backing_queue_state = BQSN1 }) + end, State, lists:duplicate(ToDrop, const)), + case AckRequired of + true -> set_synchronised(ToDrop, Dropped, Length, State1); + false -> set_synchronised(Length, State1) + end; + false -> + set_synchronised(Length, State) + end}; process_instruction({fetch, AckRequired, MsgId, Remaining}, State = #state { backing_queue = BQ, backing_queue_state = BQS, -- cgit v1.2.1 From cba76a6a6227080ddc3edd1012e056b92676cddf Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Fri, 31 Aug 2012 13:59:09 +0100 Subject: add a callback to backing queue to get the number of pending acks --- src/rabbit_backing_queue.erl | 10 +++++++--- src/rabbit_mirror_queue_master.erl | 7 +++++-- src/rabbit_variable_queue.erl | 7 +++++-- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/rabbit_backing_queue.erl b/src/rabbit_backing_queue.erl index ed5340fe..eac1db2f 100644 --- a/src/rabbit_backing_queue.erl +++ b/src/rabbit_backing_queue.erl @@ -152,6 +152,9 @@ %% Is my queue empty? -callback is_empty(state()) -> boolean(). +%% How many pending acks do we have? +-callback pending_ack(state()) -> non_neg_integer(). + %% For the next three functions, the assumption is that you're %% monitoring something like the ingress and egress rates of the %% queue. The RAM duration is thus the length of time represented by @@ -212,9 +215,10 @@ behaviour_info(callbacks) -> {delete_and_terminate, 2}, {purge, 1}, {publish, 4}, {publish_delivered, 5}, {drain_confirmed, 1}, {dropwhile, 3}, {fetch, 2}, {ack, 2}, {fold, 3}, {requeue, 2}, {len, 1}, - {is_empty, 1}, {set_ram_duration_target, 2}, {ram_duration, 1}, - {needs_timeout, 1}, {timeout, 1}, {handle_pre_hibernate, 1}, - {status, 1}, {invoke, 3}, {is_duplicate, 2}, {discard, 3}]; + {is_empty, 1}, {pending_ack, 1}, {set_ram_duration_target, 2}, + {ram_duration, 1}, {needs_timeout, 1}, {timeout, 1}, + {handle_pre_hibernate, 1}, {status, 1}, {invoke, 3}, {is_duplicate, 2}, + {discard, 3}]; behaviour_info(_Other) -> undefined. diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index 094b83c9..bd33e955 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -18,8 +18,8 @@ -export([init/3, terminate/2, delete_and_terminate/2, purge/1, publish/4, publish_delivered/5, fetch/2, ack/2, - requeue/2, len/1, is_empty/1, drain_confirmed/1, dropwhile/3, - set_ram_duration_target/2, ram_duration/1, + requeue/2, len/1, is_empty/1, pending_ack/1, drain_confirmed/1, + dropwhile/3, set_ram_duration_target/2, ram_duration/1, needs_timeout/1, timeout/1, handle_pre_hibernate/1, status/1, invoke/3, is_duplicate/2, discard/3, fold/3]). @@ -274,6 +274,9 @@ len(#state { backing_queue = BQ, backing_queue_state = BQS }) -> is_empty(#state { backing_queue = BQ, backing_queue_state = BQS }) -> BQ:is_empty(BQS). +pending_ack(#state { backing_queue = BQ, backing_queue_state = BQS }) -> + BQ:pending_ack(BQS). + set_ram_duration_target(Target, State = #state { backing_queue = BQ, backing_queue_state = BQS }) -> State #state { backing_queue_state = diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index bd606dfb..22829765 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -19,8 +19,8 @@ -export([init/3, terminate/2, delete_and_terminate/2, purge/1, publish/4, publish_delivered/5, drain_confirmed/1, dropwhile/3, fetch/2, ack/2, requeue/2, len/1, is_empty/1, - set_ram_duration_target/2, ram_duration/1, needs_timeout/1, - timeout/1, handle_pre_hibernate/1, status/1, invoke/3, + pending_ack/1, set_ram_duration_target/2, ram_duration/1, + needs_timeout/1, timeout/1, handle_pre_hibernate/1, status/1, invoke/3, is_duplicate/2, discard/3, multiple_routing_keys/0, fold/3]). -export([start/1, stop/0]). @@ -681,6 +681,9 @@ len(#vqstate { len = Len }) -> Len. is_empty(State) -> 0 == len(State). +pending_ack(#vqstate { pending_ack = Ack }) -> + gb_trees:size(Ack). + set_ram_duration_target( DurationTarget, State = #vqstate { rates = #rates { avg_egress = AvgEgressRate, -- cgit v1.2.1 From 1d2b538b8370114595da4b522b53e1cd951530b7 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Fri, 31 Aug 2012 15:44:20 +0100 Subject: get the external pending acks at the beginning --- src/rabbit_mirror_queue_master.erl | 7 ++++--- src/rabbit_mirror_queue_slave.erl | 5 +++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index bd33e955..6cfc13c7 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -96,7 +96,7 @@ init(#amqqueue { name = QName, mirror_nodes = MNodes } = Q, Recover, [rabbit_mirror_queue_misc:add_mirror(QName, Node) || Node <- MNodes1], {ok, BQ} = application:get_env(backing_queue_module), BQS = BQ:init(Q, Recover, AsyncCallback), - ok = gm:broadcast(GM, {length, BQ:len(BQS)}), + ok = gm:broadcast(GM, {length, BQ:len(BQS), BQ:pending_ack(BQS)}), #state { gm = GM, coordinator = CPid, backing_queue = BQ, @@ -375,7 +375,7 @@ discard(Msg = #basic_message { id = MsgId }, ChPid, promote_backing_queue_state(CPid, BQ, BQS, GM, SeenStatus, KS) -> Len = BQ:len(BQS), - ok = gm:broadcast(GM, {length, Len}), + ok = gm:broadcast(GM, {length, Len, BQ:pending_ack(BQS)}), #state { gm = GM, coordinator = CPid, backing_queue = BQ, @@ -406,7 +406,8 @@ length_fun() -> fun (?MODULE, State = #state { gm = GM, backing_queue = BQ, backing_queue_state = BQS }) -> - ok = gm:broadcast(GM, {length, BQ:len(BQS)}), + ok = gm:broadcast( + GM, {length, BQ:len(BQS), BQ:pending_ack(BQS)}), State end) end. diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 58b9b644..38bbf59f 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -887,8 +887,9 @@ process_instruction({sender_death, ChPid}, msg_id_status = MS1, known_senders = pmon:demonitor(ChPid, KS) } end}; -process_instruction({length, Length}, State) -> - {ok, set_synchronised(Length, State)}; +process_instruction({length, Length, ExtPending}, State) -> + {ok, set_synchronised(Length, + State #state { external_pending = ExtPending })}; process_instruction({delete_and_terminate, Reason}, State = #state { backing_queue = BQ, backing_queue_state = BQS }) -> -- cgit v1.2.1 From 975b58762b4af83a1d87dbc0fd68d24b14869662 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Fri, 31 Aug 2012 15:47:56 +0100 Subject: `length' should be the first instructions, lets assert that --- src/rabbit_mirror_queue_slave.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 38bbf59f..8f398a8e 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -133,7 +133,7 @@ init(#amqqueue { name = QueueName } = Q) -> known_senders = pmon:new(), synchronised = false, - external_pending = 0 + external_pending = undefined }, rabbit_event:notify(queue_slave_created, infos(?CREATION_EVENT_KEYS, State)), -- cgit v1.2.1 From c43a77640b11446be3fdc276930b771acb4f2dfc Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Fri, 31 Aug 2012 15:51:41 +0100 Subject: actually, it looks like the slave might receive messages before `length' This is not a problem, since the worse that can happen is that we have a bogus sync state in the until `length' is received. --- src/rabbit_mirror_queue_slave.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 8f398a8e..38bbf59f 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -133,7 +133,7 @@ init(#amqqueue { name = QueueName } = Q) -> known_senders = pmon:new(), synchronised = false, - external_pending = undefined + external_pending = 0 }, rabbit_event:notify(queue_slave_created, infos(?CREATION_EVENT_KEYS, State)), -- cgit v1.2.1 From 4a60868b78268113c92a77a8d375afe502f314bf Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Fri, 31 Aug 2012 16:03:38 +0100 Subject: do not track external pendings until we receive `length' Otherwise, we might break some assertions. --- src/rabbit_mirror_queue_slave.erl | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 38bbf59f..4e153ca1 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -133,7 +133,7 @@ init(#amqqueue { name = QueueName } = Q) -> known_senders = pmon:new(), synchronised = false, - external_pending = 0 + external_pending = undefined }, rabbit_event:notify(queue_slave_created, infos(?CREATION_EVENT_KEYS, State)), @@ -849,7 +849,10 @@ process_instruction({fetch, AckRequired, MsgId, Remaining}, {_, false} when QLen =< Remaining -> set_synchronised(Remaining, State); {_, true} when QLen =< Remaining -> - State #state { external_pending = ExtPending + 1} + State #state { external_pending = case ExtPending of + undefined -> undefined; + _ -> ExtPending + 1 + end } end}; process_instruction({ack, MsgIds, Length}, State = #state { backing_queue = BQ, @@ -915,6 +918,8 @@ maybe_store_ack(true, MsgId, AckTag, State = #state { msg_id_ack = MA, State #state { msg_id_ack = dict:store(MsgId, {Num, AckTag}, MA), ack_num = Num + 1 }. +set_synchronised(_, _, _, State = #state { external_pending = undefined }) -> + State; set_synchronised(LocalPending, RemotePending, Length, State = #state { backing_queue = BQ, backing_queue_state = BQS, -- cgit v1.2.1 From b09d33688a18af3cb6121d8a95ed70000b3635fa Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Fri, 31 Aug 2012 17:49:56 +0100 Subject: `set_synchronized' accepts the difference instead of separate args --- src/rabbit_mirror_queue_slave.erl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 4e153ca1..f11426bb 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -829,7 +829,7 @@ process_instruction({set_length, Length, Dropped, AckRequired}, StateN #state { backing_queue_state = BQSN1 }) end, State, lists:duplicate(ToDrop, const)), case AckRequired of - true -> set_synchronised(ToDrop, Dropped, Length, State1); + true -> set_synchronised(Dropped - ToDrop, Length, State1); false -> set_synchronised(Length, State1) end; false -> @@ -861,7 +861,7 @@ process_instruction({ack, MsgIds, Length}, {AckTags, MA1} = msg_ids_to_acktags(MsgIds, MA), {MsgIds1, BQS1} = BQ:ack(AckTags, BQS), [] = MsgIds1 -- MsgIds, %% ASSERTION - {ok, set_synchronised(length(AckTags), length(MsgIds), Length, + {ok, set_synchronised(length(MsgIds) - length(AckTags), Length, State #state { msg_id_ack = MA1, backing_queue_state = BQS1 })}; process_instruction({requeue, MsgIds, Length}, @@ -870,7 +870,7 @@ process_instruction({requeue, MsgIds, Length}, msg_id_ack = MA }) -> {AckTags, MA1} = msg_ids_to_acktags(MsgIds, MA), {_MsgIds, BQS1} = BQ:requeue(AckTags, BQS), - {ok, set_synchronised(length(AckTags), length(MsgIds), Length, + {ok, set_synchronised(length(MsgIds) - length(AckTags), Length, State #state { msg_id_ack = MA1, backing_queue_state = BQS1 })}; process_instruction({sender_death, ChPid}, @@ -918,13 +918,13 @@ maybe_store_ack(true, MsgId, AckTag, State = #state { msg_id_ack = MA, State #state { msg_id_ack = dict:store(MsgId, {Num, AckTag}, MA), ack_num = Num + 1 }. -set_synchronised(_, _, _, State = #state { external_pending = undefined }) -> +set_synchronised(_, _, State = #state { external_pending = undefined }) -> State; -set_synchronised(LocalPending, RemotePending, Length, +set_synchronised(PendingDelta, Length, State = #state { backing_queue = BQ, backing_queue_state = BQS, external_pending = ExtPending }) -> - ExtPending1 = ExtPending - (RemotePending - LocalPending), + ExtPending1 = ExtPending - PendingDelta, State1 = State #state { external_pending = ExtPending1 }, case ExtPending1 =:= 0 andalso Length =:= BQ:len(BQS) of true -> set_synchronised1(true, State1); @@ -932,7 +932,7 @@ set_synchronised(LocalPending, RemotePending, Length, end. set_synchronised(Length, State) -> - set_synchronised(0, 0, Length, State). + set_synchronised(0, Length, State). %% We intentionally leave out the head where a slave becomes %% unsynchronised: we assert that can never happen. -- cgit v1.2.1 From d87576a6349a78a7d1ce2c88c737000f87df69ae Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 31 Aug 2012 18:06:36 +0100 Subject: minor refactoring and cosmetics --- src/rabbit_mirror_queue_slave.erl | 71 +++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 40 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index ef43d96e..964c3e24 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -80,14 +80,12 @@ synchronised }). -start_link(Q) -> - gen_server2:start_link(?MODULE, Q, []). +start_link(Q) -> gen_server2:start_link(?MODULE, Q, []). set_maximum_since_use(QPid, Age) -> gen_server2:cast(QPid, {set_maximum_since_use, Age}). -info(QPid) -> - gen_server2:call(QPid, info, infinity). +info(QPid) -> gen_server2:call(QPid, info, infinity). init(#amqqueue { name = QueueName } = Q) -> %% We join the GM group before we add ourselves to the amqqueue @@ -351,14 +349,10 @@ prioritise_info(Msg, _State) -> %% GM %% --------------------------------------------------------------------------- -joined([SPid], _Members) -> - SPid ! {joined, self()}, - ok. +joined([SPid], _Members) -> SPid ! {joined, self()}, ok. -members_changed([_SPid], _Births, []) -> - ok; -members_changed([SPid], _Births, Deaths) -> - inform_deaths(SPid, Deaths). +members_changed([_SPid], _Births, []) -> ok; +members_changed([ SPid], _Births, Deaths) -> inform_deaths(SPid, Deaths). handle_msg([_SPid], _From, master_changed) -> ok; @@ -675,26 +669,24 @@ maybe_enqueue_message( %% msg_seq_no was at the time. We do now! ok = rabbit_misc:confirm_to_sender(ChPid, [MsgSeqNo]), SQ1 = remove_from_pending_ch(MsgId, ChPid, SQ), - State1 #state { sender_queues = SQ1, - msg_id_status = dict:erase(MsgId, MS) }; + State1 #state { msg_id_status = dict:erase(MsgId, MS), + sender_queues = SQ1 }; {ok, {published, ChPid}} -> %% It was published to the BQ and we didn't know the %% msg_seq_no so couldn't confirm it at the time. - case needs_confirming(Delivery, State1) of - never -> - SQ1 = remove_from_pending_ch(MsgId, ChPid, SQ), - State1 #state { msg_id_status = dict:erase(MsgId, MS), - sender_queues = SQ1 }; - eventually -> - State1 #state { - msg_id_status = - dict:store(MsgId, {published, ChPid, MsgSeqNo}, MS) }; - immediately -> - ok = rabbit_misc:confirm_to_sender(ChPid, [MsgSeqNo]), - SQ1 = remove_from_pending_ch(MsgId, ChPid, SQ), - State1 #state { msg_id_status = dict:erase(MsgId, MS), - sender_queues = SQ1 } - end; + {MS1, SQ1} = + case needs_confirming(Delivery, State1) of + never -> {dict:erase(MsgId, MS), + remove_from_pending_ch(MsgId, ChPid, SQ)}; + eventually -> MMS = {published, ChPid, MsgSeqNo}, + {dict:store(MsgId, MMS, MS), SQ}; + immediately -> ok = rabbit_misc:confirm_to_sender( + ChPid, [MsgSeqNo]), + {dict:erase(MsgId, MS), + remove_from_pending_ch(MsgId, ChPid, SQ)} + end, + State1 #state { msg_id_status = MS1, + sender_queues = SQ1 }; {ok, discarded} -> %% We've already heard from GM that the msg is to be %% discarded. We won't see this again. @@ -743,18 +735,17 @@ process_instruction( msg_seq_no = MsgSeqNo, message = #basic_message { id = MsgId } }, _EnqueueOnPromotion}}, MQ2} -> - %% We received the msg from the channel first. Thus we - %% need to deal with confirms here. - case needs_confirming(Delivery, State1) of - never -> - {MQ2, PendingCh, MS}; - eventually -> - {MQ2, PendingCh, - dict:store(MsgId, {published, ChPid, MsgSeqNo}, MS)}; - immediately -> - ok = rabbit_misc:confirm_to_sender(ChPid, [MsgSeqNo]), - {MQ2, PendingCh, MS} - end; + {MQ2, PendingCh, + %% We received the msg from the channel first. Thus + %% we need to deal with confirms here. + case needs_confirming(Delivery, State1) of + never -> MS; + eventually -> MMS = {published, ChPid, MsgSeqNo}, + dict:store(MsgId, MMS , MS); + immediately -> ok = rabbit_misc:confirm_to_sender( + ChPid, [MsgSeqNo]), + MS + end}; {{value, {#delivery {}, _EnqueueOnPromotion}}, _MQ2} -> %% The instruction was sent to us before we were %% within the slave_pids within the #amqqueue{} -- cgit v1.2.1 From bb1ba4a5c9f3d2219bffdb4cd6ce3f9ab202e97d Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Fri, 31 Aug 2012 18:26:30 +0100 Subject: add the delta in `set_synchronized', fixes the call in `set_length' --- src/rabbit_mirror_queue_slave.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index f11426bb..77f3d11f 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -861,7 +861,7 @@ process_instruction({ack, MsgIds, Length}, {AckTags, MA1} = msg_ids_to_acktags(MsgIds, MA), {MsgIds1, BQS1} = BQ:ack(AckTags, BQS), [] = MsgIds1 -- MsgIds, %% ASSERTION - {ok, set_synchronised(length(MsgIds) - length(AckTags), Length, + {ok, set_synchronised(length(AckTags) - length(MsgIds), Length, State #state { msg_id_ack = MA1, backing_queue_state = BQS1 })}; process_instruction({requeue, MsgIds, Length}, @@ -870,7 +870,7 @@ process_instruction({requeue, MsgIds, Length}, msg_id_ack = MA }) -> {AckTags, MA1} = msg_ids_to_acktags(MsgIds, MA), {_MsgIds, BQS1} = BQ:requeue(AckTags, BQS), - {ok, set_synchronised(length(MsgIds) - length(AckTags), Length, + {ok, set_synchronised(length(AckTags) - length(MsgIds), Length, State #state { msg_id_ack = MA1, backing_queue_state = BQS1 })}; process_instruction({sender_death, ChPid}, @@ -924,7 +924,7 @@ set_synchronised(PendingDelta, Length, State = #state { backing_queue = BQ, backing_queue_state = BQS, external_pending = ExtPending }) -> - ExtPending1 = ExtPending - PendingDelta, + ExtPending1 = ExtPending + PendingDelta, State1 = State #state { external_pending = ExtPending1 }, case ExtPending1 =:= 0 andalso Length =:= BQ:len(BQS) of true -> set_synchronised1(true, State1); -- cgit v1.2.1 From 3e464a656425438308529f5d1decd42da5060f0f Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Fri, 31 Aug 2012 18:39:00 +0100 Subject: simplify `set_length' case, fixing the ToDrop < 0 branch The unknown pending counter wasn't updated correctly --- src/rabbit_mirror_queue_slave.erl | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 77f3d11f..75c88b15 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -815,26 +815,22 @@ process_instruction({set_length, Length, Dropped, AckRequired}, State = #state { backing_queue = BQ, backing_queue_state = BQS }) -> QLen = BQ:len(BQS), - ToDrop = QLen - Length, - {ok, - case ToDrop >= 0 of - true -> - State1 = - lists:foldl( - fun (const, StateN = #state{backing_queue_state = BQSN}) -> - {{#basic_message{id = MsgId}, _, AckTag, _}, BQSN1} = - BQ:fetch(AckRequired, BQSN), - maybe_store_ack( - AckRequired, MsgId, AckTag, - StateN #state { backing_queue_state = BQSN1 }) - end, State, lists:duplicate(ToDrop, const)), - case AckRequired of - true -> set_synchronised(Dropped - ToDrop, Length, State1); - false -> set_synchronised(Length, State1) - end; - false -> - set_synchronised(Length, State) - end}; + ToDrop = case QLen - Length of + N when N > 0 -> N; + _ -> 0 + end, + State1 = lists:foldl( + fun (const, StateN = #state{backing_queue_state = BQSN}) -> + {{#basic_message{id = MsgId}, _, AckTag, _}, BQSN1} = + BQ:fetch(AckRequired, BQSN), + maybe_store_ack( + AckRequired, MsgId, AckTag, + StateN #state { backing_queue_state = BQSN1 }) + end, State, lists:duplicate(ToDrop, const)), + {ok, case AckRequired of + true -> set_synchronised(Dropped - ToDrop, Length, State1); + false -> set_synchronised(Length, State1) + end}; process_instruction({fetch, AckRequired, MsgId, Remaining}, State = #state { backing_queue = BQ, backing_queue_state = BQS, -- cgit v1.2.1 From 24dc3a1f66e1dc0661c02532a84bb9e5d4d354cf Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Fri, 31 Aug 2012 18:40:05 +0100 Subject: use `set_synchronized' instead than manually increasing --- src/rabbit_mirror_queue_slave.erl | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 75c88b15..d79067be 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -833,8 +833,7 @@ process_instruction({set_length, Length, Dropped, AckRequired}, end}; process_instruction({fetch, AckRequired, MsgId, Remaining}, State = #state { backing_queue = BQ, - backing_queue_state = BQS, - external_pending = ExtPending }) -> + backing_queue_state = BQS }) -> QLen = BQ:len(BQS), {ok, case {QLen - 1, AckRequired} of {Remaining, _} -> @@ -845,10 +844,7 @@ process_instruction({fetch, AckRequired, MsgId, Remaining}, {_, false} when QLen =< Remaining -> set_synchronised(Remaining, State); {_, true} when QLen =< Remaining -> - State #state { external_pending = case ExtPending of - undefined -> undefined; - _ -> ExtPending + 1 - end } + set_synchronised(1, Remaining, State) end}; process_instruction({ack, MsgIds, Length}, State = #state { backing_queue = BQ, -- cgit v1.2.1 From 7210856c99bfb1f488cd0d190ba993a9e3860522 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Fri, 31 Aug 2012 18:42:49 +0100 Subject: `external_pending' => `unknown_pending' --- src/rabbit_mirror_queue_slave.erl | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index d79067be..3423295a 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -78,7 +78,10 @@ known_senders, synchronised, - external_pending + %% Records the pending acks on the master for messages that we + %% do not have. This is necessary to accurately determine + %% whether the slave is synchronised or not. + unknown_pending }). start_link(Q) -> @@ -133,7 +136,7 @@ init(#amqqueue { name = QueueName } = Q) -> known_senders = pmon:new(), synchronised = false, - external_pending = undefined + unknown_pending = undefined }, rabbit_event:notify(queue_slave_created, infos(?CREATION_EVENT_KEYS, State)), @@ -884,7 +887,7 @@ process_instruction({sender_death, ChPid}, end}; process_instruction({length, Length, ExtPending}, State) -> {ok, set_synchronised(Length, - State #state { external_pending = ExtPending })}; + State #state { unknown_pending = ExtPending })}; process_instruction({delete_and_terminate, Reason}, State = #state { backing_queue = BQ, backing_queue_state = BQS }) -> @@ -910,14 +913,14 @@ maybe_store_ack(true, MsgId, AckTag, State = #state { msg_id_ack = MA, State #state { msg_id_ack = dict:store(MsgId, {Num, AckTag}, MA), ack_num = Num + 1 }. -set_synchronised(_, _, State = #state { external_pending = undefined }) -> +set_synchronised(_, _, State = #state { unknown_pending = undefined }) -> State; set_synchronised(PendingDelta, Length, State = #state { backing_queue = BQ, backing_queue_state = BQS, - external_pending = ExtPending }) -> + unknown_pending = ExtPending }) -> ExtPending1 = ExtPending + PendingDelta, - State1 = State #state { external_pending = ExtPending1 }, + State1 = State #state { unknown_pending = ExtPending1 }, case ExtPending1 =:= 0 andalso Length =:= BQ:len(BQS) of true -> set_synchronised1(true, State1); false when ExtPending1 >= 0 -> set_synchronised1(false, State1) -- cgit v1.2.1 From 35d6af87ce356f83422d692aa3de62e4dd85702c Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Fri, 31 Aug 2012 18:43:47 +0100 Subject: `set_length' => `drop' --- src/rabbit_mirror_queue_master.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index 6cfc13c7..1151fd76 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -145,7 +145,7 @@ monitor_wait([MRef | MRefs]) -> purge(State = #state { gm = GM, backing_queue = BQ, backing_queue_state = BQS }) -> - ok = gm:broadcast(GM, {set_length, 0, BQ:len(BQS), false}), + ok = gm:broadcast(GM, {drop, 0, BQ:len(BQS), false}), {Count, BQS1} = BQ:purge(BQS), {Count, State #state { backing_queue_state = BQS1, set_delivered = 0 }}. @@ -188,7 +188,7 @@ dropwhile(Pred, AckRequired, {Next, Msgs, BQS1} = BQ:dropwhile(Pred, AckRequired, BQS), Len1 = BQ:len(BQS1), Dropped = Len - Len1, - ok = gm:broadcast(GM, {set_length, Len1, Dropped, AckRequired}), + ok = gm:broadcast(GM, {drop, Len1, Dropped, AckRequired}), SetDelivered1 = lists:max([0, SetDelivered - Dropped]), {Next, Msgs, State #state { backing_queue_state = BQS1, set_delivered = SetDelivered1 } }. -- cgit v1.2.1 From 607f4a55eaa410448736f5d356c48799fac6af1f Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Fri, 31 Aug 2012 18:45:37 +0100 Subject: `set_synchronized' => `update_unknown_pending' --- src/rabbit_mirror_queue_slave.erl | 44 +++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 3423295a..8e872d4e 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -814,7 +814,7 @@ process_instruction({discard, ChPid, Msg = #basic_message { id = MsgId }}, {ok, State1 #state { sender_queues = SQ1, msg_id_status = MS1, backing_queue_state = BQS1 }}; -process_instruction({set_length, Length, Dropped, AckRequired}, +process_instruction({drop, Length, Dropped, AckRequired}, State = #state { backing_queue = BQ, backing_queue_state = BQS }) -> QLen = BQ:len(BQS), @@ -831,8 +831,8 @@ process_instruction({set_length, Length, Dropped, AckRequired}, StateN #state { backing_queue_state = BQSN1 }) end, State, lists:duplicate(ToDrop, const)), {ok, case AckRequired of - true -> set_synchronised(Dropped - ToDrop, Length, State1); - false -> set_synchronised(Length, State1) + true -> update_unknown_pending(Dropped - ToDrop, Length, State1); + false -> update_unknown_pending(Length, State1) end}; process_instruction({fetch, AckRequired, MsgId, Remaining}, State = #state { backing_queue = BQ, @@ -845,9 +845,9 @@ process_instruction({fetch, AckRequired, MsgId, Remaining}, maybe_store_ack(AckRequired, MsgId, AckTag, State #state { backing_queue_state = BQS1 }); {_, false} when QLen =< Remaining -> - set_synchronised(Remaining, State); + update_unknown_pending(Remaining, State); {_, true} when QLen =< Remaining -> - set_synchronised(1, Remaining, State) + update_unknown_pending(1, Remaining, State) end}; process_instruction({ack, MsgIds, Length}, State = #state { backing_queue = BQ, @@ -856,18 +856,18 @@ process_instruction({ack, MsgIds, Length}, {AckTags, MA1} = msg_ids_to_acktags(MsgIds, MA), {MsgIds1, BQS1} = BQ:ack(AckTags, BQS), [] = MsgIds1 -- MsgIds, %% ASSERTION - {ok, set_synchronised(length(AckTags) - length(MsgIds), Length, - State #state { msg_id_ack = MA1, - backing_queue_state = BQS1 })}; + {ok, update_unknown_pending(length(AckTags) - length(MsgIds), Length, + State #state { msg_id_ack = MA1, + backing_queue_state = BQS1 })}; process_instruction({requeue, MsgIds, Length}, State = #state { backing_queue = BQ, backing_queue_state = BQS, msg_id_ack = MA }) -> {AckTags, MA1} = msg_ids_to_acktags(MsgIds, MA), {_MsgIds, BQS1} = BQ:requeue(AckTags, BQS), - {ok, set_synchronised(length(AckTags) - length(MsgIds), Length, - State #state { msg_id_ack = MA1, - backing_queue_state = BQS1 })}; + {ok, update_unknown_pending(length(AckTags) - length(MsgIds), Length, + State #state { msg_id_ack = MA1, + backing_queue_state = BQS1 })}; process_instruction({sender_death, ChPid}, State = #state { sender_queues = SQ, msg_id_status = MS, @@ -886,8 +886,8 @@ process_instruction({sender_death, ChPid}, known_senders = pmon:demonitor(ChPid, KS) } end}; process_instruction({length, Length, ExtPending}, State) -> - {ok, set_synchronised(Length, - State #state { unknown_pending = ExtPending })}; + {ok, update_unknown_pending(Length, + State #state { unknown_pending = ExtPending })}; process_instruction({delete_and_terminate, Reason}, State = #state { backing_queue = BQ, backing_queue_state = BQS }) -> @@ -913,25 +913,25 @@ maybe_store_ack(true, MsgId, AckTag, State = #state { msg_id_ack = MA, State #state { msg_id_ack = dict:store(MsgId, {Num, AckTag}, MA), ack_num = Num + 1 }. -set_synchronised(_, _, State = #state { unknown_pending = undefined }) -> +update_unknown_pending(_, _, State = #state { unknown_pending = undefined }) -> State; -set_synchronised(PendingDelta, Length, +update_unknown_pending(PendingDelta, Length, State = #state { backing_queue = BQ, backing_queue_state = BQS, unknown_pending = ExtPending }) -> ExtPending1 = ExtPending + PendingDelta, State1 = State #state { unknown_pending = ExtPending1 }, case ExtPending1 =:= 0 andalso Length =:= BQ:len(BQS) of - true -> set_synchronised1(true, State1); - false when ExtPending1 >= 0 -> set_synchronised1(false, State1) + true -> set_synchronised(true, State1); + false when ExtPending1 >= 0 -> set_synchronised(false, State1) end. -set_synchronised(Length, State) -> - set_synchronised(0, Length, State). +update_unknown_pending(Length, State) -> + update_unknown_pending(0, Length, State). %% We intentionally leave out the head where a slave becomes %% unsynchronised: we assert that can never happen. -set_synchronised1(true, State = #state { q = #amqqueue { name = QName }, +set_synchronised(true, State = #state { q = #amqqueue { name = QName }, synchronised = false }) -> Self = self(), rabbit_misc:execute_mnesia_transaction( @@ -945,7 +945,7 @@ set_synchronised1(true, State = #state { q = #amqqueue { name = QName }, end end), State #state { synchronised = true }; -set_synchronised1(true, State) -> +set_synchronised(true, State) -> State; -set_synchronised1(false, State = #state { synchronised = false }) -> +set_synchronised(false, State = #state { synchronised = false }) -> State. -- cgit v1.2.1 From 34d80b42b7845109439b231446b8fef03ee45b4c Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Fri, 31 Aug 2012 18:53:51 +0100 Subject: revert `set_synchronized' => `update_unknown_pending' Matthias likes the old names better, arguing that the main purpose of that function is to do that mnesia transaction. --- src/rabbit_mirror_queue_slave.erl | 44 +++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 8e872d4e..3423295a 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -814,7 +814,7 @@ process_instruction({discard, ChPid, Msg = #basic_message { id = MsgId }}, {ok, State1 #state { sender_queues = SQ1, msg_id_status = MS1, backing_queue_state = BQS1 }}; -process_instruction({drop, Length, Dropped, AckRequired}, +process_instruction({set_length, Length, Dropped, AckRequired}, State = #state { backing_queue = BQ, backing_queue_state = BQS }) -> QLen = BQ:len(BQS), @@ -831,8 +831,8 @@ process_instruction({drop, Length, Dropped, AckRequired}, StateN #state { backing_queue_state = BQSN1 }) end, State, lists:duplicate(ToDrop, const)), {ok, case AckRequired of - true -> update_unknown_pending(Dropped - ToDrop, Length, State1); - false -> update_unknown_pending(Length, State1) + true -> set_synchronised(Dropped - ToDrop, Length, State1); + false -> set_synchronised(Length, State1) end}; process_instruction({fetch, AckRequired, MsgId, Remaining}, State = #state { backing_queue = BQ, @@ -845,9 +845,9 @@ process_instruction({fetch, AckRequired, MsgId, Remaining}, maybe_store_ack(AckRequired, MsgId, AckTag, State #state { backing_queue_state = BQS1 }); {_, false} when QLen =< Remaining -> - update_unknown_pending(Remaining, State); + set_synchronised(Remaining, State); {_, true} when QLen =< Remaining -> - update_unknown_pending(1, Remaining, State) + set_synchronised(1, Remaining, State) end}; process_instruction({ack, MsgIds, Length}, State = #state { backing_queue = BQ, @@ -856,18 +856,18 @@ process_instruction({ack, MsgIds, Length}, {AckTags, MA1} = msg_ids_to_acktags(MsgIds, MA), {MsgIds1, BQS1} = BQ:ack(AckTags, BQS), [] = MsgIds1 -- MsgIds, %% ASSERTION - {ok, update_unknown_pending(length(AckTags) - length(MsgIds), Length, - State #state { msg_id_ack = MA1, - backing_queue_state = BQS1 })}; + {ok, set_synchronised(length(AckTags) - length(MsgIds), Length, + State #state { msg_id_ack = MA1, + backing_queue_state = BQS1 })}; process_instruction({requeue, MsgIds, Length}, State = #state { backing_queue = BQ, backing_queue_state = BQS, msg_id_ack = MA }) -> {AckTags, MA1} = msg_ids_to_acktags(MsgIds, MA), {_MsgIds, BQS1} = BQ:requeue(AckTags, BQS), - {ok, update_unknown_pending(length(AckTags) - length(MsgIds), Length, - State #state { msg_id_ack = MA1, - backing_queue_state = BQS1 })}; + {ok, set_synchronised(length(AckTags) - length(MsgIds), Length, + State #state { msg_id_ack = MA1, + backing_queue_state = BQS1 })}; process_instruction({sender_death, ChPid}, State = #state { sender_queues = SQ, msg_id_status = MS, @@ -886,8 +886,8 @@ process_instruction({sender_death, ChPid}, known_senders = pmon:demonitor(ChPid, KS) } end}; process_instruction({length, Length, ExtPending}, State) -> - {ok, update_unknown_pending(Length, - State #state { unknown_pending = ExtPending })}; + {ok, set_synchronised(Length, + State #state { unknown_pending = ExtPending })}; process_instruction({delete_and_terminate, Reason}, State = #state { backing_queue = BQ, backing_queue_state = BQS }) -> @@ -913,25 +913,25 @@ maybe_store_ack(true, MsgId, AckTag, State = #state { msg_id_ack = MA, State #state { msg_id_ack = dict:store(MsgId, {Num, AckTag}, MA), ack_num = Num + 1 }. -update_unknown_pending(_, _, State = #state { unknown_pending = undefined }) -> +set_synchronised(_, _, State = #state { unknown_pending = undefined }) -> State; -update_unknown_pending(PendingDelta, Length, +set_synchronised(PendingDelta, Length, State = #state { backing_queue = BQ, backing_queue_state = BQS, unknown_pending = ExtPending }) -> ExtPending1 = ExtPending + PendingDelta, State1 = State #state { unknown_pending = ExtPending1 }, case ExtPending1 =:= 0 andalso Length =:= BQ:len(BQS) of - true -> set_synchronised(true, State1); - false when ExtPending1 >= 0 -> set_synchronised(false, State1) + true -> set_synchronised1(true, State1); + false when ExtPending1 >= 0 -> set_synchronised1(false, State1) end. -update_unknown_pending(Length, State) -> - update_unknown_pending(0, Length, State). +set_synchronised(Length, State) -> + set_synchronised(0, Length, State). %% We intentionally leave out the head where a slave becomes %% unsynchronised: we assert that can never happen. -set_synchronised(true, State = #state { q = #amqqueue { name = QName }, +set_synchronised1(true, State = #state { q = #amqqueue { name = QName }, synchronised = false }) -> Self = self(), rabbit_misc:execute_mnesia_transaction( @@ -945,7 +945,7 @@ set_synchronised(true, State = #state { q = #amqqueue { name = QName }, end end), State #state { synchronised = true }; -set_synchronised(true, State) -> +set_synchronised1(true, State) -> State; -set_synchronised(false, State = #state { synchronised = false }) -> +set_synchronised1(false, State = #state { synchronised = false }) -> State. -- cgit v1.2.1 From 890f46c149ef227d203ef6de7d05666235101fcd Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Fri, 31 Aug 2012 18:55:31 +0100 Subject: style --- src/rabbit_mirror_queue_slave.erl | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 3423295a..9ee020d1 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -920,11 +920,9 @@ set_synchronised(PendingDelta, Length, backing_queue_state = BQS, unknown_pending = ExtPending }) -> ExtPending1 = ExtPending + PendingDelta, - State1 = State #state { unknown_pending = ExtPending1 }, - case ExtPending1 =:= 0 andalso Length =:= BQ:len(BQS) of - true -> set_synchronised1(true, State1); - false when ExtPending1 >= 0 -> set_synchronised1(false, State1) - end. + true = ExtPending1 >= 0, + set_synchronised1(ExtPending1 =:= 0 andalso Length =:= BQ:len(BQS), + State #state { unknown_pending = ExtPending1 }). set_synchronised(Length, State) -> set_synchronised(0, Length, State). -- cgit v1.2.1 From 5cb13a8445ac522bf201118bbbfe9054be7be4fb Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 31 Aug 2012 18:55:44 +0100 Subject: cosmetic --- src/rabbit_mirror_queue_slave.erl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 9ee020d1..54546f75 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -913,6 +913,8 @@ maybe_store_ack(true, MsgId, AckTag, State = #state { msg_id_ack = MA, State #state { msg_id_ack = dict:store(MsgId, {Num, AckTag}, MA), ack_num = Num + 1 }. +set_synchronised(Length, State) -> set_synchronised(0, Length, State). + set_synchronised(_, _, State = #state { unknown_pending = undefined }) -> State; set_synchronised(PendingDelta, Length, @@ -924,9 +926,6 @@ set_synchronised(PendingDelta, Length, set_synchronised1(ExtPending1 =:= 0 andalso Length =:= BQ:len(BQS), State #state { unknown_pending = ExtPending1 }). -set_synchronised(Length, State) -> - set_synchronised(0, Length, State). - %% We intentionally leave out the head where a slave becomes %% unsynchronised: we assert that can never happen. set_synchronised1(true, State = #state { q = #amqqueue { name = QName }, -- cgit v1.2.1 From 188a756593a26db18345f6a95145f9bc9e35164e Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 31 Aug 2012 19:05:58 +0100 Subject: refactoring: inline helper function --- src/rabbit_mirror_queue_slave.erl | 49 ++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 54546f75..6882b2da 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -920,29 +920,30 @@ set_synchronised(_, _, State = #state { unknown_pending = undefined }) -> set_synchronised(PendingDelta, Length, State = #state { backing_queue = BQ, backing_queue_state = BQS, - unknown_pending = ExtPending }) -> + unknown_pending = ExtPending, + synchronised = Sync}) -> ExtPending1 = ExtPending + PendingDelta, true = ExtPending1 >= 0, - set_synchronised1(ExtPending1 =:= 0 andalso Length =:= BQ:len(BQS), - State #state { unknown_pending = ExtPending1 }). - -%% We intentionally leave out the head where a slave becomes -%% unsynchronised: we assert that can never happen. -set_synchronised1(true, State = #state { q = #amqqueue { name = QName }, - synchronised = false }) -> - Self = self(), - rabbit_misc:execute_mnesia_transaction( - fun () -> - case mnesia:read({rabbit_queue, QName}) of - [] -> - ok; - [Q1 = #amqqueue{sync_slave_pids = SSPids}] -> - Q2 = Q1#amqqueue{sync_slave_pids = [Self | SSPids]}, - rabbit_mirror_queue_misc:store_updated_slaves(Q2) - end - end), - State #state { synchronised = true }; -set_synchronised1(true, State) -> - State; -set_synchronised1(false, State = #state { synchronised = false }) -> - State. + State1 = State #state { unknown_pending = ExtPending1 }, + %% We intentionally leave out the head where a slave becomes + %% unsynchronised: we assert that can never happen. + case {Sync, ExtPending1 =:= 0 andalso Length =:= BQ:len(BQS)} of + {true, true} -> + State1; + {false, false} -> + State1; + {false, true} -> + Self = self(), + #state{ q = #amqqueue { name = QName } } = State1, + rabbit_misc:execute_mnesia_transaction( + fun () -> + case mnesia:read({rabbit_queue, QName}) of + [] -> + ok; + [Q1 = #amqqueue{sync_slave_pids = SSPids}] -> + rabbit_mirror_queue_misc:store_updated_slaves( + Q1#amqqueue{sync_slave_pids = [Self | SSPids]}) + end + end), + State1 #state { synchronised = true } + end. -- cgit v1.2.1 From 8cddf06bea147e94230e8a75a3f460c178960ac9 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 31 Aug 2012 19:08:40 +0100 Subject: refactoring: make the var match the field --- src/rabbit_mirror_queue_slave.erl | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 6882b2da..5111f46a 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -885,9 +885,8 @@ process_instruction({sender_death, ChPid}, msg_id_status = MS1, known_senders = pmon:demonitor(ChPid, KS) } end}; -process_instruction({length, Length, ExtPending}, State) -> - {ok, set_synchronised(Length, - State #state { unknown_pending = ExtPending })}; +process_instruction({length, Length, Pend}, State) -> + {ok, set_synchronised(Length, State #state { unknown_pending = Pend })}; process_instruction({delete_and_terminate, Reason}, State = #state { backing_queue = BQ, backing_queue_state = BQS }) -> @@ -920,14 +919,14 @@ set_synchronised(_, _, State = #state { unknown_pending = undefined }) -> set_synchronised(PendingDelta, Length, State = #state { backing_queue = BQ, backing_queue_state = BQS, - unknown_pending = ExtPending, + unknown_pending = Pend, synchronised = Sync}) -> - ExtPending1 = ExtPending + PendingDelta, - true = ExtPending1 >= 0, - State1 = State #state { unknown_pending = ExtPending1 }, + Pend1 = Pend + PendingDelta, + true = Pend1 >= 0, + State1 = State #state { unknown_pending = Pend1 }, %% We intentionally leave out the head where a slave becomes %% unsynchronised: we assert that can never happen. - case {Sync, ExtPending1 =:= 0 andalso Length =:= BQ:len(BQS)} of + case {Sync, Pend1 =:= 0 andalso Length =:= BQ:len(BQS)} of {true, true} -> State1; {false, false} -> -- cgit v1.2.1 From d43da6ef1a8de96ae08f48bde55b0e38fa6d2e2d Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 3 Sep 2012 15:31:48 +0100 Subject: Test that simple_one_for_one_terminate shutdowns no longer deadlock and handle reporting errors not caught during shutdown (e.g., 'killed' pids) --- src/rabbit_tests.erl | 1 + src/supervisor2.erl | 3 +- src/supervisor2_tests.erl | 100 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 src/supervisor2_tests.erl diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index ccac12c6..e1914ac2 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -32,6 +32,7 @@ -define(TIMEOUT, 5000). all_tests() -> + ok = supervisor2_tests:test_all(), passed = gm_tests:all_tests(), passed = mirrored_supervisor_tests:all_tests(), application:set_env(rabbit, file_handles_high_watermark, 10, infinity), diff --git a/src/supervisor2.erl b/src/supervisor2.erl index 89a8fd92..632b7808 100644 --- a/src/supervisor2.erl +++ b/src/supervisor2.erl @@ -834,7 +834,8 @@ terminate_simple_children(Child, Dynamics, SupName) -> timeout_stop(Child, TRef, TimeoutMsg, Timedout), ReportError = shutdown_error_reporter(SupName), Report = fun(_, ok) -> ok; - (Pid, {error, R}) -> ReportError(R, Child#child{pid = Pid}) + (Pid, {error, R}) -> ReportError(R, Child#child{pid = Pid}); + (Pid, Other) -> ReportError(Other, Child#child{pid = Pid}) end, [receive {'EXIT', Pid, Reason} -> Report(Pid, Reason) diff --git a/src/supervisor2_tests.erl b/src/supervisor2_tests.erl new file mode 100644 index 00000000..c4916791 --- /dev/null +++ b/src/supervisor2_tests.erl @@ -0,0 +1,100 @@ +%% 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 VMware, Inc. +%% Copyright (c) 2011-2012 VMware, Inc. All rights reserved. +%% + +-module(supervisor2_tests). +-behaviour(supervisor2). + +-export([test_all/0, start_sup/0, start_link/0, start_child/0]). +-export([init/1]). + +-compile(export_all). + +-include_lib("eunit/include/eunit.hrl"). + +test_all() -> + eunit:test(supervisor2, [verbose]). + +terminate_simple_children_without_deadlock_test_() -> + [{setup, fun init_supervisor/0, + {with, [fun ensure_children_are_alive/1, + fun shutdown_and_verify_all_children_died/1]}}, + {setup, fun init_supervisor/0, + {with, [fun shutdown_whilst_interleaving_exits_occur/1]}}]. + +%% +%% Public (test facing) API +%% + +start_sup() -> + supervisor2:start_link({local, ?MODULE}, ?MODULE, []). + +start_link() -> + Pid = spawn_link(fun loop_infinity/0), + {ok, Pid}. + +start_child() -> + {ok, Pid} = supervisor2:start_child(?MODULE, []), + Pid. + +%% +%% supervisor2 callbacks +%% + +init([parent]) -> + {ok, {{one_for_one, 0, 1}, + [{test_sup, {?MODULE, start_sup, []}, + transient, 5000, supervisor, [?MODULE]}]}}; +init([]) -> + {ok, {{simple_one_for_one_terminate, 0, 1}, + [{test_worker, {?MODULE, start_link, []}, + temporary, 1000, worker, []}]}}. + +%% +%% Private API +%% + +ensure_children_are_alive({_, ChildPids}) -> + ?assertEqual(true, + lists:all(fun erlang:is_process_alive/1, ChildPids)). + +shutdown_and_verify_all_children_died({Parent, ChildPids}=State) -> + ensure_children_are_alive(State), + TestSup = erlang:whereis(?MODULE), + ?assertEqual(true, erlang:is_process_alive(TestSup)), + ?assertMatch(ok, supervisor2:terminate_child(Parent, test_sup)), + ?assertMatch([], [P || P <- ChildPids, + erlang:is_process_alive(P)]), + ?assertEqual(false, erlang:is_process_alive(TestSup)). + +shutdown_whilst_interleaving_exits_occur({Parent, ChildPids}=State) -> + ensure_children_are_alive(State), + TestPid = self(), + Ref = erlang:make_ref(), + spawn(fun() -> + TestPid ! {Ref, supervisor2:terminate_child(Parent, test_sup)} + end), + [exit(P, kill) || P <- ChildPids], + receive {Ref, Res} -> + ?assertEqual(ok, Res) + end. + +init_supervisor() -> + {ok, Pid} = supervisor2:start_link(?MODULE, [parent]), + {Pid, [start_child() || _ <- lists:seq(1, 100)]}. + +loop_infinity() -> + loop_infinity(). + -- cgit v1.2.1 From 379d46dfab4175ed27121283839ffc8763a3f729 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Mon, 3 Sep 2012 16:00:40 +0100 Subject: store the depth of master and slave instead of the unknown pending msgs --- src/rabbit_mirror_queue_master.erl | 17 +++-- src/rabbit_mirror_queue_slave.erl | 144 ++++++++++++++++++++----------------- 2 files changed, 92 insertions(+), 69 deletions(-) diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index 1151fd76..62109dae 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -96,7 +96,7 @@ init(#amqqueue { name = QName, mirror_nodes = MNodes } = Q, Recover, [rabbit_mirror_queue_misc:add_mirror(QName, Node) || Node <- MNodes1], {ok, BQ} = application:get_env(backing_queue_module), BQS = BQ:init(Q, Recover, AsyncCallback), - ok = gm:broadcast(GM, {length, BQ:len(BQS), BQ:pending_ack(BQS)}), + ok = gm:broadcast(GM, {depth, depth(BQ, BQS)}), #state { gm = GM, coordinator = CPid, backing_queue = BQ, @@ -251,7 +251,7 @@ ack(AckTags, State = #state { gm = GM, {MsgIds, BQS1} = BQ:ack(AckTags, BQS), case MsgIds of [] -> ok; - _ -> ok = gm:broadcast(GM, {ack, MsgIds, BQ:len(BQS1)}) + _ -> ok = gm:broadcast(GM, {ack, MsgIds}) end, AM1 = lists:foldl(fun dict:erase/2, AM, AckTags), {MsgIds, State #state { backing_queue_state = BQS1, @@ -265,7 +265,7 @@ requeue(AckTags, State = #state { gm = GM, backing_queue = BQ, backing_queue_state = BQS }) -> {MsgIds, BQS1} = BQ:requeue(AckTags, BQS), - ok = gm:broadcast(GM, {requeue, MsgIds, BQ:len(BQS1)}), + ok = gm:broadcast(GM, {requeue, MsgIds}), {MsgIds, State #state { backing_queue_state = BQS1 }}. len(#state { backing_queue = BQ, backing_queue_state = BQS }) -> @@ -375,7 +375,7 @@ discard(Msg = #basic_message { id = MsgId }, ChPid, promote_backing_queue_state(CPid, BQ, BQS, GM, SeenStatus, KS) -> Len = BQ:len(BQS), - ok = gm:broadcast(GM, {length, Len, BQ:pending_ack(BQS)}), + ok = gm:broadcast(GM, {depth, depth(BQ, BQS)}), #state { gm = GM, coordinator = CPid, backing_queue = BQ, @@ -407,7 +407,7 @@ length_fun() -> backing_queue = BQ, backing_queue_state = BQS }) -> ok = gm:broadcast( - GM, {length, BQ:len(BQS), BQ:pending_ack(BQS)}), + GM, {depth, depth(BQ, BQS)}), State end) end. @@ -425,3 +425,10 @@ ensure_monitoring(ChPid, State = #state { coordinator = CPid, CPid, [ChPid]), State #state { known_senders = sets:add_element(ChPid, KS) } end. + +%% --------------------------------------------------------------------------- +%% Internal exports +%% --------------------------------------------------------------------------- + +depth(BQ, BQS) -> + BQ:len(BQS) + BQ:pending_ack(BQS). diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 5111f46a..5e0907be 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -77,11 +77,10 @@ msg_id_status, known_senders, - synchronised, - %% Records the pending acks on the master for messages that we - %% do not have. This is necessary to accurately determine - %% whether the slave is synchronised or not. - unknown_pending + %% The depth is the BQ len + the number of messages pending + %% acks. + depth, + master_depth }). start_link(Q) -> @@ -135,8 +134,8 @@ init(#amqqueue { name = QueueName } = Q) -> msg_id_status = dict:new(), known_senders = pmon:new(), - synchronised = false, - unknown_pending = undefined + depth = 0, + master_depth = undefined }, rabbit_event:notify(queue_slave_created, infos(?CREATION_EVENT_KEYS, State)), @@ -396,7 +395,7 @@ infos(Items, State) -> [{Item, i(Item, State)} || Item <- Items]. i(pid, _State) -> self(); i(name, #state { q = #amqqueue { name = Name } }) -> Name; i(master_pid, #state { master_pid = MPid }) -> MPid; -i(is_synchronised, #state { synchronised = Synchronised }) -> Synchronised; +i(is_synchronised, #state { depth = D, master_depth = MD }) -> D =:= MD; i(Item, _State) -> throw({bad_argument, Item}). bq_init(BQ, Q, Recover) -> @@ -771,18 +770,22 @@ process_instruction( SQ1 = dict:store(ChPid, {MQ1, PendingCh1}, SQ), State2 = State1 #state { sender_queues = SQ1, msg_id_status = MS1 }, - - {ok, - case Deliver of - false -> - BQS1 = BQ:publish(Msg, MsgProps, ChPid, BQS), - State2 #state { backing_queue_state = BQS1 }; - {true, AckRequired} -> - {AckTag, BQS1} = BQ:publish_delivered(AckRequired, Msg, MsgProps, - ChPid, BQS), - maybe_store_ack(AckRequired, MsgId, AckTag, - State2 #state { backing_queue_state = BQS1 }) - end}; + {State3, Delta} = + case Deliver of + false -> + BQS1 = BQ:publish(Msg, MsgProps, ChPid, BQS), + {State2 #state { backing_queue_state = BQS1 }, 1}; + {true, AckRequired} -> + {AckTag, BQS1} = BQ:publish_delivered( + AckRequired, Msg, MsgProps, ChPid, BQS), + {maybe_store_ack(AckRequired, MsgId, AckTag, + State2 #state {backing_queue_state = BQS1}), + case AckRequired of + true -> 1; + false -> 1 + end} + end, + {ok, set_synchronised(Delta, Delta, State3)}; process_instruction({discard, ChPid, Msg = #basic_message { id = MsgId }}, State = #state { sender_queues = SQ, backing_queue = BQ, @@ -831,43 +834,49 @@ process_instruction({set_length, Length, Dropped, AckRequired}, StateN #state { backing_queue_state = BQSN1 }) end, State, lists:duplicate(ToDrop, const)), {ok, case AckRequired of - true -> set_synchronised(Dropped - ToDrop, Length, State1); - false -> set_synchronised(Length, State1) + true -> State1; + false -> set_synchronised(-ToDrop, -Dropped, State1) end}; process_instruction({fetch, AckRequired, MsgId, Remaining}, State = #state { backing_queue = BQ, backing_queue_state = BQS }) -> QLen = BQ:len(BQS), - {ok, case {QLen - 1, AckRequired} of - {Remaining, _} -> - {{#basic_message{id = MsgId}, _IsDelivered, - AckTag, Remaining}, BQS1} = BQ:fetch(AckRequired, BQS), - maybe_store_ack(AckRequired, MsgId, AckTag, - State #state { backing_queue_state = BQS1 }); - {_, false} when QLen =< Remaining -> - set_synchronised(Remaining, State); - {_, true} when QLen =< Remaining -> - set_synchronised(1, Remaining, State) - end}; -process_instruction({ack, MsgIds, Length}, + {State1, {Delta, MasterDelta}} = + case {QLen - 1} of + Remaining -> + {{#basic_message{id = MsgId}, _IsDelivered, + AckTag, Remaining}, BQS1} = BQ:fetch(AckRequired, BQS), + {maybe_store_ack(AckRequired, MsgId, AckTag, + State #state { backing_queue_state = BQS1 }), + case AckRequired of + true -> {0, 0}; + false -> {-1, -1} + end}; + _ when QLen =< Remaining -> + {State, case AckRequired of + true -> {0, 0}; + false -> {0, -1} + end} + end, + {ok, set_synchronised(Delta, MasterDelta, State1)}; +process_instruction({ack, MsgIds}, State = #state { backing_queue = BQ, backing_queue_state = BQS, msg_id_ack = MA }) -> {AckTags, MA1} = msg_ids_to_acktags(MsgIds, MA), {MsgIds1, BQS1} = BQ:ack(AckTags, BQS), [] = MsgIds1 -- MsgIds, %% ASSERTION - {ok, set_synchronised(length(AckTags) - length(MsgIds), Length, + {ok, set_synchronised(-length(AckTags), -length(MsgIds), State #state { msg_id_ack = MA1, backing_queue_state = BQS1 })}; -process_instruction({requeue, MsgIds, Length}, +process_instruction({requeue, MsgIds}, State = #state { backing_queue = BQ, backing_queue_state = BQS, msg_id_ack = MA }) -> {AckTags, MA1} = msg_ids_to_acktags(MsgIds, MA), {_MsgIds, BQS1} = BQ:requeue(AckTags, BQS), - {ok, set_synchronised(length(AckTags) - length(MsgIds), Length, - State #state { msg_id_ack = MA1, - backing_queue_state = BQS1 })}; + {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, @@ -885,8 +894,10 @@ process_instruction({sender_death, ChPid}, msg_id_status = MS1, known_senders = pmon:demonitor(ChPid, KS) } end}; -process_instruction({length, Length, Pend}, State) -> - {ok, set_synchronised(Length, State #state { unknown_pending = Pend })}; + +process_instruction({depth, Depth}, State) -> + {ok, set_synchronised(0, 0, true, State #state { master_depth = Depth })}; + process_instruction({delete_and_terminate, Reason}, State = #state { backing_queue = BQ, backing_queue_state = BQS }) -> @@ -912,37 +923,42 @@ maybe_store_ack(true, MsgId, AckTag, State = #state { msg_id_ack = MA, State #state { msg_id_ack = dict:store(MsgId, {Num, AckTag}, MA), ack_num = Num + 1 }. -set_synchronised(Length, State) -> set_synchronised(0, Length, State). - -set_synchronised(_, _, State = #state { unknown_pending = undefined }) -> - State; -set_synchronised(PendingDelta, Length, - State = #state { backing_queue = BQ, - backing_queue_state = BQS, - unknown_pending = Pend, - synchronised = Sync}) -> - Pend1 = Pend + PendingDelta, - true = Pend1 >= 0, - State1 = State #state { unknown_pending = Pend1 }, +set_synchronised(Delta, MasterDelta, State) -> + set_synchronised(Delta, MasterDelta, false, State). + +set_synchronised(Delta, _MasterDelta, _AddAnyway, + State = #state { depth = Depth, + master_depth = undefined }) -> + State #state { depth = Depth + Delta }; +set_synchronised(Delta, MasterDelta, AddAnyway, + State = #state { depth = Depth, + master_depth = MasterDepth, + q = #amqqueue { name = QName }}) -> + Depth1 = Depth + Delta, + MasterDepth1 = MasterDepth + MasterDelta, + io:format("depths: local depths ~p ~p master depth ~p ~p~n", [Depth, Depth1, MasterDepth, MasterDepth1]), %% We intentionally leave out the head where a slave becomes %% unsynchronised: we assert that can never happen. - case {Sync, Pend1 =:= 0 andalso Length =:= BQ:len(BQS)} of - {true, true} -> - State1; - {false, false} -> - State1; - {false, true} -> + %% The `AddAnyway' param is there since in the `depth' instruction we + %% receive the master depth for the first time, and we want to set the sync + %% state anyway if we are synced. + case {Depth =:= MasterDepth, Depth1 =:= MasterDepth1} of + {WasSync, true} when not WasSync orelse AddAnyway -> Self = self(), - #state{ q = #amqqueue { name = QName } } = State1, rabbit_misc:execute_mnesia_transaction( fun () -> case mnesia:read({rabbit_queue, QName}) of [] -> ok; [Q1 = #amqqueue{sync_slave_pids = SSPids}] -> + %% We might be there already, in the `AddAnyway' + %% case + SSPids1 = SSPids -- [Self], rabbit_mirror_queue_misc:store_updated_slaves( - Q1#amqqueue{sync_slave_pids = [Self | SSPids]}) + Q1#amqqueue{sync_slave_pids = [Self | SSPids1]}) end - end), - State1 #state { synchronised = true } - end. + end); + {Same, Same} -> + ok + end, + State #state { depth = Depth1, master_depth = MasterDepth1 }. -- cgit v1.2.1 From 570dd389a5d35bc66b27ba64ad18b124b60dc84b Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Mon, 3 Sep 2012 16:23:45 +0100 Subject: forgot debug line in --- src/rabbit_mirror_queue_slave.erl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 5e0907be..bec9fab5 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -936,7 +936,6 @@ set_synchronised(Delta, MasterDelta, AddAnyway, q = #amqqueue { name = QName }}) -> Depth1 = Depth + Delta, MasterDepth1 = MasterDepth + MasterDelta, - io:format("depths: local depths ~p ~p master depth ~p ~p~n", [Depth, Depth1, MasterDepth, MasterDepth1]), %% We intentionally leave out the head where a slave becomes %% unsynchronised: we assert that can never happen. %% The `AddAnyway' param is there since in the `depth' instruction we -- cgit v1.2.1 From fef645432aa37b98ab39d9c4203a067f86ca0932 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 3 Sep 2012 16:31:35 +0100 Subject: attempt to close ssl connections gracefully but forcefully gracefully...so that there is a good chance of TLS alerts making it through, but forecfully (after a timeout) so we don't get stuck (or worse, silently) holding on to file descriptors and processes. --- src/rabbit_net.erl | 24 ++++++++++++++++++++++-- src/rabbit_networking.erl | 12 ++++++++++++ src/rabbit_reader.erl | 4 ++-- 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/rabbit_net.erl b/src/rabbit_net.erl index 698a7c9a..eacff141 100644 --- a/src/rabbit_net.erl +++ b/src/rabbit_net.erl @@ -77,6 +77,8 @@ %%--------------------------------------------------------------------------- +-define(SSL_CLOSE_TIMEOUT, 5000). + -define(IS_SSL(Sock), is_record(Sock, ssl_socket)). is_ssl(Sock) -> ?IS_SSL(Sock). @@ -148,8 +150,26 @@ send(Sock, Data) when is_port(Sock) -> gen_tcp:send(Sock, Data). close(Sock) when ?IS_SSL(Sock) -> ssl:close(Sock#ssl_socket.ssl); close(Sock) when is_port(Sock) -> gen_tcp:close(Sock). -fast_close(Sock) when ?IS_SSL(Sock) -> ok; -fast_close(Sock) when is_port(Sock) -> erlang:port_close(Sock), ok. +fast_close(Sock) when ?IS_SSL(Sock) -> + %% We cannot simply port_close the underlying tcp socket since the + %% TLS protocol is quite insistent that a proper closing handshake + %% should take place (see RFC 5245 s7.2.1). So we call ssl:close + %% instead, but that can block for a very long time if the socket + %% is in a funny state. Since there is no timeout variant of + %% ssl:close, we construct our own. + {Pid, MRef} = spawn_monitor(fun () -> ssl:close(Sock#ssl_socket.ssl) end), + erlang:send_after(?SSL_CLOSE_TIMEOUT, self(), {Pid, ssl_close_timeout}), + receive + {Pid, ssl_close_timeout} -> + erlang:demonitor(MRef, [flush]), + exit(Pid, kill); + {'DOWN', MRef, process, Pid, _Reason} -> + ok + end, + catch port_close(Sock#ssl_socket.tcp), + ok; +fast_close(Sock) when is_port(Sock) -> + catch port_close(Sock), ok. sockname(Sock) when ?IS_SSL(Sock) -> ssl:sockname(Sock#ssl_socket.ssl); sockname(Sock) when is_port(Sock) -> inet:sockname(Sock). diff --git a/src/rabbit_networking.erl b/src/rabbit_networking.erl index 94a5a2b7..2d0ded12 100644 --- a/src/rabbit_networking.erl +++ b/src/rabbit_networking.erl @@ -160,7 +160,19 @@ ssl_transform_fun(SslOpts) -> case catch ssl:ssl_accept(Sock, SslOpts, ?SSL_TIMEOUT * 1000) of {ok, SslSock} -> {ok, #ssl_socket{tcp = Sock, ssl = SslSock}}; + {error, timeout} -> + {error, {ssl_upgrade_error, timeout}}; {error, Reason} -> + %% We have no idea what state the ssl_connection + %% process is in - it could still be happily + %% going, it might be stuck, or it could be just + %% about to fail. There is little that our caller + %% can do but close the TCP socket, but this could + %% cause ssl alerts to get dropped (which is bad + %% form, according to the TLS spec). So we give + %% the ssl_connection a little bit of time to send + %% such alerts. + timer:sleep(?SSL_TIMEOUT * 1000), {error, {ssl_upgrade_error, Reason}}; {'EXIT', Reason} -> {error, {ssl_upgrade_failure, Reason}} diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index bd20deb2..4bcb347d 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -184,6 +184,7 @@ socket_op(Sock, Fun) -> {ok, Res} -> Res; {error, Reason} -> log(error, "error on AMQP connection ~p: ~p~n", [self(), Reason]), + rabbit_net:fast_close(Sock), exit(normal) end. @@ -242,8 +243,7 @@ start_connection(Parent, ChannelSupSupPid, Collector, StartHeartbeatFun, Deb, %% controlling process and hence its termination will close %% the socket. However, to keep the file_handle_cache %% accounting as accurate as possible we ought to close the - %% socket w/o delay before termination. fast_close does that, - %% though only for non-ssl sockets. + %% socket w/o delay before termination. rabbit_net:fast_close(ClientSock), rabbit_event:notify(connection_closed, [{pid, self()}]) end, -- cgit v1.2.1 From 6db853fb28bc092e39f956919ba4b8bd1031cec8 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 3 Sep 2012 16:43:52 +0100 Subject: Introduce additional, non-smp tests (using suspend) and explode the test fixture list to create 400 repetitions of the group --- src/supervisor2_tests.erl | 61 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 54 insertions(+), 7 deletions(-) diff --git a/src/supervisor2_tests.erl b/src/supervisor2_tests.erl index c4916791..342dce27 100644 --- a/src/supervisor2_tests.erl +++ b/src/supervisor2_tests.erl @@ -20,6 +20,8 @@ -export([test_all/0, start_sup/0, start_link/0, start_child/0]). -export([init/1]). +-define(NUM_CHILDREN, 1000). + -compile(export_all). -include_lib("eunit/include/eunit.hrl"). @@ -28,11 +30,20 @@ test_all() -> eunit:test(supervisor2, [verbose]). terminate_simple_children_without_deadlock_test_() -> - [{setup, fun init_supervisor/0, - {with, [fun ensure_children_are_alive/1, - fun shutdown_and_verify_all_children_died/1]}}, - {setup, fun init_supervisor/0, - {with, [fun shutdown_whilst_interleaving_exits_occur/1]}}]. + lists:flatten( + lists:duplicate( + 100,[{setup, fun init_supervisor/0, + {with, [fun ensure_children_are_alive/1, + fun shutdown_and_verify_all_children_died/1]}}, + {setup, fun init_supervisor/0, + {with, [fun shutdown_whilst_interleaving_exits_occur/1]}}, + {setup, + fun() -> erlang:system_flag(multi_scheduling, block), + ?MODULE:init_supervisor() + end, + fun(_) -> erlang:system_flag(multi_scheduling, unblock) end, + {with, + [fun shutdown_with_exits_attempting_to_overtake_downs/1]}}])). %% %% Public (test facing) API @@ -79,6 +90,40 @@ shutdown_and_verify_all_children_died({Parent, ChildPids}=State) -> erlang:is_process_alive(P)]), ?assertEqual(false, erlang:is_process_alive(TestSup)). +shutdown_with_exits_attempting_to_overtake_downs({Parent, ChildPids}=State) -> + erlang:process_flag(priority, high), + ensure_children_are_alive(State), + TestPid = self(), + TestSup = erlang:whereis(?MODULE), + Ref = erlang:make_ref(), + + ?debugFmt("Suspending process ~p~n", + [TestSup]), + true = erlang:suspend_process(TestSup), + + spawn(fun() -> + ?debugFmt("supervisor2:terminate_child/2 call in progress~n", + []), + TestPid ! {Ref, supervisor2:terminate_child(Parent, test_sup)} + end), + erlang:yield(), + + ?debugFmt("Killing ~p child pids~n", [length(ChildPids)]), + [P ! stop || P <- ChildPids], + + ?debugFmt("Resuming process ~p~n", [TestSup]), + erlang:resume_process(TestSup), + + erlang:yield(), + ?debugVal(erlang:is_process_alive(TestSup)), + ?debugFmt("Awaiting response from kill coordinator~n", []), + receive {Ref, Res} -> + ?assertEqual(ok, Res) + end, + ?assertMatch([], [P || P <- ChildPids, + erlang:is_process_alive(P)]), + ok. + shutdown_whilst_interleaving_exits_occur({Parent, ChildPids}=State) -> ensure_children_are_alive(State), TestPid = self(), @@ -93,8 +138,10 @@ shutdown_whilst_interleaving_exits_occur({Parent, ChildPids}=State) -> init_supervisor() -> {ok, Pid} = supervisor2:start_link(?MODULE, [parent]), - {Pid, [start_child() || _ <- lists:seq(1, 100)]}. + {Pid, [start_child() || _ <- lists:seq(1, ?NUM_CHILDREN)]}. loop_infinity() -> - loop_infinity(). + receive + stop -> ok + end. -- cgit v1.2.1 From 88181f450c06190d0de3f5b6104c0ba2f998e121 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 3 Sep 2012 16:55:07 +0100 Subject: cosmetic --- src/supervisor2.erl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/supervisor2.erl b/src/supervisor2.erl index 632b7808..89a8fd92 100644 --- a/src/supervisor2.erl +++ b/src/supervisor2.erl @@ -834,8 +834,7 @@ terminate_simple_children(Child, Dynamics, SupName) -> timeout_stop(Child, TRef, TimeoutMsg, Timedout), ReportError = shutdown_error_reporter(SupName), Report = fun(_, ok) -> ok; - (Pid, {error, R}) -> ReportError(R, Child#child{pid = Pid}); - (Pid, Other) -> ReportError(Other, Child#child{pid = Pid}) + (Pid, {error, R}) -> ReportError(R, Child#child{pid = Pid}) end, [receive {'EXIT', Pid, Reason} -> Report(Pid, Reason) -- cgit v1.2.1 From 9c50bea971407e86a6b2400489c701b7eba34217 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 3 Sep 2012 17:00:02 +0100 Subject: construct child exit reason in the right place --- src/supervisor2.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/supervisor2.erl b/src/supervisor2.erl index 89a8fd92..9644e17d 100644 --- a/src/supervisor2.erl +++ b/src/supervisor2.erl @@ -837,7 +837,8 @@ terminate_simple_children(Child, Dynamics, SupName) -> (Pid, {error, R}) -> ReportError(R, Child#child{pid = Pid}) end, [receive - {'EXIT', Pid, Reason} -> Report(Pid, Reason) + {'EXIT', Pid, Reason} -> + Report(Pid, child_res(Child, Reason, Timedout)) after 0 -> Report(Pid, Reply) end || {Pid, Reply} <- Replies], -- cgit v1.2.1 From e41f42aee9a6b95ca95c6dc63d2310c336f7d51f Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Mon, 3 Sep 2012 17:13:04 +0100 Subject: reset and join when changing node type --- src/rabbit_mnesia.erl | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index dc27e58b..ea2cbc1e 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -266,9 +266,8 @@ change_cluster_node_type(Type) -> "Non-clustered nodes can only be disc nodes"}}); true -> ok end, - DiscoveryNodes = all_clustered_nodes(), - {AllNodes, DiscNodes, _} = - case discover_cluster(DiscoveryNodes) of + {_, _, RunningNodes} = + case discover_cluster(all_clustered_nodes()) of {ok, Status} -> Status; {error, _Reason} -> @@ -279,19 +278,21 @@ change_cluster_node_type(Type) -> "you can use the \"update_cluster_nodes\" command to " "point to the new cluster nodes"}}) end, - WantDiscNode = case Type of - ram -> false; - disc -> true - end, - case not WantDiscNode andalso [node()] =:= DiscNodes of - true -> throw({error, - {standalone_ram_node, - "You can't change the node type to ram if the node is " - "the only disc node in its cluster. Please add more " - "disc nodes to the cluster first."}}); - false -> ok - end, - ok = init_db_with_mnesia(AllNodes, WantDiscNode, false). + Node = case RunningNodes of + [] -> + throw({error, + {no_online_cluster_nodes, + "Could not find any online cluster nodes. If the " + "cluster has changed, you can use the 'recluster' " + "command."}}); + [Node0|_] -> + Node0 + end, + ok = reset(false), + ok = join_cluster(Node, case Type of + ram -> false; + disc -> true + end). update_cluster_nodes(DiscoveryNode) -> ensure_mnesia_not_running(), -- cgit v1.2.1 From 81f3caeb1e154d3f10261c7c17776e0077692e88 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 3 Sep 2012 17:17:17 +0100 Subject: cosmetic --- src/supervisor2_tests.erl | 87 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 60 insertions(+), 27 deletions(-) diff --git a/src/supervisor2_tests.erl b/src/supervisor2_tests.erl index 342dce27..3c1997c6 100644 --- a/src/supervisor2_tests.erl +++ b/src/supervisor2_tests.erl @@ -26,24 +26,57 @@ -include_lib("eunit/include/eunit.hrl"). +test_me() -> + erlang:system_flag(multi_scheduling, block), + P = self(), + F = fun() -> [P ! {self(), hi} || _ <- lists:seq(1, 100)] end, + erlang:process_flag(priority, high), + Launcher = fun() -> + Pid = spawn(F), + erlang:suspend_process(Pid), + P ! {suspendee, Pid} + end, + Test = + fun() -> + Launcher(), + [P ! {self(), ho} || _ <- lists:seq(1, 100)] + end, + ReceiveAndResume = + fun() -> + receive {suspendee, X} -> + erlang:resume_process(X) + end + end, + Test(), + ReceiveAndResume(), + drain_q(). + +drain_q() -> + receive + X -> + ?debugFmt("Received ~p~n", [X]) + after 0 -> + ok + end. + test_all() -> eunit:test(supervisor2, [verbose]). terminate_simple_children_without_deadlock_test_() -> lists:flatten( lists:duplicate( - 100,[{setup, fun init_supervisor/0, - {with, [fun ensure_children_are_alive/1, - fun shutdown_and_verify_all_children_died/1]}}, - {setup, fun init_supervisor/0, - {with, [fun shutdown_whilst_interleaving_exits_occur/1]}}, - {setup, - fun() -> erlang:system_flag(multi_scheduling, block), - ?MODULE:init_supervisor() - end, - fun(_) -> erlang:system_flag(multi_scheduling, unblock) end, - {with, - [fun shutdown_with_exits_attempting_to_overtake_downs/1]}}])). + 100,[{setup, fun init_supervisor/0, + {with, [fun ensure_children_are_alive/1, + fun shutdown_and_verify_all_children_died/1]}}, + {setup, fun init_supervisor/0, + {with, [fun shutdown_whilst_interleaving_exits_occur/1]}}, + {setup, + fun() -> erlang:system_flag(multi_scheduling, block), + ?MODULE:init_supervisor() + end, + fun(_) -> erlang:system_flag(multi_scheduling, unblock) end, + {with, + [fun shutdown_with_exits_attempting_to_overtake_downs/1]}}])). %% %% Public (test facing) API @@ -66,8 +99,8 @@ start_child() -> init([parent]) -> {ok, {{one_for_one, 0, 1}, - [{test_sup, {?MODULE, start_sup, []}, - transient, 5000, supervisor, [?MODULE]}]}}; + [{test_sup, {?MODULE, start_sup, []}, + transient, 5000, supervisor, [?MODULE]}]}}; init([]) -> {ok, {{simple_one_for_one_terminate, 0, 1}, [{test_worker, {?MODULE, start_link, []}, @@ -79,7 +112,7 @@ init([]) -> ensure_children_are_alive({_, ChildPids}) -> ?assertEqual(true, - lists:all(fun erlang:is_process_alive/1, ChildPids)). + lists:all(fun erlang:is_process_alive/1, ChildPids)). shutdown_and_verify_all_children_died({Parent, ChildPids}=State) -> ensure_children_are_alive(State), @@ -87,7 +120,7 @@ shutdown_and_verify_all_children_died({Parent, ChildPids}=State) -> ?assertEqual(true, erlang:is_process_alive(TestSup)), ?assertMatch(ok, supervisor2:terminate_child(Parent, test_sup)), ?assertMatch([], [P || P <- ChildPids, - erlang:is_process_alive(P)]), + erlang:is_process_alive(P)]), ?assertEqual(false, erlang:is_process_alive(TestSup)). shutdown_with_exits_attempting_to_overtake_downs({Parent, ChildPids}=State) -> @@ -98,14 +131,14 @@ shutdown_with_exits_attempting_to_overtake_downs({Parent, ChildPids}=State) -> Ref = erlang:make_ref(), ?debugFmt("Suspending process ~p~n", - [TestSup]), + [TestSup]), true = erlang:suspend_process(TestSup), spawn(fun() -> - ?debugFmt("supervisor2:terminate_child/2 call in progress~n", - []), - TestPid ! {Ref, supervisor2:terminate_child(Parent, test_sup)} - end), + ?debugFmt("supervisor2:terminate_child/2 call in progress~n", + []), + TestPid ! {Ref, supervisor2:terminate_child(Parent, test_sup)} + end), erlang:yield(), ?debugFmt("Killing ~p child pids~n", [length(ChildPids)]), @@ -118,10 +151,10 @@ shutdown_with_exits_attempting_to_overtake_downs({Parent, ChildPids}=State) -> ?debugVal(erlang:is_process_alive(TestSup)), ?debugFmt("Awaiting response from kill coordinator~n", []), receive {Ref, Res} -> - ?assertEqual(ok, Res) + ?assertEqual(ok, Res) end, ?assertMatch([], [P || P <- ChildPids, - erlang:is_process_alive(P)]), + erlang:is_process_alive(P)]), ok. shutdown_whilst_interleaving_exits_occur({Parent, ChildPids}=State) -> @@ -129,11 +162,11 @@ shutdown_whilst_interleaving_exits_occur({Parent, ChildPids}=State) -> TestPid = self(), Ref = erlang:make_ref(), spawn(fun() -> - TestPid ! {Ref, supervisor2:terminate_child(Parent, test_sup)} - end), + TestPid ! {Ref, supervisor2:terminate_child(Parent, test_sup)} + end), [exit(P, kill) || P <- ChildPids], receive {Ref, Res} -> - ?assertEqual(ok, Res) + ?assertEqual(ok, Res) end. init_supervisor() -> @@ -142,6 +175,6 @@ init_supervisor() -> loop_infinity() -> receive - stop -> ok + stop -> ok end. -- cgit v1.2.1 From f13f0324d1d0370afaf9a550e905b3a982a0570d Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 3 Sep 2012 17:34:39 +0100 Subject: oops --- src/supervisor2_tests.erl | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/src/supervisor2_tests.erl b/src/supervisor2_tests.erl index 3c1997c6..69d17b7f 100644 --- a/src/supervisor2_tests.erl +++ b/src/supervisor2_tests.erl @@ -26,39 +26,6 @@ -include_lib("eunit/include/eunit.hrl"). -test_me() -> - erlang:system_flag(multi_scheduling, block), - P = self(), - F = fun() -> [P ! {self(), hi} || _ <- lists:seq(1, 100)] end, - erlang:process_flag(priority, high), - Launcher = fun() -> - Pid = spawn(F), - erlang:suspend_process(Pid), - P ! {suspendee, Pid} - end, - Test = - fun() -> - Launcher(), - [P ! {self(), ho} || _ <- lists:seq(1, 100)] - end, - ReceiveAndResume = - fun() -> - receive {suspendee, X} -> - erlang:resume_process(X) - end - end, - Test(), - ReceiveAndResume(), - drain_q(). - -drain_q() -> - receive - X -> - ?debugFmt("Received ~p~n", [X]) - after 0 -> - ok - end. - test_all() -> eunit:test(supervisor2, [verbose]). -- cgit v1.2.1 From 084efae8e5a973eea0a76892584df72a100f0e02 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Mon, 3 Sep 2012 17:40:38 +0100 Subject: cosmetics --- src/supervisor2.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/supervisor2.erl b/src/supervisor2.erl index 9644e17d..e26df7d7 100644 --- a/src/supervisor2.erl +++ b/src/supervisor2.erl @@ -838,7 +838,7 @@ terminate_simple_children(Child, Dynamics, SupName) -> end, [receive {'EXIT', Pid, Reason} -> - Report(Pid, child_res(Child, Reason, Timedout)) + Report(Pid, child_res(Child, Reason, Timedout)) after 0 -> Report(Pid, Reply) end || {Pid, Reply} <- Replies], -- cgit v1.2.1 From 7b1694673e5e3598e8a91a2955b158727c763e7b Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 3 Sep 2012 17:46:18 +0100 Subject: remove useless test case --- src/supervisor2_tests.erl | 47 +++-------------------------------------------- 1 file changed, 3 insertions(+), 44 deletions(-) diff --git a/src/supervisor2_tests.erl b/src/supervisor2_tests.erl index 69d17b7f..4a14dd2d 100644 --- a/src/supervisor2_tests.erl +++ b/src/supervisor2_tests.erl @@ -36,14 +36,7 @@ terminate_simple_children_without_deadlock_test_() -> {with, [fun ensure_children_are_alive/1, fun shutdown_and_verify_all_children_died/1]}}, {setup, fun init_supervisor/0, - {with, [fun shutdown_whilst_interleaving_exits_occur/1]}}, - {setup, - fun() -> erlang:system_flag(multi_scheduling, block), - ?MODULE:init_supervisor() - end, - fun(_) -> erlang:system_flag(multi_scheduling, unblock) end, - {with, - [fun shutdown_with_exits_attempting_to_overtake_downs/1]}}])). + {with, [fun shutdown_whilst_interleaving_exits_occur/1]}}])). %% %% Public (test facing) API @@ -90,40 +83,6 @@ shutdown_and_verify_all_children_died({Parent, ChildPids}=State) -> erlang:is_process_alive(P)]), ?assertEqual(false, erlang:is_process_alive(TestSup)). -shutdown_with_exits_attempting_to_overtake_downs({Parent, ChildPids}=State) -> - erlang:process_flag(priority, high), - ensure_children_are_alive(State), - TestPid = self(), - TestSup = erlang:whereis(?MODULE), - Ref = erlang:make_ref(), - - ?debugFmt("Suspending process ~p~n", - [TestSup]), - true = erlang:suspend_process(TestSup), - - spawn(fun() -> - ?debugFmt("supervisor2:terminate_child/2 call in progress~n", - []), - TestPid ! {Ref, supervisor2:terminate_child(Parent, test_sup)} - end), - erlang:yield(), - - ?debugFmt("Killing ~p child pids~n", [length(ChildPids)]), - [P ! stop || P <- ChildPids], - - ?debugFmt("Resuming process ~p~n", [TestSup]), - erlang:resume_process(TestSup), - - erlang:yield(), - ?debugVal(erlang:is_process_alive(TestSup)), - ?debugFmt("Awaiting response from kill coordinator~n", []), - receive {Ref, Res} -> - ?assertEqual(ok, Res) - end, - ?assertMatch([], [P || P <- ChildPids, - erlang:is_process_alive(P)]), - ok. - shutdown_whilst_interleaving_exits_occur({Parent, ChildPids}=State) -> ensure_children_are_alive(State), TestPid = self(), @@ -131,7 +90,7 @@ shutdown_whilst_interleaving_exits_occur({Parent, ChildPids}=State) -> spawn(fun() -> TestPid ! {Ref, supervisor2:terminate_child(Parent, test_sup)} end), - [exit(P, kill) || P <- ChildPids], + [P ! stop || P <- ChildPids], receive {Ref, Res} -> ?assertEqual(ok, Res) end. @@ -142,6 +101,6 @@ init_supervisor() -> loop_infinity() -> receive - stop -> ok + stop -> ok end. -- cgit v1.2.1 From fe02b54dc8b4c49f0a9164ef0677dced4a9c5a11 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 3 Sep 2012 17:51:22 +0100 Subject: cosmetic (again) --- src/supervisor2_tests.erl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/supervisor2_tests.erl b/src/supervisor2_tests.erl index 4a14dd2d..ce5857a3 100644 --- a/src/supervisor2_tests.erl +++ b/src/supervisor2_tests.erl @@ -59,8 +59,8 @@ start_child() -> init([parent]) -> {ok, {{one_for_one, 0, 1}, - [{test_sup, {?MODULE, start_sup, []}, - transient, 5000, supervisor, [?MODULE]}]}}; + [{test_sup, {?MODULE, start_sup, []}, + transient, 5000, supervisor, [?MODULE]}]}}; init([]) -> {ok, {{simple_one_for_one_terminate, 0, 1}, [{test_worker, {?MODULE, start_link, []}, @@ -72,7 +72,7 @@ init([]) -> ensure_children_are_alive({_, ChildPids}) -> ?assertEqual(true, - lists:all(fun erlang:is_process_alive/1, ChildPids)). + lists:all(fun erlang:is_process_alive/1, ChildPids)). shutdown_and_verify_all_children_died({Parent, ChildPids}=State) -> ensure_children_are_alive(State), @@ -80,7 +80,7 @@ shutdown_and_verify_all_children_died({Parent, ChildPids}=State) -> ?assertEqual(true, erlang:is_process_alive(TestSup)), ?assertMatch(ok, supervisor2:terminate_child(Parent, test_sup)), ?assertMatch([], [P || P <- ChildPids, - erlang:is_process_alive(P)]), + erlang:is_process_alive(P)]), ?assertEqual(false, erlang:is_process_alive(TestSup)). shutdown_whilst_interleaving_exits_occur({Parent, ChildPids}=State) -> @@ -88,11 +88,11 @@ shutdown_whilst_interleaving_exits_occur({Parent, ChildPids}=State) -> TestPid = self(), Ref = erlang:make_ref(), spawn(fun() -> - TestPid ! {Ref, supervisor2:terminate_child(Parent, test_sup)} - end), + TestPid ! {Ref, supervisor2:terminate_child(Parent, test_sup)} + end), [P ! stop || P <- ChildPids], receive {Ref, Res} -> - ?assertEqual(ok, Res) + ?assertEqual(ok, Res) end. init_supervisor() -> -- cgit v1.2.1 From a785a62035bf8508692a165420debe701e35c737 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Mon, 3 Sep 2012 17:56:57 +0100 Subject: cosmetics --- src/supervisor2_tests.erl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/supervisor2_tests.erl b/src/supervisor2_tests.erl index ce5857a3..26933cfc 100644 --- a/src/supervisor2_tests.erl +++ b/src/supervisor2_tests.erl @@ -32,11 +32,11 @@ test_all() -> terminate_simple_children_without_deadlock_test_() -> lists:flatten( lists:duplicate( - 100,[{setup, fun init_supervisor/0, - {with, [fun ensure_children_are_alive/1, - fun shutdown_and_verify_all_children_died/1]}}, - {setup, fun init_supervisor/0, - {with, [fun shutdown_whilst_interleaving_exits_occur/1]}}])). + 100, [{setup, fun init_supervisor/0, + {with, [fun ensure_children_are_alive/1, + fun shutdown_and_verify_all_children_died/1]}}, + {setup, fun init_supervisor/0, + {with, [fun shutdown_whilst_interleaving_exits_occur/1]}}])). %% %% Public (test facing) API -- cgit v1.2.1 From 5159b9ef18fc46ead6d92dcf7d84fdd7ffaad868 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Mon, 3 Sep 2012 18:04:10 +0100 Subject: cosmetics! --- src/supervisor2_tests.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/supervisor2_tests.erl b/src/supervisor2_tests.erl index 26933cfc..2827e6ef 100644 --- a/src/supervisor2_tests.erl +++ b/src/supervisor2_tests.erl @@ -74,7 +74,7 @@ ensure_children_are_alive({_, ChildPids}) -> ?assertEqual(true, lists:all(fun erlang:is_process_alive/1, ChildPids)). -shutdown_and_verify_all_children_died({Parent, ChildPids}=State) -> +shutdown_and_verify_all_children_died({Parent, ChildPids} = State) -> ensure_children_are_alive(State), TestSup = erlang:whereis(?MODULE), ?assertEqual(true, erlang:is_process_alive(TestSup)), @@ -83,7 +83,7 @@ shutdown_and_verify_all_children_died({Parent, ChildPids}=State) -> erlang:is_process_alive(P)]), ?assertEqual(false, erlang:is_process_alive(TestSup)). -shutdown_whilst_interleaving_exits_occur({Parent, ChildPids}=State) -> +shutdown_whilst_interleaving_exits_occur({Parent, ChildPids} = State) -> ensure_children_are_alive(State), TestPid = self(), Ref = erlang:make_ref(), -- cgit v1.2.1 From b9100c6a425f65f2f5a338b5ad65dbfce5ac0ce3 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Mon, 3 Sep 2012 18:11:21 +0100 Subject: export only what's necessary, cosmetics --- src/supervisor2_tests.erl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/supervisor2_tests.erl b/src/supervisor2_tests.erl index 2827e6ef..6898cfa6 100644 --- a/src/supervisor2_tests.erl +++ b/src/supervisor2_tests.erl @@ -22,7 +22,7 @@ -define(NUM_CHILDREN, 1000). --compile(export_all). +-export([test_all/0]). -include_lib("eunit/include/eunit.hrl"). @@ -103,4 +103,3 @@ loop_infinity() -> receive stop -> ok end. - -- cgit v1.2.1 From 05a8e69c70a290a3b27c48abac1f4c069ad5a92e Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 3 Sep 2012 18:34:49 +0100 Subject: explain --- src/rabbit_net.erl | 11 ++++++++--- src/rabbit_reader.erl | 1 + 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/rabbit_net.erl b/src/rabbit_net.erl index eacff141..038154c3 100644 --- a/src/rabbit_net.erl +++ b/src/rabbit_net.erl @@ -154,9 +154,14 @@ fast_close(Sock) when ?IS_SSL(Sock) -> %% We cannot simply port_close the underlying tcp socket since the %% TLS protocol is quite insistent that a proper closing handshake %% should take place (see RFC 5245 s7.2.1). So we call ssl:close - %% instead, but that can block for a very long time if the socket - %% is in a funny state. Since there is no timeout variant of - %% ssl:close, we construct our own. + %% instead, but that can block for a very long time, e.g. when + %% there is lots of pending output and there is tcp backpressure, + %% or the ssl_connection process has entered the the + %% workaround_transport_delivery_problems function during + %% termination, which, inexplicably, does a gen_tcp:recv(Socket, + %% 0), which may never return if the client doesn't send a FIN or + %% that gets swallowed by the network. Since there is no timeout + %% variant of ssl:close, we construct our own. {Pid, MRef} = spawn_monitor(fun () -> ssl:close(Sock#ssl_socket.ssl) end), erlang:send_after(?SSL_CLOSE_TIMEOUT, self(), {Pid, ssl_close_timeout}), receive diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 4bcb347d..aef48b20 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -184,6 +184,7 @@ socket_op(Sock, Fun) -> {ok, Res} -> Res; {error, Reason} -> log(error, "error on AMQP connection ~p: ~p~n", [self(), Reason]), + %% NB: this is tcp socket, even in case of ssl rabbit_net:fast_close(Sock), exit(normal) end. -- cgit v1.2.1 From 24a1161a52e32f170c9fdf549e152a14771be41e Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Tue, 4 Sep 2012 10:56:32 +0100 Subject: rework tests for most simple case of child termination increase test coverage for whole of terminate_simple_children fix bug in timeout handling clause of terminate_simple_children cosmetic (trailing whitespace) --- src/supervisor2.erl | 106 +++++++++++++++++++++---------------------- src/supervisor2_tests.erl | 113 ++++++++++++++++++---------------------------- 2 files changed, 98 insertions(+), 121 deletions(-) diff --git a/src/supervisor2.erl b/src/supervisor2.erl index e26df7d7..5af38573 100644 --- a/src/supervisor2.erl +++ b/src/supervisor2.erl @@ -255,10 +255,10 @@ behaviour_info(_Other) -> %%% --------------------------------------------------- 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. %%% --------------------------------------------------- @@ -298,9 +298,9 @@ check_childspecs(ChildSpecs) when is_list(ChildSpecs) -> check_childspecs(X) -> {error, {badarg, X}}. %%% --------------------------------------------------- -%%% +%%% %%% Initialize the supervisor. -%%% +%%% %%% --------------------------------------------------- init({SupName, Mod, Args}) -> process_flag(trap_exit, true), @@ -319,7 +319,7 @@ init({SupName, Mod, Args}) -> Error -> {stop, {bad_return, {Mod, init, Error}}} end. - + init_children(State, StartSpec) -> SupName = State#state.name, case check_startspec(StartSpec) of @@ -349,7 +349,7 @@ 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 +%% Purpose: Start all children. The new list contains #child's %% with pids. %% Returns: {ok, NChildren} | {error, NChildren} %% NChildren = [#child] in termination order (reversed @@ -381,7 +381,7 @@ do_start_child(SupName, Child) -> NChild = Child#child{pid = Pid}, report_progress(NChild, SupName), {ok, Pid, Extra}; - ignore -> + ignore -> {ok, undefined}; {error, What} -> {error, What}; What -> {error, What} @@ -400,12 +400,12 @@ do_start_child_i(M, F, A) -> What -> {error, What} end. - + %%% --------------------------------------------------- -%%% +%%% %%% Callback functions. -%%% +%%% %%% --------------------------------------------------- handle_call({start_child, EArgs}, _From, State) when ?is_simple(State) -> #child{mfa = {M, F, A}} = hd(State#state.children), @@ -414,11 +414,11 @@ handle_call({start_child, EArgs}, _From, State) when ?is_simple(State) -> {ok, undefined} -> {reply, {ok, undefined}, State}; {ok, Pid} -> - NState = 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 = + NState = State#state{dynamics = ?DICT:store(Pid, Args, State#state.dynamics)}, {reply, {ok, Pid, Extra}, NState}; What -> @@ -497,7 +497,7 @@ handle_call(which_children, _From, 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", + error_logger:error_msg("ERROR: Supervisor received cast-message 'null'~n", []), {noreply, State}. @@ -527,7 +527,7 @@ handle_info({'EXIT', Pid, Reason}, 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}. %% @@ -577,13 +577,13 @@ check_flags({Strategy, MaxIntensity, Period}) -> check_flags(What) -> {bad_flags, What}. -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) 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 @@ -604,7 +604,7 @@ update_childspec1([Child|OldC], Children, KeepOld) -> end; update_childspec1([], Children, KeepOld) -> % Return them in (keeped) reverse start order. - lists:reverse(Children ++ KeepOld). + lists:reverse(Children ++ KeepOld). update_chsp(OldCh, Children) -> case lists:map(fun (Ch) when OldCh#child.name =:= Ch#child.name -> @@ -618,7 +618,7 @@ update_chsp(OldCh, Children) -> NewC -> {ok, NewC} end. - + %%% --------------------------------------------------- %%% Start a new child. %%% --------------------------------------------------- @@ -630,12 +630,12 @@ handle_start_child(Child, State) -> {ok, Pid} -> Children = State#state.children, {{ok, Pid}, - State#state{children = + State#state{children = [Child#child{pid = Pid}|Children]}}; {ok, Pid, Extra} -> Children = State#state.children, {{ok, Pid, Extra}, - State#state{children = + State#state{children = [Child#child{pid = Pid}|Children]}}; {error, What} -> {{error, {What, Child}}, State} @@ -866,7 +866,7 @@ timeout_stop(#child{shutdown = Time}, TRef, Msg, false) when is_integer(Time) -> after 0 -> ok end; -timeout_stop(#child{}, ok, _Msg, _Timedout) -> +timeout_stop(#child{}, _TRef, _Msg, _Timedout) -> ok. do_terminate(Child, SupName) when Child#child.pid =/= undefined -> @@ -888,17 +888,17 @@ do_terminate(Child, _SupName) -> Child. %%----------------------------------------------------------------- -%% 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), @@ -908,16 +908,16 @@ shutdown(Pid, brutal_kill) -> {'DOWN', _MRef, process, Pid, OtherReason} -> {error, OtherReason} end; - {error, Reason} -> + {error, Reason} -> {error, Reason} end; 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} -> @@ -929,14 +929,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... @@ -946,22 +946,22 @@ 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 + %% 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. - - + + %%----------------------------------------------------------------- %% Child/State manipulating functions. %%----------------------------------------------------------------- @@ -1015,7 +1015,7 @@ remove_child(Child, State) -> %% Args: SupName = {local, atom()} | {global, atom()} | self %% Type = {Strategy, MaxIntensity, Period} %% Strategy = one_for_one | one_for_all | simple_one_for_one | -%% rest_for_one +%% rest_for_one %% MaxIntensity = integer() %% Period = integer() %% Mod :== atom() @@ -1110,10 +1110,10 @@ validChildType(supervisor) -> true; validChildType(worker) -> true; validChildType(What) -> throw({invalid_child_type, What}). -validName(_Name) -> true. +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}). @@ -1131,7 +1131,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; @@ -1157,7 +1157,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, diff --git a/src/supervisor2_tests.erl b/src/supervisor2_tests.erl index 6898cfa6..599dfac9 100644 --- a/src/supervisor2_tests.erl +++ b/src/supervisor2_tests.erl @@ -17,89 +17,66 @@ -module(supervisor2_tests). -behaviour(supervisor2). --export([test_all/0, start_sup/0, start_link/0, start_child/0]). +-export([test_all/0, start_link/0]). -export([init/1]). --define(NUM_CHILDREN, 1000). - --export([test_all/0]). - -include_lib("eunit/include/eunit.hrl"). +-define(TEST_RUNS, 2000). +-define(SLOW_TEST_RUNS, 45). +-define(CHILDREN, 100). + test_all() -> - eunit:test(supervisor2, [verbose]). + eunit:test(?MODULE, [verbose]). -terminate_simple_children_without_deadlock_test_() -> - lists:flatten( - lists:duplicate( - 100, [{setup, fun init_supervisor/0, - {with, [fun ensure_children_are_alive/1, - fun shutdown_and_verify_all_children_died/1]}}, - {setup, fun init_supervisor/0, - {with, [fun shutdown_whilst_interleaving_exits_occur/1]}}])). +simple_child_shutdown_without_deadlock_test_() -> + lists:duplicate(?TEST_RUNS, + [{timeout, 60, fun test_clean_stop/0}]). -%% -%% Public (test facing) API -%% +simple_child_shutdown_with_timeout_test_() -> + lists:duplicate(?SLOW_TEST_RUNS, + [{timeout, 120, fun test_timeout/0}]). -start_sup() -> - supervisor2:start_link({local, ?MODULE}, ?MODULE, []). +test_clean_stop() -> + test_it(stop). -start_link() -> - Pid = spawn_link(fun loop_infinity/0), - {ok, Pid}. +test_timeout() -> + test_it(ignored). -start_child() -> - {ok, Pid} = supervisor2:start_child(?MODULE, []), - Pid. +test_it(SigStop) -> + {ok, Pid} = supervisor2:start_link(?MODULE, [?CHILDREN]), + start_and_terminate_children(SigStop, Pid, ?CHILDREN), + unlink(Pid), + exit(Pid, shutdown). -%% -%% supervisor2 callbacks -%% +start_link() -> + Pid = spawn_link(fun () -> + process_flag(trap_exit, true), + receive stop -> ok end + end), + {ok, Pid}. -init([parent]) -> +init([N]) -> {ok, {{one_for_one, 0, 1}, - [{test_sup, {?MODULE, start_sup, []}, - transient, 5000, supervisor, [?MODULE]}]}}; + [{test_sup, {supervisor2, start_link, + [{local, ?MODULE}, ?MODULE, []]}, + transient, N * 100, supervisor, [?MODULE]}]}}; init([]) -> {ok, {{simple_one_for_one_terminate, 0, 1}, [{test_worker, {?MODULE, start_link, []}, - temporary, 1000, worker, []}]}}. - -%% -%% Private API -%% - -ensure_children_are_alive({_, ChildPids}) -> - ?assertEqual(true, - lists:all(fun erlang:is_process_alive/1, ChildPids)). - -shutdown_and_verify_all_children_died({Parent, ChildPids} = State) -> - ensure_children_are_alive(State), - TestSup = erlang:whereis(?MODULE), - ?assertEqual(true, erlang:is_process_alive(TestSup)), - ?assertMatch(ok, supervisor2:terminate_child(Parent, test_sup)), - ?assertMatch([], [P || P <- ChildPids, - erlang:is_process_alive(P)]), - ?assertEqual(false, erlang:is_process_alive(TestSup)). - -shutdown_whilst_interleaving_exits_occur({Parent, ChildPids} = State) -> - ensure_children_are_alive(State), - TestPid = self(), - Ref = erlang:make_ref(), - spawn(fun() -> - TestPid ! {Ref, supervisor2:terminate_child(Parent, test_sup)} - end), - [P ! stop || P <- ChildPids], - receive {Ref, Res} -> - ?assertEqual(ok, Res) - end. - -init_supervisor() -> - {ok, Pid} = supervisor2:start_link(?MODULE, [parent]), - {Pid, [start_child() || _ <- lists:seq(1, ?NUM_CHILDREN)]}. - -loop_infinity() -> + temporary, 1000, worker, [?MODULE]}]}}. + +start_and_terminate_children(SigStop, Sup, N) -> + TestSupPid = whereis(?MODULE), + ChildPids = [begin + {ok, ChildPid} = supervisor2:start_child(TestSupPid, []), + ChildPid + end || _ <- lists:seq(1, N)], + erlang:monitor(process, TestSupPid), + [P ! SigStop || P <- ChildPids], + ?assertEqual(ok, supervisor2:terminate_child(Sup, test_sup)), + ?assertMatch({ok,_}, supervisor2:restart_child(Sup, test_sup)), receive - stop -> ok + {'DOWN', _MRef, process, TestSupPid, Reason} -> + ?assertMatch(shutdown, Reason) end. -- cgit v1.2.1 From 27c6ee8132bcd8a0d0924624f5a5085cbe80508b Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Tue, 4 Sep 2012 11:23:18 +0100 Subject: cosmetics --- src/supervisor2_tests.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/supervisor2_tests.erl b/src/supervisor2_tests.erl index 599dfac9..dbb3bea3 100644 --- a/src/supervisor2_tests.erl +++ b/src/supervisor2_tests.erl @@ -75,8 +75,8 @@ start_and_terminate_children(SigStop, Sup, N) -> erlang:monitor(process, TestSupPid), [P ! SigStop || P <- ChildPids], ?assertEqual(ok, supervisor2:terminate_child(Sup, test_sup)), - ?assertMatch({ok,_}, supervisor2:restart_child(Sup, test_sup)), + ?assertMatch({ok, _}, supervisor2:restart_child(Sup, test_sup)), receive {'DOWN', _MRef, process, TestSupPid, Reason} -> - ?assertMatch(shutdown, Reason) + ?assertMatch(shutdown, Reason) end. -- cgit v1.2.1 From 5794c238301c46b57d2c3e849b52f022f0e482e6 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Tue, 4 Sep 2012 12:57:08 +0100 Subject: fix depth delta in the `publish' instruction --- src/rabbit_mirror_queue_slave.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index bec9fab5..dce69675 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -782,7 +782,7 @@ process_instruction( State2 #state {backing_queue_state = BQS1}), case AckRequired of true -> 1; - false -> 1 + false -> 0 end} end, {ok, set_synchronised(Delta, Delta, State3)}; -- cgit v1.2.1 From c4d78500e17f080300710a9e36dcb85740c13675 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Tue, 4 Sep 2012 13:00:47 +0100 Subject: was getting `set_length' instead of `drop' --- src/rabbit_mirror_queue_slave.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index dce69675..6f3d1382 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -817,7 +817,7 @@ process_instruction({discard, ChPid, Msg = #basic_message { id = MsgId }}, {ok, State1 #state { sender_queues = SQ1, msg_id_status = MS1, backing_queue_state = BQS1 }}; -process_instruction({set_length, Length, Dropped, AckRequired}, +process_instruction({drop, Length, Dropped, AckRequired}, State = #state { backing_queue = BQ, backing_queue_state = BQS }) -> QLen = BQ:len(BQS), -- cgit v1.2.1 From 0f1837d57b8ec249b28bf5915dbfeb5e3aeb4cb8 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Tue, 4 Sep 2012 13:21:02 +0100 Subject: refactor tests --- src/supervisor2_tests.erl | 55 ++++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 29 deletions(-) diff --git a/src/supervisor2_tests.erl b/src/supervisor2_tests.erl index dbb3bea3..5c5e6d85 100644 --- a/src/supervisor2_tests.erl +++ b/src/supervisor2_tests.erl @@ -30,24 +30,35 @@ test_all() -> eunit:test(?MODULE, [verbose]). simple_child_shutdown_without_deadlock_test_() -> - lists:duplicate(?TEST_RUNS, - [{timeout, 60, fun test_clean_stop/0}]). + [{timeout, ?TEST_RUNS * 10, + check_shutdown_handling(stop, ?TEST_RUNS, ?CHILDREN)}]. simple_child_shutdown_with_timeout_test_() -> - lists:duplicate(?SLOW_TEST_RUNS, - [{timeout, 120, fun test_timeout/0}]). + [{timeout, ?SLOW_TEST_RUNS * 10, + check_shutdown_handling(ignored, ?SLOW_TEST_RUNS, ?CHILDREN)}]. -test_clean_stop() -> - test_it(stop). - -test_timeout() -> - test_it(ignored). - -test_it(SigStop) -> - {ok, Pid} = supervisor2:start_link(?MODULE, [?CHILDREN]), - start_and_terminate_children(SigStop, Pid, ?CHILDREN), - unlink(Pid), - exit(Pid, shutdown). +check_shutdown_handling(SigStop, Iterations, ChildCount) -> + fun() -> + {ok, Sup} = supervisor2:start_link(?MODULE, [?CHILDREN]), + [begin + TestSupPid = erlang:whereis(?MODULE), + ChildPids = [begin + {ok, ChildPid} = + supervisor2:start_child(TestSupPid, []), + ChildPid + end || _ <- lists:seq(1, ChildCount)], + erlang:monitor(process, TestSupPid), + [P ! SigStop || P <- ChildPids], + ?assertEqual(ok, supervisor2:terminate_child(Sup, test_sup)), + {ok, _} = supervisor2:restart_child(Sup, test_sup), + receive + {'DOWN', _MRef, process, TestSupPid, Reason} -> + ?assertEqual(shutdown, Reason) + end + end || _ <- lists:seq(1, Iterations)], + unlink(Sup), + exit(Sup, shutdown) + end. start_link() -> Pid = spawn_link(fun () -> @@ -66,17 +77,3 @@ init([]) -> [{test_worker, {?MODULE, start_link, []}, temporary, 1000, worker, [?MODULE]}]}}. -start_and_terminate_children(SigStop, Sup, N) -> - TestSupPid = whereis(?MODULE), - ChildPids = [begin - {ok, ChildPid} = supervisor2:start_child(TestSupPid, []), - ChildPid - end || _ <- lists:seq(1, N)], - erlang:monitor(process, TestSupPid), - [P ! SigStop || P <- ChildPids], - ?assertEqual(ok, supervisor2:terminate_child(Sup, test_sup)), - ?assertMatch({ok, _}, supervisor2:restart_child(Sup, test_sup)), - receive - {'DOWN', _MRef, process, TestSupPid, Reason} -> - ?assertMatch(shutdown, Reason) - end. -- cgit v1.2.1 From f0c218288954052e4786e65228f5ad8be24c85ec Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Tue, 4 Sep 2012 15:42:22 +0100 Subject: track the delta of the depths, and replace `pending_ack' with `depth' in BQ The kill-multi test is still failing... --- src/rabbit_backing_queue.erl | 6 +-- src/rabbit_mirror_queue_master.erl | 19 +++----- src/rabbit_mirror_queue_slave.erl | 92 +++++++++++++++++--------------------- src/rabbit_variable_queue.erl | 6 +-- 4 files changed, 52 insertions(+), 71 deletions(-) diff --git a/src/rabbit_backing_queue.erl b/src/rabbit_backing_queue.erl index eac1db2f..d69a6c3b 100644 --- a/src/rabbit_backing_queue.erl +++ b/src/rabbit_backing_queue.erl @@ -152,8 +152,8 @@ %% Is my queue empty? -callback is_empty(state()) -> boolean(). -%% How many pending acks do we have? --callback pending_ack(state()) -> non_neg_integer(). +%% What's the queue depth, where depth = length + number of pending acks +-callback depth(state()) -> non_neg_integer(). %% For the next three functions, the assumption is that you're %% monitoring something like the ingress and egress rates of the @@ -215,7 +215,7 @@ behaviour_info(callbacks) -> {delete_and_terminate, 2}, {purge, 1}, {publish, 4}, {publish_delivered, 5}, {drain_confirmed, 1}, {dropwhile, 3}, {fetch, 2}, {ack, 2}, {fold, 3}, {requeue, 2}, {len, 1}, - {is_empty, 1}, {pending_ack, 1}, {set_ram_duration_target, 2}, + {is_empty, 1}, {depth, 1}, {set_ram_duration_target, 2}, {ram_duration, 1}, {needs_timeout, 1}, {timeout, 1}, {handle_pre_hibernate, 1}, {status, 1}, {invoke, 3}, {is_duplicate, 2}, {discard, 3}]; diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index 62109dae..ad66d059 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -18,7 +18,7 @@ -export([init/3, terminate/2, delete_and_terminate/2, purge/1, publish/4, publish_delivered/5, fetch/2, ack/2, - requeue/2, len/1, is_empty/1, pending_ack/1, drain_confirmed/1, + requeue/2, len/1, is_empty/1, depth/1, drain_confirmed/1, dropwhile/3, set_ram_duration_target/2, ram_duration/1, needs_timeout/1, timeout/1, handle_pre_hibernate/1, status/1, invoke/3, is_duplicate/2, discard/3, fold/3]). @@ -96,7 +96,7 @@ init(#amqqueue { name = QName, mirror_nodes = MNodes } = Q, Recover, [rabbit_mirror_queue_misc:add_mirror(QName, Node) || Node <- MNodes1], {ok, BQ} = application:get_env(backing_queue_module), BQS = BQ:init(Q, Recover, AsyncCallback), - ok = gm:broadcast(GM, {depth, depth(BQ, BQS)}), + ok = gm:broadcast(GM, {depth, BQ:depth(BQS)}), #state { gm = GM, coordinator = CPid, backing_queue = BQ, @@ -274,8 +274,8 @@ len(#state { backing_queue = BQ, backing_queue_state = BQS }) -> is_empty(#state { backing_queue = BQ, backing_queue_state = BQS }) -> BQ:is_empty(BQS). -pending_ack(#state { backing_queue = BQ, backing_queue_state = BQS }) -> - BQ:pending_ack(BQS). +depth(#state { backing_queue = BQ, backing_queue_state = BQS }) -> + BQ:depth(BQS). set_ram_duration_target(Target, State = #state { backing_queue = BQ, backing_queue_state = BQS }) -> @@ -375,7 +375,7 @@ discard(Msg = #basic_message { id = MsgId }, ChPid, promote_backing_queue_state(CPid, BQ, BQS, GM, SeenStatus, KS) -> Len = BQ:len(BQS), - ok = gm:broadcast(GM, {depth, depth(BQ, BQS)}), + ok = gm:broadcast(GM, {depth, BQ:depth(BQS)}), #state { gm = GM, coordinator = CPid, backing_queue = BQ, @@ -407,7 +407,7 @@ length_fun() -> backing_queue = BQ, backing_queue_state = BQS }) -> ok = gm:broadcast( - GM, {depth, depth(BQ, BQS)}), + GM, {depth, BQ:depth(BQS)}), State end) end. @@ -425,10 +425,3 @@ ensure_monitoring(ChPid, State = #state { coordinator = CPid, CPid, [ChPid]), State #state { known_senders = sets:add_element(ChPid, KS) } end. - -%% --------------------------------------------------------------------------- -%% Internal exports -%% --------------------------------------------------------------------------- - -depth(BQ, BQS) -> - BQ:len(BQS) + BQ:pending_ack(BQS). diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 6f3d1382..ee65a0a7 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -77,10 +77,8 @@ msg_id_status, known_senders, - %% The depth is the BQ len + the number of messages pending - %% acks. - depth, - master_depth + %% Master depth - local depth + depth_delta }). start_link(Q) -> @@ -134,8 +132,7 @@ init(#amqqueue { name = QueueName } = Q) -> msg_id_status = dict:new(), known_senders = pmon:new(), - depth = 0, - master_depth = undefined + depth_delta = undefined }, rabbit_event:notify(queue_slave_created, infos(?CREATION_EVENT_KEYS, State)), @@ -395,7 +392,7 @@ infos(Items, State) -> [{Item, i(Item, State)} || Item <- Items]. i(pid, _State) -> self(); i(name, #state { q = #amqqueue { name = Name } }) -> Name; i(master_pid, #state { master_pid = MPid }) -> MPid; -i(is_synchronised, #state { depth = D, master_depth = MD }) -> D =:= MD; +i(is_synchronised, #state { depth_delta = DD }) -> DD =:= 0; i(Item, _State) -> throw({bad_argument, Item}). bq_init(BQ, Q, Recover) -> @@ -770,22 +767,16 @@ process_instruction( SQ1 = dict:store(ChPid, {MQ1, PendingCh1}, SQ), State2 = State1 #state { sender_queues = SQ1, msg_id_status = MS1 }, - {State3, Delta} = - case Deliver of - false -> - BQS1 = BQ:publish(Msg, MsgProps, ChPid, BQS), - {State2 #state { backing_queue_state = BQS1 }, 1}; - {true, AckRequired} -> - {AckTag, BQS1} = BQ:publish_delivered( - AckRequired, Msg, MsgProps, ChPid, BQS), - {maybe_store_ack(AckRequired, MsgId, AckTag, - State2 #state {backing_queue_state = BQS1}), - case AckRequired of - true -> 1; - false -> 0 - end} - end, - {ok, set_synchronised(Delta, Delta, State3)}; + {ok, case Deliver of + false -> + BQS1 = BQ:publish(Msg, MsgProps, ChPid, BQS), + State2 #state { backing_queue_state = BQS1 }; + {true, AckRequired} -> + {AckTag, BQS1} = BQ:publish_delivered( + AckRequired, Msg, MsgProps, ChPid, BQS), + maybe_store_ack(AckRequired, MsgId, AckTag, + State2 #state {backing_queue_state = BQS1}) + end}; process_instruction({discard, ChPid, Msg = #basic_message { id = MsgId }}, State = #state { sender_queues = SQ, backing_queue = BQ, @@ -835,30 +826,27 @@ process_instruction({drop, Length, Dropped, AckRequired}, end, State, lists:duplicate(ToDrop, const)), {ok, case AckRequired of true -> State1; - false -> set_synchronised(-ToDrop, -Dropped, State1) + false -> set_synchronised(ToDrop - Dropped, State1) end}; process_instruction({fetch, AckRequired, MsgId, Remaining}, State = #state { backing_queue = BQ, backing_queue_state = BQS }) -> QLen = BQ:len(BQS), - {State1, {Delta, MasterDelta}} = + {State1, Delta} = case {QLen - 1} of Remaining -> {{#basic_message{id = MsgId}, _IsDelivered, AckTag, Remaining}, BQS1} = BQ:fetch(AckRequired, BQS), {maybe_store_ack(AckRequired, MsgId, AckTag, State #state { backing_queue_state = BQS1 }), - case AckRequired of - true -> {0, 0}; - false -> {-1, -1} - end}; + 0}; _ when QLen =< Remaining -> {State, case AckRequired of - true -> {0, 0}; - false -> {0, -1} + true -> 0; + false -> -1 end} end, - {ok, set_synchronised(Delta, MasterDelta, State1)}; + {ok, set_synchronised(Delta, State1)}; process_instruction({ack, MsgIds}, State = #state { backing_queue = BQ, backing_queue_state = BQS, @@ -866,7 +854,7 @@ process_instruction({ack, MsgIds}, {AckTags, MA1} = msg_ids_to_acktags(MsgIds, MA), {MsgIds1, BQS1} = BQ:ack(AckTags, BQS), [] = MsgIds1 -- MsgIds, %% ASSERTION - {ok, set_synchronised(-length(AckTags), -length(MsgIds), + {ok, set_synchronised(length(MsgIds1) - length(MsgIds), State #state { msg_id_ack = MA1, backing_queue_state = BQS1 })}; process_instruction({requeue, MsgIds}, @@ -895,8 +883,11 @@ process_instruction({sender_death, ChPid}, known_senders = pmon:demonitor(ChPid, KS) } end}; -process_instruction({depth, Depth}, State) -> - {ok, set_synchronised(0, 0, true, State #state { master_depth = Depth })}; +process_instruction({depth, Depth}, + State = #state { backing_queue = BQ, + backing_queue_state = BQS }) -> + {ok, set_synchronised( + 0, true, State #state { depth_delta = Depth - BQ:depth(BQS) })}; process_instruction({delete_and_terminate, Reason}, State = #state { backing_queue = BQ, @@ -923,26 +914,23 @@ maybe_store_ack(true, MsgId, AckTag, State = #state { msg_id_ack = MA, State #state { msg_id_ack = dict:store(MsgId, {Num, AckTag}, MA), ack_num = Num + 1 }. -set_synchronised(Delta, MasterDelta, State) -> - set_synchronised(Delta, MasterDelta, false, State). - -set_synchronised(Delta, _MasterDelta, _AddAnyway, - State = #state { depth = Depth, - master_depth = undefined }) -> - State #state { depth = Depth + Delta }; -set_synchronised(Delta, MasterDelta, AddAnyway, - State = #state { depth = Depth, - master_depth = MasterDepth, - q = #amqqueue { name = QName }}) -> - Depth1 = Depth + Delta, - MasterDepth1 = MasterDepth + MasterDelta, +set_synchronised(Delta, State) -> + set_synchronised(Delta, false, State). + +set_synchronised(_Delta, _AddAnyway, + State = #state { depth_delta = undefined }) -> + State; +set_synchronised(Delta, AddAnyway, + State = #state { depth_delta = DepthDelta, + q = #amqqueue { name = QName }}) -> + DepthDelta1 = DepthDelta + Delta, %% We intentionally leave out the head where a slave becomes %% unsynchronised: we assert that can never happen. %% The `AddAnyway' param is there since in the `depth' instruction we %% receive the master depth for the first time, and we want to set the sync %% state anyway if we are synced. - case {Depth =:= MasterDepth, Depth1 =:= MasterDepth1} of - {WasSync, true} when not WasSync orelse AddAnyway -> + case DepthDelta1 =:= 0 of + true when not (DepthDelta =:= 0) orelse AddAnyway -> Self = self(), rabbit_misc:execute_mnesia_transaction( fun () -> @@ -957,7 +945,7 @@ set_synchronised(Delta, MasterDelta, AddAnyway, Q1#amqqueue{sync_slave_pids = [Self | SSPids1]}) end end); - {Same, Same} -> + _ when DepthDelta1 > 0-> ok end, - State #state { depth = Depth1, master_depth = MasterDepth1 }. + State #state { depth_delta = DepthDelta1 }. diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 22829765..98c45717 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -19,7 +19,7 @@ -export([init/3, terminate/2, delete_and_terminate/2, purge/1, publish/4, publish_delivered/5, drain_confirmed/1, dropwhile/3, fetch/2, ack/2, requeue/2, len/1, is_empty/1, - pending_ack/1, set_ram_duration_target/2, ram_duration/1, + depth/1, set_ram_duration_target/2, ram_duration/1, needs_timeout/1, timeout/1, handle_pre_hibernate/1, status/1, invoke/3, is_duplicate/2, discard/3, multiple_routing_keys/0, fold/3]). @@ -681,8 +681,8 @@ len(#vqstate { len = Len }) -> Len. is_empty(State) -> 0 == len(State). -pending_ack(#vqstate { pending_ack = Ack }) -> - gb_trees:size(Ack). +depth(State = #vqstate { pending_ack = Ack }) -> + len(State) + gb_trees:size(Ack). set_ram_duration_target( DurationTarget, State = #vqstate { -- cgit v1.2.1 From 03d460f3d2384a36ee086683bfee7e797479b239 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 4 Sep 2012 15:43:59 +0100 Subject: Another attempt at explaining disc vs RAM nodes, and capitalise RAM. --- docs/rabbitmqctl.1.xml | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/docs/rabbitmqctl.1.xml b/docs/rabbitmqctl.1.xml index 4a038da0..834e7b81 100644 --- a/docs/rabbitmqctl.1.xml +++ b/docs/rabbitmqctl.1.xml @@ -300,32 +300,33 @@ --ram - If provided, the node will join the cluster as a ram node. + If provided, the node will join the cluster as a RAM node. - Instruct the node to become member of the cluster that the + Instruct the node to become a member of the cluster that the specified node is in. Before clustering, the node is reset, so be careful when using this command. For this command to succeed the RabbitMQ application must have been stopped, e.g. with stop_app. - Cluster nodes can be of two types: disk or ram. Disk nodes - replicate data in ram and on disk, thus providing redundancy in - the event of node failure and recovery from global events such as - power failure across all nodes. Ram nodes replicate data in ram - only and are mainly used for scalability. A cluster must always - have at least one disk node. Note that the queue data will always - be on disc, including on ram nodes. This makes ram nodes more + Cluster nodes can be of two types: disk or RAM. Disk nodes + replicate data in RAM and on disk, thus providing redundancy in + the event of node failure and recovery from global events such + as power failure across all nodes. RAM nodes replicate data in + RAM only (with the exception of queue contents, which can reside + on disc if the queue is persistent or too big to fit in memory) + and are mainly used for scalability. RAM nodes are more performant only when managing resources (e.g. adding/removing - queues, exchanges, or bindings). + queues, exchanges, or bindings). A cluster must always have at + least one disk node, and usually should have more than one. The node will be a disk node by default. If you wish to wish to - create a ram node, provide the --ram flag. + create a RAM node, provide the --ram flag. After executing the cluster command, whenever @@ -379,13 +380,13 @@ Changes the type of the cluster node. The node must be stopped for - this operation to succeed, and when turning a node into a ram node + this operation to succeed, and when turning a node into a RAM node the node must not be the only disk node in the cluster. For example: rabbitmqctl change_cluster_node_type disk - This command will turn a ram node into a disk node. + This command will turn a RAM node into a disk node. -- cgit v1.2.1 From b2939a35eaa917738a798762379da0d29154c04e Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Tue, 4 Sep 2012 15:50:30 +0100 Subject: remove differences with default --- src/rabbit_mirror_queue_slave.erl | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index ee65a0a7..190a0139 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -767,16 +767,18 @@ process_instruction( SQ1 = dict:store(ChPid, {MQ1, PendingCh1}, SQ), State2 = State1 #state { sender_queues = SQ1, msg_id_status = MS1 }, - {ok, case Deliver of - false -> - BQS1 = BQ:publish(Msg, MsgProps, ChPid, BQS), - State2 #state { backing_queue_state = BQS1 }; - {true, AckRequired} -> - {AckTag, BQS1} = BQ:publish_delivered( - AckRequired, Msg, MsgProps, ChPid, BQS), - maybe_store_ack(AckRequired, MsgId, AckTag, - State2 #state {backing_queue_state = BQS1}) - end}; + + {ok, + case Deliver of + false -> + BQS1 = BQ:publish(Msg, MsgProps, ChPid, BQS), + State2 #state { backing_queue_state = BQS1 }; + {true, AckRequired} -> + {AckTag, BQS1} = BQ:publish_delivered(AckRequired, Msg, MsgProps, + ChPid, BQS), + maybe_store_ack(AckRequired, MsgId, AckTag, + State2 #state {backing_queue_state = BQS1}) + end}; process_instruction({discard, ChPid, Msg = #basic_message { id = MsgId }}, State = #state { sender_queues = SQ, backing_queue = BQ, -- cgit v1.2.1 From 13a5c4c3e17deeac959e5d35b91e2b08a98f6a6b Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Tue, 4 Sep 2012 15:51:50 +0100 Subject: cosmetics --- src/rabbit_mirror_queue_slave.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 3ce3749e..2e9035c8 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -768,7 +768,7 @@ process_instruction( {AckTag, BQS1} = BQ:publish_delivered(AckRequired, Msg, MsgProps, ChPid, BQS), maybe_store_ack(AckRequired, MsgId, AckTag, - State2 #state {backing_queue_state = BQS1}) + State2 #state { backing_queue_state = BQS1 }) end}; process_instruction({discard, ChPid, Msg = #basic_message { id = MsgId }}, State = #state { sender_queues = SQ, -- cgit v1.2.1 From 5c8326a4892c43f1e7e7db12284288c5479509f9 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Tue, 4 Sep 2012 15:57:38 +0100 Subject: cosmetics --- src/rabbit_mirror_queue_master.erl | 3 +-- src/rabbit_mirror_queue_slave.erl | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index ad66d059..fb9f7e34 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -406,8 +406,7 @@ length_fun() -> fun (?MODULE, State = #state { gm = GM, backing_queue = BQ, backing_queue_state = BQS }) -> - ok = gm:broadcast( - GM, {depth, BQ:depth(BQS)}), + ok = gm:broadcast(GM, {depth, BQ:depth(BQS)}), State end) end. diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 2e9035c8..28acc022 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -875,13 +875,11 @@ process_instruction({sender_death, ChPid}, msg_id_status = MS1, known_senders = pmon:demonitor(ChPid, KS) } end}; - process_instruction({depth, Depth}, State = #state { backing_queue = BQ, backing_queue_state = BQS }) -> {ok, set_synchronised( 0, true, State #state { depth_delta = Depth - BQ:depth(BQS) })}; - process_instruction({delete_and_terminate, Reason}, State = #state { backing_queue = BQ, backing_queue_state = BQS }) -> -- cgit v1.2.1 From c3d7b796d946d357edb6717f5518e5ccc459c275 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Tue, 4 Sep 2012 17:04:27 +0100 Subject: cosmetics --- src/rabbit_mirror_queue_slave.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 28acc022..6ddfc3a3 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -936,7 +936,7 @@ set_synchronised(Delta, AddAnyway, Q1#amqqueue{sync_slave_pids = [Self | SSPids1]}) end end); - _ when DepthDelta1 > 0-> + _ when DepthDelta1 > 0 -> ok end, State #state { depth_delta = DepthDelta1 }. -- cgit v1.2.1 From 0eb7feccc5d625cf4827c9db341968a3cb28b01b Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 4 Sep 2012 17:53:43 +0100 Subject: tweak test - drop eunit - we don't use it elsewhere and it adds little value here - lower the TEST_RUNS * CHILDREN count - no need to run 'kill' part of test with lots of iterations/children - stop on first failed run - on failure, return iteration and exit reason --- src/supervisor2_tests.erl | 71 +++++++++++++++++++++-------------------------- 1 file changed, 31 insertions(+), 40 deletions(-) diff --git a/src/supervisor2_tests.erl b/src/supervisor2_tests.erl index 5c5e6d85..e42ded7b 100644 --- a/src/supervisor2_tests.erl +++ b/src/supervisor2_tests.erl @@ -20,45 +20,37 @@ -export([test_all/0, start_link/0]). -export([init/1]). --include_lib("eunit/include/eunit.hrl"). - --define(TEST_RUNS, 2000). --define(SLOW_TEST_RUNS, 45). --define(CHILDREN, 100). - test_all() -> - eunit:test(?MODULE, [verbose]). - -simple_child_shutdown_without_deadlock_test_() -> - [{timeout, ?TEST_RUNS * 10, - check_shutdown_handling(stop, ?TEST_RUNS, ?CHILDREN)}]. + ok = check_shutdown(stop, 200, 200, 2000), + ok = check_shutdown(ignored, 1, 2, 2000). -simple_child_shutdown_with_timeout_test_() -> - [{timeout, ?SLOW_TEST_RUNS * 10, - check_shutdown_handling(ignored, ?SLOW_TEST_RUNS, ?CHILDREN)}]. - -check_shutdown_handling(SigStop, Iterations, ChildCount) -> - fun() -> - {ok, Sup} = supervisor2:start_link(?MODULE, [?CHILDREN]), - [begin - TestSupPid = erlang:whereis(?MODULE), - ChildPids = [begin - {ok, ChildPid} = - supervisor2:start_child(TestSupPid, []), - ChildPid - end || _ <- lists:seq(1, ChildCount)], - erlang:monitor(process, TestSupPid), - [P ! SigStop || P <- ChildPids], - ?assertEqual(ok, supervisor2:terminate_child(Sup, test_sup)), - {ok, _} = supervisor2:restart_child(Sup, test_sup), - receive - {'DOWN', _MRef, process, TestSupPid, Reason} -> - ?assertEqual(shutdown, Reason) - end - end || _ <- lists:seq(1, Iterations)], - unlink(Sup), - exit(Sup, shutdown) - end. +check_shutdown(SigStop, Iterations, ChildCount, SupTimeout) -> + {ok, Sup} = supervisor2:start_link(?MODULE, [SupTimeout]), + Res = lists:foldl( + fun (I, ok) -> + TestSupPid = erlang:whereis(?MODULE), + ChildPids = + [begin + {ok, ChildPid} = + supervisor2:start_child(TestSupPid, []), + ChildPid + end || _ <- lists:seq(1, ChildCount)], + MRef = erlang:monitor(process, TestSupPid), + [P ! SigStop || P <- ChildPids], + ok = supervisor2:terminate_child(Sup, test_sup), + {ok, _} = supervisor2:restart_child(Sup, test_sup), + receive + {'DOWN', MRef, process, TestSupPid, shutdown} -> + ok; + {'DOWN', MRef, process, TestSupPid, Reason} -> + {error, {I, Reason}} + end; + (_, R) -> + R + end, ok, lists:seq(1, Iterations)), + unlink(Sup), + exit(Sup, shutdown), + Res. start_link() -> Pid = spawn_link(fun () -> @@ -67,13 +59,12 @@ start_link() -> end), {ok, Pid}. -init([N]) -> +init([Timeout]) -> {ok, {{one_for_one, 0, 1}, [{test_sup, {supervisor2, start_link, [{local, ?MODULE}, ?MODULE, []]}, - transient, N * 100, supervisor, [?MODULE]}]}}; + transient, Timeout, supervisor, [?MODULE]}]}}; init([]) -> {ok, {{simple_one_for_one_terminate, 0, 1}, [{test_worker, {?MODULE, start_link, []}, temporary, 1000, worker, [?MODULE]}]}}. - -- cgit v1.2.1 From 491b5eccd1ee6d9e851e79d7d8991af2a21e4713 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 4 Sep 2012 17:55:11 +0100 Subject: Reify a touch more. --- src/rabbit_mirror_queue_slave.erl | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 3fc33f72..792adc95 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -154,11 +154,8 @@ init_it(Self, Node, QueueName) -> mnesia:read({rabbit_queue, QueueName}), case [Pid || Pid <- [QPid | MPids], node(Pid) =:= Node] of [] -> - %% Add to the end, so they are in descending order of age, see - %% rabbit_mirror_queue_misc:promote_slave/1 - MPids1 = MPids ++ [Self], rabbit_mirror_queue_misc:store_updated_slaves( - Q1#amqqueue{slave_pids = MPids1}), + Q1#amqqueue{slave_pids = add_slave(Self, MPids)}), {new, QPid}; [QPid] -> case rabbit_misc:is_process_alive(QPid) of @@ -168,13 +165,17 @@ init_it(Self, Node, QueueName) -> [SPid] -> case rabbit_misc:is_process_alive(SPid) of true -> existing; - false -> MPids1 = (MPids -- [SPid]) ++ [Self], + false -> MPids1 = add_slave(Self, MPids -- [SPid]), rabbit_mirror_queue_misc:store_updated_slaves( Q1#amqqueue{slave_pids = MPids1}), {new, QPid} end end. +%% Add to the end, so they are in descending order of age, see +%% rabbit_mirror_queue_misc:promote_slave/1 +add_slave(New, MPids) -> MPids ++ [New]. + handle_call({deliver, Delivery = #delivery { immediate = true }}, From, State) -> %% It is safe to reply 'false' here even if a) we've not seen the -- cgit v1.2.1 From fe3106415e65713a034ff72bbf9d2842d7e7e889 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 4 Sep 2012 18:13:46 +0100 Subject: more reification, plus cosmetics --- src/rabbit_mirror_queue_slave.erl | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 792adc95..61bd54b9 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -153,28 +153,23 @@ init_it(Self, Node, QueueName) -> [Q1 = #amqqueue { pid = QPid, slave_pids = MPids }] = mnesia:read({rabbit_queue, QueueName}), case [Pid || Pid <- [QPid | MPids], node(Pid) =:= Node] of - [] -> - rabbit_mirror_queue_misc:store_updated_slaves( - Q1#amqqueue{slave_pids = add_slave(Self, MPids)}), - {new, QPid}; - [QPid] -> - case rabbit_misc:is_process_alive(QPid) of - true -> duplicate_live_master; - false -> {stale, QPid} - end; - [SPid] -> - case rabbit_misc:is_process_alive(SPid) of - true -> existing; - false -> MPids1 = add_slave(Self, MPids -- [SPid]), - rabbit_mirror_queue_misc:store_updated_slaves( - Q1#amqqueue{slave_pids = MPids1}), - {new, QPid} - end + [] -> add_slave(Q1, Self, MPids), + {new, QPid}; + [QPid] -> case rabbit_misc:is_process_alive(QPid) of + true -> duplicate_live_master; + false -> {stale, QPid} + end; + [SPid] -> case rabbit_misc:is_process_alive(SPid) of + true -> existing; + false -> add_slave(Q1, Self, MPids -- [SPid]), + {new, QPid} + end end. %% Add to the end, so they are in descending order of age, see %% rabbit_mirror_queue_misc:promote_slave/1 -add_slave(New, MPids) -> MPids ++ [New]. +add_slave(Q, New, MPids) -> rabbit_mirror_queue_misc:store_updated_slaves( + Q#amqqueue{slave_pids = MPids ++ [New]}). handle_call({deliver, Delivery = #delivery { immediate = true }}, From, State) -> -- cgit v1.2.1 From 4d81ce03d196bf97ce3fda8969095e00e895d6ee Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 5 Sep 2012 10:08:51 +0100 Subject: Reduce difference to default --- src/rabbit_mirror_queue_slave.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 30f13f10..5ed5f063 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -927,9 +927,9 @@ set_synchronised(true, State = #state { q = #amqqueue { name = QName }, case mnesia:read({rabbit_queue, QName}) of [] -> ok; - [Q = #amqqueue{sync_slave_pids = SSPids}] -> - Q1 = Q#amqqueue{sync_slave_pids = [Self | SSPids]}, - rabbit_mirror_queue_misc:store_updated_slaves(Q1) + [Q1 = #amqqueue{sync_slave_pids = SSPids}] -> + Q2 = Q1#amqqueue{sync_slave_pids = [Self | SSPids]}, + rabbit_mirror_queue_misc:store_updated_slaves(Q2) end end), State #state { synchronised = true }; -- cgit v1.2.1 From 6578389b1df1937fb08bb7a1621b50f09cb7416e Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 5 Sep 2012 10:27:28 +0100 Subject: Add specs, remove misleading "%% temp" comment. --- src/rabbit_amqqueue.erl | 4 ++-- src/rabbit_mirror_queue_master.erl | 4 +++- src/rabbit_mirror_queue_misc.erl | 16 ++++++++++------ 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 96cf226e..65d3001a 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -29,8 +29,6 @@ -export([notify_down_all/2, limit_all/3]). -export([on_node_down/1]). -export([update/2, store_queue/1, policy_changed/2]). - -%% temp -export([start_mirroring/1, stop_mirroring/1]). %% internal @@ -165,6 +163,8 @@ -spec(store_queue/1 :: (rabbit_types:amqqueue()) -> 'ok'). -spec(policy_changed/2 :: (rabbit_types:amqqueue(), rabbit_types:amqqueue()) -> 'ok'). +-spec(start_mirroring/1 :: (pid()) -> 'ok'). +-spec(stop_mirroring/1 :: (pid()) -> 'ok'). -endif. diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index 9f2305ba..e35b0808 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -27,7 +27,6 @@ -export([promote_backing_queue_state/6, sender_death_fun/0, length_fun/0]). -%% temp -export([init_with_existing_bq/3, stop_mirroring/1]). -behaviour(rabbit_backing_queue). @@ -66,6 +65,9 @@ (pid(), atom(), any(), pid(), dict(), [pid()]) -> master_state()). -spec(sender_death_fun/0 :: () -> death_fun()). -spec(length_fun/0 :: () -> length_fun()). +-spec(init_with_existing_bq/3 :: (rabbit_types:amqqueue(), atom(), any()) -> + master_state()). +-spec(stop_mirroring/1 :: (master_state()) -> {atom(), any()}). -endif. diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 7caa96b5..5ad04ff7 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -18,14 +18,12 @@ -export([remove_from_queue/2, on_node_up/0, drop_mirror/2, drop_mirror/3, add_mirror/2, add_mirror/3, - report_deaths/4, store_updated_slaves/1]). + report_deaths/4, store_updated_slaves/1, suggested_queue_nodes/1, + is_mirrored/1, update_mirrors/2]). -%% temp --export([suggested_queue_nodes/1, is_mirrored/1, update_mirrors/2]). -%% for testing +%% for testing only -export([suggested_queue_nodes/4]). - -include("rabbit.hrl"). %%---------------------------------------------------------------------------- @@ -45,6 +43,11 @@ -> rabbit_types:ok_or_error(any())). -spec(store_updated_slaves/1 :: (rabbit_types:amqqueue()) -> rabbit_types:amqqueue()). +-spec(suggested_queue_nodes/1 :: (rabbit_types:amqqueue()) -> + {node(), [node()]}). +-spec(is_mirrored/1 :: (rabbit_types:amqqueue()) -> boolean()). +-spec(update_mirrors/2 :: + (rabbit_types:amqqueue(), rabbit_types:amqqueue()) -> 'ok'). -endif. @@ -286,5 +289,6 @@ update_mirrors(OldQ = #amqqueue{name = QName, pid = QPid}, Add = NewNodes -- OldNodes, Remove = OldNodes -- NewNodes, [ok = drop_mirror(QName, Node) || Node <- Remove], - [ok = add_mirror(QName, Node) || Node <- Add] + [ok = add_mirror(QName, Node) || Node <- Add], + ok end. -- cgit v1.2.1 From 7d58dbfd96c6a5b87e7a5a3520106dacbf365254 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 5 Sep 2012 10:39:46 +0100 Subject: No we don't. The point is that if a mirror is dropping out of the queue, does it need to explicitly leave the GM group? The answer is no: the master is linked to the coordinator is linked to the GM / the slave is linked to the GM. --- src/rabbit_mirror_queue_master.erl | 1 - src/rabbit_mirror_queue_slave.erl | 1 - 2 files changed, 2 deletions(-) diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index e35b0808..473d9671 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -126,7 +126,6 @@ terminate({shutdown, dropped} = Reason, %% in without this node being restarted. Thus we must do the full %% blown delete_and_terminate now, but only locally: we do not %% broadcast delete_and_terminate. - ok = gm:leave(GM), %% TODO presumably we need this? State #state { backing_queue_state = BQ:delete_and_terminate(Reason, BQS), set_delivered = 0 }; terminate(Reason, diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 5ed5f063..f245f913 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -297,7 +297,6 @@ terminate({shutdown, dropped} = R, #state { gm = GM, backing_queue = BQ, backing_queue_state = BQS }) -> %% See rabbit_mirror_queue_master:terminate/2 - ok = gm:leave(GM), %% TODO presumably we need this? BQ:delete_and_terminate(R, BQS); terminate(Reason, #state { q = Q, gm = GM, -- cgit v1.2.1 From 1b0b6a904b2071582e4745c6cd621cda8427763f Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 5 Sep 2012 10:41:13 +0100 Subject: Gah --- src/rabbit_mirror_queue_master.erl | 3 +-- src/rabbit_mirror_queue_slave.erl | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index 473d9671..8b71060c 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -117,8 +117,7 @@ stop_mirroring(State = #state { coordinator = CPid, {BQ, BQS}. terminate({shutdown, dropped} = Reason, - State = #state { gm = GM, - backing_queue = BQ, + State = #state { backing_queue = BQ, backing_queue_state = BQS }) -> %% Backing queue termination - this node has been explicitly %% dropped. Normally, non-durable queues would be tidied up on diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index f245f913..9a4d5cbe 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -293,8 +293,7 @@ terminate(_Reason, #state { backing_queue_state = undefined }) -> %% We've received a delete_and_terminate from gm, thus nothing to %% do here. ok; -terminate({shutdown, dropped} = R, #state { gm = GM, - backing_queue = BQ, +terminate({shutdown, dropped} = R, #state { backing_queue = BQ, backing_queue_state = BQS }) -> %% See rabbit_mirror_queue_master:terminate/2 BQ:delete_and_terminate(R, BQS); -- cgit v1.2.1 From f3ccf9f0d0fa477af40c1eef30358d7d760c0c0d Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Wed, 5 Sep 2012 11:36:31 +0100 Subject: don't record pid when using -detached --- docs/rabbitmq-server.1.xml | 5 +++-- scripts/rabbitmq-server | 15 ++++++++++++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/docs/rabbitmq-server.1.xml b/docs/rabbitmq-server.1.xml index 620b1dd3..6003271a 100644 --- a/docs/rabbitmq-server.1.xml +++ b/docs/rabbitmq-server.1.xml @@ -110,8 +110,9 @@ Defaults to 5672. Start the server process in the background. Note that this will - cause the wrong pid to be recorded in the pid file, since the pid - of the shell that launches the detached process is captured. + cause the pid not to be written to the pid file, since the shell + will start a detached process and thus the wrong one would be + recorded. For example: rabbitmq-server -detached diff --git a/scripts/rabbitmq-server b/scripts/rabbitmq-server index 34915b3d..1ab3c722 100755 --- a/scripts/rabbitmq-server +++ b/scripts/rabbitmq-server @@ -65,9 +65,18 @@ case "$(uname -s)" in CYGWIN*) # we make no attempt to record the cygwin pid; rabbitmqctl wait # will not be able to make sense of it anyway ;; - *) mkdir -p $(dirname ${RABBITMQ_PID_FILE}); - echo $$ > ${RABBITMQ_PID_FILE} - ;; + *) # When -detached is passed, we don't write the pid, since it'd be the + # wrong one + detached="" + for opt in "$@"; do + if [ "$opt" = "-detached" ]; then + detached="true" + fi + done + if [ ! $detached ]; then + mkdir -p $(dirname ${RABBITMQ_PID_FILE}); + echo $$ > ${RABBITMQ_PID_FILE} + fi esac RABBITMQ_EBIN_ROOT="${RABBITMQ_HOME}/ebin" -- cgit v1.2.1 From da233a3ff5b6e25d46b2738e9399fac282e8d690 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Wed, 5 Sep 2012 12:50:03 +0100 Subject: typo in rabbitmqctl man page --- docs/rabbitmqctl.1.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/rabbitmqctl.1.xml b/docs/rabbitmqctl.1.xml index 834e7b81..b476f486 100644 --- a/docs/rabbitmqctl.1.xml +++ b/docs/rabbitmqctl.1.xml @@ -459,7 +459,7 @@ The need for this command is motivated by the fact that clusters can change while a node is offline. Consider the situation in - which node A and B are clustered. A goes down, C clusters with C, + which node A and B are clustered. A goes down, C clusters with B, and then B leaves the cluster. When A wakes up, it'll try to contact B, but this will fail since B is not in the cluster anymore. update_cluster_nodes -n A C will solve -- cgit v1.2.1 From 798fd411d873ad2b5f0fbee58ad2011717fa8529 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 5 Sep 2012 13:25:03 +0100 Subject: Call add_mirror/2 before drop_mirror/2, and explain why. --- src/rabbit_mirror_queue_misc.erl | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 5ad04ff7..9fb18457 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -288,7 +288,17 @@ update_mirrors(OldQ = #amqqueue{name = QName, pid = QPid}, NewNodes = All(suggested_queue_nodes(NewQ)), Add = NewNodes -- OldNodes, Remove = OldNodes -- NewNodes, - [ok = drop_mirror(QName, Node) || Node <- Remove], + %% When a mirror dies, remove_from_queue/2 + %% might have to add new slaves (in + %% "exactly" mode). It will check mnesia to + %% see which slaves there currently are. If + %% drop_mirror/2 is invoked first then when + %% we end up in remove_from_queue/2 it will + %% not see the slaves that add_mirror/2 will + %% add, and also want to add them (even + %% though we are not responding to the death + %% of a mirror). Breakage ensues. [ok = add_mirror(QName, Node) || Node <- Add], + [ok = drop_mirror(QName, Node) || Node <- Remove], ok end. -- cgit v1.2.1 From 1222f40fffaacc43a1741757f084952e260e6d0c Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 5 Sep 2012 13:28:04 +0100 Subject: Cosmetic: give that comment room to breath. --- src/rabbit_mirror_queue_misc.erl | 41 ++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 9fb18457..5217e276 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -277,28 +277,29 @@ is_mirrored(Q) -> _ -> false end. -update_mirrors(OldQ = #amqqueue{name = QName, pid = QPid}, - NewQ = #amqqueue{name = QName, pid = QPid}) -> +update_mirrors(OldQ = #amqqueue{pid = QPid}, + NewQ = #amqqueue{pid = QPid}) -> case {is_mirrored(OldQ), is_mirrored(NewQ)} of {false, false} -> ok; {true, false} -> rabbit_amqqueue:stop_mirroring(QPid); {false, true} -> rabbit_amqqueue:start_mirroring(QPid); - {true, true} -> All = fun ({A,B}) -> [A|B] end, - OldNodes = All(actual_queue_nodes(OldQ)), - NewNodes = All(suggested_queue_nodes(NewQ)), - Add = NewNodes -- OldNodes, - Remove = OldNodes -- NewNodes, - %% When a mirror dies, remove_from_queue/2 - %% might have to add new slaves (in - %% "exactly" mode). It will check mnesia to - %% see which slaves there currently are. If - %% drop_mirror/2 is invoked first then when - %% we end up in remove_from_queue/2 it will - %% not see the slaves that add_mirror/2 will - %% add, and also want to add them (even - %% though we are not responding to the death - %% of a mirror). Breakage ensues. - [ok = add_mirror(QName, Node) || Node <- Add], - [ok = drop_mirror(QName, Node) || Node <- Remove], - ok + {true, true} -> update_mirrors0(OldQ, NewQ) end. + +update_mirrors0(OldQ = #amqqueue{name = QName}, + NewQ = #amqqueue{name = QName}) -> + All = fun ({A,B}) -> [A|B] end, + OldNodes = All(actual_queue_nodes(OldQ)), + NewNodes = All(suggested_queue_nodes(NewQ)), + Add = NewNodes -- OldNodes, + Remove = OldNodes -- NewNodes, + %% When a mirror dies, remove_from_queue/2 might have to add new + %% slaves (in "exactly" mode). It will check mnesia to see which + %% slaves there currently are. If drop_mirror/2 is invoked first + %% then when we end up in remove_from_queue/2 it will not see the + %% slaves that add_mirror/2 will add, and also want to add them + %% (even though we are not responding to the death of a + %% mirror). Breakage ensues. + [ok = add_mirror(QName, Node) || Node <- Add], + [ok = drop_mirror(QName, Node) || Node <- Remove], + ok. -- cgit v1.2.1 From dc63b6550d0fca9ec60c1fb66f5530923f66fc6d Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 5 Sep 2012 14:54:17 +0100 Subject: Also exclude DeadNodes from the list of new nodes to start mirrors on; we have checked for running_clustered_nodes() but that could be out of date. --- src/rabbit_mirror_queue_misc.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 5217e276..ad9dfa0d 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -101,7 +101,7 @@ remove_from_queue0(QueueName, DeadGMPids) -> {_, OldNodes} = actual_queue_nodes(Q1), {_, NewNodes} = suggested_queue_nodes(Q1), {ok, QPid1, [QPid | SPids] -- Alive, - NewNodes -- OldNodes}; + (NewNodes -- OldNodes) -- DeadNodes}; _ -> %% Master has changed, and we're not it, %% so leave alone to allow the promoted -- cgit v1.2.1 From df6fc31afc8c28655a9a079f9998f7ef022c4406 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 5 Sep 2012 15:18:15 +0100 Subject: cosmetic --- src/rabbit_mirror_queue_misc.erl | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index ad9dfa0d..ebaae995 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -291,8 +291,6 @@ update_mirrors0(OldQ = #amqqueue{name = QName}, All = fun ({A,B}) -> [A|B] end, OldNodes = All(actual_queue_nodes(OldQ)), NewNodes = All(suggested_queue_nodes(NewQ)), - Add = NewNodes -- OldNodes, - Remove = OldNodes -- NewNodes, %% When a mirror dies, remove_from_queue/2 might have to add new %% slaves (in "exactly" mode). It will check mnesia to see which %% slaves there currently are. If drop_mirror/2 is invoked first @@ -300,6 +298,6 @@ update_mirrors0(OldQ = #amqqueue{name = QName}, %% slaves that add_mirror/2 will add, and also want to add them %% (even though we are not responding to the death of a %% mirror). Breakage ensues. - [ok = add_mirror(QName, Node) || Node <- Add], - [ok = drop_mirror(QName, Node) || Node <- Remove], + [ok = add_mirror(QName, Node) || Node <- NewNodes -- OldNodes], + [ok = drop_mirror(QName, Node) || Node <- OldNodes -- NewNodes], ok. -- cgit v1.2.1 From 80a7e6f6b6e76c801f89a94197724839c2727d8c Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Thu, 6 Sep 2012 13:53:02 +0100 Subject: emit warning when starting detached rabbit --- scripts/rabbitmq-server | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/rabbitmq-server b/scripts/rabbitmq-server index 1ab3c722..ba48de51 100755 --- a/scripts/rabbitmq-server +++ b/scripts/rabbitmq-server @@ -73,7 +73,9 @@ case "$(uname -s)" in detached="true" fi done - if [ ! $detached ]; then + if [ $detached ]; then + echo "Starting detached process, the pid file won't be written" 1>&2 + else mkdir -p $(dirname ${RABBITMQ_PID_FILE}); echo $$ > ${RABBITMQ_PID_FILE} fi -- cgit v1.2.1 From 9f782d9b0379c4c937f430cdab0f0b6c5b0d76b4 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 6 Sep 2012 14:42:10 +0100 Subject: Not sure it's worth explaining that, the explanation is not clear and I can't think how to improve it. --- docs/rabbitmq-server.1.xml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/rabbitmq-server.1.xml b/docs/rabbitmq-server.1.xml index 6003271a..32ae842c 100644 --- a/docs/rabbitmq-server.1.xml +++ b/docs/rabbitmq-server.1.xml @@ -110,9 +110,7 @@ Defaults to 5672. Start the server process in the background. Note that this will - cause the pid not to be written to the pid file, since the shell - will start a detached process and thus the wrong one would be - recorded. + cause the pid not to be written to the pid file. For example: rabbitmq-server -detached -- cgit v1.2.1 From 6e1eb0674a55aa288a4831deff815761ae72007d Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 6 Sep 2012 14:44:53 +0100 Subject: Terseness. --- scripts/rabbitmq-server | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/rabbitmq-server b/scripts/rabbitmq-server index ba48de51..e1686627 100755 --- a/scripts/rabbitmq-server +++ b/scripts/rabbitmq-server @@ -74,7 +74,7 @@ case "$(uname -s)" in fi done if [ $detached ]; then - echo "Starting detached process, the pid file won't be written" 1>&2 + echo "Warning: PID file not written; -detached was passed." 1>&2 else mkdir -p $(dirname ${RABBITMQ_PID_FILE}); echo $$ > ${RABBITMQ_PID_FILE} -- cgit v1.2.1 From ac94cb000726c30296fbe36ffd17d01861f9fac4 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Thu, 6 Sep 2012 16:45:36 +0100 Subject: two little fixes... It turns out that the problems I had with the kill-multi test on default was because the messages were start publishing right after the queue was created. I thought that once queue.declare returned, it meant that the queue was present on all nodes, but it wasn't, and for this reason we had the mismatching messages and the purging. Once I discovered that, I went back two my code and immediately discovered two very silly mistakes. I feel silly as well now. I think that bug 25130 still has a reason to extist, but I wouldn't worry about it now. --- src/rabbit_mirror_queue_slave.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 6ddfc3a3..3e45f026 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -826,7 +826,7 @@ process_instruction({fetch, AckRequired, MsgId, Remaining}, backing_queue_state = BQS }) -> QLen = BQ:len(BQS), {State1, Delta} = - case {QLen - 1} of + case QLen - 1 of Remaining -> {{#basic_message{id = MsgId}, _IsDelivered, AckTag, Remaining}, BQS1} = BQ:fetch(AckRequired, BQS), @@ -936,7 +936,7 @@ set_synchronised(Delta, AddAnyway, Q1#amqqueue{sync_slave_pids = [Self | SSPids1]}) end end); - _ when DepthDelta1 > 0 -> + _ when DepthDelta1 >= 0 -> ok end, State #state { depth_delta = DepthDelta1 }. -- cgit v1.2.1 From d3cf2296e2247c15226189b00646a6b8784b7c3b Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Thu, 6 Sep 2012 16:50:35 +0100 Subject: Backout changeset 3ac70be8c5a2 --- src/rabbit_parameter_validation.erl | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/rabbit_parameter_validation.erl b/src/rabbit_parameter_validation.erl index 04ddcc13..0247643d 100644 --- a/src/rabbit_parameter_validation.erl +++ b/src/rabbit_parameter_validation.erl @@ -36,8 +36,6 @@ list(_Name, Term) when is_list(Term) -> list(Name, Term) -> {error, "~s should be list, actually was ~p", [Name, Term]}. -regex(Name, Empty) when Empty == <<>> orelse Empty == [] -> - {error, "~s should be a regular expression but was empty.", [Name]}; regex(Name, Term) -> case re:compile(Term) of {ok, _} -> ok; -- cgit v1.2.1 From f6608db0967558512db0259884448f39d43fd6e1 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Thu, 6 Sep 2012 17:51:47 +0100 Subject: Permit absent policy priority --- src/rabbit_parameter_validation.erl | 2 ++ src/rabbit_policy.erl | 15 ++++++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/rabbit_parameter_validation.erl b/src/rabbit_parameter_validation.erl index 0247643d..d421a33d 100644 --- a/src/rabbit_parameter_validation.erl +++ b/src/rabbit_parameter_validation.erl @@ -55,6 +55,8 @@ proplist(Name, Constraints, Term) when is_list(Term) -> {[{error, "Key \"~s\" not found in ~s", [Key, Name]} | Results0], Term0}; {false, optional} -> + {Results0, Term0}; + {false, {optional, _Default}} -> {Results0, Term0} end end, {[], Term}, Constraints), diff --git a/src/rabbit_policy.erl b/src/rabbit_policy.erl index 3400a7d5..6c36df87 100644 --- a/src/rabbit_policy.erl +++ b/src/rabbit_policy.erl @@ -82,7 +82,7 @@ notify_clear(VHost, <<"policy">>, _Name) -> list(VHost) -> lists:sort(fun sort_pred/2, - [[{<<"name">>, pget(key, P)} | pget(value, P)] + [[{<<"name">>, pget(key, P)} | defaults(pget(value, P))] || P <- rabbit_runtime_parameters:list(VHost, <<"policy">>)]). update_policies(VHost) -> @@ -157,7 +157,16 @@ sort_pred(A, B) -> %%---------------------------------------------------------------------------- +defaults(Props) -> + Def = [{Key, Def} || {Key, _Fun, {optional, Def}} <- policy_validation()], + lists:foldl(fun ({Key, Default}, Props1) -> + case pget(Key, Props1) of + undefined -> [{Key, Default} | Props1]; + _ -> Props1 + end + end, Props, Def). + policy_validation() -> - [{<<"priority">>, fun rabbit_parameter_validation:number/2, mandatory}, + [{<<"priority">>, fun rabbit_parameter_validation:number/2, {optional, 0}}, {<<"pattern">>, fun rabbit_parameter_validation:regex/2, mandatory}, - {<<"policy">>, fun rabbit_parameter_validation:list/2, mandatory}]. + {<<"policy">>, fun rabbit_parameter_validation:list/2, mandatory}]. -- cgit v1.2.1 From 6b4a9e5df98a5127e4ef6e783bedc85fa249dbe0 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Thu, 6 Sep 2012 18:01:24 +0100 Subject: Constrain policy regex patterns to binaries --- src/rabbit_parameter_validation.erl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/rabbit_parameter_validation.erl b/src/rabbit_parameter_validation.erl index d421a33d..c40104d7 100644 --- a/src/rabbit_parameter_validation.erl +++ b/src/rabbit_parameter_validation.erl @@ -36,12 +36,14 @@ list(_Name, Term) when is_list(Term) -> list(Name, Term) -> {error, "~s should be list, actually was ~p", [Name, Term]}. -regex(Name, Term) -> +regex(Name, Term) when is_binary(Term) -> case re:compile(Term) of {ok, _} -> ok; {error, Reason} -> {error, "~s should be regular expression " "but is invalid: ~p", [Name, Reason]} - end. + end; +regex(Name, Term) -> + {error, "~s should be a binary but was ~p", [Name, Term]}. proplist(Name, Constraints, Term) when is_list(Term) -> {Results, Remainder} -- cgit v1.2.1 From b969ffe4618aa88a08946f771c4817116748e26e Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Fri, 7 Sep 2012 11:08:19 +0100 Subject: Backout changeset e847a6c6dc9b --- src/rabbit_policy.erl | 30 +++++++----------------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/src/rabbit_policy.erl b/src/rabbit_policy.erl index 6c36df87..c7f4d021 100644 --- a/src/rabbit_policy.erl +++ b/src/rabbit_policy.erl @@ -81,12 +81,11 @@ notify_clear(VHost, <<"policy">>, _Name) -> %%---------------------------------------------------------------------------- list(VHost) -> - lists:sort(fun sort_pred/2, - [[{<<"name">>, pget(key, P)} | defaults(pget(value, P))] - || P <- rabbit_runtime_parameters:list(VHost, <<"policy">>)]). + [[{<<"name">>, pget(key, P)} | defaults(pget(value, P))] + || P <- rabbit_runtime_parameters:list(VHost, <<"policy">>)]. update_policies(VHost) -> - Policies = add_compile(list(VHost)), + Policies = list(VHost), {Xs, Qs} = rabbit_misc:execute_mnesia_transaction( fun() -> {[update_exchange(X, Policies) || @@ -99,7 +98,7 @@ update_policies(VHost) -> ok. update_exchange(X = #exchange{name = XName, policy = OldPolicy}, Policies) -> - NewPolicy = strip_compile(match(XName, Policies)), + NewPolicy = match(XName, Policies), case NewPolicy of OldPolicy -> no_change; _ -> rabbit_exchange:update( @@ -108,7 +107,7 @@ update_exchange(X = #exchange{name = XName, policy = OldPolicy}, Policies) -> end. update_queue(Q = #amqqueue{name = QName, policy = OldPolicy}, Policies) -> - NewPolicy = strip_compile(match(QName, Policies)), + NewPolicy = match(QName, Policies), case NewPolicy of OldPolicy -> no_change; _ -> rabbit_amqqueue:update( @@ -124,34 +123,19 @@ notify({Q1 = #amqqueue{}, Q2 = #amqqueue{}}) -> rabbit_amqqueue:policy_changed(Q1, Q2). match(Name, Policies) -> - case lists:filter(fun (P) -> matches(Name, P) end, Policies) of + case lists:sort(fun sort_pred/2, [P || P <- Policies, matches(Name, P)]) of [] -> undefined; [Policy | _Rest] -> Policy end. matches(#resource{name = Name}, Policy) -> case re:run(binary_to_list(Name), - pattern_pref(Policy), + binary_to_list(pget(<<"pattern">>, Policy)), [{capture, none}]) of nomatch -> false; match -> true end. -add_compile(Policies) -> - [ begin - {ok, MP} = re:compile(binary_to_list(pget(<<"pattern">>, Policy))), - [{<<"compiled">>, MP} | Policy] - end || Policy <- Policies ]. - -strip_compile(undefined) -> undefined; -strip_compile(Policy) -> proplists:delete(<<"compiled">>, Policy). - -pattern_pref(Policy) -> - case pget(<<"compiled">>, Policy) of - undefined -> binary_to_list(pget(<<"pattern">>, Policy)); - Compiled -> Compiled - end. - sort_pred(A, B) -> pget(<<"priority">>, A) >= pget(<<"priority">>, B). -- cgit v1.2.1 From 07d06b9612aa173bdd72d6e923cd19a0b8620cd5 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 7 Sep 2012 13:09:36 +0100 Subject: Add a boolean validator. --- src/rabbit_parameter_validation.erl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/rabbit_parameter_validation.erl b/src/rabbit_parameter_validation.erl index af940dde..5abb230c 100644 --- a/src/rabbit_parameter_validation.erl +++ b/src/rabbit_parameter_validation.erl @@ -16,7 +16,7 @@ -module(rabbit_parameter_validation). --export([number/2, binary/2, list/2, proplist/3]). +-export([number/2, binary/2, boolean/2, list/2, proplist/3]). number(_Name, Term) when is_number(Term) -> ok; @@ -30,6 +30,11 @@ binary(_Name, Term) when is_binary(Term) -> binary(Name, Term) -> {error, "~s should be binary, actually was ~p", [Name, Term]}. +boolean(_Name, Term) when is_boolean(Term) -> + ok; +boolean(Name, Term) -> + {error, "~s should be boolean, actually was ~p", [Name, Term]}. + list(_Name, Term) when is_list(Term) -> ok; -- cgit v1.2.1 From c2c071a28c47e529e907ac687334515b3d4cf5e2 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 7 Sep 2012 13:10:25 +0100 Subject: Add a backdoor to allow the direct client to set a trusted user-id. Yes, this is ugly, but the diff is small. --- src/rabbit_channel.erl | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 69fe0edc..23a80cf1 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -459,11 +459,15 @@ check_write_permitted(Resource, #ch{user = User}) -> check_read_permitted(Resource, #ch{user = User}) -> check_resource_access(User, Resource, read). -check_user_id_header(#'P_basic'{user_id = undefined}, _) -> - ok; -check_user_id_header(#'P_basic'{user_id = Username}, +check_user_id_header(Props = #'P_basic'{user_id = undefined}, _) -> + Props; +%% We rely on the fact that the codec can't express this. So we must +%% be talking to the direct client, which can do anything anyway. +check_user_id_header(Props = #'P_basic'{user_id = {trust, Username}}, _) -> + Props#'P_basic'{user_id = Username}; +check_user_id_header(Props = #'P_basic'{user_id = Username}, #ch{user = #user{username = Username}}) -> - ok; + Props; check_user_id_header(#'P_basic'{user_id = Claimed}, #ch{user = #user{username = Actual}}) -> precondition_failed( @@ -608,8 +612,11 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, check_internal_exchange(Exchange), %% We decode the content's properties here because we're almost %% certain to want to look at delivery-mode and priority. - DecodedContent = rabbit_binary_parser:ensure_content_decoded(Content), - check_user_id_header(DecodedContent#content.properties, State), + DecodedContent0 = rabbit_binary_parser:ensure_content_decoded(Content), + DecodedContent = + DecodedContent0#content{ + properties = check_user_id_header( + DecodedContent0#content.properties, State)}, {MsgSeqNo, State1} = case {TxStatus, ConfirmEnabled} of {none, false} -> {undefined, State}; -- cgit v1.2.1 From 4728b927b095ed69b68345e5622dbb9796c97cb1 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Fri, 7 Sep 2012 13:25:33 +0100 Subject: handle ttl'ed messages in bulk --- src/rabbit_amqqueue_process.erl | 72 ++++++++++++++++++++--------------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index f5a3a5f1..6f5a879b 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -726,8 +726,7 @@ drop_expired_messages(State = #q{backing_queue_state = BQS, {Next, BQS2}; _ -> {Next, Msgs, BQS2} = BQ:dropwhile(ExpirePred, true, BQS), - lists:foreach(fun({Msg, AckTag}) -> DLXFun(Msg, AckTag) end, - Msgs), + DLXFun(Msgs), {Next, BQS2} end, ensure_ttl_timer(case Props of @@ -767,37 +766,17 @@ ack_if_no_dlx(_AckTags, State) -> dead_letter_fun(_Reason, #q{dlx = undefined}) -> undefined; dead_letter_fun(Reason, _State) -> - fun(Msg, AckTag) -> - gen_server2:cast(self(), {dead_letter, {Msg, AckTag}, Reason}) - end. - -dead_letter_publish(Msg, Reason, State = #q{publish_seqno = MsgSeqNo}) -> - DLMsg = #basic_message{exchange_name = XName} = - make_dead_letter_msg(Reason, Msg, State), - case rabbit_exchange:lookup(XName) of - {ok, X} -> - Delivery = rabbit_basic:delivery(false, false, DLMsg, MsgSeqNo), - {Queues, Cycles} = detect_dead_letter_cycles( - DLMsg, rabbit_exchange:route(X, Delivery)), - lists:foreach(fun log_cycle_once/1, Cycles), - QPids = rabbit_amqqueue:lookup(Queues), - {_, DeliveredQPids} = rabbit_amqqueue:deliver(QPids, Delivery), - DeliveredQPids; - {error, not_found} -> - [] - end. - -dead_letter_msg(Msg, AckTag, Reason, State = #q{publish_seqno = MsgSeqNo, - unconfirmed = UC}) -> - QPids = dead_letter_publish(Msg, Reason, State), - State1 = State#q{queue_monitors = pmon:monitor_all( - QPids, State#q.queue_monitors), - publish_seqno = MsgSeqNo + 1}, - case QPids of - [] -> cleanup_after_confirm([AckTag], State1); - _ -> UC1 = dtree:insert(MsgSeqNo, QPids, AckTag, UC), - noreply(State1#q{unconfirmed = UC1}) - end. + fun(Msgs) -> gen_server2:cast(self(), {dead_letter, Msgs, Reason}) end. + +dead_letter_publish(Msg, Reason, X, State = #q{publish_seqno = MsgSeqNo}) -> + DLMsg = make_dead_letter_msg(Reason, Msg, State), + Delivery = rabbit_basic:delivery(false, false, DLMsg, MsgSeqNo), + {Queues, Cycles} = detect_dead_letter_cycles( + DLMsg, rabbit_exchange:route(X, Delivery)), + lists:foreach(fun log_cycle_once/1, Cycles), + QPids = rabbit_amqqueue:lookup(Queues), + {_, DeliveredQPids} = rabbit_amqqueue:deliver(QPids, Delivery), + DeliveredQPids. handle_queue_down(QPid, Reason, State = #q{queue_monitors = QMons, unconfirmed = UC}) -> @@ -1244,7 +1223,12 @@ handle_cast({reject, AckTags, Requeue, ChPid}, State) -> true -> fun (State1) -> requeue_and_run(AckTags, State1) end; false -> fun (State1 = #q{backing_queue = BQ, backing_queue_state = BQS}) -> - Fun = dead_letter_fun(rejected, State1), + Fun = + case dead_letter_fun(rejected, State1) of + undefined -> undefined; + F -> fun(M, A) -> F([{M, A}]) + end + end, BQS1 = BQ:fold(Fun, BQS, AckTags), ack_if_no_dlx( AckTags, @@ -1296,8 +1280,24 @@ handle_cast({set_maximum_since_use, Age}, State) -> ok = file_handle_cache:set_maximum_since_use(Age), noreply(State); -handle_cast({dead_letter, {Msg, AckTag}, Reason}, State) -> - dead_letter_msg(Msg, AckTag, Reason, State); +handle_cast({dead_letter, Msgs, Reason}, State = #q{dlx = XName}) -> + case rabbit_exchange:lookup(XName) of + {ok, X} -> + noreply(lists:foldl( + fun({Msg, AckTag}, State1 = #q{publish_seqno = SeqNo, + unconfirmed = UC}) -> + QPids = dead_letter_publish(Msg, Reason, X, + State1), + UC1 = dtree:insert(SeqNo, QPids, AckTag, UC), + QMons = pmon:monitor_all(QPids, + State1#q.queue_monitors), + State1#q{queue_monitors = QMons, + publish_seqno = SeqNo + 1, + unconfirmed = UC1} + end, State, Msgs)); + {error, not_found} -> + cleanup_after_confirm([AckTag || {_, AckTag} <- Msgs], State) + end; handle_cast(wake_up, State) -> noreply(State). -- cgit v1.2.1 From 4f489bd425cd9bcfd47ed1d2de84650840446be9 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Fri, 7 Sep 2012 13:46:56 +0100 Subject: cosmetics --- src/rabbit_types.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_types.erl b/src/rabbit_types.erl index 732c29b6..8966bcab 100644 --- a/src/rabbit_types.erl +++ b/src/rabbit_types.erl @@ -64,7 +64,7 @@ #basic_message{exchange_name :: rabbit_exchange:name(), routing_keys :: [rabbit_router:routing_key()], content :: content(), - id :: msg_id(), + id :: msg_id(), is_persistent :: boolean()}). -type(message() :: basic_message()). -type(delivery() :: -- cgit v1.2.1 From 52ca07595547a746cf999dea7c8b22d0a4865cc3 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Fri, 7 Sep 2012 17:07:45 +0100 Subject: per-message ttl, based on the `expiration' property Right now I'm just emitting a warning when the expiration can't be parsed - we probably want to reject the publish. --- src/rabbit_amqqueue_process.erl | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index f5a3a5f1..e3ccf223 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -540,7 +540,7 @@ attempt_delivery(#delivery{sender = SenderPid, message = Message}, Confirm, {false, BQS1} -> deliver_msgs_to_consumers( fun (AckRequired, State1 = #q{backing_queue_state = BQS2}) -> - Props = message_properties(Confirm, State1), + Props = message_properties(Message, Confirm, State1), {AckTag, BQS3} = BQ:publish_delivered( AckRequired, Message, Props, SenderPid, BQS2), @@ -575,7 +575,7 @@ deliver_or_enqueue(Delivery = #delivery{message = Message, {false, State1} -> State2 = #q{backing_queue = BQ, backing_queue_state = BQS} = maybe_record_confirm_message(Confirm, State1), - Props = message_properties(Confirm, State2), + Props = message_properties(Message, Confirm, State2), BQS1 = BQ:publish(Message, Props, SenderPid, BQS), ensure_ttl_timer(Props#message_properties.expiry, State2#q{backing_queue_state = BQS1}) @@ -705,12 +705,29 @@ discard_delivery(#delivery{sender = SenderPid, backing_queue_state = BQS}) -> State#q{backing_queue_state = BQ:discard(Message, SenderPid, BQS)}. -message_properties(Confirm, #q{ttl = TTL}) -> - #message_properties{expiry = calculate_msg_expiry(TTL), +message_properties(Message, Confirm, #q{ttl = TTL}) -> + #message_properties{expiry = calculate_msg_expiry(Message, TTL), needs_confirming = needs_confirming(Confirm)}. -calculate_msg_expiry(undefined) -> undefined; -calculate_msg_expiry(TTL) -> now_micros() + (TTL * 1000). +calculate_msg_expiry(#basic_message{content = Content}, TTL) -> + #content{properties = #'P_basic'{expiration = Expiration}} = + rabbit_binary_parser:ensure_content_decoded(Content), + ParseError = + fun () -> rabbit_log:warning("could not parse expiration '~s'~n.", + [Expiration]) + end, + Milli = case Expiration of + undefined -> TTL; + B -> case string:to_integer(binary_to_list(B)) of + {error, no_integer} -> ParseError(), TTL; + {N, ""} -> N; + {_, _ } -> ParseError(), TTL + end + end, + case Milli of + undefined -> undefined; + _ -> now_micros() + Milli * 1000 + end. drop_expired_messages(State = #q{ttl = undefined}) -> State; -- cgit v1.2.1 From e3dfbe70bc1b949d647e6d6d7d5cc2c3b3ece472 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Fri, 7 Sep 2012 17:09:22 +0100 Subject: binary, not string --- src/rabbit_amqqueue_process.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index e3ccf223..d5f678af 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -713,7 +713,7 @@ calculate_msg_expiry(#basic_message{content = Content}, TTL) -> #content{properties = #'P_basic'{expiration = Expiration}} = rabbit_binary_parser:ensure_content_decoded(Content), ParseError = - fun () -> rabbit_log:warning("could not parse expiration '~s'~n.", + fun () -> rabbit_log:warning("could not parse expiration '~p'~n.", [Expiration]) end, Milli = case Expiration of -- cgit v1.2.1 From 20a4d5c9dfc9bddaea36d1f93893077b8b958f5f Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 7 Sep 2012 18:07:57 +0100 Subject: Typos and rephrasing --- docs/rabbitmqctl.1.xml | 6 +++--- src/rabbit_control_main.erl | 2 +- src/rabbit_mnesia.erl | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/rabbitmqctl.1.xml b/docs/rabbitmqctl.1.xml index b476f486..1af93e85 100644 --- a/docs/rabbitmqctl.1.xml +++ b/docs/rabbitmqctl.1.xml @@ -325,7 +325,7 @@ least one disk node, and usually should have more than one. - The node will be a disk node by default. If you wish to wish to + The node will be a disk node by default. If you wish to create a RAM node, provide the --ram flag. @@ -408,8 +408,8 @@ Enables node removal from an offline node. This is only useful in the situation where all the nodes are offline and the last node to go down cannot be brought online, thus - preventing the whole cluster to start. It should not be used - in any other circumstances since it can lead to + preventing the whole cluster from starting. It should not be + used in any other circumstances since it can lead to inconsistencies. diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index 997b8e4d..bd01a1b1 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -261,7 +261,7 @@ action(change_cluster_node_type, Node, [Type], _Opts, Inform) action(update_cluster_nodes, Node, [ClusterNodeS], _Opts, Inform) -> ClusterNode = list_to_atom(ClusterNodeS), - Inform("Re-clustering ~p with ~p", [Node, ClusterNode]), + Inform("Updating cluster nodes for ~p from ~p", [Node, ClusterNode]), rpc_call(Node, rabbit_mnesia, update_cluster_nodes, [ClusterNode]); action(forget_cluster_node, Node, [ClusterNodeS], Opts, Inform) -> diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index ea2cbc1e..7ff70576 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -321,7 +321,7 @@ update_cluster_nodes(DiscoveryNode) -> ok. -%% We proceed like this: try to remove the node locally. If the node if offline, +%% We proceed like this: try to remove the node locally. If the node is offline, %% we remove the node if: %% * This node is a disc node %% * All other nodes are offline @@ -353,7 +353,7 @@ forget_cluster_node(Node, RemoveWhenOffline) -> "offline node. That's dangerous, but can be " "done with the --offline flag. Please consult " "the manual for rabbitmqctl for more " - "informations."}}) + "information."}}) end; Err = {error, _} -> throw(Err) -- cgit v1.2.1 From 87d441a54f7a781472ab979104c251ae208dfaa9 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Sat, 8 Sep 2012 12:32:29 +0100 Subject: check validity of the expiration in basic.publish Checking it always is not a good idea, since we don't always decode the properties. --- src/rabbit_amqqueue_process.erl | 17 +++++------------ src/rabbit_basic.erl | 16 +++++++++++++++- src/rabbit_channel.erl | 11 +++++++++-- 3 files changed, 29 insertions(+), 15 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index d5f678af..66ffb156 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -710,19 +710,12 @@ message_properties(Message, Confirm, #q{ttl = TTL}) -> needs_confirming = needs_confirming(Confirm)}. calculate_msg_expiry(#basic_message{content = Content}, TTL) -> - #content{properties = #'P_basic'{expiration = Expiration}} = + #content{properties = Props} = rabbit_binary_parser:ensure_content_decoded(Content), - ParseError = - fun () -> rabbit_log:warning("could not parse expiration '~p'~n.", - [Expiration]) - end, - Milli = case Expiration of - undefined -> TTL; - B -> case string:to_integer(binary_to_list(B)) of - {error, no_integer} -> ParseError(), TTL; - {N, ""} -> N; - {_, _ } -> ParseError(), TTL - end + %% We assert that the expiration must be valid - we check in che channel. + Milli = case rabbit_basic:parse_expiration(Props) of + {ok, undefined} -> TTL; + {ok, N } -> N end, case Milli of undefined -> undefined; diff --git a/src/rabbit_basic.erl b/src/rabbit_basic.erl index 734456d3..eb304de5 100644 --- a/src/rabbit_basic.erl +++ b/src/rabbit_basic.erl @@ -20,7 +20,8 @@ -export([publish/4, publish/6, publish/1, message/3, message/4, properties/1, append_table_header/3, - extract_headers/1, map_headers/2, delivery/4, header_routes/1]). + extract_headers/1, map_headers/2, delivery/4, header_routes/1, + parse_expiration/1]). -export([build_content/2, from_content/1]). %%---------------------------------------------------------------------------- @@ -72,6 +73,9 @@ binary() | [binary()]) -> rabbit_types:content()). -spec(from_content/1 :: (rabbit_types:content()) -> {rabbit_framing:amqp_property_record(), binary()}). +-spec(parse_expiration/1 :: + (rabbit_framing:amqp_property_record()) + -> rabbit_types:ok_or_error2(non_neg_integer(), any())). -endif. @@ -226,3 +230,13 @@ header_routes(HeadersTable) -> {Type, _Val} -> throw({error, {unacceptable_type_in_header, binary_to_list(HeaderKey), Type}}) end || HeaderKey <- ?ROUTING_HEADERS]). + +parse_expiration(#'P_basic'{expiration = Expiration}) -> + case Expiration of + undefined -> {ok, undefined}; + B -> case string:to_integer(binary_to_list(B)) of + {error, no_integer} = E -> E; + {N, ""} -> {ok, N}; + {_, _ } -> {error, leftover_string} + end + end. diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 69fe0edc..78f5d3b8 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -608,8 +608,15 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, check_internal_exchange(Exchange), %% We decode the content's properties here because we're almost %% certain to want to look at delivery-mode and priority. - DecodedContent = rabbit_binary_parser:ensure_content_decoded(Content), - check_user_id_header(DecodedContent#content.properties, State), + DecodedContent = #content {properties = Props} = + rabbit_binary_parser:ensure_content_decoded(Content), + check_user_id_header(Props, State), + case rabbit_basic:parse_expiration(Props) of + {ok, _} -> ok; + {error, E} -> rabbit_misc:protocol_error( + invalid_expiration, "cannot parse expiration '~p': ~p", + [Props#'P_basic'.expiration, E]) + end, {MsgSeqNo, State1} = case {TxStatus, ConfirmEnabled} of {none, false} -> {undefined, State}; -- cgit v1.2.1 From 0c4649b89582fc57c1c7cd50f4dbc7f38bea8ac0 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Sat, 8 Sep 2012 12:39:33 +0100 Subject: a bit more informative error --- src/rabbit_basic.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_basic.erl b/src/rabbit_basic.erl index eb304de5..e2181875 100644 --- a/src/rabbit_basic.erl +++ b/src/rabbit_basic.erl @@ -237,6 +237,6 @@ parse_expiration(#'P_basic'{expiration = Expiration}) -> B -> case string:to_integer(binary_to_list(B)) of {error, no_integer} = E -> E; {N, ""} -> {ok, N}; - {_, _ } -> {error, leftover_string} + {_, S } -> {error, {leftover_string, S}} end end. -- cgit v1.2.1 From 0e35244caf9a2e2aad0ae6ab6bf7d697b94a8b2e Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Mon, 10 Sep 2012 10:04:44 +0100 Subject: Backout changeset 25ee0d6a9c30 --- src/rabbit_parameter_validation.erl | 2 -- src/rabbit_policy.erl | 13 ++----------- 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/src/rabbit_parameter_validation.erl b/src/rabbit_parameter_validation.erl index c40104d7..2235340f 100644 --- a/src/rabbit_parameter_validation.erl +++ b/src/rabbit_parameter_validation.erl @@ -57,8 +57,6 @@ proplist(Name, Constraints, Term) when is_list(Term) -> {[{error, "Key \"~s\" not found in ~s", [Key, Name]} | Results0], Term0}; {false, optional} -> - {Results0, Term0}; - {false, {optional, _Default}} -> {Results0, Term0} end end, {[], Term}, Constraints), diff --git a/src/rabbit_policy.erl b/src/rabbit_policy.erl index c7f4d021..d130c2ee 100644 --- a/src/rabbit_policy.erl +++ b/src/rabbit_policy.erl @@ -81,7 +81,7 @@ notify_clear(VHost, <<"policy">>, _Name) -> %%---------------------------------------------------------------------------- list(VHost) -> - [[{<<"name">>, pget(key, P)} | defaults(pget(value, P))] + [[{<<"name">>, pget(key, P)} | pget(value, P)] || P <- rabbit_runtime_parameters:list(VHost, <<"policy">>)]. update_policies(VHost) -> @@ -141,16 +141,7 @@ sort_pred(A, B) -> %%---------------------------------------------------------------------------- -defaults(Props) -> - Def = [{Key, Def} || {Key, _Fun, {optional, Def}} <- policy_validation()], - lists:foldl(fun ({Key, Default}, Props1) -> - case pget(Key, Props1) of - undefined -> [{Key, Default} | Props1]; - _ -> Props1 - end - end, Props, Def). - policy_validation() -> - [{<<"priority">>, fun rabbit_parameter_validation:number/2, {optional, 0}}, + [{<<"priority">>, fun rabbit_parameter_validation:number/2, mandatory}, {<<"pattern">>, fun rabbit_parameter_validation:regex/2, mandatory}, {<<"policy">>, fun rabbit_parameter_validation:list/2, mandatory}]. -- cgit v1.2.1 From 18e85807bee45cc33da857e047994ecfb79935da Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Mon, 10 Sep 2012 10:29:09 +0100 Subject: Reduce over-engineering of default policy priority --- src/rabbit_policy.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rabbit_policy.erl b/src/rabbit_policy.erl index d130c2ee..4eb7c2ba 100644 --- a/src/rabbit_policy.erl +++ b/src/rabbit_policy.erl @@ -22,7 +22,7 @@ -include("rabbit.hrl"). --import(rabbit_misc, [pget/2]). +-import(rabbit_misc, [pget/2, pget/3]). -export([register/0]). -export([name/1, get/2, set/1]). @@ -137,11 +137,11 @@ matches(#resource{name = Name}, Policy) -> end. sort_pred(A, B) -> - pget(<<"priority">>, A) >= pget(<<"priority">>, B). + pget(<<"priority">>, A, 0) >= pget(<<"priority">>, B, 0). %%---------------------------------------------------------------------------- policy_validation() -> - [{<<"priority">>, fun rabbit_parameter_validation:number/2, mandatory}, + [{<<"priority">>, fun rabbit_parameter_validation:number/2, optional}, {<<"pattern">>, fun rabbit_parameter_validation:regex/2, mandatory}, {<<"policy">>, fun rabbit_parameter_validation:list/2, mandatory}]. -- cgit v1.2.1 From d5e9bebeb20873ee4b8140a168310a5aa5d81d72 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Mon, 10 Sep 2012 10:32:42 +0100 Subject: check that the ttl values are less than what `erlang:send_after' likes Since it's an extension to begin with, I think it's OK to limit it. I doubt anyone will ever want a ttl of more than 50 days, and if they really complain we can fix it in the future. --- src/rabbit_amqqueue.erl | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 461b25eb..7628d109 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -40,6 +40,8 @@ -define(INTEGER_ARG_TYPES, [byte, short, signedint, long]). +-define(MAX_EXPIRY_TIMER, 4294967295). + -define(MORE_CONSUMER_CREDIT_AFTER, 50). -define(FAILOVER_WAIT_MILLIS, 100). @@ -397,16 +399,18 @@ check_int_arg({Type, _}, _) -> check_positive_int_arg({Type, Val}, Args) -> case check_int_arg({Type, Val}, Args) of - ok when Val > 0 -> ok; - ok -> {error, {value_zero_or_less, Val}}; - Error -> Error + ok when Val > ?MAX_EXPIRY_TIMER -> {error, {value_too_big, Val}}; + ok when Val > 0 -> ok; + ok -> {error, {value_zero_or_less, Val}}; + Error -> Error end. check_non_neg_int_arg({Type, Val}, Args) -> case check_int_arg({Type, Val}, Args) of - ok when Val >= 0 -> ok; - ok -> {error, {value_less_than_zero, Val}}; - Error -> Error + ok when Val > ?MAX_EXPIRY_TIMER -> {error, {value_too_big, Val}}; + ok when Val >= 0 -> ok; + ok -> {error, {value_less_than_zero, Val}}; + Error -> Error end. check_dlxrk_arg({longstr, _}, Args) -> -- cgit v1.2.1 From 4984185a9429d417f6dbc103790ee65154613ad9 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Mon, 10 Sep 2012 13:45:57 +0100 Subject: add mochinum.erl, which is needed by mochijson2.erl --- Makefile | 3 +- src/mochinum.erl | 358 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 359 insertions(+), 2 deletions(-) create mode 100644 src/mochinum.erl diff --git a/Makefile b/Makefile index f3729cfa..820dc32d 100644 --- a/Makefile +++ b/Makefile @@ -146,8 +146,7 @@ $(SOURCE_DIR)/rabbit_framing_amqp_0_8.erl: codegen.py $(AMQP_CODEGEN_DIR)/amqp_c $(PYTHON) codegen.py body $(AMQP_SPEC_JSON_FILES_0_8) $@ dialyze: $(BEAM_TARGETS) $(BASIC_PLT) - dialyzer --plt $(BASIC_PLT) --no_native --fullpath \ - -Wrace_conditions $(BEAM_TARGETS) + dialyzer --plt $(BASIC_PLT) --no_native --fullpath $(BEAM_TARGETS) # rabbit.plt is used by rabbitmq-erlang-client's dialyze make target create-plt: $(RABBIT_PLT) diff --git a/src/mochinum.erl b/src/mochinum.erl new file mode 100644 index 00000000..4ea7a22a --- /dev/null +++ b/src/mochinum.erl @@ -0,0 +1,358 @@ +%% This file is a copy of `mochijson2.erl' from mochiweb, revision +%% d541e9a0f36c00dcadc2e589f20e47fbf46fc76f. For the license, see +%% `LICENSE-MIT-Mochi'. + +%% @copyright 2007 Mochi Media, Inc. +%% @author Bob Ippolito + +%% @doc Useful numeric algorithms for floats that cover some deficiencies +%% in the math module. More interesting is digits/1, which implements +%% the algorithm from: +%% http://www.cs.indiana.edu/~burger/fp/index.html +%% See also "Printing Floating-Point Numbers Quickly and Accurately" +%% in Proceedings of the SIGPLAN '96 Conference on Programming Language +%% Design and Implementation. + +-module(mochinum). +-author("Bob Ippolito "). +-export([digits/1, frexp/1, int_pow/2, int_ceil/1]). + +%% IEEE 754 Float exponent bias +-define(FLOAT_BIAS, 1022). +-define(MIN_EXP, -1074). +-define(BIG_POW, 4503599627370496). + +%% External API + +%% @spec digits(number()) -> string() +%% @doc Returns a string that accurately represents the given integer or float +%% using a conservative amount of digits. Great for generating +%% human-readable output, or compact ASCII serializations for floats. +digits(N) when is_integer(N) -> + integer_to_list(N); +digits(0.0) -> + "0.0"; +digits(Float) -> + {Frac1, Exp1} = frexp_int(Float), + [Place0 | Digits0] = digits1(Float, Exp1, Frac1), + {Place, Digits} = transform_digits(Place0, Digits0), + R = insert_decimal(Place, Digits), + case Float < 0 of + true -> + [$- | R]; + _ -> + R + end. + +%% @spec frexp(F::float()) -> {Frac::float(), Exp::float()} +%% @doc Return the fractional and exponent part of an IEEE 754 double, +%% equivalent to the libc function of the same name. +%% F = Frac * pow(2, Exp). +frexp(F) -> + frexp1(unpack(F)). + +%% @spec int_pow(X::integer(), N::integer()) -> Y::integer() +%% @doc Moderately efficient way to exponentiate integers. +%% int_pow(10, 2) = 100. +int_pow(_X, 0) -> + 1; +int_pow(X, N) when N > 0 -> + int_pow(X, N, 1). + +%% @spec int_ceil(F::float()) -> integer() +%% @doc Return the ceiling of F as an integer. The ceiling is defined as +%% F when F == trunc(F); +%% trunc(F) when F < 0; +%% trunc(F) + 1 when F > 0. +int_ceil(X) -> + T = trunc(X), + case (X - T) of + Pos when Pos > 0 -> T + 1; + _ -> T + end. + + +%% Internal API + +int_pow(X, N, R) when N < 2 -> + R * X; +int_pow(X, N, R) -> + int_pow(X * X, N bsr 1, case N band 1 of 1 -> R * X; 0 -> R end). + +insert_decimal(0, S) -> + "0." ++ S; +insert_decimal(Place, S) when Place > 0 -> + L = length(S), + case Place - L of + 0 -> + S ++ ".0"; + N when N < 0 -> + {S0, S1} = lists:split(L + N, S), + S0 ++ "." ++ S1; + N when N < 6 -> + %% More places than digits + S ++ lists:duplicate(N, $0) ++ ".0"; + _ -> + insert_decimal_exp(Place, S) + end; +insert_decimal(Place, S) when Place > -6 -> + "0." ++ lists:duplicate(abs(Place), $0) ++ S; +insert_decimal(Place, S) -> + insert_decimal_exp(Place, S). + +insert_decimal_exp(Place, S) -> + [C | S0] = S, + S1 = case S0 of + [] -> + "0"; + _ -> + S0 + end, + Exp = case Place < 0 of + true -> + "e-"; + false -> + "e+" + end, + [C] ++ "." ++ S1 ++ Exp ++ integer_to_list(abs(Place - 1)). + + +digits1(Float, Exp, Frac) -> + Round = ((Frac band 1) =:= 0), + case Exp >= 0 of + true -> + BExp = 1 bsl Exp, + case (Frac =/= ?BIG_POW) of + true -> + scale((Frac * BExp * 2), 2, BExp, BExp, + Round, Round, Float); + false -> + scale((Frac * BExp * 4), 4, (BExp * 2), BExp, + Round, Round, Float) + end; + false -> + case (Exp =:= ?MIN_EXP) orelse (Frac =/= ?BIG_POW) of + true -> + scale((Frac * 2), 1 bsl (1 - Exp), 1, 1, + Round, Round, Float); + false -> + scale((Frac * 4), 1 bsl (2 - Exp), 2, 1, + Round, Round, Float) + end + end. + +scale(R, S, MPlus, MMinus, LowOk, HighOk, Float) -> + Est = int_ceil(math:log10(abs(Float)) - 1.0e-10), + %% Note that the scheme implementation uses a 326 element look-up table + %% for int_pow(10, N) where we do not. + case Est >= 0 of + true -> + fixup(R, S * int_pow(10, Est), MPlus, MMinus, Est, + LowOk, HighOk); + false -> + Scale = int_pow(10, -Est), + fixup(R * Scale, S, MPlus * Scale, MMinus * Scale, Est, + LowOk, HighOk) + end. + +fixup(R, S, MPlus, MMinus, K, LowOk, HighOk) -> + TooLow = case HighOk of + true -> + (R + MPlus) >= S; + false -> + (R + MPlus) > S + end, + case TooLow of + true -> + [(K + 1) | generate(R, S, MPlus, MMinus, LowOk, HighOk)]; + false -> + [K | generate(R * 10, S, MPlus * 10, MMinus * 10, LowOk, HighOk)] + end. + +generate(R0, S, MPlus, MMinus, LowOk, HighOk) -> + D = R0 div S, + R = R0 rem S, + TC1 = case LowOk of + true -> + R =< MMinus; + false -> + R < MMinus + end, + TC2 = case HighOk of + true -> + (R + MPlus) >= S; + false -> + (R + MPlus) > S + end, + case TC1 of + false -> + case TC2 of + false -> + [D | generate(R * 10, S, MPlus * 10, MMinus * 10, + LowOk, HighOk)]; + true -> + [D + 1] + end; + true -> + case TC2 of + false -> + [D]; + true -> + case R * 2 < S of + true -> + [D]; + false -> + [D + 1] + end + end + end. + +unpack(Float) -> + <> = <>, + {Sign, Exp, Frac}. + +frexp1({_Sign, 0, 0}) -> + {0.0, 0}; +frexp1({Sign, 0, Frac}) -> + Exp = log2floor(Frac), + <> = <>, + {Frac1, -(?FLOAT_BIAS) - 52 + Exp}; +frexp1({Sign, Exp, Frac}) -> + <> = <>, + {Frac1, Exp - ?FLOAT_BIAS}. + +log2floor(Int) -> + log2floor(Int, 0). + +log2floor(0, N) -> + N; +log2floor(Int, N) -> + log2floor(Int bsr 1, 1 + N). + + +transform_digits(Place, [0 | Rest]) -> + transform_digits(Place, Rest); +transform_digits(Place, Digits) -> + {Place, [$0 + D || D <- Digits]}. + + +frexp_int(F) -> + case unpack(F) of + {_Sign, 0, Frac} -> + {Frac, ?MIN_EXP}; + {_Sign, Exp, Frac} -> + {Frac + (1 bsl 52), Exp - 53 - ?FLOAT_BIAS} + end. + +%% +%% Tests +%% +-ifdef(TEST). +-include_lib("eunit/include/eunit.hrl"). + +int_ceil_test() -> + ?assertEqual(1, int_ceil(0.0001)), + ?assertEqual(0, int_ceil(0.0)), + ?assertEqual(1, int_ceil(0.99)), + ?assertEqual(1, int_ceil(1.0)), + ?assertEqual(-1, int_ceil(-1.5)), + ?assertEqual(-2, int_ceil(-2.0)), + ok. + +int_pow_test() -> + ?assertEqual(1, int_pow(1, 1)), + ?assertEqual(1, int_pow(1, 0)), + ?assertEqual(1, int_pow(10, 0)), + ?assertEqual(10, int_pow(10, 1)), + ?assertEqual(100, int_pow(10, 2)), + ?assertEqual(1000, int_pow(10, 3)), + ok. + +digits_test() -> + ?assertEqual("0", + digits(0)), + ?assertEqual("0.0", + digits(0.0)), + ?assertEqual("1.0", + digits(1.0)), + ?assertEqual("-1.0", + digits(-1.0)), + ?assertEqual("0.1", + digits(0.1)), + ?assertEqual("0.01", + digits(0.01)), + ?assertEqual("0.001", + digits(0.001)), + ?assertEqual("1.0e+6", + digits(1000000.0)), + ?assertEqual("0.5", + digits(0.5)), + ?assertEqual("4503599627370496.0", + digits(4503599627370496.0)), + %% small denormalized number + %% 4.94065645841246544177e-324 =:= 5.0e-324 + <> = <<0,0,0,0,0,0,0,1>>, + ?assertEqual("5.0e-324", + digits(SmallDenorm)), + ?assertEqual(SmallDenorm, + list_to_float(digits(SmallDenorm))), + %% large denormalized number + %% 2.22507385850720088902e-308 + <> = <<0,15,255,255,255,255,255,255>>, + ?assertEqual("2.225073858507201e-308", + digits(BigDenorm)), + ?assertEqual(BigDenorm, + list_to_float(digits(BigDenorm))), + %% small normalized number + %% 2.22507385850720138309e-308 + <> = <<0,16,0,0,0,0,0,0>>, + ?assertEqual("2.2250738585072014e-308", + digits(SmallNorm)), + ?assertEqual(SmallNorm, + list_to_float(digits(SmallNorm))), + %% large normalized number + %% 1.79769313486231570815e+308 + <> = <<127,239,255,255,255,255,255,255>>, + ?assertEqual("1.7976931348623157e+308", + digits(LargeNorm)), + ?assertEqual(LargeNorm, + list_to_float(digits(LargeNorm))), + %% issue #10 - mochinum:frexp(math:pow(2, -1074)). + ?assertEqual("5.0e-324", + digits(math:pow(2, -1074))), + ok. + +frexp_test() -> + %% zero + ?assertEqual({0.0, 0}, frexp(0.0)), + %% one + ?assertEqual({0.5, 1}, frexp(1.0)), + %% negative one + ?assertEqual({-0.5, 1}, frexp(-1.0)), + %% small denormalized number + %% 4.94065645841246544177e-324 + <> = <<0,0,0,0,0,0,0,1>>, + ?assertEqual({0.5, -1073}, frexp(SmallDenorm)), + %% large denormalized number + %% 2.22507385850720088902e-308 + <> = <<0,15,255,255,255,255,255,255>>, + ?assertEqual( + {0.99999999999999978, -1022}, + frexp(BigDenorm)), + %% small normalized number + %% 2.22507385850720138309e-308 + <> = <<0,16,0,0,0,0,0,0>>, + ?assertEqual({0.5, -1021}, frexp(SmallNorm)), + %% large normalized number + %% 1.79769313486231570815e+308 + <> = <<127,239,255,255,255,255,255,255>>, + ?assertEqual( + {0.99999999999999989, 1024}, + frexp(LargeNorm)), + %% issue #10 - mochinum:frexp(math:pow(2, -1074)). + ?assertEqual( + {0.5, -1073}, + frexp(math:pow(2, -1074))), + ok. + +-endif. -- cgit v1.2.1 From d30dda5a869a60b64c98adf11095b62c9baa3843 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 10 Sep 2012 14:16:06 +0100 Subject: Simplify --- src/rabbit_policy.erl | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/rabbit_policy.erl b/src/rabbit_policy.erl index 4eb7c2ba..69480c9c 100644 --- a/src/rabbit_policy.erl +++ b/src/rabbit_policy.erl @@ -129,12 +129,7 @@ match(Name, Policies) -> end. matches(#resource{name = Name}, Policy) -> - case re:run(binary_to_list(Name), - binary_to_list(pget(<<"pattern">>, Policy)), - [{capture, none}]) of - nomatch -> false; - match -> true - end. + match =:= re:run(Name, pget(<<"pattern">>, Policy), [{capture, none}]). sort_pred(A, B) -> pget(<<"priority">>, A, 0) >= pget(<<"priority">>, B, 0). -- cgit v1.2.1 From 9c95750053cb70063fd948d958358b3c468aee04 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 10 Sep 2012 15:22:27 +0100 Subject: Rename --- src/rabbit_channel.erl | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 23a80cf1..2a082781 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -459,28 +459,28 @@ check_write_permitted(Resource, #ch{user = User}) -> check_read_permitted(Resource, #ch{user = User}) -> check_resource_access(User, Resource, read). -check_user_id_header(Props = #'P_basic'{user_id = undefined}, _) -> +check_internal_exchange(#exchange{name = Name, internal = true}) -> + rabbit_misc:protocol_error(access_refused, + "cannot publish to internal ~s", + [rabbit_misc:rs(Name)]); +check_internal_exchange(_) -> + ok. + +ensure_user_id_header(Props = #'P_basic'{user_id = undefined}, _) -> Props; %% We rely on the fact that the codec can't express this. So we must %% be talking to the direct client, which can do anything anyway. -check_user_id_header(Props = #'P_basic'{user_id = {trust, Username}}, _) -> +ensure_user_id_header(Props = #'P_basic'{user_id = {trust, Username}}, _) -> Props#'P_basic'{user_id = Username}; -check_user_id_header(Props = #'P_basic'{user_id = Username}, - #ch{user = #user{username = Username}}) -> +ensure_user_id_header(Props = #'P_basic'{user_id = Username}, + #ch{user = #user{username = Username}}) -> Props; -check_user_id_header(#'P_basic'{user_id = Claimed}, - #ch{user = #user{username = Actual}}) -> +ensure_user_id_header(#'P_basic'{user_id = Claimed}, + #ch{user = #user{username = Actual}}) -> precondition_failed( "user_id property set to '~s' but authenticated user was '~s'", [Claimed, Actual]). -check_internal_exchange(#exchange{name = Name, internal = true}) -> - rabbit_misc:protocol_error(access_refused, - "cannot publish to internal ~s", - [rabbit_misc:rs(Name)]); -check_internal_exchange(_) -> - ok. - expand_queue_name_shortcut(<<>>, #ch{most_recently_declared_queue = <<>>}) -> rabbit_misc:protocol_error( not_found, "no previously declared queue", []); @@ -615,7 +615,7 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, DecodedContent0 = rabbit_binary_parser:ensure_content_decoded(Content), DecodedContent = DecodedContent0#content{ - properties = check_user_id_header( + properties = ensure_user_id_header( DecodedContent0#content.properties, State)}, {MsgSeqNo, State1} = case {TxStatus, ConfirmEnabled} of -- cgit v1.2.1 From dbbb424225d52fc41be696cec9f9ba462daf19c0 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Mon, 10 Sep 2012 16:20:49 +0100 Subject: increase the seqno when the DLX is not found --- src/rabbit_amqqueue_process.erl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 6f5a879b..b1f09622 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1296,7 +1296,9 @@ handle_cast({dead_letter, Msgs, Reason}, State = #q{dlx = XName}) -> unconfirmed = UC1} end, State, Msgs)); {error, not_found} -> - cleanup_after_confirm([AckTag || {_, AckTag} <- Msgs], State) + cleanup_after_confirm( + [AckTag || {_, AckTag} <- Msgs], + State#q{publish_seqno = State#q.publish_seqno + length(Msgs)}) end; handle_cast(wake_up, State) -> -- cgit v1.2.1 From e1268219a74a4e4af6cdcfdfe90d3a7256db970a Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 10 Sep 2012 17:21:31 +0100 Subject: Attempt at simplifying check_cluster_consistency/0, but I am having trouble with systest. --- src/rabbit_mnesia.erl | 76 +++++++++++++++++++++++++++------------------------ 1 file changed, 40 insertions(+), 36 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 7ff70576..c7b177c5 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -703,47 +703,43 @@ wait_for_tables(TableNames) -> %% This does not guarantee us much, but it avoids some situations that will %% definitely end up badly check_cluster_consistency() -> - AllNodes = ordsets:del_element(node(), all_clustered_nodes()), %% We want to find 0 or 1 consistent nodes. - case - lists:foldl( - fun(Node, {error, Error}) -> - case rpc:call(Node, rabbit_mnesia, node_info, []) of - {badrpc, _Reason} -> - {error, Error}; - {OTP, Rabbit, Res} -> - rabbit_misc:sequence_error( - [check_otp_consistency(OTP), - check_rabbit_consistency(Rabbit), - case Res of - {ok, Status} -> - check_nodes_consistency(Node, Status); - {error, _Reason} -> - {error, Error} - end]) - end; - (_Node, {ok, Status}) -> {ok, Status} - end, {error, no_nodes}, AllNodes) + case lists:foldl( + fun (Node, not_found) -> check_cluster_consistency(Node); + (_Node, {ok, Status}) -> {ok, Status} + end, not_found, ordsets:del_element(node(), all_clustered_nodes())) of {ok, Status = {RemoteAllNodes, _, _}} -> case ordsets:is_subset(all_clustered_nodes(), RemoteAllNodes) of true -> ok; - false -> %% We delete the schema here since we have more nodes - %% than the actually clustered ones, and there is no - %% way to remove those nodes from our schema - %% otherwise. On the other hand, we are sure that there - %% is another online node that we can use to sync the - %% tables with. There is a race here: if between this - %% check and the `init_db' invocation the cluster gets - %% disbanded, we're left with a node with no mnesia - %% data that will try to connect to offline nodes. + false -> %% We delete the schema here since we think we are + %% clustered with nodes that are no longer in the + %% cluster and there is no other way to remove them + %% from our schema. On the other hand, we are sure + %% that there is another online node that we can use + %% to sync the tables with. There is a race here: if + %% between this check and the `init_db' invocation the + %% cluster gets disbanded, we're left with a node with + %% no mnesia data that will try to connect to offline + %% nodes. mnesia:delete_schema([node()]) end, rabbit_node_monitor:write_cluster_status_file(Status); - {error, no_nodes} -> - ok; - {error, Error} -> - throw({error, Error}) + ignore -> + ok + end. + +check_cluster_consistency(Node) -> + case rpc:call(Node, rabbit_mnesia, node_info, []) of + {badrpc, _Reason} -> + not_found; + {_OTP, _Rabbit, {error, _}} -> + not_found; + {OTP, Rabbit, {ok, Status}} -> + case check_consistency(OTP, Rabbit, Node, Status) of + {error, Error} -> throw({error, Error}); + {ok, Res} -> {ok, Res} + end end. %%-------------------------------------------------------------------- @@ -1132,6 +1128,16 @@ is_running_remote() -> {proplists:is_defined(rabbit, application:which_applications(infinity)), node()}. +check_consistency(OTP, Rabbit) -> + rabbit_misc:sequence_error( + [check_otp_consistency(OTP), check_rabbit_consistency(Rabbit)]). + +check_consistency(OTP, Rabbit, Node, Status) -> + rabbit_misc:sequence_error( + [check_otp_consistency(OTP), + check_rabbit_consistency(Rabbit), + check_nodes_consistency(Node, Status)]). + check_nodes_consistency(Node, RemoteStatus = {RemoteAllNodes, _, _}) -> ThisNode = node(), case ordsets:is_element(ThisNode, RemoteAllNodes) of @@ -1179,9 +1185,7 @@ find_good_node([Node | Nodes]) -> {badrpc, _Reason} -> find_good_node(Nodes); {OTP, Rabbit, _} -> - case rabbit_misc:sequence_error([check_otp_consistency(OTP), - check_rabbit_consistency(Rabbit)]) - of + case check_consistency(OTP, Rabbit) of {error, _} -> find_good_node(Nodes); ok -> {ok, Node} end -- cgit v1.2.1 From cbdf828e67c64ef2c63d0d581494b7792d9cf921 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 10 Sep 2012 17:33:16 +0100 Subject: Oops --- src/rabbit_mnesia.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index c7b177c5..b5d35b95 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -725,7 +725,7 @@ check_cluster_consistency() -> mnesia:delete_schema([node()]) end, rabbit_node_monitor:write_cluster_status_file(Status); - ignore -> + not_found -> ok end. -- cgit v1.2.1 From b55289c1acf32b4c75691f51ec3bc00e5ec2187e Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Tue, 11 Sep 2012 10:13:35 +0100 Subject: pushed changes to Makefile by mistake --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 820dc32d..f3729cfa 100644 --- a/Makefile +++ b/Makefile @@ -146,7 +146,8 @@ $(SOURCE_DIR)/rabbit_framing_amqp_0_8.erl: codegen.py $(AMQP_CODEGEN_DIR)/amqp_c $(PYTHON) codegen.py body $(AMQP_SPEC_JSON_FILES_0_8) $@ dialyze: $(BEAM_TARGETS) $(BASIC_PLT) - dialyzer --plt $(BASIC_PLT) --no_native --fullpath $(BEAM_TARGETS) + dialyzer --plt $(BASIC_PLT) --no_native --fullpath \ + -Wrace_conditions $(BEAM_TARGETS) # rabbit.plt is used by rabbitmq-erlang-client's dialyze make target create-plt: $(RABBIT_PLT) -- cgit v1.2.1 From d22b4b01e69a75a073c7a67d6a3c92fba92e2075 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 11 Sep 2012 13:09:29 +0100 Subject: Remove the previous hack, add a check for the "impersonator" tag instead. --- src/rabbit_channel.erl | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 2a082781..e50e823c 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -459,6 +459,21 @@ check_write_permitted(Resource, #ch{user = User}) -> check_read_permitted(Resource, #ch{user = User}) -> check_resource_access(User, Resource, read). +check_user_id_header(#'P_basic'{user_id = undefined}, _) -> + ok; +check_user_id_header(#'P_basic'{user_id = Username}, + #ch{user = #user{username = Username}}) -> + ok; +check_user_id_header(#'P_basic'{user_id = Claimed}, + #ch{user = #user{username = Actual, + tags = Tags}}) -> + case lists:member(impersonator, Tags) of + true -> ok; + false -> precondition_failed( + "user_id property set to '~s' but authenticated user was " + "'~s'", [Claimed, Actual]) + end. + check_internal_exchange(#exchange{name = Name, internal = true}) -> rabbit_misc:protocol_error(access_refused, "cannot publish to internal ~s", @@ -466,21 +481,6 @@ check_internal_exchange(#exchange{name = Name, internal = true}) -> check_internal_exchange(_) -> ok. -ensure_user_id_header(Props = #'P_basic'{user_id = undefined}, _) -> - Props; -%% We rely on the fact that the codec can't express this. So we must -%% be talking to the direct client, which can do anything anyway. -ensure_user_id_header(Props = #'P_basic'{user_id = {trust, Username}}, _) -> - Props#'P_basic'{user_id = Username}; -ensure_user_id_header(Props = #'P_basic'{user_id = Username}, - #ch{user = #user{username = Username}}) -> - Props; -ensure_user_id_header(#'P_basic'{user_id = Claimed}, - #ch{user = #user{username = Actual}}) -> - precondition_failed( - "user_id property set to '~s' but authenticated user was '~s'", - [Claimed, Actual]). - expand_queue_name_shortcut(<<>>, #ch{most_recently_declared_queue = <<>>}) -> rabbit_misc:protocol_error( not_found, "no previously declared queue", []); @@ -612,11 +612,8 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, check_internal_exchange(Exchange), %% We decode the content's properties here because we're almost %% certain to want to look at delivery-mode and priority. - DecodedContent0 = rabbit_binary_parser:ensure_content_decoded(Content), - DecodedContent = - DecodedContent0#content{ - properties = ensure_user_id_header( - DecodedContent0#content.properties, State)}, + DecodedContent = rabbit_binary_parser:ensure_content_decoded(Content), + check_user_id_header(DecodedContent#content.properties, State), {MsgSeqNo, State1} = case {TxStatus, ConfirmEnabled} of {none, false} -> {undefined, State}; -- cgit v1.2.1 From 94b75a4ac2adc4a003db8b6dc688d70369bb803f Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 11 Sep 2012 13:10:01 +0100 Subject: Allow direct client users to provide a complete #user{} record rather than looking it up for them. --- src/rabbit_direct.erl | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/rabbit_direct.erl b/src/rabbit_direct.erl index c87b1dc1..a669a2b3 100644 --- a/src/rabbit_direct.erl +++ b/src/rabbit_direct.erl @@ -31,8 +31,8 @@ -spec(force_event_refresh/0 :: () -> 'ok'). -spec(list/0 :: () -> [pid()]). -spec(list_local/0 :: () -> [pid()]). --spec(connect/5 :: (rabbit_types:username(), rabbit_types:vhost(), - rabbit_types:protocol(), pid(), +-spec(connect/5 :: ((rabbit_types:username() | rabbit_types:user()), + rabbit_types:vhost(), rabbit_types:protocol(), pid(), rabbit_event:event_props()) -> {'ok', {rabbit_types:user(), rabbit_framing:amqp_table()}}). @@ -64,22 +64,22 @@ list() -> %%---------------------------------------------------------------------------- +connect(User = #user{}, VHost, Protocol, Pid, Infos) -> + try rabbit_access_control:check_vhost_access(User, VHost) of + ok -> ok = pg_local:join(rabbit_direct, Pid), + rabbit_event:notify(connection_created, Infos), + {ok, {User, rabbit_reader:server_properties(Protocol)}} + catch + exit:#amqp_error{name = access_refused} -> + {error, access_refused} + end; + connect(Username, VHost, Protocol, Pid, Infos) -> case rabbit:is_running() of true -> case rabbit_access_control:check_user_login(Username, []) of - {ok, User} -> - try rabbit_access_control:check_vhost_access(User, VHost) of - ok -> ok = pg_local:join(rabbit_direct, Pid), - rabbit_event:notify(connection_created, Infos), - {ok, {User, - rabbit_reader:server_properties(Protocol)}} - catch - exit:#amqp_error{name = access_refused} -> - {error, access_refused} - end; - {refused, _Msg, _Args} -> - {error, auth_failure} + {ok, User} -> connect(User, VHost, Protocol, Pid, Infos); + {refused, _M, _A} -> {error, auth_failure} end; false -> {error, broker_not_found_on_node} -- cgit v1.2.1 From bd3f124776d33abfa759f7f8ba87da8e118a0da2 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 11 Sep 2012 13:25:42 +0100 Subject: cosmetic --- src/rabbit_amqqueue.erl | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 7628d109..d566ac87 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -694,11 +694,10 @@ safe_delegate_call_ok(F, Pids) -> fun () -> ok end, fun () -> F(Pid) end) end), - case lists:filter(fun ({_Pid, {exit, {R, _}, _}}) -> - rabbit_misc:is_abnormal_exit(R); - ({_Pid, _}) -> - false - end, Bads) of + case lists:filter( + fun ({_Pid, {exit, {R, _}, _}}) -> rabbit_misc:is_abnormal_exit(R); + ({_Pid, _}) -> false + end, Bads) of [] -> ok; Bads1 -> {error, Bads1} end. -- cgit v1.2.1 From cf4d9a2ba41792eaf989d8ff3da48c4a2f32b49b Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Tue, 11 Sep 2012 15:07:17 +0100 Subject: do not bump the publish seqno when we can't publish DLX'ed msgs Emile notices that it's not necessary --- src/rabbit_amqqueue_process.erl | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index b1f09622..6f5a879b 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1296,9 +1296,7 @@ handle_cast({dead_letter, Msgs, Reason}, State = #q{dlx = XName}) -> unconfirmed = UC1} end, State, Msgs)); {error, not_found} -> - cleanup_after_confirm( - [AckTag || {_, AckTag} <- Msgs], - State#q{publish_seqno = State#q.publish_seqno + length(Msgs)}) + cleanup_after_confirm([AckTag || {_, AckTag} <- Msgs], State) end; handle_cast(wake_up, State) -> -- cgit v1.2.1 From cbf6876df093db5c71e7cb100b880e309d837401 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Tue, 11 Sep 2012 15:07:31 +0100 Subject: cosmetics --- src/rabbit_amqqueue_process.erl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 6f5a879b..20ba4574 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1284,13 +1284,13 @@ handle_cast({dead_letter, Msgs, Reason}, State = #q{dlx = XName}) -> case rabbit_exchange:lookup(XName) of {ok, X} -> noreply(lists:foldl( - fun({Msg, AckTag}, State1 = #q{publish_seqno = SeqNo, - unconfirmed = UC}) -> + fun({Msg, AckTag}, State1 = #q{publish_seqno = SeqNo, + unconfirmed = UC, + queue_monitors = QMon}) -> QPids = dead_letter_publish(Msg, Reason, X, State1), UC1 = dtree:insert(SeqNo, QPids, AckTag, UC), - QMons = pmon:monitor_all(QPids, - State1#q.queue_monitors), + QMons = pmon:monitor_all(QPids, QMon), State1#q{queue_monitors = QMons, publish_seqno = SeqNo + 1, unconfirmed = UC1} -- cgit v1.2.1 From 71960baed31288a26b5964527b8f6d10144b2de8 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Tue, 11 Sep 2012 18:37:50 +0100 Subject: split cluster status file and running nodes file --- src/rabbit_mnesia.erl | 15 ++-- src/rabbit_node_monitor.erl | 200 ++++++++++++++++++++------------------------ 2 files changed, 97 insertions(+), 118 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index b5d35b95..31672512 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -119,7 +119,7 @@ prepare() -> ensure_mnesia_dir(), - rabbit_node_monitor:prepare_cluster_status_file(), + rabbit_node_monitor:prepare_cluster_status_files(), check_cluster_consistency(). init() -> @@ -250,7 +250,7 @@ reset(Force) -> end, %% remove persisted messages and any other garbage we find ok = rabbit_file:recursive_delete(filelib:wildcard(dir() ++ "/*")), - ok = rabbit_node_monitor:reset_cluster_status_file(), + ok = rabbit_node_monitor:reset_cluster_status(), ok. %% We need to make sure that we don't end up in a distributed Erlang system with @@ -311,7 +311,7 @@ update_cluster_nodes(DiscoveryNode) -> true -> %% As in `check_consistency/0', we can safely delete the schema %% here, since it'll be replicated from the other nodes mnesia:delete_schema([node()]), - rabbit_node_monitor:write_cluster_status_file(Status), + rabbit_node_monitor:write_cluster_status(Status), init_db_with_mnesia(AllNodes, is_disc_node(), false); false -> throw({error, {inconsistent_cluster, @@ -487,7 +487,7 @@ cluster_status(WhichNodes, ForceMnesia) -> fun() -> running_nodes(AllNodes) end}}; {error, _Reason} when not ForceMnesia -> {AllNodes, DiscNodes, RunningNodes} = - rabbit_node_monitor:read_cluster_status_file(), + rabbit_node_monitor:read_cluster_status(), %% The cluster status file records the status when the node %% is online, but we know for sure that the node is offline %% now, so we can remove it from the list of running nodes. @@ -573,7 +573,7 @@ init_db(ClusterNodes, WantDiscNode, Force) -> end end, ensure_schema_integrity(), - rabbit_node_monitor:update_cluster_status_file(), + rabbit_node_monitor:update_cluster_status(), ok. init_db_and_upgrade(ClusterNodes, WantDiscNode, Force) -> @@ -724,7 +724,7 @@ check_cluster_consistency() -> %% nodes. mnesia:delete_schema([node()]) end, - rabbit_node_monitor:write_cluster_status_file(Status); + rabbit_node_monitor:write_cluster_status(Status); not_found -> ok end. @@ -1174,7 +1174,8 @@ is_virgin_node() -> {error, enoent} -> true; {ok, []} -> true; {ok, [File]} -> (dir() ++ "/" ++ File) =:= - rabbit_node_monitor:cluster_status_file_name(); + [rabbit_node_monitor:cluster_status_file_name(), + rabbit_node_monitor:running_nodes_file_name()]; {ok, _} -> false end. diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index d29408de..b98fa5fd 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -19,11 +19,12 @@ -behaviour(gen_server). -export([cluster_status_file_name/0, - prepare_cluster_status_file/0, - write_cluster_status_file/1, - read_cluster_status_file/0, - update_cluster_status_file/0, - reset_cluster_status_file/0, + running_nodes_file_name/0, + prepare_cluster_status_files/0, + write_cluster_status/1, + read_cluster_status/0, + update_cluster_status/0, + reset_cluster_status/0, joined_cluster/2, notify_joined_cluster/0, @@ -49,12 +50,12 @@ -ifdef(use_specs). -spec(cluster_status_file_name/0 :: () -> string()). --spec(prepare_cluster_status_file/0 :: () -> 'ok'). --spec(write_cluster_status_file/1 :: (rabbit_mnesia:cluster_status()) - -> 'ok'). --spec(read_cluster_status_file/0 :: () -> rabbit_mnesia:cluster_status()). --spec(update_cluster_status_file/0 :: () -> 'ok'). --spec(reset_cluster_status_file/0 :: () -> 'ok'). +-spec(prepare_cluster_status_files/0 :: () -> 'ok'). +-spec(write_cluster_status/1 :: (rabbit_mnesia:cluster_status()) + -> 'ok'). +-spec(read_cluster_status/0 :: () -> rabbit_mnesia:cluster_status()). +-spec(update_cluster_status/0 :: () -> 'ok'). +-spec(reset_cluster_status/0 :: () -> 'ok'). -spec(joined_cluster/2 :: (node(), boolean()) -> 'ok'). -spec(notify_joined_cluster/0 :: () -> 'ok'). @@ -85,70 +86,72 @@ cluster_status_file_name() -> rabbit_mnesia:dir() ++ "/cluster_nodes.config". -prepare_cluster_status_file() -> - NotPresent = - fun (AllNodes0, WantDiscNode) -> - ThisNode = [node()], - - RunningNodes0 = legacy_read_previously_running_nodes(), - legacy_delete_previously_running_nodes(), - - RunningNodes = lists:usort(RunningNodes0 ++ ThisNode), - AllNodes = - lists:usort(AllNodes0 ++ RunningNodes), - DiscNodes = case WantDiscNode of - true -> ThisNode; - false -> [] - end, +running_nodes_file_name() -> + filename:join(rabbit_mnesia:dir(), "nodes_running_at_shutdown"). - ok = write_cluster_status_file({AllNodes, DiscNodes, - RunningNodes}) +prepare_cluster_status_files() -> + RunningNodes1 = case try_read_file(running_nodes_file_name()) of + {ok, [Nodes]} when is_list(Nodes) -> Nodes; + non_existant -> [] + end, + {AllNodes1, WantDiscNode} = + case try_read_file(cluster_status_file_name()) of + {ok, [{_, _}]} -> + ok; + {ok, [AllNodes0]} when is_list(AllNodes0) -> + {AllNodes0, legacy_should_be_disc_node(AllNodes0)}; + non_existant -> + {[], true} end, - case try_read_cluster_status_file() of - {ok, _} -> - ok; - {error, {invalid_term, _, [AllNodes]}} -> - %% Legacy file - NotPresent(AllNodes, legacy_should_be_disc_node(AllNodes)); - {error, {cannot_read_file, _, enoent}} -> - NotPresent([], true) - end. - -write_cluster_status_file(Status) -> - FileName = cluster_status_file_name(), - case rabbit_file:write_term_file(FileName, [Status]) of - ok -> ok; - {error, Reason} -> - throw({error, {cannot_write_cluster_status_file, - FileName, Reason}}) + ThisNode = [node()], + + RunningNodes2 = lists:usort(RunningNodes1 ++ ThisNode), + AllNodes2 = lists:usort(AllNodes1 ++ RunningNodes2), + DiscNodes = case WantDiscNode of + true -> ThisNode; + false -> [] + end, + + ok = write_cluster_status({AllNodes2, DiscNodes, RunningNodes2}). + +write_cluster_status({All, Disc, Running}) -> + ClusterStatusFN = cluster_status_file_name(), + Res = case rabbit_file:write_term_file(ClusterStatusFN, [{All, Disc}]) of + ok -> + RunningNodesFN = running_nodes_file_name(), + {RunningNodesFN, + rabbit_file:write_term_file(RunningNodesFN, [Running])}; + E1 = {error, _} -> + {ClusterStatusFN, E1} + end, + case Res of + {_, ok} -> ok; + {FN, {error, E2}} -> throw({error, {could_not_write_file, FN, E2}}) end. -try_read_cluster_status_file() -> - FileName = cluster_status_file_name(), +try_read_file(FileName) -> case rabbit_file:read_term_file(FileName) of - {ok, [{_, _, _} = Status]} -> - {ok, Status}; - {ok, Term} -> - {error, {invalid_term, FileName, Term}}; - {error, Reason} -> - {error, {cannot_read_file, FileName, Reason}} + {ok, Term} -> {ok, Term}; + {error, enoent} -> non_existant; + {error, E} -> throw({error, {cannot_read_file, FileName, E}}) end. -read_cluster_status_file() -> - case try_read_cluster_status_file() of - {ok, Status} -> - Status; - {error, Reason} -> - throw({error, {cannot_read_cluster_status_file, Reason}}) +read_cluster_status() -> + case {try_read_file(cluster_status_file_name()), + try_read_file(running_nodes_file_name())} of + {{ok, [{All, Disc}]}, {ok, [Running]}} when is_list(Running) -> + {All, Disc, Running}; + {_, _} -> + throw({error, corrupt_or_missing_cluster_files}) end. -update_cluster_status_file() -> +update_cluster_status() -> {ok, Status} = rabbit_mnesia:cluster_status_from_mnesia(), - write_cluster_status_file(Status). + write_cluster_status(Status). -reset_cluster_status_file() -> - write_cluster_status_file({[node()], [node()], [node()]}). +reset_cluster_status() -> + write_cluster_status({[node()], [node()], [node()]}). %%---------------------------------------------------------------------------- %% Cluster notifications @@ -198,42 +201,42 @@ handle_cast({node_up, Node, IsDiscNode}, State) -> case is_already_monitored({rabbit, Node}) of true -> {noreply, State}; false -> rabbit_log:info("rabbit on node ~p up~n", [Node]), - {AllNodes, DiscNodes, RunningNodes} = read_cluster_status_file(), - write_cluster_status_file( - {ordsets:add_element(Node, AllNodes), - case IsDiscNode of - true -> ordsets:add_element(Node, DiscNodes); - false -> DiscNodes - end, - ordsets:add_element(Node, RunningNodes)}), + {AllNodes, DiscNodes, RunningNodes} = read_cluster_status(), + write_cluster_status({ordsets:add_element(Node, AllNodes), + case IsDiscNode of + true -> ordsets:add_element( + Node, DiscNodes); + false -> DiscNodes + end, + ordsets:add_element(Node, RunningNodes)}), erlang:monitor(process, {rabbit, Node}), ok = handle_live_rabbit(Node), {noreply, State} end; handle_cast({joined_cluster, Node, IsDiscNode}, State) -> - {AllNodes, DiscNodes, RunningNodes} = read_cluster_status_file(), - write_cluster_status_file({ordsets:add_element(Node, AllNodes), - case IsDiscNode of - true -> ordsets:add_element(Node, - DiscNodes); - false -> DiscNodes - end, - RunningNodes}), + {AllNodes, DiscNodes, RunningNodes} = read_cluster_status(), + write_cluster_status({ordsets:add_element(Node, AllNodes), + case IsDiscNode of + true -> ordsets:add_element(Node, + DiscNodes); + false -> DiscNodes + end, + RunningNodes}), {noreply, State}; handle_cast({left_cluster, Node}, State) -> - {AllNodes, DiscNodes, RunningNodes} = read_cluster_status_file(), - write_cluster_status_file({ordsets:del_element(Node, AllNodes), - ordsets:del_element(Node, DiscNodes), - ordsets:del_element(Node, RunningNodes)}), + {AllNodes, DiscNodes, RunningNodes} = read_cluster_status(), + write_cluster_status({ordsets:del_element(Node, AllNodes), + ordsets:del_element(Node, DiscNodes), + ordsets:del_element(Node, RunningNodes)}), {noreply, State}; handle_cast(_Msg, State) -> {noreply, State}. handle_info({'DOWN', _MRef, process, {rabbit, Node}, _Reason}, State) -> rabbit_log:info("rabbit on node ~p down~n", [Node]), - {AllNodes, DiscNodes, RunningNodes} = read_cluster_status_file(), - write_cluster_status_file({AllNodes, DiscNodes, - ordsets:del_element(Node, RunningNodes)}), + {AllNodes, DiscNodes, RunningNodes} = read_cluster_status(), + write_cluster_status({AllNodes, DiscNodes, + ordsets:del_element(Node, RunningNodes)}), ok = handle_dead_rabbit(Node), {noreply, State}; handle_info(_Info, State) -> @@ -285,28 +288,3 @@ is_already_monitored(Item) -> legacy_should_be_disc_node(DiscNodes) -> DiscNodes == [] orelse lists:member(node(), DiscNodes). - -%%-------------------------------------------------------------------- -%% Legacy functions related to the "running nodes" file -%%-------------------------------------------------------------------- - -legacy_running_nodes_filename() -> - filename:join(rabbit_mnesia:dir(), "nodes_running_at_shutdown"). - -legacy_read_previously_running_nodes() -> - FileName = legacy_running_nodes_filename(), - case rabbit_file:read_term_file(FileName) of - {ok, [Nodes]} -> Nodes; - {error, enoent} -> []; - {error, Reason} -> throw({error, {cannot_read_previous_nodes_file, - FileName, Reason}}) - end. - -legacy_delete_previously_running_nodes() -> - FileName = legacy_running_nodes_filename(), - case file:delete(FileName) of - ok -> ok; - {error, enoent} -> ok; - {error, Reason} -> throw({error, {cannot_delete_previous_nodes_file, - FileName, Reason}}) - end. -- cgit v1.2.1 From 2c84bf988c1a842aca763b4bcff6ff9aed5aaab4 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Tue, 11 Sep 2012 18:49:21 +0100 Subject: oops --- src/rabbit_node_monitor.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index b98fa5fd..c5c70708 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -96,8 +96,8 @@ prepare_cluster_status_files() -> end, {AllNodes1, WantDiscNode} = case try_read_file(cluster_status_file_name()) of - {ok, [{_, _}]} -> - ok; + {ok, [{AllNodes, DiscNodes0}]} -> + {AllNodes, lists:member(node(), DiscNodes0)}; {ok, [AllNodes0]} when is_list(AllNodes0) -> {AllNodes0, legacy_should_be_disc_node(AllNodes0)}; non_existant -> -- cgit v1.2.1 From c744654c7db8426329c9f75ec9527431e01862bc Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Tue, 11 Sep 2012 18:56:13 +0100 Subject: do not give up when finding an inconsistent node --- src/rabbit_mnesia.erl | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 31672512..aaff219b 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -705,9 +705,10 @@ wait_for_tables(TableNames) -> check_cluster_consistency() -> %% We want to find 0 or 1 consistent nodes. case lists:foldl( - fun (Node, not_found) -> check_cluster_consistency(Node); + fun (Node, {error, _}) -> check_cluster_consistency(Node); (_Node, {ok, Status}) -> {ok, Status} - end, not_found, ordsets:del_element(node(), all_clustered_nodes())) + end, {error, not_found}, + ordsets:del_element(node(), all_clustered_nodes())) of {ok, Status = {RemoteAllNodes, _, _}} -> case ordsets:is_subset(all_clustered_nodes(), RemoteAllNodes) of @@ -725,19 +726,21 @@ check_cluster_consistency() -> mnesia:delete_schema([node()]) end, rabbit_node_monitor:write_cluster_status(Status); - not_found -> - ok + {error, not_found} -> + ok; + E = {error, _} -> + throw(E) end. check_cluster_consistency(Node) -> case rpc:call(Node, rabbit_mnesia, node_info, []) of {badrpc, _Reason} -> - not_found; + {error, not_found}; {_OTP, _Rabbit, {error, _}} -> - not_found; + {error, not_found}; {OTP, Rabbit, {ok, Status}} -> case check_consistency(OTP, Rabbit, Node, Status) of - {error, Error} -> throw({error, Error}); + E = {error, _} -> E; {ok, Res} -> {ok, Res} end end. -- cgit v1.2.1 From 792206ceeb27a39b96413f9dc380120910f937a5 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Wed, 12 Sep 2012 10:52:44 +0100 Subject: more informative and useful error messages --- src/rabbit_node_monitor.erl | 4 ++++ src/rabbit_upgrade.erl | 10 ++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index c5c70708..0d3423c9 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -90,8 +90,10 @@ running_nodes_file_name() -> filename:join(rabbit_mnesia:dir(), "nodes_running_at_shutdown"). prepare_cluster_status_files() -> + CorruptFiles = fun () -> throw({error, corrupt_cluster_status_files}) end, RunningNodes1 = case try_read_file(running_nodes_file_name()) of {ok, [Nodes]} when is_list(Nodes) -> Nodes; + {ok, _ } -> CorruptFiles(); non_existant -> [] end, {AllNodes1, WantDiscNode} = @@ -100,6 +102,8 @@ prepare_cluster_status_files() -> {AllNodes, lists:member(node(), DiscNodes0)}; {ok, [AllNodes0]} when is_list(AllNodes0) -> {AllNodes0, legacy_should_be_disc_node(AllNodes0)}; + {ok, _} -> + CorruptFiles(); non_existant -> {[], true} end, diff --git a/src/rabbit_upgrade.erl b/src/rabbit_upgrade.erl index c43ce236..3fbfeed0 100644 --- a/src/rabbit_upgrade.erl +++ b/src/rabbit_upgrade.erl @@ -152,12 +152,14 @@ upgrade_mode(AllNodes) -> {true, []} -> primary; {true, _} -> - %% TODO: Here I'm assuming that the various cluster status - %% files are consistent with each other, I think I can - %% provide a solution if they're not... + Filename = rabbit_node_monitor:running_nodes_filename(), die("Cluster upgrade needed but other disc nodes shut " "down after this one.~nPlease first start the last " - "disc node to shut down.~n", []); + "disc node to shut down.~n~nNote: if several disc " + "nodes were shut down simultaneously they may " + "all~nshow this message. In which case, remove " + "the lock file on one of them and~nstart that node. " + "The lock file on this node is:~n~n ~s ", [Filename]); {false, _} -> die("Cluster upgrade needed but this is a ram node.~n" "Please first start the last disc node to shut down.", -- cgit v1.2.1 From fdc19fcfe80e2342c63641d7715157e5e02f7268 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Wed, 12 Sep 2012 13:24:24 +0100 Subject: always call init_db with all nodes --- src/rabbit_mnesia.erl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index aaff219b..7801b4bd 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -182,10 +182,10 @@ join_cluster(DiscoveryNode, WantDiscNode) -> ensure_mnesia_not_running(), ensure_mnesia_dir(), - {ClusterNodes, DiscNodes, _} = case discover_cluster(DiscoveryNode) of - {ok, Res} -> Res; - {error, Reason} -> throw({error, Reason}) - end, + {ClusterNodes, _, _} = case discover_cluster(DiscoveryNode) of + {ok, Res} -> Res; + E = {error, _} -> throw(E) + end, case lists:member(node(), ClusterNodes) of true -> throw({error, {already_clustered, @@ -203,7 +203,7 @@ join_cluster(DiscoveryNode, WantDiscNode) -> rabbit_misc:local_info_msg("Clustering with ~p~n", [ClusterNodes]), %% Join the cluster - ok = init_db_with_mnesia(DiscNodes, WantDiscNode, false), + ok = init_db_with_mnesia(ClusterNodes, WantDiscNode, false), rabbit_node_monitor:notify_joined_cluster(), @@ -1113,7 +1113,7 @@ change_extra_db_nodes(ClusterNodes0, Force) -> case mnesia:change_config(extra_db_nodes, ClusterNodes) of {ok, []} when not Force andalso ClusterNodes =/= [] -> throw({error, {failed_to_cluster_with, ClusterNodes, - "Mnesia could not connect to any disc nodes."}}); + "Mnesia could not connect to any nodes."}}); {ok, Nodes} -> Nodes end. -- cgit v1.2.1 From de8ebece1efd57e697ffe0a444c2c98ea64331cb Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Wed, 12 Sep 2012 17:35:46 +0100 Subject: pick the lowest between the message and queue TTL --- src/rabbit_amqqueue_process.erl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 66ffb156..38d85f79 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -713,9 +713,11 @@ calculate_msg_expiry(#basic_message{content = Content}, TTL) -> #content{properties = Props} = rabbit_binary_parser:ensure_content_decoded(Content), %% We assert that the expiration must be valid - we check in che channel. - Milli = case rabbit_basic:parse_expiration(Props) of - {ok, undefined} -> TTL; - {ok, N } -> N + Milli = case {rabbit_basic:parse_expiration(Props), TTL} of + {{ok, undefined}, _ } -> TTL; + {{ok, N }, undefined} -> N; + {{ok, N }, M } when N < M -> N; + {{ok, N }, M } when M < N -> M end, case Milli of undefined -> undefined; -- cgit v1.2.1 From c81cdfcdde01cee96735d3d26b1a3dc1e97d0da1 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Thu, 13 Sep 2012 12:18:55 +0100 Subject: add mochi license information --- LICENSE | 3 +++ 1 file changed, 3 insertions(+) diff --git a/LICENSE b/LICENSE index 89640485..9feeceac 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,8 @@ This package, the RabbitMQ server is licensed under the MPL. For the MPL, please see LICENSE-MPL-RabbitMQ. +The files `mochijson2.erl' and `mochinum.erl' are (c) 2007 Mochi Media, Inc and +licensed under a MIT license, see LICENSE-MIT-Mochi. + If you have any questions regarding licensing, please contact us at info@rabbitmq.com. -- cgit v1.2.1 From 4233031e8189bb4058502ab0cfa699e5fec05bb8 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Thu, 13 Sep 2012 13:18:46 +0100 Subject: check the cluster consistency after the upgrade --- src/rabbit.erl | 9 +++++++-- src/rabbit_mnesia.erl | 12 ++++-------- src/rabbit_node_monitor.erl | 1 + 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index f7953c55..7a021e37 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -300,7 +300,8 @@ start() -> %% We do not want to HiPE compile or upgrade %% mnesia after just restarting the app ok = ensure_application_loaded(), - ok = rabbit_mnesia:prepare(), + ok = rabbit_node_monitor:prepare_cluster_status_files(), + ok = rabbit_mnesia:check_cluster_consistency(), ok = ensure_working_log_handlers(), ok = app_utils:start_applications(app_startup_order()), ok = print_plugin_info(rabbit_plugins:active()) @@ -310,9 +311,13 @@ boot() -> start_it(fun() -> ok = ensure_application_loaded(), maybe_hipe_compile(), - ok = rabbit_mnesia:prepare(), + ok = rabbit_node_monitor:prepare_cluster_status_files(), ok = ensure_working_log_handlers(), ok = rabbit_upgrade:maybe_upgrade_mnesia(), + %% It's important that the consistency check happens after + %% the upgrade, since if we are a secondary node the + %% primary node will have forgotten us + ok = rabbit_mnesia:check_cluster_consistency(), Plugins = rabbit_plugins:setup(), ToBeLoaded = Plugins ++ ?APPS, ok = app_utils:load_applications(ToBeLoaded), diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 7801b4bd..e1a68fc7 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -17,8 +17,7 @@ -module(rabbit_mnesia). --export([prepare/0, - init/0, +-export([init/0, join_cluster/2, reset/0, force_reset/0, @@ -42,6 +41,8 @@ empty_ram_only_tables/0, copy_db/1, wait_for_tables/0, + check_cluster_consistency/0, + ensure_mnesia_dir/0, on_node_up/1, on_node_down/1 @@ -70,7 +71,6 @@ ordsets:ordset(node())}). %% Main interface --spec(prepare/0 :: () -> 'ok'). -spec(init/0 :: () -> 'ok'). -spec(join_cluster/2 :: ([node()], boolean()) -> 'ok'). -spec(reset/0 :: () -> 'ok'). @@ -100,6 +100,7 @@ -spec(copy_db/1 :: (file:filename()) -> rabbit_types:ok_or_error(any())). -spec(wait_for_tables/1 :: ([atom()]) -> 'ok'). -spec(check_cluster_consistency/0 :: () -> 'ok'). +-spec(ensure_mnesia_dir/0 :: () -> 'ok'). %% Hooks used in `rabbit_node_monitor' -spec(on_node_up/1 :: (node()) -> 'ok'). @@ -117,11 +118,6 @@ %% Main interface %%---------------------------------------------------------------------------- -prepare() -> - ensure_mnesia_dir(), - rabbit_node_monitor:prepare_cluster_status_files(), - check_cluster_consistency(). - init() -> ensure_mnesia_running(), ensure_mnesia_dir(), diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index 0d3423c9..397032a4 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -90,6 +90,7 @@ running_nodes_file_name() -> filename:join(rabbit_mnesia:dir(), "nodes_running_at_shutdown"). prepare_cluster_status_files() -> + rabbit_mnesia:ensure_mnesia_dir(), CorruptFiles = fun () -> throw({error, corrupt_cluster_status_files}) end, RunningNodes1 = case try_read_file(running_nodes_file_name()) of {ok, [Nodes]} when is_list(Nodes) -> Nodes; -- cgit v1.2.1 From 2f7f8b83f592d9b71b34cc1fc258d04e5f6563f2 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Thu, 13 Sep 2012 15:41:42 +0100 Subject: get cluster nodes from mnesia when legacy/non existant status file This is necessary because the upgrade process needs to remove the replicas of the schemas. --- src/rabbit_node_monitor.erl | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index 397032a4..b2d7bb60 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -102,11 +102,12 @@ prepare_cluster_status_files() -> {ok, [{AllNodes, DiscNodes0}]} -> {AllNodes, lists:member(node(), DiscNodes0)}; {ok, [AllNodes0]} when is_list(AllNodes0) -> - {AllNodes0, legacy_should_be_disc_node(AllNodes0)}; + {legacy_cluster_nodes(AllNodes0), + legacy_should_be_disc_node(AllNodes0)}; {ok, _} -> CorruptFiles(); non_existant -> - {[], true} + {legacy_cluster_nodes([]), true} end, ThisNode = [node()], @@ -291,5 +292,10 @@ is_already_monitored(Item) -> (_) -> false end, Monitors). +legacy_cluster_nodes(Nodes) -> + %% We get all the info that we can, including the nodes from mnesia, which + %% will be there if the node is a disc node (empty list otherwise) + lists:usort(Nodes ++ mnesia:system_info(db_nodes)). + legacy_should_be_disc_node(DiscNodes) -> DiscNodes == [] orelse lists:member(node(), DiscNodes). -- cgit v1.2.1 From 6d1cb8049cc987838d441c1eb969178a09f949d0 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 13 Sep 2012 16:01:10 +0100 Subject: Remove the Mnesia record after we are done with it. This requires a certain amount of churn - we need the queue name to remove the GM group, but the VQ does not know it. So we end up asking the GM (of all people!) --- src/gm.erl | 33 ++++++++++++++++++++++++--------- src/rabbit_mirror_queue_master.erl | 5 ++++- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/src/gm.erl b/src/gm.erl index f88ed18f..90433e84 100644 --- a/src/gm.erl +++ b/src/gm.erl @@ -77,9 +77,13 @@ %% confirmed_broadcast/2 directly from the callback module otherwise %% you will deadlock the entire group. %% -%% group_members/1 -%% Provide the Pid. Returns a list of the current group members. +%% info/1 +%% Provide the Pid. Returns a proplist with various facts, including +%% the group name and the current group members. %% +%% forget_group/1 +%% Provide the group name. Removes its mnesia record. Makes no attempt +%% to ensure the group is empty. %% %% Implementation Overview %% ----------------------- @@ -373,7 +377,7 @@ -behaviour(gen_server2). -export([create_tables/0, start_link/3, leave/1, broadcast/2, - confirmed_broadcast/2, group_members/1]). + confirmed_broadcast/2, info/1, forget_group/1]). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3, prioritise_info/2]). @@ -431,7 +435,8 @@ -spec(leave/1 :: (pid()) -> 'ok'). -spec(broadcast/2 :: (pid(), any()) -> 'ok'). -spec(confirmed_broadcast/2 :: (pid(), any()) -> 'ok'). --spec(group_members/1 :: (pid()) -> [pid()]). +-spec(info/1 :: (pid()) -> rabbit_types:infos()). +-spec(forget_group/1 :: (group_name()) -> 'ok'). %% The joined, members_changed and handle_msg callbacks can all return %% any of the following terms: @@ -514,9 +519,15 @@ broadcast(Server, Msg) -> confirmed_broadcast(Server, Msg) -> gen_server2:call(Server, {confirmed_broadcast, Msg}, infinity). -group_members(Server) -> - gen_server2:call(Server, group_members, infinity). +info(Server) -> + gen_server2:call(Server, info, infinity). +forget_group(GroupName) -> + {atomic, ok} = mnesia:sync_transaction( + fun () -> + mnesia:delete({?GROUP_TABLE, GroupName}) + end), + ok. init([GroupName, Module, Args]) -> {MegaSecs, Secs, MicroSecs} = now(), @@ -553,12 +564,16 @@ handle_call({confirmed_broadcast, Msg}, _From, handle_call({confirmed_broadcast, Msg}, From, State) -> internal_broadcast(Msg, From, State); -handle_call(group_members, _From, +handle_call(info, _From, State = #state { members_state = undefined }) -> reply(not_joined, State); -handle_call(group_members, _From, State = #state { view = View }) -> - reply(get_pids(alive_view_members(View)), State); +handle_call(info, _From, State = #state { group_name = GroupName, + module = Module, + view = View }) -> + reply([{group_name, GroupName}, + {module, Module}, + {group_members, get_pids(alive_view_members(View))}], State); handle_call({add_on_right, _NewMember}, _From, State = #state { members_state = undefined }) -> diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index 477449e3..bb05f60b 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -127,10 +127,13 @@ terminate(Reason, delete_and_terminate(Reason, State = #state { gm = GM, backing_queue = BQ, backing_queue_state = BQS }) -> - Slaves = [Pid || Pid <- gm:group_members(GM), node(Pid) =/= node()], + Info = gm:info(GM), + Slaves = [Pid || Pid <- proplists:get_value(group_members, Info), + node(Pid) =/= node()], MRefs = [erlang:monitor(process, S) || S <- Slaves], ok = gm:broadcast(GM, {delete_and_terminate, Reason}), monitor_wait(MRefs), + ok = gm:forget_group(proplists:get_value(group_name, Info)), State #state { backing_queue_state = BQ:delete_and_terminate(Reason, BQS), set_delivered = 0 }. -- cgit v1.2.1 From 6a5d5733a1254472eefb5a69bc946ccfb94ca33f Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Thu, 13 Sep 2012 17:48:36 +0100 Subject: updated the essay to explain how the sync status works now --- src/rabbit_mirror_queue_coordinator.erl | 44 +++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/src/rabbit_mirror_queue_coordinator.erl b/src/rabbit_mirror_queue_coordinator.erl index 10debb0b..4455b441 100644 --- a/src/rabbit_mirror_queue_coordinator.erl +++ b/src/rabbit_mirror_queue_coordinator.erl @@ -132,25 +132,31 @@ %% gm should be processed as normal, but fetches which are for %% messages the slave has never seen should be ignored. Similarly, %% acks for messages the slave never fetched should be -%% ignored. Eventually, as the master is consumed from, the messages -%% at the head of the queue which were there before the slave joined -%% will disappear, and the slave will become fully synced with the -%% state of the master. The detection of the sync-status of a slave is -%% done entirely based on length: if the slave and the master both -%% agree on the length of the queue after the fetch of the head of the -%% queue (or a 'set_length' results in a slave having to drop some -%% messages from the head of its queue), then the queues must be in -%% sync. The only other possibility is that the slave's queue is -%% shorter, and thus the fetch should be ignored. In case slaves are -%% joined to an empty queue which only goes on to receive publishes, -%% they start by asking the master to broadcast its length. This is -%% enough for slaves to always be able to work out when their head -%% does not differ from the master (and is much simpler and cheaper -%% than getting the master to hang on to the guid of the msg at the -%% head of its queue). When a slave is promoted to a master, it -%% unilaterally broadcasts its length, in order to solve the problem -%% of length requests from new slaves being unanswered by a dead -%% master. +%% ignored. Similarly, we don't republish rejected messages that we +%% haven't seen. Eventually, as the master is consumed from, the +%% messages at the head of the queue which were there before the slave +%% joined will disappear, and the slave will become fully synced with +%% the state of the master. +%% +%% The detection of the sync-status is based on the depth of the BQs, +%% where the depth is defined as the sum of the length of the BQ (as +%% per BQ:len) and the messages pending an acknowledgement. When the +%% depth of the slave is equal to the master's, then the slave is +%% synchronised. We only store the difference between the two for +%% simplicity. Comparing the length is not enough since we need to +%% take into account rejected messages which will make it back into +%% the master queue but can't go back in the slave, since we don't +%% want "holes" in the slave queue. Note that the depth, and the +%% length likewise, must always be shorter on the slave - we assert +%% that in various places. In case slaves are joined to an empty queue +%% which only goes on to receive publishes, they start by asking the +%% master to broadcast its depth. This is enough for slaves to always +%% be able to work out when their head does not differ from the master +%% (and is much simpler and cheaper than getting the master to hang on +%% to the guid of the msg at the head of its queue). When a slave is +%% promoted to a master, it unilaterally broadcasts its length, in +%% order to solve the problem of length requests from new slaves being +%% unanswered by a dead master. %% %% Obviously, due to the async nature of communication across gm, the %% slaves can fall behind. This does not matter from a sync pov: if -- cgit v1.2.1 From c7d2862d2dbb50f67ff6d8da22ac51adac2ff120 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Fri, 14 Sep 2012 10:59:58 +0100 Subject: fix runtime parameters test, and little bug with them --- src/rabbit_runtime_parameters.erl | 3 ++- src/rabbit_tests.erl | 15 +++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/rabbit_runtime_parameters.erl b/src/rabbit_runtime_parameters.erl index b932f122..b58b459a 100644 --- a/src/rabbit_runtime_parameters.erl +++ b/src/rabbit_runtime_parameters.erl @@ -208,7 +208,8 @@ lookup_component(Component) -> end. format(Term) -> - list_to_binary(rabbit_misc:json_encode(rabbit_misc:term_to_json(Term))). + {ok, JSON} = rabbit_misc:json_encode(rabbit_misc:term_to_json(Term)), + list_to_binary(JSON). flatten_errors(L) -> case [{F, A} || I <- lists:flatten([L]), {error, F, A} <- [I]] of diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index e1914ac2..4a6627de 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -1152,22 +1152,21 @@ test_runtime_parameters() -> Bad = fun(L) -> {error_string, _} = control_action(set_parameter, L) end, %% Acceptable for bijection - Good(["test", "good", "<<\"ignore\">>"]), + Good(["test", "good", "\"ignore\""]), Good(["test", "good", "123"]), Good(["test", "good", "true"]), Good(["test", "good", "false"]), Good(["test", "good", "null"]), - Good(["test", "good", "[{<<\"key\">>, <<\"value\">>}]"]), + Good(["test", "good", "{\"key\": \"value\"}"]), - %% Various forms of fail due to non-bijectability + %% Invalid json Bad(["test", "good", "atom"]), - Bad(["test", "good", "{tuple, foo}"]), - Bad(["test", "good", "[{<<\"key\">>, <<\"value\">>, 1}]"]), - Bad(["test", "good", "[{key, <<\"value\">>}]"]), + Bad(["test", "good", "{\"foo\": \"bar\""]), + Bad(["test", "good", "{foo: \"bar\"}"]), %% Test actual validation hook - Good(["test", "maybe", "<<\"good\">>"]), - Bad(["test", "maybe", "<<\"bad\">>"]), + Good(["test", "maybe", "\"good\""]), + Bad(["test", "maybe", "\"bad\""]), ok = control_action(list_parameters, []), -- cgit v1.2.1 From a67d4fe6781a3b1bfbe99d11bc4d5ca78ebbf14e Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 14 Sep 2012 12:05:10 +0100 Subject: Variant of {start,stop}_applications that takes an error handler. --- src/app_utils.erl | 41 +++++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/src/app_utils.erl b/src/app_utils.erl index 4bef83a5..fdf6ed41 100644 --- a/src/app_utils.erl +++ b/src/app_utils.erl @@ -15,17 +15,21 @@ %% -module(app_utils). --export([load_applications/1, start_applications/1, - stop_applications/1, app_dependency_order/2, +-export([load_applications/1, start_applications/1, start_applications/2, + stop_applications/1, stop_applications/2, app_dependency_order/2, wait_for_applications/1]). -ifdef(use_specs). --spec load_applications([atom()]) -> 'ok'. --spec start_applications([atom()]) -> 'ok'. --spec stop_applications([atom()]) -> 'ok'. --spec wait_for_applications([atom()]) -> 'ok'. --spec app_dependency_order([atom()], boolean()) -> [digraph:vertex()]. +-type error_handler() :: fun((atom(), any()) -> 'ok'). + +-spec load_applications([atom()]) -> 'ok'. +-spec start_applications([atom()]) -> 'ok'. +-spec stop_applications([atom()]) -> 'ok'. +-spec start_applications([atom()], error_handler()) -> 'ok'. +-spec stop_applications([atom()], error_handler()) -> 'ok'. +-spec wait_for_applications([atom()]) -> 'ok'. +-spec app_dependency_order([atom()], boolean()) -> [digraph:vertex()]. -endif. @@ -37,21 +41,34 @@ load_applications(Apps) -> ok. start_applications(Apps) -> + start_applications( + Apps, fun (App, Reason) -> + throw({error, {cannot_start_application, App, Reason}}) + end). + +stop_applications(Apps) -> + stop_applications( + Apps, fun (App, Reason) -> + throw({error, {cannot_stop_application, App, Reason}}) + end). + +start_applications(Apps, ErrorHandler) -> manage_applications(fun lists:foldl/3, fun application:start/1, fun application:stop/1, already_started, - cannot_start_application, + ErrorHandler, Apps). -stop_applications(Apps) -> +stop_applications(Apps, ErrorHandler) -> manage_applications(fun lists:foldr/3, fun application:stop/1, fun application:start/1, not_started, - cannot_stop_application, + ErrorHandler, Apps). + wait_for_applications(Apps) -> [wait_for_application(App) || App <- Apps], ok. @@ -107,14 +124,14 @@ app_dependencies(App) -> {ok, Lst} -> Lst end. -manage_applications(Iterate, Do, Undo, SkipError, ErrorTag, Apps) -> +manage_applications(Iterate, Do, Undo, SkipError, ErrorHandler, Apps) -> Iterate(fun (App, Acc) -> case Do(App) of ok -> [App | Acc]; {error, {SkipError, _}} -> Acc; {error, Reason} -> lists:foreach(Undo, Acc), - throw({error, {ErrorTag, App, Reason}}) + ErrorHandler(App, Reason) end end, [], Apps), ok. -- cgit v1.2.1 From 48a77b93a2ef54deb201fcc3dc085239601cd03e Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 14 Sep 2012 12:06:08 +0100 Subject: Try to handle startup errors in apps that aren't rabbit at least vaguely intelligently. --- src/rabbit.erl | 42 +++++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index ed258c71..6fa1a12a 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -301,7 +301,8 @@ start() -> %% mnesia after just restarting the app ok = ensure_application_loaded(), ok = ensure_working_log_handlers(), - ok = app_utils:start_applications(app_startup_order()), + ok = app_utils:start_applications( + app_startup_order(), fun handle_app_error/2), ok = print_plugin_info(rabbit_plugins:active()) end). @@ -316,10 +317,17 @@ boot() -> ok = app_utils:load_applications(ToBeLoaded), StartupApps = app_utils:app_dependency_order(ToBeLoaded, false), - ok = app_utils:start_applications(StartupApps), + ok = app_utils:start_applications( + StartupApps, fun handle_app_error/2), ok = print_plugin_info(Plugins) end). +handle_app_error(App, {bad_return, {_MFA, {'EXIT', {Reason, _}}}}) -> + boot_error({could_not_start, App, Reason}, not_available); + +handle_app_error(App, Reason) -> + boot_error({could_not_start, App, Reason}, not_available). + start_it(StartFun) -> try StartFun() @@ -444,7 +452,7 @@ run_boot_step({StepName, Attributes}) -> [try apply(M,F,A) catch - _:Reason -> boot_step_error(Reason, erlang:get_stacktrace()) + _:Reason -> boot_error(Reason, erlang:get_stacktrace()) end || {M,F,A} <- MFAs], io:format("done~n"), ok @@ -483,14 +491,14 @@ sort_boot_steps(UnsortedSteps) -> {mfa, {M,F,A}} <- Attributes, not erlang:function_exported(M, F, length(A))] of [] -> SortedSteps; - MissingFunctions -> boot_error( + MissingFunctions -> basic_boot_error( "Boot step functions not exported: ~p~n", [MissingFunctions]) end; {error, {vertex, duplicate, StepName}} -> - boot_error("Duplicate boot step name: ~w~n", [StepName]); + basic_boot_error("Duplicate boot step name: ~w~n", [StepName]); {error, {edge, Reason, From, To}} -> - boot_error( + basic_boot_error( "Could not add boot step dependency of ~w on ~w:~n~s", [To, From, case Reason of @@ -504,7 +512,7 @@ sort_boot_steps(UnsortedSteps) -> end]) end. -boot_step_error({error, {timeout_waiting_for_tables, _}}, _Stacktrace) -> +boot_error({error, {timeout_waiting_for_tables, _}}, _Stacktrace) -> {Err, Nodes} = case rabbit_mnesia:read_previously_running_nodes() of [] -> {"Timeout contacting cluster nodes. Since RabbitMQ was" @@ -515,15 +523,19 @@ boot_step_error({error, {timeout_waiting_for_tables, _}}, _Stacktrace) -> "Timeout contacting cluster nodes: ~p.~n", [Ns]), Ns} end, - boot_error(Err ++ rabbit_nodes:diagnostics(Nodes) ++ "~n~n", []); - -boot_step_error(Reason, Stacktrace) -> - boot_error("Error description:~n ~p~n~n" - "Log files (may contain more information):~n ~s~n ~s~n~n" - "Stack trace:~n ~p~n~n", - [Reason, log_location(kernel), log_location(sasl), Stacktrace]). + basic_boot_error(Err ++ rabbit_nodes:diagnostics(Nodes) ++ "~n~n", []); + +boot_error(Reason, Stacktrace) -> + Fmt = "Error description:~n ~p~n~n" + "Log files (may contain more information):~n ~s~n ~s~n~n", + Args = [Reason, log_location(kernel), log_location(sasl)], + case Stacktrace of + not_available -> basic_boot_error(Fmt, Args); + _ -> basic_boot_error(Fmt ++ "Stack trace:~n ~p~n~n", + Args ++ [Stacktrace]) + end. -boot_error(Format, Args) -> +basic_boot_error(Format, Args) -> io:format("~n~nBOOT FAILED~n===========~n~n" ++ Format, Args), error_logger:error_msg(Format, Args), timer:sleep(1000), -- cgit v1.2.1 From 711bafdd87a402c27c75a5eb2b6c140e2dda0fb8 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Fri, 14 Sep 2012 13:38:25 +0100 Subject: fix comment in node_monitor, keep {error, enoent} in `try_read_file' --- src/rabbit_node_monitor.erl | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index b2d7bb60..23b894dc 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -70,16 +70,12 @@ %% Cluster file operations %%---------------------------------------------------------------------------- -%% The cluster node status file contains all we need to know about the cluster: +%% The cluster file information is kept in two files. The "cluster status file" +%% contains all the clustered nodes and the disc nodes. The "running nodes +%% file" contains the currently running nodes or the running nodes at shutdown +%% when the node is down. %% -%% * All the clustered nodes -%% * The disc nodes -%% * The running nodes. -%% -%% If the current node is a disc node it will be included in the disc nodes -%% list. -%% -%% We strive to keep the file up to date and we rely on this assumption in +%% We strive to keep the files up to date and we rely on this assumption in %% various situations. Obviously when mnesia is offline the information we have %% will be outdated, but it can't be otherwise. @@ -95,7 +91,7 @@ prepare_cluster_status_files() -> RunningNodes1 = case try_read_file(running_nodes_file_name()) of {ok, [Nodes]} when is_list(Nodes) -> Nodes; {ok, _ } -> CorruptFiles(); - non_existant -> [] + {error, enoent} -> [] end, {AllNodes1, WantDiscNode} = case try_read_file(cluster_status_file_name()) of @@ -106,7 +102,7 @@ prepare_cluster_status_files() -> legacy_should_be_disc_node(AllNodes0)}; {ok, _} -> CorruptFiles(); - non_existant -> + {error, enoent} -> {legacy_cluster_nodes([]), true} end, @@ -139,7 +135,7 @@ write_cluster_status({All, Disc, Running}) -> try_read_file(FileName) -> case rabbit_file:read_term_file(FileName) of {ok, Term} -> {ok, Term}; - {error, enoent} -> non_existant; + {error, enoent} -> {error, enoent}; {error, E} -> throw({error, {cannot_read_file, FileName, E}}) end. -- cgit v1.2.1 From 36637fa8ef9da75466ff98656f37bd72ff5fd921 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Fri, 14 Sep 2012 15:02:09 +0100 Subject: Tweak docs --- src/file_handle_cache.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/file_handle_cache.erl b/src/file_handle_cache.erl index c3c5ed8e..e1fa3d70 100644 --- a/src/file_handle_cache.erl +++ b/src/file_handle_cache.erl @@ -120,11 +120,11 @@ %% do not need to worry about their handles being closed by the server %% - reopening them when necessary is handled transparently. %% -%% The server also supports obtain, release and transfer. obtain +%% The server also supports obtain, release and transfer. obtain/{0,1} %% blocks until a file descriptor is available, at which point the %% requesting process is considered to 'own' more descriptor(s). -%% release is the inverse operation and releases previously obtained -%% descriptor(s). transfer transfers ownership of file descriptor(s) +%% release/{0,1} is the inverse operation and releases previously obtained +%% descriptor(s). transfer/{1,2} transfers ownership of file descriptor(s) %% between processes. It is non-blocking. Obtain has a %% lower limit, set by the ?OBTAIN_LIMIT/1 macro. File handles can use %% the entire limit, but will be evicted by obtain calls up to the -- cgit v1.2.1 From 7cf07f3020439490d4ef62536a27a58f8fa1fc7f Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 15 Sep 2012 12:27:20 +0100 Subject: guard deletes on disk tables in order to eliminate superfluos fsyncs Unfortunately this makes queue deletion O(binding_count^2), so further work is needed. --- src/rabbit_amqqueue.erl | 7 ++++++- src/rabbit_binding.erl | 14 +++++++++++--- src/rabbit_exchange.erl | 7 ++++++- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index d566ac87..b2473f91 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -578,7 +578,12 @@ flush_all(QPids, ChPid) -> internal_delete1(QueueName) -> ok = mnesia:delete({rabbit_queue, QueueName}), - ok = mnesia:delete({rabbit_durable_queue, QueueName}), + %% this 'guarded' delete prevents unnecessary writes to the mnesia + %% disk log + case mnesia:wread({rabbit_durable_queue, QueueName}) of + [] -> ok; + [_] -> ok = mnesia:delete({rabbit_durable_queue, QueueName}) + end, %% we want to execute some things, as decided by rabbit_exchange, %% after the transaction. rabbit_binding:remove_for_destination(QueueName). diff --git a/src/rabbit_binding.erl b/src/rabbit_binding.erl index f0ea514d..540e221f 100644 --- a/src/rabbit_binding.erl +++ b/src/rabbit_binding.erl @@ -281,17 +281,17 @@ remove_for_source(SrcName) -> mnesia:match_object(rabbit_route, Match, write) ++ mnesia:match_object(rabbit_durable_route, Match, write)), [begin - sync_route(Route, fun mnesia:delete_object/3), + sync_route(Route, fun delete_object/3), Route#route.binding end || Route <- Routes]. remove_for_destination(Dst) -> remove_for_destination( - Dst, fun (R) -> sync_route(R, fun mnesia:delete_object/3) end). + Dst, fun (R) -> sync_route(R, fun delete_object/3) end). remove_transient_for_destination(Dst) -> remove_for_destination( - Dst, fun (R) -> sync_transient_route(R, fun mnesia:delete_object/3) end). + Dst, fun (R) -> sync_transient_route(R, fun delete_object/3) end). %%---------------------------------------------------------------------------- @@ -308,6 +308,14 @@ binding_action(Binding = #binding{source = SrcName, Fun(Src, Dst, Binding#binding{args = SortedArgs}) end). +delete_object(Tab, Record, LockKind) -> + %% this 'guarded' delete prevents unnecessary writes to the mnesia + %% disk log + case mnesia:match_object(Tab, Record, LockKind) of + [] -> ok; + [_] -> mnesia:delete_object(Tab, Record, LockKind) + end. + sync_route(R, Fun) -> sync_route(R, true, true, Fun). sync_route(Route, true, true, Fun) -> diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index 57c571f1..4cc96ef5 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -402,7 +402,12 @@ conditional_delete(X = #exchange{name = XName}) -> end. unconditional_delete(X = #exchange{name = XName}) -> - ok = mnesia:delete({rabbit_durable_exchange, XName}), + %% this 'guarded' delete prevents unnecessary writes to the mnesia + %% disk log + case mnesia:wread({rabbit_durable_exchange, XName}) of + [] -> ok; + [_] -> ok = mnesia:delete({rabbit_durable_exchange, XName}) + end, ok = mnesia:delete({rabbit_exchange, XName}), ok = mnesia:delete({rabbit_exchange_serial, XName}), Bindings = rabbit_binding:remove_for_source(XName), -- cgit v1.2.1 From 82428758641f8ec6d955cb34aade975cdd659d2e Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 15 Sep 2012 13:25:10 +0100 Subject: bring queue/exchange removal cost back down to O(binding_count) by performing all mnesia read operations before writes --- src/rabbit_binding.erl | 50 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/src/rabbit_binding.erl b/src/rabbit_binding.erl index 540e221f..a7730a1a 100644 --- a/src/rabbit_binding.erl +++ b/src/rabbit_binding.erl @@ -277,21 +277,21 @@ has_for_source(SrcName) -> remove_for_source(SrcName) -> lock_route_tables(), Match = #route{binding = #binding{source = SrcName, _ = '_'}}, - Routes = lists:usort( - mnesia:match_object(rabbit_route, Match, write) ++ - mnesia:match_object(rabbit_durable_route, Match, write)), - [begin - sync_route(Route, fun delete_object/3), - Route#route.binding - end || Route <- Routes]. - -remove_for_destination(Dst) -> - remove_for_destination( - Dst, fun (R) -> sync_route(R, fun delete_object/3) end). + remove_routes( + lists:usort(mnesia:match_object(rabbit_route, Match, write) ++ + mnesia:match_object(rabbit_durable_route, Match, write))). + +remove_for_destination(DstName) -> + remove_for_destination(DstName, fun remove_routes/1). -remove_transient_for_destination(Dst) -> +remove_transient_for_destination(DstName) -> remove_for_destination( - Dst, fun (R) -> sync_transient_route(R, fun delete_object/3) end). + DstName, fun (Routes) -> + [begin + ok = sync_transient_route(R, fun delete_object/3), + R#route.binding + end || R <- Routes] + end). %%---------------------------------------------------------------------------- @@ -378,16 +378,26 @@ lock_route_tables() -> rabbit_semi_durable_route, rabbit_durable_route]]. -remove_for_destination(DstName, DeleteFun) -> +remove_routes(Routes) -> + %% This partitioning allows us to suppress unnecessary delete + %% operations on disk tables, which require an fsync. + {TransientRoutes, DurableRoutes} = + lists:partition(fun (R) -> mnesia:match_object( + rabbit_durable_route, R, write) == [] end, + Routes), + [ok = sync_transient_route(R, fun mnesia:delete_object/3) || + R <- TransientRoutes], + [ok = sync_route(R, fun mnesia:delete_object/3) || + R <- DurableRoutes], + [R#route.binding || R <- Routes]. + +remove_for_destination(DstName, Fun) -> lock_route_tables(), Match = reverse_route( #route{binding = #binding{destination = DstName, _ = '_'}}), - ReverseRoutes = mnesia:match_object(rabbit_reverse_route, Match, write), - Bindings = [begin - Route = reverse_route(ReverseRoute), - ok = DeleteFun(Route), - Route#route.binding - end || ReverseRoute <- ReverseRoutes], + Routes = [reverse_route(R) || R <- mnesia:match_object( + rabbit_reverse_route, Match, write)], + Bindings = Fun(Routes), group_bindings_fold(fun maybe_auto_delete/3, new_deletions(), lists:keysort(#binding.source, Bindings)). -- cgit v1.2.1 From 709f2fdd04a9c4c84366c7ea9452f4a0073f6fd2 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 15 Sep 2012 20:55:40 +0100 Subject: simplify and handle missing 'same ttl' case(!) --- src/rabbit_amqqueue_process.erl | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index dc8ae711..2c3cd64d 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -713,15 +713,10 @@ calculate_msg_expiry(#basic_message{content = Content}, TTL) -> #content{properties = Props} = rabbit_binary_parser:ensure_content_decoded(Content), %% We assert that the expiration must be valid - we check in che channel. - Milli = case {rabbit_basic:parse_expiration(Props), TTL} of - {{ok, undefined}, _ } -> TTL; - {{ok, N }, undefined} -> N; - {{ok, N }, M } when N < M -> N; - {{ok, N }, M } when M < N -> M - end, - case Milli of + {ok, MsgTTL} = rabbit_basic:parse_expiration(Props), + case lists:min([TTL, MsgTTL]) of undefined -> undefined; - _ -> now_micros() + Milli * 1000 + T -> now_micros() + T * 1000 end. drop_expired_messages(State = #q{ttl = undefined}) -> -- cgit v1.2.1 From 95de62098bcd2b41d313aa8dbd20c5ebb6498cb3 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 17 Sep 2012 07:01:20 +0100 Subject: correct disk space log message and remove ambiguity --- src/rabbit_disk_monitor.erl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/rabbit_disk_monitor.erl b/src/rabbit_disk_monitor.erl index e72181c0..40193320 100644 --- a/src/rabbit_disk_monitor.erl +++ b/src/rabbit_disk_monitor.erl @@ -148,10 +148,10 @@ internal_update(State = #state { limit = Limit, NewAlarmed = CurrentFreeBytes < LimitBytes, case {Alarmed, NewAlarmed} of {false, true} -> - emit_update_info("exceeded", CurrentFreeBytes, LimitBytes), + emit_update_info("insufficient", CurrentFreeBytes, LimitBytes), rabbit_alarm:set_alarm({{resource_limit, disk, node()}, []}); {true, false} -> - emit_update_info("below limit", CurrentFreeBytes, LimitBytes), + emit_update_info("sufficient", CurrentFreeBytes, LimitBytes), rabbit_alarm:clear_alarm({resource_limit, disk, node()}); _ -> ok @@ -187,10 +187,10 @@ interpret_limit({mem_relative, R}) -> interpret_limit(L) -> L. -emit_update_info(State, CurrentFree, Limit) -> +emit_update_info(StateStr, CurrentFree, Limit) -> rabbit_log:info( - "Disk free space limit now ~s. Free bytes:~p Limit:~p~n", - [State, 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), -- cgit v1.2.1 From 3e1b317a8781fbc643d073604349ba9c09bf9d0e Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 17 Sep 2012 07:49:25 +0100 Subject: 1MB = 1000000B for disks --- src/rabbit_disk_monitor.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_disk_monitor.erl b/src/rabbit_disk_monitor.erl index 40193320..6330d555 100644 --- a/src/rabbit_disk_monitor.erl +++ b/src/rabbit_disk_monitor.erl @@ -137,7 +137,7 @@ dir() -> rabbit_mnesia:dir(). set_disk_limits(State, Limit) -> State1 = State#state { limit = Limit }, rabbit_log:info("Disk free limit set to ~pMB~n", - [trunc(interpret_limit(Limit) / 1048576)]), + [trunc(interpret_limit(Limit) / 1000000)]), internal_update(State1). internal_update(State = #state { limit = Limit, -- cgit v1.2.1 From 27ef3f0d08dd6d1246b16a1886b1218879917de0 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 17 Sep 2012 08:46:04 +0100 Subject: file_name -> filename The latter is used in more places and also by OTP. In the process fix a bug - rabbit_upgrade was referencing rabbit_node_monitor:running_nodes_filename/0, which was in fact named running_nodes_file_name. That function was also missing a spec. And it was in the "wrong" place w.r.t. the function order in the module ;) --- src/rabbit_mnesia.erl | 4 ++-- src/rabbit_msg_store.erl | 10 +++++----- src/rabbit_node_monitor.erl | 30 +++++++++++++++--------------- src/rabbit_queue_index.erl | 8 ++++---- 4 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index e1a68fc7..40600063 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -1173,8 +1173,8 @@ is_virgin_node() -> {error, enoent} -> true; {ok, []} -> true; {ok, [File]} -> (dir() ++ "/" ++ File) =:= - [rabbit_node_monitor:cluster_status_file_name(), - rabbit_node_monitor:running_nodes_file_name()]; + [rabbit_node_monitor:cluster_status_filename(), + rabbit_node_monitor:running_nodes_filename()]; {ok, _} -> false end. diff --git a/src/rabbit_msg_store.erl b/src/rabbit_msg_store.erl index d69dad1f..c2e55022 100644 --- a/src/rabbit_msg_store.erl +++ b/src/rabbit_msg_store.erl @@ -1394,7 +1394,7 @@ filenum_to_name(File) -> integer_to_list(File) ++ ?FILE_EXTENSION. filename_to_num(FileName) -> list_to_integer(filename:rootname(FileName)). -list_sorted_file_names(Dir, Ext) -> +list_sorted_filenames(Dir, Ext) -> lists:sort(fun (A, B) -> filename_to_num(A) < filename_to_num(B) end, filelib:wildcard("*" ++ Ext, Dir)). @@ -1531,8 +1531,8 @@ count_msg_refs(Gen, Seed, State) -> end. recover_crashed_compactions(Dir) -> - FileNames = list_sorted_file_names(Dir, ?FILE_EXTENSION), - TmpFileNames = list_sorted_file_names(Dir, ?FILE_EXTENSION_TMP), + FileNames = list_sorted_filenames(Dir, ?FILE_EXTENSION), + TmpFileNames = list_sorted_filenames(Dir, ?FILE_EXTENSION_TMP), lists:foreach( fun (TmpFileName) -> NonTmpRelatedFileName = @@ -1609,7 +1609,7 @@ build_index(false, {MsgRefDeltaGen, MsgRefDeltaGenInit}, ok = count_msg_refs(MsgRefDeltaGen, MsgRefDeltaGenInit, State), {ok, Pid} = gatherer:start_link(), case [filename_to_num(FileName) || - FileName <- list_sorted_file_names(Dir, ?FILE_EXTENSION)] of + FileName <- list_sorted_filenames(Dir, ?FILE_EXTENSION)] of [] -> build_index(Pid, undefined, [State #msstate.current_file], State); Files -> {Offset, State1} = build_index(Pid, undefined, Files, State), @@ -2023,7 +2023,7 @@ transform_dir(BaseDir, Store, TransformFun) -> CopyFile = fun (Src, Dst) -> {ok, _Bytes} = file:copy(Src, Dst), ok end, case filelib:is_dir(TmpDir) of true -> throw({error, transform_failed_previously}); - false -> FileList = list_sorted_file_names(Dir, ?FILE_EXTENSION), + false -> FileList = list_sorted_filenames(Dir, ?FILE_EXTENSION), foreach_file(Dir, TmpDir, TransformFile, FileList), foreach_file(Dir, fun file:delete/1, FileList), foreach_file(TmpDir, Dir, CopyFile, FileList), diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index 23b894dc..64c801f2 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -18,8 +18,8 @@ -behaviour(gen_server). --export([cluster_status_file_name/0, - running_nodes_file_name/0, +-export([running_nodes_filename/0, + cluster_status_filename/0, prepare_cluster_status_files/0, write_cluster_status/1, read_cluster_status/0, @@ -49,10 +49,10 @@ -ifdef(use_specs). --spec(cluster_status_file_name/0 :: () -> string()). +-spec(running_nodes_filename/0 :: () -> string()). +-spec(cluster_status_filename/0 :: () -> string()). -spec(prepare_cluster_status_files/0 :: () -> 'ok'). --spec(write_cluster_status/1 :: (rabbit_mnesia:cluster_status()) - -> 'ok'). +-spec(write_cluster_status/1 :: (rabbit_mnesia:cluster_status()) -> 'ok'). -spec(read_cluster_status/0 :: () -> rabbit_mnesia:cluster_status()). -spec(update_cluster_status/0 :: () -> 'ok'). -spec(reset_cluster_status/0 :: () -> 'ok'). @@ -79,22 +79,22 @@ %% various situations. Obviously when mnesia is offline the information we have %% will be outdated, but it can't be otherwise. -cluster_status_file_name() -> - rabbit_mnesia:dir() ++ "/cluster_nodes.config". - -running_nodes_file_name() -> +running_nodes_filename() -> filename:join(rabbit_mnesia:dir(), "nodes_running_at_shutdown"). +cluster_status_filename() -> + rabbit_mnesia:dir() ++ "/cluster_nodes.config". + prepare_cluster_status_files() -> rabbit_mnesia:ensure_mnesia_dir(), CorruptFiles = fun () -> throw({error, corrupt_cluster_status_files}) end, - RunningNodes1 = case try_read_file(running_nodes_file_name()) of + RunningNodes1 = case try_read_file(running_nodes_filename()) of {ok, [Nodes]} when is_list(Nodes) -> Nodes; {ok, _ } -> CorruptFiles(); {error, enoent} -> [] end, {AllNodes1, WantDiscNode} = - case try_read_file(cluster_status_file_name()) of + case try_read_file(cluster_status_filename()) of {ok, [{AllNodes, DiscNodes0}]} -> {AllNodes, lists:member(node(), DiscNodes0)}; {ok, [AllNodes0]} when is_list(AllNodes0) -> @@ -118,10 +118,10 @@ prepare_cluster_status_files() -> ok = write_cluster_status({AllNodes2, DiscNodes, RunningNodes2}). write_cluster_status({All, Disc, Running}) -> - ClusterStatusFN = cluster_status_file_name(), + ClusterStatusFN = cluster_status_filename(), Res = case rabbit_file:write_term_file(ClusterStatusFN, [{All, Disc}]) of ok -> - RunningNodesFN = running_nodes_file_name(), + RunningNodesFN = running_nodes_filename(), {RunningNodesFN, rabbit_file:write_term_file(RunningNodesFN, [Running])}; E1 = {error, _} -> @@ -140,8 +140,8 @@ try_read_file(FileName) -> end. read_cluster_status() -> - case {try_read_file(cluster_status_file_name()), - try_read_file(running_nodes_file_name())} of + case {try_read_file(cluster_status_filename()), + try_read_file(running_nodes_filename())} of {{ok, [{All, Disc}]}, {ok, [Running]}} when is_list(Running) -> {All, Disc, Running}; {_, _} -> diff --git a/src/rabbit_queue_index.erl b/src/rabbit_queue_index.erl index 3ef769c7..6d6c648a 100644 --- a/src/rabbit_queue_index.erl +++ b/src/rabbit_queue_index.erl @@ -400,19 +400,19 @@ blank_state_dir(Dir) -> on_sync = fun (_) -> ok end, unsynced_msg_ids = gb_sets:new() }. -clean_file_name(Dir) -> filename:join(Dir, ?CLEAN_FILENAME). +clean_filename(Dir) -> filename:join(Dir, ?CLEAN_FILENAME). detect_clean_shutdown(Dir) -> - case rabbit_file:delete(clean_file_name(Dir)) of + case rabbit_file:delete(clean_filename(Dir)) of ok -> true; {error, enoent} -> false end. read_shutdown_terms(Dir) -> - rabbit_file:read_term_file(clean_file_name(Dir)). + rabbit_file:read_term_file(clean_filename(Dir)). store_clean_shutdown(Terms, Dir) -> - CleanFileName = clean_file_name(Dir), + CleanFileName = clean_filename(Dir), ok = rabbit_file:ensure_dir(CleanFileName), rabbit_file:write_term_file(CleanFileName, Terms). -- cgit v1.2.1 From 1fd784d838b780bc282bdf726f6a74a27928ca66 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 17 Sep 2012 13:27:16 +0100 Subject: Cope with the fact that rabbit_mnesia:running_clustered_nodes/0 now does not include node() during boot. --- src/rabbit_mirror_queue_misc.erl | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index ebaae995..5325f1be 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -236,8 +236,17 @@ suggested_queue_nodes(Q) -> _ -> MNode0 end, suggested_queue_nodes(policy(<<"ha-mode">>, Q), policy(<<"ha-params">>, Q), - {MNode, SNodes}, - rabbit_mnesia:running_clustered_nodes()). + {MNode, SNodes}, clusterable_nodes()). + +clusterable_nodes() -> + %% We may end up here via on_node_up/0, in which case we are still + %% booting - rabbit_mnesia:running_clustered_nodes/0 will report + %% us as not running. + Nodes = rabbit_mnesia:running_clustered_nodes(), + case lists:member(node(), Nodes) of + true -> Nodes; + false -> [node() | Nodes] + end. policy(Policy, Q) -> case rabbit_policy:get(Policy, Q) of -- cgit v1.2.1 From 39cc7a0a0185de0f4868354344013648f8228a18 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 17 Sep 2012 13:36:48 +0100 Subject: Simplify --- src/rabbit_mirror_queue_misc.erl | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 5325f1be..f53319c0 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -242,11 +242,7 @@ clusterable_nodes() -> %% We may end up here via on_node_up/0, in which case we are still %% booting - rabbit_mnesia:running_clustered_nodes/0 will report %% us as not running. - Nodes = rabbit_mnesia:running_clustered_nodes(), - case lists:member(node(), Nodes) of - true -> Nodes; - false -> [node() | Nodes] - end. + lists:usort([node() | rabbit_mnesia:running_clustered_nodes()]). policy(Policy, Q) -> case rabbit_policy:get(Policy, Q) of -- cgit v1.2.1 From ddf7763729950ad71d782818b3bb105f95e458e9 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 17 Sep 2012 14:15:11 +0100 Subject: nuke 'immediate' --- include/rabbit.hrl | 2 +- src/rabbit_amqqueue.erl | 42 +++++++++++++------------------- src/rabbit_amqqueue_process.erl | 28 ++++----------------- src/rabbit_basic.erl | 32 ++++++++++++------------ src/rabbit_channel.erl | 21 +++++++--------- src/rabbit_error_logger.erl | 2 +- src/rabbit_mirror_queue_slave.erl | 51 ++++++++++++--------------------------- src/rabbit_tests.erl | 5 ++-- src/rabbit_types.erl | 1 - 9 files changed, 66 insertions(+), 118 deletions(-) diff --git a/include/rabbit.hrl b/include/rabbit.hrl index d6fac46d..fff92205 100644 --- a/include/rabbit.hrl +++ b/include/rabbit.hrl @@ -73,7 +73,7 @@ is_persistent}). -record(ssl_socket, {tcp, ssl}). --record(delivery, {mandatory, immediate, sender, message, msg_seq_no}). +-record(delivery, {mandatory, sender, message, msg_seq_no}). -record(amqp_error, {name, explanation = "", method = none}). -record(event, {type, props, timestamp}). diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index d566ac87..f80559ba 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -60,7 +60,7 @@ -type(msg_id() :: non_neg_integer()). -type(ok_or_errors() :: 'ok' | {'error', [{'error' | 'exit' | 'throw', any()}]}). --type(routing_result() :: 'routed' | 'unroutable' | 'not_delivered'). +-type(routing_result() :: 'routed' | 'unroutable'). -type(queue_or_not_found() :: rabbit_types:amqqueue() | 'not_found'). -spec(start/0 :: () -> [name()]). @@ -645,18 +645,17 @@ pseudo_queue(QueueName, Pid) -> slave_pids = [], mirror_nodes = undefined}. -deliver([], #delivery{mandatory = false, immediate = false}, _Flow) -> +deliver([], #delivery{mandatory = false}, _Flow) -> %% /dev/null optimisation {routed, []}; -deliver(Qs, Delivery = #delivery{mandatory = false, immediate = false}, Flow) -> - %% optimisation: when Mandatory = false and Immediate = false, - %% rabbit_amqqueue:deliver will deliver the message to the queue - %% process asynchronously, and return true, which means all the - %% QPids will always be returned. It is therefore safe to use a - %% fire-and-forget cast here and return the QPids - the semantics - %% is preserved. This scales much better than the non-immediate - %% case below. +deliver(Qs, Delivery = #delivery{mandatory = false}, Flow) -> + %% optimisation: when Mandatory = false, rabbit_amqqueue:deliver + %% will deliver the message to the queue process asynchronously, + %% and return true, which means all the QPids will always be + %% returned. It is therefore safe to use a fire-and-forget cast + %% here and return the QPids - the semantics is preserved. This + %% scales much better than the case below. QPids = qpids(Qs), case Flow of flow -> [credit_flow:send(QPid) || QPid <- QPids]; @@ -668,21 +667,14 @@ deliver(Qs, Delivery = #delivery{mandatory = false, immediate = false}, Flow) -> end), {routed, QPids}; -deliver(Qs, Delivery = #delivery{mandatory = Mandatory, immediate = Immediate}, - _Flow) -> - QPids = qpids(Qs), - {Success, _} = - delegate:invoke( - QPids, fun (QPid) -> - gen_server2:call(QPid, {deliver, Delivery}, infinity) - end), - case {Mandatory, Immediate, - lists:foldl(fun ({QPid, true}, {_, H}) -> {true, [QPid | H]}; - ({_, false}, {_, H}) -> {true, H} - end, {false, []}, Success)} of - {true, _ , {false, []}} -> {unroutable, []}; - {_ , true, {_ , []}} -> {not_delivered, []}; - {_ , _ , {_ , R}} -> {routed, R} +deliver(Qs, Delivery, _Flow) -> + case delegate:invoke( + qpids(Qs), fun (QPid) -> + ok = gen_server2:call(QPid, {deliver, Delivery}, + infinity) + end) of + {[], _} -> {unroutable, []}; + {R , _} -> {routed, [QPid || {QPid, ok} <- R]} end. qpids(Qs) -> lists:append([[QPid | SPids] || diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 20ba4574..d6a5523a 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -770,7 +770,7 @@ dead_letter_fun(Reason, _State) -> dead_letter_publish(Msg, Reason, X, State = #q{publish_seqno = MsgSeqNo}) -> DLMsg = make_dead_letter_msg(Reason, Msg, State), - Delivery = rabbit_basic:delivery(false, false, DLMsg, MsgSeqNo), + Delivery = rabbit_basic:delivery(false, DLMsg, MsgSeqNo), {Queues, Cycles} = detect_dead_letter_cycles( DLMsg, rabbit_exchange:route(X, Delivery)), lists:foreach(fun log_cycle_once/1, Cycles), @@ -1032,27 +1032,9 @@ handle_call({info, Items}, _From, State) -> handle_call(consumers, _From, State) -> reply(consumers(State), State); -handle_call({deliver, Delivery = #delivery{immediate = true}}, _From, State) -> - %% FIXME: Is this correct semantics? - %% - %% I'm worried in particular about the case where an exchange has - %% two queues against a particular routing key, and a message is - %% sent in immediate mode through the binding. In non-immediate - %% mode, both queues get the message, saving it for later if - %% there's noone ready to receive it just now. In immediate mode, - %% should both queues still get the message, somehow, or should - %% just all ready-to-consume queues get the message, with unready - %% queues discarding the message? - %% - Confirm = should_confirm_message(Delivery, State), - {Delivered, State1} = attempt_delivery(Delivery, Confirm, State), - reply(Delivered, case Delivered of - true -> maybe_record_confirm_message(Confirm, State1); - false -> discard_delivery(Delivery, State1) - end); - -handle_call({deliver, Delivery = #delivery{mandatory = true}}, From, State) -> - gen_server2:reply(From, true), +handle_call({deliver, Delivery}, From, State) -> + %% Synchronous, "mandatory" deliver mode. + gen_server2:reply(From, ok), noreply(deliver_or_enqueue(Delivery, State)); handle_call({notify_down, ChPid}, From, State) -> @@ -1198,7 +1180,7 @@ handle_cast({run_backing_queue, Mod, Fun}, State) -> handle_cast({deliver, Delivery = #delivery{sender = Sender}, Flow}, State = #q{senders = Senders}) -> - %% Asynchronous, non-"mandatory", non-"immediate" deliver mode. + %% Asynchronous, non-"mandatory" deliver mode. Senders1 = case Flow of flow -> credit_flow:ack(Sender), pmon:monitor(Sender, Senders); diff --git a/src/rabbit_basic.erl b/src/rabbit_basic.erl index 734456d3..db2b7e95 100644 --- a/src/rabbit_basic.erl +++ b/src/rabbit_basic.erl @@ -18,9 +18,9 @@ -include("rabbit.hrl"). -include("rabbit_framing.hrl"). --export([publish/4, publish/6, publish/1, +-export([publish/4, publish/5, publish/1, message/3, message/4, properties/1, append_table_header/3, - extract_headers/1, map_headers/2, delivery/4, header_routes/1]). + extract_headers/1, map_headers/2, delivery/3, header_routes/1]). -export([build_content/2, from_content/1]). %%---------------------------------------------------------------------------- @@ -40,13 +40,13 @@ -spec(publish/4 :: (exchange_input(), rabbit_router:routing_key(), properties_input(), body_input()) -> publish_result()). --spec(publish/6 :: - (exchange_input(), rabbit_router:routing_key(), boolean(), boolean(), +-spec(publish/5 :: + (exchange_input(), rabbit_router:routing_key(), boolean(), properties_input(), body_input()) -> publish_result()). -spec(publish/1 :: (rabbit_types:delivery()) -> publish_result()). --spec(delivery/4 :: - (boolean(), boolean(), rabbit_types:message(), undefined | integer()) -> +-spec(delivery/3 :: + (boolean(), rabbit_types:message(), undefined | integer()) -> rabbit_types:delivery()). -spec(message/4 :: (rabbit_exchange:name(), rabbit_router:routing_key(), @@ -80,18 +80,16 @@ %% Convenience function, for avoiding round-trips in calls across the %% erlang distributed network. publish(Exchange, RoutingKeyBin, Properties, Body) -> - publish(Exchange, RoutingKeyBin, false, false, Properties, Body). + publish(Exchange, RoutingKeyBin, false, Properties, Body). %% Convenience function, for avoiding round-trips in calls across the %% erlang distributed network. -publish(X = #exchange{name = XName}, RKey, Mandatory, Immediate, Props, Body) -> - publish(X, delivery(Mandatory, Immediate, - message(XName, RKey, properties(Props), Body), - undefined)); -publish(XName, RKey, Mandatory, Immediate, Props, Body) -> - publish(delivery(Mandatory, Immediate, - message(XName, RKey, properties(Props), Body), - undefined)). +publish(X = #exchange{name = XName}, RKey, Mandatory, Props, Body) -> + Message = message(XName, RKey, properties(Props), Body), + publish(X, delivery(Mandatory, Message, undefined)); +publish(XName, RKey, Mandatory, Props, Body) -> + Message = message(XName, RKey, properties(Props), Body), + publish(delivery(Mandatory, Message, undefined)). publish(Delivery = #delivery{ message = #basic_message{exchange_name = XName}}) -> @@ -105,8 +103,8 @@ publish(X, Delivery) -> {RoutingRes, DeliveredQPids} = rabbit_amqqueue:deliver(Qs, Delivery), {ok, RoutingRes, DeliveredQPids}. -delivery(Mandatory, Immediate, Message, MsgSeqNo) -> - #delivery{mandatory = Mandatory, immediate = Immediate, sender = self(), +delivery(Mandatory, Message, MsgSeqNo) -> + #delivery{mandatory = Mandatory, sender = self(), message = Message, msg_seq_no = MsgSeqNo}. build_content(Properties, BodyBin) when is_binary(BodyBin) -> diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index e50e823c..e8f3aab3 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -598,10 +598,12 @@ handle_method(_Method, _, #ch{tx_status = TxStatus}) handle_method(#'access.request'{},_, State) -> {reply, #'access.request_ok'{ticket = 1}, State}; +handle_method(#'basic.publish'{immediate = true}, _Content, _State) -> + rabbit_misc:protocol_error(not_implemented, "immediate=true", []); + handle_method(#'basic.publish'{exchange = ExchangeNameBin, routing_key = RoutingKey, - mandatory = Mandatory, - immediate = Immediate}, + mandatory = Mandatory}, Content, State = #ch{virtual_host = VHostPath, tx_status = TxStatus, confirm_enabled = ConfirmEnabled, @@ -623,8 +625,7 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, case rabbit_basic:message(ExchangeName, RoutingKey, DecodedContent) of {ok, Message} -> rabbit_trace:tap_trace_in(Message, TraceState), - Delivery = rabbit_basic:delivery(Mandatory, Immediate, Message, - MsgSeqNo), + Delivery = rabbit_basic:delivery(Mandatory, Message, MsgSeqNo), QNames = rabbit_exchange:route(Exchange, Delivery), {noreply, case TxStatus of @@ -1342,20 +1343,16 @@ deliver_to_queues({Delivery = #delivery{message = Message = #basic_message{ QPid <- DeliveredQPids]], publish, State2), State2. -process_routing_result(unroutable, _, XName, MsgSeqNo, Msg, State) -> +process_routing_result(unroutable, _, XName, MsgSeqNo, Msg, State) -> ok = basic_return(Msg, State, no_route), maybe_incr_stats([{Msg#basic_message.exchange_name, 1}], return_unroutable, State), record_confirm(MsgSeqNo, XName, State); -process_routing_result(not_delivered, _, XName, MsgSeqNo, Msg, State) -> - ok = basic_return(Msg, State, no_consumers), - maybe_incr_stats([{XName, 1}], return_not_delivered, State), - record_confirm(MsgSeqNo, XName, State); -process_routing_result(routed, [], XName, MsgSeqNo, _, State) -> +process_routing_result(routed, [], XName, MsgSeqNo, _, State) -> record_confirm(MsgSeqNo, XName, State); -process_routing_result(routed, _, _, undefined, _, State) -> +process_routing_result(routed, _, _, undefined, _, State) -> State; -process_routing_result(routed, QPids, XName, MsgSeqNo, _, State) -> +process_routing_result(routed, QPids, XName, MsgSeqNo, _, State) -> State#ch{unconfirmed = dtree:insert(MsgSeqNo, QPids, XName, State#ch.unconfirmed)}. diff --git a/src/rabbit_error_logger.erl b/src/rabbit_error_logger.erl index f1672f4e..a9af2d8a 100644 --- a/src/rabbit_error_logger.erl +++ b/src/rabbit_error_logger.erl @@ -81,7 +81,7 @@ publish1(RoutingKey, Format, Data, LogExch) -> %% second resolution, not millisecond. Timestamp = rabbit_misc:now_ms() div 1000, {ok, _RoutingRes, _DeliveredQPids} = - rabbit_basic:publish(LogExch, RoutingKey, false, false, + rabbit_basic:publish(LogExch, RoutingKey, #'P_basic'{content_type = <<"text/plain">>, timestamp = Timestamp}, list_to_binary(io_lib:format(Format, Data))), diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 3e45f026..1f6567e0 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -70,7 +70,7 @@ sync_timer_ref, rate_timer_ref, - sender_queues, %% :: Pid -> {Q {Msg, Bool}, Set MsgId} + sender_queues, %% :: Pid -> {Q Msg, Set MsgId} msg_id_ack, %% :: MsgId -> AckTag ack_num, @@ -167,27 +167,10 @@ init_it(Self, Node, QueueName) -> end end. -handle_call({deliver, Delivery = #delivery { immediate = true }}, - From, State) -> - %% It is safe to reply 'false' here even if a) we've not seen the - %% msg via gm, or b) the master dies before we receive the msg via - %% gm. In the case of (a), we will eventually receive the msg via - %% gm, and it's only the master's result to the channel that is - %% important. In the case of (b), if the master does die and we do - %% get promoted then at that point we have no consumers, thus - %% 'false' is precisely the correct answer. However, we must be - %% careful to _not_ enqueue the message in this case. - - %% Note this is distinct from the case where we receive the msg - %% via gm first, then we're promoted to master, and only then do - %% we receive the msg from the channel. - gen_server2:reply(From, false), %% master may deliver it, not us - noreply(maybe_enqueue_message(Delivery, false, State)); - -handle_call({deliver, Delivery = #delivery { mandatory = true }}, - From, State) -> - gen_server2:reply(From, true), %% amqqueue throws away the result anyway - noreply(maybe_enqueue_message(Delivery, true, State)); +handle_call({deliver, Delivery}, From, State) -> + %% Synchronous, "mandatory" deliver mode. + gen_server2:reply(From, ok), + noreply(maybe_enqueue_message(Delivery, State)); handle_call({gm_deaths, Deaths}, From, State = #state { q = #amqqueue { name = QueueName }, @@ -232,12 +215,12 @@ handle_cast({gm, Instruction}, State) -> handle_process_result(process_instruction(Instruction, State)); handle_cast({deliver, Delivery = #delivery{sender = Sender}, Flow}, State) -> - %% Asynchronous, non-"mandatory", non-"immediate" deliver mode. + %% Asynchronous, non-"mandatory", deliver mode. case Flow of flow -> credit_flow:ack(Sender); noflow -> ok end, - noreply(maybe_enqueue_message(Delivery, true, State)); + noreply(maybe_enqueue_message(Delivery, State)); handle_cast({set_maximum_since_use, Age}, State) -> ok = file_handle_cache:set_maximum_since_use(Age), @@ -554,7 +537,7 @@ promote_me(From, #state { q = Q = #amqqueue { name = QName }, NumAckTags = [NumAckTag || {_MsgId, NumAckTag} <- dict:to_list(MA)], AckTags = [AckTag || {_Num, AckTag} <- lists:sort(NumAckTags)], Deliveries = [Delivery || {_ChPid, {PubQ, _PendCh}} <- dict:to_list(SQ), - {Delivery, true} <- queue:to_list(PubQ)], + Delivery <- queue:to_list(PubQ)], QueueState = rabbit_amqqueue_process:init_with_backing_queue_state( Q1, rabbit_mirror_queue_master, MasterState, RateTRef, AckTags, Deliveries, KS, MTC), @@ -655,14 +638,13 @@ maybe_enqueue_message( Delivery = #delivery { message = #basic_message { id = MsgId }, msg_seq_no = MsgSeqNo, sender = ChPid }, - EnqueueOnPromotion, State = #state { sender_queues = SQ, msg_id_status = MS }) -> State1 = ensure_monitoring(ChPid, State), %% We will never see {published, ChPid, MsgSeqNo} here. case dict:find(MsgId, MS) of error -> {MQ, PendingCh} = get_sender_queue(ChPid, SQ), - MQ1 = queue:in({Delivery, EnqueueOnPromotion}, MQ), + MQ1 = queue:in(Delivery, MQ), SQ1 = dict:store(ChPid, {MQ1, PendingCh}, SQ), State1 #state { sender_queues = SQ1 }; {ok, {confirmed, ChPid}} -> @@ -732,10 +714,9 @@ process_instruction( {empty, _MQ2} -> {MQ, sets:add_element(MsgId, PendingCh), dict:store(MsgId, {published, ChPid}, MS)}; - {{value, {Delivery = #delivery { - msg_seq_no = MsgSeqNo, - message = #basic_message { id = MsgId } }, - _EnqueueOnPromotion}}, MQ2} -> + {{value, Delivery = #delivery { + msg_seq_no = MsgSeqNo, + message = #basic_message { id = MsgId } }}, MQ2} -> {MQ2, PendingCh, %% We received the msg from the channel first. Thus %% we need to deal with confirms here. @@ -747,7 +728,7 @@ process_instruction( ChPid, [MsgSeqNo]), MS end}; - {{value, {#delivery {}, _EnqueueOnPromotion}}, _MQ2} -> + {{value, #delivery {}}, _MQ2} -> %% The instruction was sent to us before we were %% within the slave_pids within the #amqqueue{} %% record. We'll never receive the message directly @@ -784,12 +765,12 @@ process_instruction({discard, ChPid, Msg = #basic_message { id = MsgId }}, {empty, _MQ} -> {MQ, sets:add_element(MsgId, PendingCh), dict:store(MsgId, discarded, MS)}; - {{value, {#delivery { message = #basic_message { id = MsgId } }, - _EnqueueOnPromotion}}, MQ2} -> + {{value, #delivery { message = #basic_message { id = MsgId } }}, + MQ2} -> %% We've already seen it from the channel, we're not %% going to see this again, so don't add it to MS {MQ2, PendingCh, MS}; - {{value, {#delivery {}, _EnqueueOnPromotion}}, _MQ2} -> + {{value, #delivery {}}, _MQ2} -> %% The instruction was sent to us before we were %% within the slave_pids within the #amqqueue{} %% record. We'll never receive the message directly diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 3cc0e5db..08535e7d 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -656,7 +656,6 @@ test_topic_expect_match(X, List) -> #'P_basic'{}, <<>>), Res = rabbit_exchange_type_topic:route( X, #delivery{mandatory = false, - immediate = false, sender = self(), message = Message}), ExpectedRes = lists:map( @@ -2194,8 +2193,8 @@ publish_and_confirm(Q, Payload, Count) -> Msg = rabbit_basic:message(rabbit_misc:r(<<>>, exchange, <<>>), <<>>, #'P_basic'{delivery_mode = 2}, Payload), - Delivery = #delivery{mandatory = false, immediate = false, - sender = self(), message = Msg, msg_seq_no = Seq}, + Delivery = #delivery{mandatory = false, sender = self(), + message = Msg, msg_seq_no = Seq}, {routed, _} = rabbit_amqqueue:deliver([Q], Delivery) end || Seq <- Seqs], wait_for_confirms(gb_sets:from_list(Seqs)). diff --git a/src/rabbit_types.erl b/src/rabbit_types.erl index 8966bcab..f488afb4 100644 --- a/src/rabbit_types.erl +++ b/src/rabbit_types.erl @@ -69,7 +69,6 @@ -type(message() :: basic_message()). -type(delivery() :: #delivery{mandatory :: boolean(), - immediate :: boolean(), sender :: pid(), message :: message()}). -type(message_properties() :: -- cgit v1.2.1 From 1971c67f48bdf3087cafa2330ec7667582a32f33 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 17 Sep 2012 14:55:20 +0100 Subject: Increase the amount of symmetry. --- src/rabbit_binding.erl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/rabbit_binding.erl b/src/rabbit_binding.erl index a7730a1a..2e462354 100644 --- a/src/rabbit_binding.erl +++ b/src/rabbit_binding.erl @@ -285,13 +285,7 @@ remove_for_destination(DstName) -> remove_for_destination(DstName, fun remove_routes/1). remove_transient_for_destination(DstName) -> - remove_for_destination( - DstName, fun (Routes) -> - [begin - ok = sync_transient_route(R, fun delete_object/3), - R#route.binding - end || R <- Routes] - end). + remove_for_destination(DstName, fun remove_transient_routes/1). %%---------------------------------------------------------------------------- @@ -391,6 +385,12 @@ remove_routes(Routes) -> R <- DurableRoutes], [R#route.binding || R <- Routes]. +remove_transient_routes(Routes) -> + [begin + ok = sync_transient_route(R, fun delete_object/3), + R#route.binding + end || R <- Routes]. + remove_for_destination(DstName, Fun) -> lock_route_tables(), Match = reverse_route( -- cgit v1.2.1 From 0028ddbddf5e06c97e926a3d6cc472750fb34ec5 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Mon, 17 Sep 2012 14:58:49 +0100 Subject: fix messed up rabbitmqctl usage --- docs/rabbitmqctl.1.xml | 27 +++------------------------ 1 file changed, 3 insertions(+), 24 deletions(-) diff --git a/docs/rabbitmqctl.1.xml b/docs/rabbitmqctl.1.xml index 1af93e85..11d85e9e 100644 --- a/docs/rabbitmqctl.1.xml +++ b/docs/rabbitmqctl.1.xml @@ -366,16 +366,8 @@ - - - - - change_cluster_node_type - - disk | ram - - + change_cluster_node_type disk | ram @@ -390,15 +382,8 @@ - - - - - forget_cluster_node - --offline - - + forget_cluster_node --offline @@ -429,14 +414,8 @@ - - - - - update_cluster_nodes - clusternode - + update_cluster_nodes clusternode -- cgit v1.2.1 From 9261402fce87d6f5e98ae862723f9e5949c7566a Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 17 Sep 2012 15:30:25 +0100 Subject: Typo --- src/rabbit_amqqueue_process.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 2c3cd64d..c3d717fb 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -712,7 +712,7 @@ message_properties(Message, Confirm, #q{ttl = TTL}) -> calculate_msg_expiry(#basic_message{content = Content}, TTL) -> #content{properties = Props} = rabbit_binary_parser:ensure_content_decoded(Content), - %% We assert that the expiration must be valid - we check in che channel. + %% We assert that the expiration must be valid - we check in the channel. {ok, MsgTTL} = rabbit_basic:parse_expiration(Props), case lists:min([TTL, MsgTTL]) of undefined -> undefined; -- cgit v1.2.1 From d4fc4cbe253d6d6677d70c0a0f8b494a6cad69ba Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Mon, 17 Sep 2012 15:57:09 +0100 Subject: refactor the check for the expiry out of `basic.publish' --- src/rabbit_channel.erl | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 78f5d3b8..ab13b2fe 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -470,6 +470,14 @@ check_user_id_header(#'P_basic'{user_id = Claimed}, "user_id property set to '~s' but authenticated user was '~s'", [Claimed, Actual]). +check_expiration_header(Props) -> + case rabbit_basic:parse_expiration(Props) of + {ok, _} -> ok; + {error, E} -> rabbit_misc:protocol_error( + invalid_expiration, "cannot parse expiration '~p': ~p", + [Props#'P_basic'.expiration, E]) + end. + check_internal_exchange(#exchange{name = Name, internal = true}) -> rabbit_misc:protocol_error(access_refused, "cannot publish to internal ~s", @@ -611,12 +619,7 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, DecodedContent = #content {properties = Props} = rabbit_binary_parser:ensure_content_decoded(Content), check_user_id_header(Props, State), - case rabbit_basic:parse_expiration(Props) of - {ok, _} -> ok; - {error, E} -> rabbit_misc:protocol_error( - invalid_expiration, "cannot parse expiration '~p': ~p", - [Props#'P_basic'.expiration, E]) - end, + check_expiration_header(Props), {MsgSeqNo, State1} = case {TxStatus, ConfirmEnabled} of {none, false} -> {undefined, State}; -- cgit v1.2.1 From cc904e726e83529e4a1a58f4072f66d50e9359d2 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 17 Sep 2012 16:14:41 +0100 Subject: ensure that slaves confirm messages when ttl=0 Fixed in the simplest possible, rather than most efficient, way - by removing an optimisation. --- src/rabbit_amqqueue_process.erl | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 20ba4574..e647627c 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -560,18 +560,15 @@ attempt_delivery(#delivery{sender = SenderPid, message = Message}, Confirm, end. deliver_or_enqueue(Delivery = #delivery{message = Message, - msg_seq_no = MsgSeqNo, sender = SenderPid}, State) -> Confirm = should_confirm_message(Delivery, State), case attempt_delivery(Delivery, Confirm, State) of {true, State1} -> maybe_record_confirm_message(Confirm, State1); - %% the next two are optimisations + %% the next one is an optimisations + %% TODO: optimise the Confirm =/= never case too {false, State1 = #q{ttl = 0, dlx = undefined}} when Confirm == never -> discard_delivery(Delivery, State1); - {false, State1 = #q{ttl = 0, dlx = undefined}} -> - rabbit_misc:confirm_to_sender(SenderPid, [MsgSeqNo]), - discard_delivery(Delivery, State1); {false, State1} -> State2 = #q{backing_queue = BQ, backing_queue_state = BQS} = maybe_record_confirm_message(Confirm, State1), -- cgit v1.2.1 From d2d11571f678ae4797befeb893473d11195f03e3 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 17 Sep 2012 21:50:58 +0100 Subject: some tidying up - normal_init/2 -> init/2 - DiscNode -> WantDiscNode (when it's a boolean) - shrink comment width - 'of' never goes on a line by itself - save some newlines - introduce leave_cluster/1 helper function --- src/rabbit_mnesia.erl | 343 +++++++++++++++++++++++++------------------------- 1 file changed, 169 insertions(+), 174 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 40600063..dbd524f1 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -123,37 +123,38 @@ init() -> ensure_mnesia_dir(), case is_virgin_node() of true -> init_from_config(); - false -> normal_init(is_disc_node(), all_clustered_nodes()) + false -> init(is_disc_node(), all_clustered_nodes()) end, %% We intuitively expect the global name server to be synced when - %% Mnesia is up. In fact that's not guaranteed to be the case - let's - %% make it so. + %% Mnesia is up. In fact that's not guaranteed to be the case - + %% let's make it so. ok = global:sync(), ok. -normal_init(DiscNode, AllNodes) -> - init_db_and_upgrade(AllNodes, DiscNode, DiscNode). +init(WantDiscNode, AllNodes) -> + init_db_and_upgrade(AllNodes, WantDiscNode, WantDiscNode). init_from_config() -> - {ok, {TryNodes, DiscNode}} = + {ok, {TryNodes, WantDiscNode}} = application:get_env(rabbit, cluster_nodes), case find_good_node(TryNodes -- [node()]) of {ok, Node} -> rabbit_log:info("Node '~p' selected for clustering from " "configuration~n", [Node]), {ok, {_, DiscNodes, _}} = discover_cluster(Node), - init_db_and_upgrade(DiscNodes, DiscNode, false), + init_db_and_upgrade(DiscNodes, WantDiscNode, false), rabbit_node_monitor:notify_joined_cluster(); none -> rabbit_log:warning("Could not find any suitable node amongst the " "ones provided in the configuration: ~p~n", [TryNodes]), - normal_init(true, [node()]) + init(true, [node()]) end. -%% Make the node join a cluster. The node will be reset automatically before we -%% actually cluster it. The nodes provided will be used to find out about the -%% nodes in the cluster. +%% Make the node join a cluster. The node will be reset automatically +%% before we actually cluster it. The nodes provided will be used to +%% find out about the nodes in the cluster. +%% %% This function will fail if: %% %% * The node is currently the only disc node of its cluster @@ -161,9 +162,9 @@ init_from_config() -> %% * The node is currently already clustered with the cluster of the nodes %% provided %% -%% Note that we make no attempt to verify that the nodes provided are all in the -%% same cluster, we simply pick the first online node and we cluster to its -%% cluster. +%% Note that we make no attempt to verify that the nodes provided are +%% all in the same cluster, we simply pick the first online node and +%% we cluster to its cluster. join_cluster(DiscoveryNode, WantDiscNode) -> case is_disc_and_clustered() andalso [node()] =:= clustered_disc_nodes() of true -> throw({error, @@ -190,10 +191,10 @@ join_cluster(DiscoveryNode, WantDiscNode) -> false -> ok end, - %% reset the node. this simplifies things and it will be needed in this case - %% - we're joining a new cluster with new nodes which are not in synch with - %% the current node. I also lifts the burden of reseting the node from the - %% user. + %% reset the node. this simplifies things and it will be needed in + %% this case - we're joining a new cluster with new nodes which + %% are not in synch with the current node. I also lifts the burden + %% of reseting the node from the user. reset(false), rabbit_misc:local_info_msg("Clustering with ~p~n", [ClusterNodes]), @@ -224,9 +225,9 @@ reset(Force) -> false -> AllNodes = all_clustered_nodes(), %% Reconnecting so that we will get an up to date nodes. - %% We don't need to check for consistency because we are resetting. - %% Force=true here so that reset still works when clustered with a - %% node which is down. + %% We don't need to check for consistency because we are + %% resetting. Force=true here so that reset still works + %% when clustered with a node which is down. init_db_with_mnesia(AllNodes, is_disc_node(), false, true), case is_disc_and_clustered() andalso [node()] =:= clustered_disc_nodes() @@ -249,9 +250,9 @@ reset(Force) -> ok = rabbit_node_monitor:reset_cluster_status(), ok. -%% We need to make sure that we don't end up in a distributed Erlang system with -%% nodes while not being in an Mnesia cluster with them. We don't handle that -%% well. +%% We need to make sure that we don't end up in a distributed Erlang +%% system with nodes while not being in an Mnesia cluster with +%% them. We don't handle that well. disconnect_nodes(Nodes) -> [erlang:disconnect_node(N) || N <- Nodes]. change_cluster_node_type(Type) -> @@ -304,25 +305,28 @@ update_cluster_nodes(DiscoveryNode) -> "Could not connect to the cluster node provided"}}) end, case ordsets:is_element(node(), AllNodes) of - true -> %% As in `check_consistency/0', we can safely delete the schema - %% here, since it'll be replicated from the other nodes - mnesia:delete_schema([node()]), - rabbit_node_monitor:write_cluster_status(Status), - init_db_with_mnesia(AllNodes, is_disc_node(), false); - false -> throw({error, - {inconsistent_cluster, - "The nodes provided do not have this node as part of " - "the cluster"}}) + true -> + %% As in `check_consistency/0', we can safely delete the + %% schema here, since it'll be replicated from the other + %% nodes + mnesia:delete_schema([node()]), + rabbit_node_monitor:write_cluster_status(Status), + init_db_with_mnesia(AllNodes, is_disc_node(), false); + false -> + throw({error, + {inconsistent_cluster, + "The nodes provided do not have this node as part of " + "the cluster"}}) end, - ok. -%% We proceed like this: try to remove the node locally. If the node is offline, -%% we remove the node if: +%% We proceed like this: try to remove the node locally. If the node +%% is offline, we remove the node if: %% * This node is a disc node %% * All other nodes are offline -%% * This node was, at the best of our knowledge (see comment below) the last -%% or second to last after the node we're removing to go down +%% * This node was, at the best of our knowledge (see comment below) +%% the last or second to last after the node we're removing to go +%% down forget_cluster_node(Node, RemoveWhenOffline) -> case ordsets:is_element(Node, all_clustered_nodes()) of true -> ok; @@ -356,21 +360,19 @@ forget_cluster_node(Node, RemoveWhenOffline) -> end. remove_node_offline_node(Node) -> - case {ordsets:del_element(Node, - running_nodes(all_clustered_nodes())), - is_disc_node()} - of + case {ordsets:del_element(Node, running_nodes(all_clustered_nodes())), + is_disc_node()} of {[], true} -> - %% Note that while we check if the nodes was the last to go down, - %% apart from the node we're removing from, this is still unsafe. - %% Consider the situation in which A and B are clustered. A goes - %% down, and records B as the running node. Then B gets clustered - %% with C, C goes down and B goes down. In this case, C is the - %% second-to-last, but we don't know that and we'll remove B from A + %% Note that while we check if the nodes was the last to + %% go down, apart from the node we're removing from, this + %% is still unsafe. Consider the situation in which A and + %% B are clustered. A goes down, and records B as the + %% running node. Then B gets clustered with C, C goes down + %% and B goes down. In this case, C is the second-to-last, + %% but we don't know that and we'll remove B from A %% anyway, even if that will lead to bad things. case ordsets:subtract(running_clustered_nodes(), - ordsets:from_list([node(), Node])) - of + ordsets:from_list([node(), Node])) of [] -> start_mnesia(), try [mnesia:force_load_table(T) || @@ -420,23 +422,19 @@ is_clustered() -> Nodes = all_clustered_nodes(), [node()] =/= Nodes andalso [] =/= Nodes. -is_disc_and_clustered() -> - is_disc_node() andalso is_clustered(). +is_disc_and_clustered() -> is_disc_node() andalso is_clustered(). %% Functions that retrieve the nodes in the cluster will rely on the status file %% if offline. -all_clustered_nodes() -> - cluster_status(all). +all_clustered_nodes() -> cluster_status(all). -clustered_disc_nodes() -> - cluster_status(disc). +clustered_disc_nodes() -> cluster_status(disc). -clustered_ram_nodes() -> - ordsets:subtract(cluster_status(all), cluster_status(disc)). +clustered_ram_nodes() -> ordsets:subtract(cluster_status(all), + cluster_status(disc)). -running_clustered_nodes() -> - cluster_status(running). +running_clustered_nodes() -> cluster_status(running). running_clustered_disc_nodes() -> {_, DiscNodes, RunningNodes} = cluster_status(), @@ -446,37 +444,39 @@ running_clustered_disc_nodes() -> %% from mnesia. Obviously it'll work only when mnesia is running. mnesia_nodes() -> case mnesia:system_info(is_running) of - no -> {error, mnesia_not_running}; - yes -> %% If the tables are not present, it means that `init_db/3' - %% hasn't been run yet. In other words, either we are a virgin - %% node or a restarted RAM node. In both cases we're not - %% interested in what mnesia has to say. - IsDiscNode = mnesia:system_info(use_dir), - Tables = mnesia:system_info(tables), - {Table, _} = case table_definitions(case IsDiscNode of - true -> disc; - false -> ram - end) of [T|_] -> T end, - case lists:member(Table, Tables) of - true -> - AllNodes = - ordsets:from_list(mnesia:system_info(db_nodes)), - DiscCopies = ordsets:from_list( - mnesia:table_info(schema, disc_copies)), - DiscNodes = - case IsDiscNode of - true -> ordsets:add_element(node(), DiscCopies); - false -> DiscCopies - end, - {ok, {AllNodes, DiscNodes}}; - false -> - {error, tables_not_present} - end + no -> + {error, mnesia_not_running}; + yes -> + %% If the tables are not present, it means that + %% `init_db/3' hasn't been run yet. In other words, either + %% we are a virgin node or a restarted RAM node. In both + %% cases we're not interested in what mnesia has to say. + IsDiscNode = mnesia:system_info(use_dir), + Tables = mnesia:system_info(tables), + {Table, _} = case table_definitions(case IsDiscNode of + true -> disc; + false -> ram + end) of [T|_] -> T end, + case lists:member(Table, Tables) of + true -> + AllNodes = + ordsets:from_list(mnesia:system_info(db_nodes)), + DiscCopies = ordsets:from_list( + mnesia:table_info(schema, disc_copies)), + DiscNodes = + case IsDiscNode of + true -> ordsets:add_element(node(), DiscCopies); + false -> DiscCopies + end, + {ok, {AllNodes, DiscNodes}}; + false -> + {error, tables_not_present} + end end. cluster_status(WhichNodes, ForceMnesia) -> - %% I don't want to call `running_nodes/1' unless if necessary, since it can - %% deadlock when stopping applications. + %% I don't want to call `running_nodes/1' unless if necessary, + %% since it can deadlock when stopping applications. Nodes = case mnesia_nodes() of {ok, {AllNodes, DiscNodes}} -> {ok, {AllNodes, DiscNodes, @@ -484,9 +484,10 @@ cluster_status(WhichNodes, ForceMnesia) -> {error, _Reason} when not ForceMnesia -> {AllNodes, DiscNodes, RunningNodes} = rabbit_node_monitor:read_cluster_status(), - %% The cluster status file records the status when the node - %% is online, but we know for sure that the node is offline - %% now, so we can remove it from the list of running nodes. + %% The cluster status file records the status when + %% the node is online, but we know for sure that + %% the node is offline now, so we can remove it + %% from the list of running nodes. {ok, {AllNodes, DiscNodes, fun() -> ordsets:del_element(node(), RunningNodes) end}}; @@ -509,11 +510,9 @@ cluster_status(WhichNodes) -> {ok, Status} = cluster_status(WhichNodes, false), Status. -cluster_status() -> - cluster_status(status). +cluster_status() -> cluster_status(status). -cluster_status_from_mnesia() -> - cluster_status(status, true). +cluster_status_from_mnesia() -> cluster_status(status, true). node_info() -> {erlang:system_info(otp_release), rabbit_misc:version(), @@ -525,21 +524,22 @@ is_disc_node() -> dir() -> mnesia:system_info(directory). -table_names() -> - [Tab || {Tab, _} <- table_definitions()]. +table_names() -> [Tab || {Tab, _} <- table_definitions()]. %%---------------------------------------------------------------------------- %% Operations on the db %%---------------------------------------------------------------------------- -%% Adds the provided nodes to the mnesia cluster, creating a new schema if there -%% is the need to and catching up if there are other nodes in the cluster -%% already. It also updates the cluster status file. +%% Adds the provided nodes to the mnesia cluster, creating a new +%% schema if there is the need to and catching up if there are other +%% nodes in the cluster already. It also updates the cluster status +%% file. init_db(ClusterNodes, WantDiscNode, Force) -> Nodes = change_extra_db_nodes(ClusterNodes, Force), - %% Note that we use `system_info' here and not the cluster status since when - %% we start rabbit for the first time the cluster status will say we are a - %% disc node but the tables won't be present yet. + %% Note that we use `system_info' here and not the cluster status + %% since when we start rabbit for the first time the cluster + %% status will say we are a disc node but the tables won't be + %% present yet. WasDiscNode = mnesia:system_info(use_dir), case {Nodes, WasDiscNode, WantDiscNode} of {[], _, false} -> @@ -556,11 +556,11 @@ init_db(ClusterNodes, WantDiscNode, Force) -> ensure_version_ok( rpc:call(AnotherNode, rabbit_version, recorded, [])), ok = wait_for_replicated_tables(), - %% The sequence in which we delete the schema and then the other - %% tables is important: if we delete the schema first when moving to - %% RAM mnesia will loudly complain since it doesn't make much sense - %% to do that. But when moving to disc, we need to move the schema - %% first. + %% The sequence in which we delete the schema and then the + %% other tables is important: if we delete the schema + %% first when moving to RAM mnesia will loudly complain + %% since it doesn't make much sense to do that. But when + %% moving to disc, we need to move the schema first. case WantDiscNode of true -> create_local_table_copy(schema, disc_copies), create_local_table_copies(disc); @@ -579,8 +579,8 @@ init_db_and_upgrade(ClusterNodes, WantDiscNode, Force) -> starting_from_scratch -> rabbit_version:record_desired(); version_not_available -> schema_ok_or_move() end, - %% `maybe_upgrade_local' restarts mnesia, so ram nodes will forget about the - %% cluster + %% `maybe_upgrade_local' restarts mnesia, so ram nodes will forget + %% about the cluster case WantDiscNode of false -> start_mnesia(), change_extra_db_nodes(ClusterNodes, true), @@ -696,8 +696,8 @@ wait_for_tables(TableNames) -> throw({error, {failed_waiting_for_tables, Reason}}) end. -%% This does not guarantee us much, but it avoids some situations that will -%% definitely end up badly +%% This does not guarantee us much, but it avoids some situations that +%% will definitely end up badly check_cluster_consistency() -> %% We want to find 0 or 1 consistent nodes. case lists:foldl( @@ -708,18 +708,21 @@ check_cluster_consistency() -> of {ok, Status = {RemoteAllNodes, _, _}} -> case ordsets:is_subset(all_clustered_nodes(), RemoteAllNodes) of - true -> ok; - false -> %% We delete the schema here since we think we are - %% clustered with nodes that are no longer in the - %% cluster and there is no other way to remove them - %% from our schema. On the other hand, we are sure - %% that there is another online node that we can use - %% to sync the tables with. There is a race here: if - %% between this check and the `init_db' invocation the - %% cluster gets disbanded, we're left with a node with - %% no mnesia data that will try to connect to offline - %% nodes. - mnesia:delete_schema([node()]) + true -> + ok; + false -> + %% We delete the schema here since we think we are + %% clustered with nodes that are no longer in the + %% cluster and there is no other way to remove + %% them from our schema. On the other hand, we are + %% sure that there is another online node that we + %% can use to sync the tables with. There is a + %% race here: if between this check and the + %% `init_db' invocation the cluster gets + %% disbanded, we're left with a node with no + %% mnesia data that will try to connect to offline + %% nodes. + mnesia:delete_schema([node()]) end, rabbit_node_monitor:write_cluster_status(Status); {error, not_found} -> @@ -764,9 +767,7 @@ on_node_down(_Node) -> discover_cluster(Nodes) when is_list(Nodes) -> lists:foldl(fun (_, {ok, Res}) -> {ok, Res}; (Node, {error, _}) -> discover_cluster(Node) - end, - {error, no_nodes_provided}, - Nodes); + end, {error, no_nodes_provided}, Nodes); discover_cluster(Node) -> OfflineError = {error, {cannot_discover_cluster, @@ -966,7 +967,8 @@ ensure_version_ok({ok, DiscVersion}) -> ensure_version_ok({error, _}) -> ok = rabbit_version:record_desired(). -%% We only care about disc nodes since ram nodes are supposed to catch up only +%% We only care about disc nodes since ram nodes are supposed to catch +%% up only create_schema() -> stop_mnesia(), rabbit_misc:ensure_ok(mnesia:create_schema([node()]), cannot_create_schema), @@ -1039,17 +1041,19 @@ create_local_table_copy(Tab, Type) -> remove_node_if_mnesia_running(Node) -> case mnesia:system_info(is_running) of - yes -> %% Deleting the the schema copy of the node will result in the - %% node being removed from the cluster, with that change being - %% propagated to all nodes - case mnesia:del_table_copy(schema, Node) of - {atomic, ok} -> - rabbit_node_monitor:notify_left_cluster(Node), - ok; - {aborted, Reason} -> - {error, {failed_to_remove_node, Node, Reason}} - end; - no -> {error, mnesia_not_running} + yes -> + %% Deleting the the schema copy of the node will result in + %% the node being removed from the cluster, with that + %% change being propagated to all nodes + case mnesia:del_table_copy(schema, Node) of + {atomic, ok} -> + rabbit_node_monitor:notify_left_cluster(Node), + ok; + {aborted, Reason} -> + {error, {failed_to_remove_node, Node, Reason}} + end; + no -> + {error, mnesia_not_running} end. leave_cluster() -> @@ -1059,24 +1063,7 @@ leave_cluster() -> {false, []} -> ok; {_, AllNodes} -> - case lists:any( - fun (Node) -> - case rpc:call(Node, rabbit_mnesia, - remove_node_if_mnesia_running, - [node()]) - of - ok -> - true; - {error, mnesia_not_running} -> - false; - {error, Reason} -> - throw({error, Reason}); - {badrpc, nodedown} -> - false - end - end, - AllNodes) - of + case lists:any(fun leave_cluster/1, AllNodes) of true -> ok; false -> throw({error, {no_running_cluster_nodes, @@ -1085,6 +1072,15 @@ leave_cluster() -> end end. +leave_cluster(Node) -> + case rpc:call(Node, + rabbit_mnesia, remove_node_if_mnesia_running, [node()]) of + ok -> true; + {error, mnesia_not_running} -> false; + {error, Reason} -> throw({error, Reason}); + {badrpc, nodedown} -> false + end. + wait_for(Condition) -> error_logger:info_msg("Waiting for ~p...~n", [Condition]), timer:sleep(1000). @@ -1114,10 +1110,10 @@ change_extra_db_nodes(ClusterNodes0, Force) -> Nodes end. -%% What we really want is nodes running rabbit, not running mnesia. Using -%% `rabbit_mnesia:system_info(running_db_nodes)' will return false positives -%% when we are actually just doing cluster operations (e.g. joining the -%% cluster). +%% What we really want is nodes running rabbit, not running +%% mnesia. Using `rabbit_mnesia:system_info(running_db_nodes)' will +%% return false positives when we are actually just doing cluster +%% operations (e.g. joining the cluster). running_nodes(Nodes) -> {Replies, _BadNodes} = rpc:multicall(Nodes, rabbit_mnesia, is_running_remote, []), @@ -1162,12 +1158,13 @@ check_otp_consistency(Remote) -> check_rabbit_consistency(Remote) -> check_version_consistency(rabbit_misc:version(), Remote, "Rabbit"). -%% 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 tables while still being -%% non-virgin. What we do instead is to check if the mnesia directory is non -%% existant or empty, with the exception of the cluster status file, which will -%% be there thanks to `rabbit_node_monitor:prepare_cluster_status_file/0'. +%% 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 +%% tables while still being non-virgin. What we do instead is to +%% check if the mnesia directory is non existant or empty, with the +%% exception of the cluster status file, which will be there thanks to +%% `rabbit_node_monitor:prepare_cluster_status_file/0'. is_virgin_node() -> case rabbit_file:list_dir(dir()) of {error, enoent} -> true; @@ -1182,11 +1179,9 @@ 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, _} -> - case check_consistency(OTP, Rabbit) of - {error, _} -> find_good_node(Nodes); - ok -> {ok, Node} - end + {badrpc, _Reason} -> find_good_node(Nodes); + {OTP, Rabbit, _} -> case check_consistency(OTP, Rabbit) of + {error, _} -> find_good_node(Nodes); + ok -> {ok, Node} + end end. -- cgit v1.2.1 From f668305b3633852b1640a0afe9952b0ed1e0d840 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 17 Sep 2012 22:32:23 +0100 Subject: correct comment --- src/rabbit_mnesia.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index dbd524f1..3ee4c2e8 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -1111,7 +1111,7 @@ change_extra_db_nodes(ClusterNodes0, Force) -> end. %% What we really want is nodes running rabbit, not running -%% mnesia. Using `rabbit_mnesia:system_info(running_db_nodes)' will +%% mnesia. Using `mnesia:system_info(running_db_nodes)' will %% return false positives when we are actually just doing cluster %% operations (e.g. joining the cluster). running_nodes(Nodes) -> -- cgit v1.2.1 From 703c27d907d40012bdb19c6f17588df0e156429f Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 17 Sep 2012 23:14:38 +0100 Subject: move all the error descriptions to the end ...so they don't clutter the logic. and some other minor cosmetic changes. --- src/rabbit_mnesia.erl | 162 +++++++++++++++++++++++--------------------------- 1 file changed, 74 insertions(+), 88 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 3ee4c2e8..f19046a0 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -167,12 +167,7 @@ init_from_config() -> %% we cluster to its cluster. join_cluster(DiscoveryNode, WantDiscNode) -> case is_disc_and_clustered() andalso [node()] =:= clustered_disc_nodes() of - true -> throw({error, - {standalone_ram_node, - "You can't cluster a node if it's the only " - "disc node in its existing cluster. If new nodes " - "joined while this node was offline, use " - "\"update_cluster_nodes\" to add them manually"}}); + true -> e(clustering_only_disc_node); _ -> ok end, @@ -185,9 +180,7 @@ join_cluster(DiscoveryNode, WantDiscNode) -> end, case lists:member(node(), ClusterNodes) of - true -> throw({error, {already_clustered, - "You are already clustered with the nodes you " - "have selected"}}); + true -> e(already_clustered); false -> ok end, @@ -232,11 +225,7 @@ reset(Force) -> case is_disc_and_clustered() andalso [node()] =:= clustered_disc_nodes() of - true -> throw({error, {standalone_ram_node, - "You can't reset a node if it's the " - "only disc node in a cluster. Please " - "convert another node of the cluster " - "to a disc node first."}}); + true -> e(resetting_only_disc_node); false -> ok end, leave_cluster(), @@ -259,31 +248,17 @@ change_cluster_node_type(Type) -> ensure_mnesia_dir(), ensure_mnesia_not_running(), case is_clustered() of - false -> throw({error, {not_clustered, - "Non-clustered nodes can only be disc nodes"}}); + false -> e(not_clustered); true -> ok end, {_, _, RunningNodes} = case discover_cluster(all_clustered_nodes()) of - {ok, Status} -> - Status; - {error, _Reason} -> - throw({error, - {cannot_connect_to_cluster, - "Could not connect to the cluster nodes present in " - "this node status file. If the cluster has changed, " - "you can use the \"update_cluster_nodes\" command to " - "point to the new cluster nodes"}}) - end, + {ok, Status} -> Status; + {error, _Reason} -> e(cannot_connect_to_cluster) + end, Node = case RunningNodes of - [] -> - throw({error, - {no_online_cluster_nodes, - "Could not find any online cluster nodes. If the " - "cluster has changed, you can use the 'recluster' " - "command."}}); - [Node0|_] -> - Node0 + [] -> e(no_online_cluster_nodes); + [Node0|_] -> Node0 end, ok = reset(false), ok = join_cluster(Node, case Type of @@ -297,12 +272,8 @@ update_cluster_nodes(DiscoveryNode) -> Status = {AllNodes, _, _} = case discover_cluster(DiscoveryNode) of - {ok, Status0} -> - Status0; - {error, _Reason} -> - throw({error, - {cannot_connect_to_node, - "Could not connect to the cluster node provided"}}) + {ok, Status0} -> Status0; + {error, _Reason} -> e(cannot_connect_to_node) end, case ordsets:is_element(node(), AllNodes) of true -> @@ -313,10 +284,7 @@ update_cluster_nodes(DiscoveryNode) -> rabbit_node_monitor:write_cluster_status(Status), init_db_with_mnesia(AllNodes, is_disc_node(), false); false -> - throw({error, - {inconsistent_cluster, - "The nodes provided do not have this node as part of " - "the cluster"}}) + e(inconsistent_cluster) end, ok. @@ -330,31 +298,19 @@ update_cluster_nodes(DiscoveryNode) -> forget_cluster_node(Node, RemoveWhenOffline) -> case ordsets:is_element(Node, all_clustered_nodes()) of true -> ok; - false -> throw({error, {not_a_cluster_node, - "The node selected is not in the cluster."}}) + false -> e(not_a_cluster_node) end, case {mnesia:system_info(is_running), RemoveWhenOffline} of - {yes, true} -> throw({error, {online_node_offline_flag, - "You set the --offline flag, which is " - "used to remove nodes remotely from " - "offline nodes, but this node is " - "online. "}}); + {yes, true} -> e(online_node_offline_flag); _ -> ok end, case remove_node_if_mnesia_running(Node) of ok -> ok; + {error, mnesia_not_running} when RemoveWhenOffline -> + remove_node_offline_node(Node); {error, mnesia_not_running} -> - case RemoveWhenOffline of - true -> remove_node_offline_node(Node); - false -> throw({error, - {offline_node_no_offline_flag, - "You are trying to remove a node from an " - "offline node. That's dangerous, but can be " - "done with the --offline flag. Please consult " - "the manual for rabbitmqctl for more " - "information."}}) - end; + e(offline_node_no_offline_flag); Err = {error, _} -> throw(Err) end. @@ -382,20 +338,10 @@ remove_node_offline_node(Node) -> after stop_mnesia() end; - _ -> throw({error, - {not_last_node_to_go_down, - "The node you're trying to remove from was not " - "the last to go down (excluding the node you are " - "removing). Please use the the last node to go " - "down to remove nodes when the cluster is " - "offline."}}) + _ -> e(not_last_node_to_go_down) end; {_, _} -> - throw({error, - {removing_node_from_offline_node, - "To remove a node remotely from an offline node, the node " - "you're removing from must be a disc node and all the " - "other nodes must be offline."}}) + e(removing_node_from_offline_node) end. @@ -424,8 +370,8 @@ is_clustered() -> is_disc_and_clustered() -> is_disc_node() andalso is_clustered(). -%% Functions that retrieve the nodes in the cluster will rely on the status file -%% if offline. +%% Functions that retrieve the nodes in the cluster will rely on the +%% status file if offline. all_clustered_nodes() -> cluster_status(all). @@ -440,8 +386,9 @@ running_clustered_disc_nodes() -> {_, DiscNodes, RunningNodes} = cluster_status(), ordsets:intersection(DiscNodes, RunningNodes). -%% This function is the actual source of information, since it gets the data -%% from mnesia. Obviously it'll work only when mnesia is running. +%% This function is the actual source of information, since it gets +%% the data from mnesia. Obviously it'll work only when mnesia is +%% running. mnesia_nodes() -> case mnesia:system_info(is_running) of no -> @@ -777,7 +724,8 @@ discover_cluster(Node) -> {error, {cannot_discover_cluster, "You provided the current node as node to cluster with"}}; false -> - case rpc:call(Node, rabbit_mnesia, cluster_status_from_mnesia, []) of + case rpc:call(Node, + rabbit_mnesia, cluster_status_from_mnesia, []) of {badrpc, _Reason} -> OfflineError; {error, mnesia_not_running} -> OfflineError; {ok, Res} -> {ok, Res} @@ -1060,16 +1008,11 @@ leave_cluster() -> case {is_clustered(), running_nodes(ordsets:del_element(node(), all_clustered_nodes()))} of - {false, []} -> - ok; - {_, AllNodes} -> - case lists:any(fun leave_cluster/1, AllNodes) of - true -> ok; - false -> throw({error, - {no_running_cluster_nodes, - "You cannot leave a cluster if no online " - "nodes are present"}}) - end + {false, []} -> ok; + {_, AllNodes} -> case lists:any(fun leave_cluster/1, AllNodes) of + true -> ok; + false -> e(no_running_cluster_nodes) + end end. leave_cluster(Node) -> @@ -1185,3 +1128,46 @@ find_good_node([Node | Nodes]) -> ok -> {ok, Node} end end. + +e(Tag) -> throw({error, {Tag, error_description(Tag)}}). + +error_description(clustering_only_disc_node) -> + "You cannot cluster a node if it is the only disc node in its existing " + " cluster. If new nodes joined while this node was offline, use " + "\"update_cluster_nodes\" to add them manually."; +error_description(resetting_only_disc_node) -> + "You cannot reset a node when it is the only disc node in a cluster. " + "Please convert another node of the cluster to a disc node first."; +error_description(already_clustered) -> + "You are already clustered with the nodes you have selected."; +error_description(not_clustered) -> + "Non-clustered nodes can only be disc nodes."; +error_description(cannot_connect_to_cluster) -> + "Could not connect to the cluster nodes present in this node's " + "status file. If the cluster has changed, you can use the " + "\"update_cluster_nodes\" command to point to the new cluster nodes."; +error_description(no_online_cluster_nodes) -> + "Could not find any online cluster nodes. If the cluster has changed, " + "you can use the 'recluster' command."; +error_description(cannot_connect_to_node) -> + "Could not connect to the cluster node provided."; +error_description(inconsistent_cluster) -> + "The nodes provided do not have this node as part of the cluster."; +error_description(not_a_cluster_node) -> + "The node selected is not in the cluster."; +error_description(online_node_offline_flag) -> + "You set the --offline flag, which is used to remove nodes remotely from " + "offline nodes, but this node is online."; +error_description(offline_node_no_offline_flag) -> + "You are trying to remove a node from an offline node. That is dangerous, " + "but can be done with the --offline flag. Please consult the manual " + "for rabbitmqctl for more information."; +error_description(not_last_node_to_go_down) -> + "The node you're trying to remove from was not the last to go down " + "(excluding the node you are removing). Please use the the last node " + "to go down to remove nodes when the cluster is offline."; +error_description(removing_node_from_offline_node) -> + "To remove a node remotely from an offline node, the node you're removing " + "from must be a disc node and all the other nodes must be offline."; +error_description(no_running_cluster_nodes) -> + "You cannot leave a cluster if no online nodes are present.". -- cgit v1.2.1 From b55cd7f597ad377e61d78002fab73c3f575ed9ce Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Tue, 18 Sep 2012 10:24:59 +0100 Subject: Allow direct connections to supply a password --- src/rabbit_direct.erl | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/rabbit_direct.erl b/src/rabbit_direct.erl index a669a2b3..a3431321 100644 --- a/src/rabbit_direct.erl +++ b/src/rabbit_direct.erl @@ -31,7 +31,8 @@ -spec(force_event_refresh/0 :: () -> 'ok'). -spec(list/0 :: () -> [pid()]). -spec(list_local/0 :: () -> [pid()]). --spec(connect/5 :: ((rabbit_types:username() | rabbit_types:user()), +-spec(connect/5 :: ((rabbit_types:username() | rabbit_types:user() | + {rabbit_types:username(), rabbit_types:password()}), rabbit_types:vhost(), rabbit_types:protocol(), pid(), rabbit_event:event_props()) -> {'ok', {rabbit_types:user(), @@ -74,10 +75,17 @@ connect(User = #user{}, VHost, Protocol, Pid, Infos) -> {error, access_refused} end; +connect({Username, Password}, VHost, Protocol, Pid, Infos) -> + connect0(check_user_pass_login, Username, Password, VHost, Protocol, Pid, + Infos); + connect(Username, VHost, Protocol, Pid, Infos) -> + connect0(check_user_login, Username, [], VHost, Protocol, Pid, Infos). + +connect0(FunctionName, U, P, VHost, Protocol, Pid, Infos) -> case rabbit:is_running() of true -> - case rabbit_access_control:check_user_login(Username, []) of + case rabbit_access_control:FunctionName(U, P) of {ok, User} -> connect(User, VHost, Protocol, Pid, Infos); {refused, _M, _A} -> {error, auth_failure} end; @@ -85,6 +93,7 @@ connect(Username, VHost, Protocol, Pid, Infos) -> {error, broker_not_found_on_node} end. + start_channel(Number, ClientChannelPid, ConnPid, ConnName, Protocol, User, VHost, Capabilities, Collector) -> {ok, _, {ChannelPid, _}} = -- cgit v1.2.1 From e34e20b6aa39da232afb119b7968f85c074ab9e1 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Tue, 18 Sep 2012 10:59:42 +0100 Subject: ditch the booleans to control decisions on disc/ram nodes --- src/rabbit_control_main.erl | 7 ++- src/rabbit_mnesia.erl | 108 +++++++++++++++++++++----------------------- src/rabbit_node_monitor.erl | 23 +++++----- src/rabbit_tests.erl | 12 ++--- src/rabbit_upgrade.erl | 21 +++++---- 5 files changed, 86 insertions(+), 85 deletions(-) diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index bd01a1b1..a6c4fe67 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -247,9 +247,12 @@ action(force_reset, Node, [], _Opts, Inform) -> action(join_cluster, Node, [ClusterNodeS], Opts, Inform) -> ClusterNode = list_to_atom(ClusterNodeS), - DiscNode = not proplists:get_bool(?RAM_OPT, Opts), + NodeType = case proplists:get_bool(?RAM_OPT, Opts) of + true -> ram; + false -> disc + end, Inform("Clustering node ~p with ~p", [Node, ClusterNode]), - rpc_call(Node, rabbit_mnesia, join_cluster, [ClusterNode, DiscNode]); + rpc_call(Node, rabbit_mnesia, join_cluster, [ClusterNode, NodeType]); action(change_cluster_node_type, Node, ["ram"], _Opts, Inform) -> Inform("Turning ~p into a ram node", [Node]), diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index f19046a0..fd4cc260 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -31,7 +31,7 @@ all_clustered_nodes/0, clustered_disc_nodes/0, running_clustered_nodes/0, - is_disc_node/0, + node_type/0, dir/0, table_names/0, wait_for_tables/1, @@ -72,7 +72,7 @@ %% Main interface -spec(init/0 :: () -> 'ok'). --spec(join_cluster/2 :: ([node()], boolean()) -> 'ok'). +-spec(join_cluster/2 :: ([node()], node_type()) -> 'ok'). -spec(reset/0 :: () -> 'ok'). -spec(force_reset/0 :: () -> 'ok'). -spec(update_cluster_nodes/1 :: (node()) -> 'ok'). @@ -87,14 +87,14 @@ -spec(all_clustered_nodes/0 :: () -> [node()]). -spec(clustered_disc_nodes/0 :: () -> [node()]). -spec(running_clustered_nodes/0 :: () -> [node()]). --spec(is_disc_node/0 :: () -> boolean()). +-spec(node_type/0 :: () -> node_type()). -spec(dir/0 :: () -> file:filename()). -spec(table_names/0 :: () -> [atom()]). -spec(cluster_status_from_mnesia/0 :: () -> {'ok', cluster_status()} | {'error', any()}). %% Operations on the db and utils, mainly used in `rabbit_upgrade' and `rabbit' --spec(init_db/3 :: ([node()], boolean(), boolean()) -> 'ok'). +-spec(init_db/3 :: ([node()], node_type(), boolean()) -> 'ok'). -spec(empty_ram_only_tables/0 :: () -> 'ok'). -spec(create_tables/0 :: () -> 'ok'). -spec(copy_db/1 :: (file:filename()) -> rabbit_types:ok_or_error(any())). @@ -123,7 +123,7 @@ init() -> ensure_mnesia_dir(), case is_virgin_node() of true -> init_from_config(); - false -> init(is_disc_node(), all_clustered_nodes()) + false -> init(node_type(), all_clustered_nodes()) end, %% We intuitively expect the global name server to be synced when %% Mnesia is up. In fact that's not guaranteed to be the case - @@ -131,18 +131,18 @@ init() -> ok = global:sync(), ok. -init(WantDiscNode, AllNodes) -> - init_db_and_upgrade(AllNodes, WantDiscNode, WantDiscNode). +init(NodeType, AllNodes) -> + init_db_and_upgrade(AllNodes, NodeType, NodeType =:= disc). init_from_config() -> - {ok, {TryNodes, WantDiscNode}} = + {ok, {TryNodes, NodeType}} = application:get_env(rabbit, cluster_nodes), case find_good_node(TryNodes -- [node()]) of {ok, Node} -> rabbit_log:info("Node '~p' selected for clustering from " "configuration~n", [Node]), {ok, {_, DiscNodes, _}} = discover_cluster(Node), - init_db_and_upgrade(DiscNodes, WantDiscNode, false), + init_db_and_upgrade(DiscNodes, NodeType, false), rabbit_node_monitor:notify_joined_cluster(); none -> rabbit_log:warning("Could not find any suitable node amongst the " @@ -165,7 +165,7 @@ init_from_config() -> %% Note that we make no attempt to verify that the nodes provided are %% all in the same cluster, we simply pick the first online node and %% we cluster to its cluster. -join_cluster(DiscoveryNode, WantDiscNode) -> +join_cluster(DiscoveryNode, NodeType) -> case is_disc_and_clustered() andalso [node()] =:= clustered_disc_nodes() of true -> e(clustering_only_disc_node); _ -> ok @@ -193,7 +193,7 @@ join_cluster(DiscoveryNode, WantDiscNode) -> rabbit_misc:local_info_msg("Clustering with ~p~n", [ClusterNodes]), %% Join the cluster - ok = init_db_with_mnesia(ClusterNodes, WantDiscNode, false), + ok = init_db_with_mnesia(ClusterNodes, NodeType, false), rabbit_node_monitor:notify_joined_cluster(), @@ -221,7 +221,7 @@ reset(Force) -> %% We don't need to check for consistency because we are %% resetting. Force=true here so that reset still works %% when clustered with a node which is down. - init_db_with_mnesia(AllNodes, is_disc_node(), false, true), + init_db_with_mnesia(AllNodes, node_type(), false, true), case is_disc_and_clustered() andalso [node()] =:= clustered_disc_nodes() of @@ -261,10 +261,7 @@ change_cluster_node_type(Type) -> [Node0|_] -> Node0 end, ok = reset(false), - ok = join_cluster(Node, case Type of - ram -> false; - disc -> true - end). + ok = join_cluster(Node, Type). update_cluster_nodes(DiscoveryNode) -> ensure_mnesia_not_running(), @@ -282,7 +279,7 @@ update_cluster_nodes(DiscoveryNode) -> %% nodes mnesia:delete_schema([node()]), rabbit_node_monitor:write_cluster_status(Status), - init_db_with_mnesia(AllNodes, is_disc_node(), false); + init_db_with_mnesia(AllNodes, node_type(), false); false -> e(inconsistent_cluster) end, @@ -317,8 +314,8 @@ forget_cluster_node(Node, RemoveWhenOffline) -> remove_node_offline_node(Node) -> case {ordsets:del_element(Node, running_nodes(all_clustered_nodes())), - is_disc_node()} of - {[], true} -> + node_type()} of + {[], disc} -> %% Note that while we check if the nodes was the last to %% go down, apart from the node we're removing from, this %% is still unsafe. Consider the situation in which A and @@ -368,7 +365,7 @@ is_clustered() -> Nodes = all_clustered_nodes(), [node()] =/= Nodes andalso [] =/= Nodes. -is_disc_and_clustered() -> is_disc_node() andalso is_clustered(). +is_disc_and_clustered() -> node_type() =:= disc andalso is_clustered(). %% Functions that retrieve the nodes in the cluster will rely on the %% status file if offline. @@ -398,12 +395,12 @@ mnesia_nodes() -> %% `init_db/3' hasn't been run yet. In other words, either %% we are a virgin node or a restarted RAM node. In both %% cases we're not interested in what mnesia has to say. - IsDiscNode = mnesia:system_info(use_dir), + NodeType = case mnesia:system_info(use_dir) of + true -> disc; + false -> ram + end, Tables = mnesia:system_info(tables), - {Table, _} = case table_definitions(case IsDiscNode of - true -> disc; - false -> ram - end) of [T|_] -> T end, + {Table, _} = case table_definitions(NodeType) of [T|_] -> T end, case lists:member(Table, Tables) of true -> AllNodes = @@ -411,9 +408,9 @@ mnesia_nodes() -> DiscCopies = ordsets:from_list( mnesia:table_info(schema, disc_copies)), DiscNodes = - case IsDiscNode of - true -> ordsets:add_element(node(), DiscCopies); - false -> DiscCopies + case NodeType of + disc -> ordsets:add_element(node(), DiscCopies); + ram -> DiscCopies end, {ok, {AllNodes, DiscNodes}}; false -> @@ -465,9 +462,12 @@ node_info() -> {erlang:system_info(otp_release), rabbit_misc:version(), cluster_status_from_mnesia()}. -is_disc_node() -> +node_type() -> DiscNodes = clustered_disc_nodes(), - DiscNodes =:= [] orelse ordsets:is_element(node(), DiscNodes). + case DiscNodes =:= [] orelse ordsets:is_element(node(), DiscNodes) of + true -> disc; + false -> ram + end. dir() -> mnesia:system_info(directory). @@ -481,21 +481,21 @@ table_names() -> [Tab || {Tab, _} <- table_definitions()]. %% schema if there is the need to and catching up if there are other %% nodes in the cluster already. It also updates the cluster status %% file. -init_db(ClusterNodes, WantDiscNode, Force) -> +init_db(ClusterNodes, NodeType, Force) -> Nodes = change_extra_db_nodes(ClusterNodes, Force), %% Note that we use `system_info' here and not the cluster status %% since when we start rabbit for the first time the cluster %% status will say we are a disc node but the tables won't be %% present yet. WasDiscNode = mnesia:system_info(use_dir), - case {Nodes, WasDiscNode, WantDiscNode} of - {[], _, false} -> + case {Nodes, WasDiscNode, NodeType} of + {[], _, ram} -> %% Standalone ram node, we don't want that throw({error, cannot_create_standalone_ram_node}); - {[], false, true} -> + {[], false, disc} -> %% RAM -> disc, starting from scratch ok = create_schema(); - {[], true, true} -> + {[], true, disc} -> %% First disc node up ok; {[AnotherNode | _], _, _} -> @@ -508,19 +508,19 @@ init_db(ClusterNodes, WantDiscNode, Force) -> %% first when moving to RAM mnesia will loudly complain %% since it doesn't make much sense to do that. But when %% moving to disc, we need to move the schema first. - case WantDiscNode of - true -> create_local_table_copy(schema, disc_copies), - create_local_table_copies(disc); - false -> create_local_table_copies(ram), - create_local_table_copy(schema, ram_copies) + case NodeType of + disc -> create_local_table_copy(schema, disc_copies), + create_local_table_copies(disc); + ram -> create_local_table_copies(ram), + create_local_table_copy(schema, ram_copies) end end, ensure_schema_integrity(), rabbit_node_monitor:update_cluster_status(), ok. -init_db_and_upgrade(ClusterNodes, WantDiscNode, Force) -> - ok = init_db(ClusterNodes, WantDiscNode, Force), +init_db_and_upgrade(ClusterNodes, NodeType, Force) -> + ok = init_db(ClusterNodes, NodeType, Force), ok = case rabbit_upgrade:maybe_upgrade_local() of ok -> ok; starting_from_scratch -> rabbit_version:record_desired(); @@ -528,24 +528,24 @@ init_db_and_upgrade(ClusterNodes, WantDiscNode, Force) -> end, %% `maybe_upgrade_local' restarts mnesia, so ram nodes will forget %% about the cluster - case WantDiscNode of - false -> start_mnesia(), - change_extra_db_nodes(ClusterNodes, true), - wait_for_replicated_tables(); - true -> ok + case NodeType of + disc -> start_mnesia(), + change_extra_db_nodes(ClusterNodes, true), + wait_for_replicated_tables(); + ram -> ok end, ok. -init_db_with_mnesia(ClusterNodes, WantDiscNode, CheckConsistency, Force) -> +init_db_with_mnesia(ClusterNodes, NodeType, CheckConsistency, Force) -> start_mnesia(CheckConsistency), try - init_db_and_upgrade(ClusterNodes, WantDiscNode, Force) + init_db_and_upgrade(ClusterNodes, NodeType, Force) after stop_mnesia() end. -init_db_with_mnesia(ClusterNodes, WantDiscNode, Force) -> - init_db_with_mnesia(ClusterNodes, WantDiscNode, true, Force). +init_db_with_mnesia(ClusterNodes, NodeType, Force) -> + init_db_with_mnesia(ClusterNodes, NodeType, true, Force). ensure_mnesia_dir() -> MnesiaDir = dir() ++ "/", @@ -878,11 +878,7 @@ check_table_content(Tab, TabDef) -> end. check_tables(Fun) -> - case [Error || {Tab, TabDef} <- table_definitions( - case is_disc_node() of - true -> disc; - false -> ram - end), + case [Error || {Tab, TabDef} <- table_definitions(node_type()), case Fun(Tab, TabDef) of ok -> Error = none, false; {error, Error} -> true diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index 64c801f2..846b2ac8 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -163,7 +163,7 @@ joined_cluster(Node, IsDiscNode) -> gen_server:cast(?SERVER, {rabbit_join, Node, IsDiscNode}). notify_joined_cluster() -> - cluster_multicall(joined_cluster, [node(), rabbit_mnesia:is_disc_node()]), + cluster_multicall(joined_cluster, [node(), rabbit_mnesia:node_type()]), ok. left_cluster(Node) -> @@ -178,7 +178,7 @@ node_up(Node, IsDiscNode) -> gen_server:cast(?SERVER, {node_up, Node, IsDiscNode}). notify_node_up() -> - Nodes = cluster_multicall(node_up, [node(), rabbit_mnesia:is_disc_node()]), + Nodes = cluster_multicall(node_up, [node(), rabbit_mnesia:node_type()]), %% register other active rabbits with this rabbit [ node_up(N, ordsets:is_element(N, rabbit_mnesia:clustered_disc_nodes())) || N <- Nodes ], @@ -199,29 +199,28 @@ handle_call(_Request, _From, State) -> %% Note: when updating the status file, we can't simply write the mnesia %% information since the message can (and will) overtake the mnesia propagation. -handle_cast({node_up, Node, IsDiscNode}, State) -> +handle_cast({node_up, Node, NodeType}, State) -> case is_already_monitored({rabbit, Node}) of true -> {noreply, State}; false -> rabbit_log:info("rabbit on node ~p up~n", [Node]), {AllNodes, DiscNodes, RunningNodes} = read_cluster_status(), write_cluster_status({ordsets:add_element(Node, AllNodes), - case IsDiscNode of - true -> ordsets:add_element( - Node, DiscNodes); - false -> DiscNodes + case NodeType of + disc -> ordsets:add_element( + Node, DiscNodes); + ram -> DiscNodes end, ordsets:add_element(Node, RunningNodes)}), erlang:monitor(process, {rabbit, Node}), ok = handle_live_rabbit(Node), {noreply, State} end; -handle_cast({joined_cluster, Node, IsDiscNode}, State) -> +handle_cast({joined_cluster, Node, NodeType}, State) -> {AllNodes, DiscNodes, RunningNodes} = read_cluster_status(), write_cluster_status({ordsets:add_element(Node, AllNodes), - case IsDiscNode of - true -> ordsets:add_element(Node, - DiscNodes); - false -> DiscNodes + case NodeType of + disc -> ordsets:add_element(Node, DiscNodes); + ram -> DiscNodes end, RunningNodes}), {noreply, State}; diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 3cc0e5db..85172461 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -1512,15 +1512,15 @@ clean_logs(Files, Suffix) -> ok. assert_ram_node() -> - case rabbit_mnesia:is_disc_node() of - true -> exit('not_ram_node'); - false -> ok + case rabbit_mnesia:node_type() of + disc -> exit('not_ram_node'); + ram -> ok end. assert_disc_node() -> - case rabbit_mnesia:is_disc_node() of - true -> ok; - false -> exit('not_disc_node') + case rabbit_mnesia:node_type() of + disc -> ok; + ram -> exit('not_disc_node') end. delete_file(File) -> diff --git a/src/rabbit_upgrade.erl b/src/rabbit_upgrade.erl index 3fbfeed0..681d3d3e 100644 --- a/src/rabbit_upgrade.erl +++ b/src/rabbit_upgrade.erl @@ -148,10 +148,10 @@ upgrade_mode(AllNodes) -> case nodes_running(AllNodes) of [] -> AfterUs = rabbit_mnesia:running_clustered_nodes() -- [node()], - case {is_disc_node_legacy(), AfterUs} of - {true, []} -> + case {node_type_legacy(), AfterUs} of + {disc, []} -> primary; - {true, _} -> + {disc, _} -> Filename = rabbit_node_monitor:running_nodes_filename(), die("Cluster upgrade needed but other disc nodes shut " "down after this one.~nPlease first start the last " @@ -160,7 +160,7 @@ upgrade_mode(AllNodes) -> "all~nshow this message. In which case, remove " "the lock file on one of them and~nstart that node. " "The lock file on this node is:~n~n ~s ", [Filename]); - {false, _} -> + {ram, _} -> die("Cluster upgrade needed but this is a ram node.~n" "Please first start the last disc node to shut down.", []) @@ -216,11 +216,11 @@ force_tables() -> secondary_upgrade(AllNodes) -> %% must do this before we wipe out schema - IsDiscNode = is_disc_node_legacy(), + NodeType = node_type_legacy(), rabbit_misc:ensure_ok(mnesia:delete_schema([node()]), cannot_delete_schema), rabbit_misc:ensure_ok(mnesia:start(), cannot_start_mnesia), - ok = rabbit_mnesia:init_db(AllNodes, IsDiscNode, true), + ok = rabbit_mnesia:init_db(AllNodes, NodeType, true), ok = rabbit_version:record_desired_for_scope(mnesia), ok. @@ -268,13 +268,16 @@ lock_filename() -> lock_filename(dir()). lock_filename(Dir) -> filename:join(Dir, ?LOCK_FILENAME). backup_dir() -> dir() ++ "-upgrade-backup". -is_disc_node_legacy() -> +node_type_legacy() -> %% This is pretty ugly but we can't start Mnesia and ask it (will %% hang), we can't look at the config file (may not include us %% even if we're a disc node). We also can't use - %% rabbit_mnesia:is_disc_node/0 because that will give false + %% rabbit_mnesia:node_type/0 because that will give false %% postivies on Rabbit up to 2.5.1. - filelib:is_regular(filename:join(dir(), "rabbit_durable_exchange.DCD")). + case filelib:is_regular(filename:join(dir(), "rabbit_durable_exchange.DCD")) of + true -> disc; + false -> ram + end. %% NB: we cannot use rabbit_log here since it may not have been %% started yet -- cgit v1.2.1 From 367b27c8c7c1149fe9944876593dd1a8bfb0035a Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Tue, 18 Sep 2012 11:40:12 +0100 Subject: update rabbit_app.in --- ebin/rabbit_app.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ebin/rabbit_app.in b/ebin/rabbit_app.in index 78842281..9b1ff8bd 100644 --- a/ebin/rabbit_app.in +++ b/ebin/rabbit_app.in @@ -33,7 +33,7 @@ {default_user_tags, [administrator]}, {default_vhost, <<"/">>}, {default_permissions, [<<".*">>, <<".*">>, <<".*">>]}, - {cluster_nodes, {[], true}}, + {cluster_nodes, {[], disc}}, {server_properties, []}, {collect_statistics, none}, {collect_statistics_interval, 5000}, -- cgit v1.2.1 From 09c3ea55dd3f25bb88b7694db5c67827e71ddc2f Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Tue, 18 Sep 2012 11:42:15 +0100 Subject: equality => pattern match --- src/rabbit_mnesia.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index fd4cc260..0cea9ecc 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -719,11 +719,11 @@ discover_cluster(Node) -> OfflineError = {error, {cannot_discover_cluster, "The nodes provided is either offline or not running"}}, - case Node =:= node() of - true -> + case node() of + Node-> {error, {cannot_discover_cluster, "You provided the current node as node to cluster with"}}; - false -> + _ -> case rpc:call(Node, rabbit_mnesia, cluster_status_from_mnesia, []) of {badrpc, _Reason} -> OfflineError; -- cgit v1.2.1 From ae391b362ac1f447e5b4b975b64c71a0b592b088 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 18 Sep 2012 11:42:55 +0100 Subject: Pull our attempts to list all nodes out of transactions and loops. --- src/rabbit_mirror_queue_misc.erl | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index f53319c0..603a490b 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -73,6 +73,7 @@ remove_from_queue(QueueName, DeadGMPids) -> remove_from_queue0(QueueName, DeadGMPids) -> DeadNodes = [node(DeadGMPid) || DeadGMPid <- DeadGMPids], + ClusterNodes = clusterable_nodes() -- DeadNodes, rabbit_misc:execute_mnesia_transaction( fun () -> %% Someone else could have deleted the queue before we @@ -99,9 +100,10 @@ remove_from_queue0(QueueName, DeadGMPids) -> %% "exactly" mode can cause this to %% happen. {_, OldNodes} = actual_queue_nodes(Q1), - {_, NewNodes} = suggested_queue_nodes(Q1), + {_, NewNodes} = suggested_queue_nodes( + Q1, ClusterNodes), {ok, QPid1, [QPid | SPids] -- Alive, - (NewNodes -- OldNodes) -- DeadNodes}; + NewNodes -- OldNodes}; _ -> %% Master has changed, and we're not it, %% so leave alone to allow the promoted @@ -113,12 +115,14 @@ remove_from_queue0(QueueName, DeadGMPids) -> end). on_node_up() -> + ClusterNodes = clusterable_nodes(), QNames = rabbit_misc:execute_mnesia_transaction( fun () -> mnesia:foldl( fun (Q = #amqqueue{name = QName}, QNames0) -> - {_MNode, SNodes} = suggested_queue_nodes(Q), + {_MNode, SNodes} = suggested_queue_nodes( + Q, ClusterNodes), case lists:member(node(), SNodes) of true -> [QName | QNames0]; false -> QNames0 @@ -229,15 +233,21 @@ promote_slave([SPid | SPids]) -> %% the one to promote is the oldest. {SPid, SPids}. -suggested_queue_nodes(Q) -> +suggested_queue_nodes(Q) -> suggested_queue_nodes(Q, clusterable_nodes()). + +%% This variant exists so we can pull a call to clusterable_nodes() +%% out of a loop or transaction or both. +suggested_queue_nodes(Q, ClusterNodes) -> {MNode0, SNodes} = actual_queue_nodes(Q), MNode = case MNode0 of none -> node(); _ -> MNode0 end, suggested_queue_nodes(policy(<<"ha-mode">>, Q), policy(<<"ha-params">>, Q), - {MNode, SNodes}, clusterable_nodes()). + {MNode, SNodes}, ClusterNodes). +%% TODO we should probably just redefine +%% rabbit_mnesia:running_clustered_nodes/0? Waiting on Francesco. clusterable_nodes() -> %% We may end up here via on_node_up/0, in which case we are still %% booting - rabbit_mnesia:running_clustered_nodes/0 will report -- cgit v1.2.1 From 4c6090372bbaac96b5569bf5281c6861c636e7e4 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Tue, 18 Sep 2012 12:16:03 +0100 Subject: never use lists constructs when dealing with ordsets... ...except in the `is_empty' macro, that cuts down a lot of cruft when ispecting sets for emptiness. --- src/rabbit_mnesia.erl | 97 +++++++++++++++++++++++++++++---------------------- 1 file changed, 55 insertions(+), 42 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 0cea9ecc..b0d7c9b8 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -60,6 +60,9 @@ -include("rabbit.hrl"). +%% This is safe because ordsets are lists. +-define(empty_set(Set), (Set =:= [])). + %%---------------------------------------------------------------------------- -ifdef(use_specs). @@ -67,12 +70,12 @@ -export_type([node_type/0, cluster_status/0]). -type(node_type() :: disc | ram). --type(cluster_status() :: {ordsets:ordset(node()), ordsets:ordset(node()), - ordsets:ordset(node())}). +-type(node_set() :: ordsets:ordset(node())). +-type(cluster_status() :: {node_set(), node_set(), node_set()}). %% Main interface -spec(init/0 :: () -> 'ok'). --spec(join_cluster/2 :: ([node()], node_type()) -> 'ok'). +-spec(join_cluster/2 :: (node(), node_type()) -> 'ok'). -spec(reset/0 :: () -> 'ok'). -spec(force_reset/0 :: () -> 'ok'). -spec(update_cluster_nodes/1 :: (node()) -> 'ok'). @@ -80,13 +83,13 @@ -spec(forget_cluster_node/2 :: (node(), boolean()) -> 'ok'). %% Various queries to get the status of the db --spec(status/0 :: () -> [{'nodes', [{node_type(), [node()]}]} | - {'running_nodes', [node()]}]). +-spec(status/0 :: () -> [{'nodes', [{node_type(), node_set()}]} | + {'running_nodes', node_set()}]). -spec(is_db_empty/0 :: () -> boolean()). -spec(is_clustered/0 :: () -> boolean()). --spec(all_clustered_nodes/0 :: () -> [node()]). --spec(clustered_disc_nodes/0 :: () -> [node()]). --spec(running_clustered_nodes/0 :: () -> [node()]). +-spec(all_clustered_nodes/0 :: () -> node_set()). +-spec(clustered_disc_nodes/0 :: () -> node_set()). +-spec(running_clustered_nodes/0 :: () -> node_set()). -spec(node_type/0 :: () -> node_type()). -spec(dir/0 :: () -> file:filename()). -spec(table_names/0 :: () -> [atom()]). @@ -94,7 +97,7 @@ {'error', any()}). %% Operations on the db and utils, mainly used in `rabbit_upgrade' and `rabbit' --spec(init_db/3 :: ([node()], node_type(), boolean()) -> 'ok'). +-spec(init_db/3 :: (node_set(), node_type(), boolean()) -> 'ok'). -spec(empty_ram_only_tables/0 :: () -> 'ok'). -spec(create_tables/0 :: () -> 'ok'). -spec(copy_db/1 :: (file:filename()) -> rabbit_types:ok_or_error(any())). @@ -148,7 +151,7 @@ init_from_config() -> rabbit_log:warning("Could not find any suitable node amongst the " "ones provided in the configuration: ~p~n", [TryNodes]), - init(true, [node()]) + init(true, ordsets:from_list([node()])) end. %% Make the node join a cluster. The node will be reset automatically @@ -166,7 +169,7 @@ init_from_config() -> %% all in the same cluster, we simply pick the first online node and %% we cluster to its cluster. join_cluster(DiscoveryNode, NodeType) -> - case is_disc_and_clustered() andalso [node()] =:= clustered_disc_nodes() of + case is_disc_and_clustered() andalso is_only_disc_node() of true -> e(clustering_only_disc_node); _ -> ok end, @@ -222,8 +225,7 @@ reset(Force) -> %% resetting. Force=true here so that reset still works %% when clustered with a node which is down. init_db_with_mnesia(AllNodes, node_type(), false, true), - case is_disc_and_clustered() andalso - [node()] =:= clustered_disc_nodes() + case is_disc_and_clustered() andalso is_only_disc_node() of true -> e(resetting_only_disc_node); false -> ok @@ -256,7 +258,7 @@ change_cluster_node_type(Type) -> {ok, Status} -> Status; {error, _Reason} -> e(cannot_connect_to_cluster) end, - Node = case RunningNodes of + Node = case ordsets:to_list(RunningNodes) of [] -> e(no_online_cluster_nodes); [Node0|_] -> Node0 end, @@ -315,7 +317,7 @@ forget_cluster_node(Node, RemoveWhenOffline) -> remove_node_offline_node(Node) -> case {ordsets:del_element(Node, running_nodes(all_clustered_nodes())), node_type()} of - {[], disc} -> + {Nodes, disc} when ?empty_set(Nodes) -> %% Note that while we check if the nodes was the last to %% go down, apart from the node we're removing from, this %% is still unsafe. Consider the situation in which A and @@ -324,18 +326,18 @@ remove_node_offline_node(Node) -> %% and B goes down. In this case, C is the second-to-last, %% but we don't know that and we'll remove B from A %% anyway, even if that will lead to bad things. - case ordsets:subtract(running_clustered_nodes(), - ordsets:from_list([node(), Node])) of - [] -> start_mnesia(), - try - [mnesia:force_load_table(T) || - T <- rabbit_mnesia:table_names()], - forget_cluster_node(Node, false), - ensure_mnesia_running() - after - stop_mnesia() - end; - _ -> e(not_last_node_to_go_down) + case ?empty_set(ordsets:subtract(running_clustered_nodes(), + ordsets:from_list([node(), Node]))) + of true -> start_mnesia(), + try + [mnesia:force_load_table(T) || + T <- rabbit_mnesia:table_names()], + forget_cluster_node(Node, false), + ensure_mnesia_running() + after + stop_mnesia() + end; + false -> e(not_last_node_to_go_down) end; {_, _} -> e(removing_node_from_offline_node) @@ -347,8 +349,8 @@ remove_node_offline_node(Node) -> %%---------------------------------------------------------------------------- status() -> - IfNonEmpty = fun (_, []) -> []; - (Type, Nodes) -> [{Type, Nodes}] + IfNonEmpty = fun (_, Nodes) when ?empty_set(Nodes) -> []; + (Type, Nodes) -> [{Type, ordsets:to_list(Nodes)}] end, [{nodes, (IfNonEmpty(disc, clustered_disc_nodes()) ++ IfNonEmpty(ram, clustered_ram_nodes()))}] ++ @@ -362,8 +364,8 @@ is_db_empty() -> table_names()). is_clustered() -> - Nodes = all_clustered_nodes(), - [node()] =/= Nodes andalso [] =/= Nodes. + AllNodes = all_clustered_nodes(), + not is_only_node(AllNodes) andalso not ?empty_set(AllNodes). is_disc_and_clustered() -> node_type() =:= disc andalso is_clustered(). @@ -464,7 +466,7 @@ node_info() -> node_type() -> DiscNodes = clustered_disc_nodes(), - case DiscNodes =:= [] orelse ordsets:is_element(node(), DiscNodes) of + case ?empty_set(DiscNodes) orelse ordsets:is_element(node(), DiscNodes) of true -> disc; false -> ram end. @@ -696,13 +698,13 @@ check_cluster_consistency(Node) -> %%-------------------------------------------------------------------- on_node_up(Node) -> - case running_clustered_disc_nodes() =:= [Node] of + case is_only_node(Node, running_clustered_disc_nodes()) of true -> rabbit_log:info("cluster contains disc nodes again~n"); false -> ok end. on_node_down(_Node) -> - case running_clustered_disc_nodes() =:= [] of + case ?empty_set(running_clustered_disc_nodes()) of true -> rabbit_log:info("only running disc node went down~n"); false -> ok end. @@ -1004,11 +1006,13 @@ leave_cluster() -> case {is_clustered(), running_nodes(ordsets:del_element(node(), all_clustered_nodes()))} of - {false, []} -> ok; - {_, AllNodes} -> case lists:any(fun leave_cluster/1, AllNodes) of - true -> ok; - false -> e(no_running_cluster_nodes) - end + {false, Nodes} when ?empty_set(Nodes) -> + ok; + {_, AllNodes} -> + case lists:any(fun leave_cluster/1, AllNodes) of + true -> ok; + false -> e(no_running_cluster_nodes) + end end. leave_cluster(Node) -> @@ -1040,9 +1044,9 @@ stop_mnesia() -> ensure_mnesia_not_running(). change_extra_db_nodes(ClusterNodes0, Force) -> - ClusterNodes = lists:usort(ClusterNodes0) -- [node()], - case mnesia:change_config(extra_db_nodes, ClusterNodes) of - {ok, []} when not Force andalso ClusterNodes =/= [] -> + ClusterNodes = ordsets:del_element(node(), ClusterNodes0), + case mnesia:change_config(extra_db_nodes, ordsets:to_list(ClusterNodes)) of + {ok, []} when not Force andalso not ?empty_set(ClusterNodes) -> throw({error, {failed_to_cluster_with, ClusterNodes, "Mnesia could not connect to any nodes."}}); {ok, Nodes} -> @@ -1167,3 +1171,12 @@ error_description(removing_node_from_offline_node) -> "from must be a disc node and all the other nodes must be offline."; error_description(no_running_cluster_nodes) -> "You cannot leave a cluster if no online nodes are present.". + +is_only_node(Node, Nodes) -> + ordsets:is_element(Node, Nodes) andalso ordsets:size(Nodes) =:= 1. + +is_only_node(Nodes) -> + is_only_node(node(), Nodes). + +is_only_disc_node() -> + is_only_node(clustered_disc_nodes()). -- cgit v1.2.1 From b96c4708eb5a7f49cc8dbbd602632d87081e6253 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Tue, 18 Sep 2012 12:23:12 +0100 Subject: forgot one list --- src/rabbit_mnesia.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index b0d7c9b8..1e555b16 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -1058,9 +1058,9 @@ change_extra_db_nodes(ClusterNodes0, Force) -> %% return false positives when we are actually just doing cluster %% operations (e.g. joining the cluster). running_nodes(Nodes) -> - {Replies, _BadNodes} = - rpc:multicall(Nodes, rabbit_mnesia, is_running_remote, []), - [Node || {Running, Node} <- Replies, Running]. + {Replies, _BadNodes} = rpc:multicall(ordsets:to_list(Nodes), rabbit_mnesia, + is_running_remote, []), + ordsets:from_list([Node || {Running, Node} <- Replies, Running]). is_running_remote() -> {proplists:is_defined(rabbit, application:which_applications(infinity)), -- cgit v1.2.1 From 22e0ef19de7036340dfa962a30b822fa03ca1820 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Tue, 18 Sep 2012 12:31:21 +0100 Subject: another list in the wild... --- src/rabbit_mnesia.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 1e555b16..dec332d4 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -1009,7 +1009,7 @@ leave_cluster() -> {false, Nodes} when ?empty_set(Nodes) -> ok; {_, AllNodes} -> - case lists:any(fun leave_cluster/1, AllNodes) of + case lists:any(fun leave_cluster/1, ordsets:to_list(AllNodes)) of true -> ok; false -> e(no_running_cluster_nodes) end -- cgit v1.2.1 From be1c181db088e2c6cf567748b82a568c5202b536 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Tue, 18 Sep 2012 12:40:21 +0100 Subject: remove ?empty_set --- src/rabbit_mnesia.erl | 56 +++++++++++++++++++++++++++------------------------ 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index dec332d4..4541e9e4 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -60,9 +60,6 @@ -include("rabbit.hrl"). -%% This is safe because ordsets are lists. --define(empty_set(Set), (Set =:= [])). - %%---------------------------------------------------------------------------- -ifdef(use_specs). @@ -315,9 +312,10 @@ forget_cluster_node(Node, RemoveWhenOffline) -> end. remove_node_offline_node(Node) -> - case {ordsets:del_element(Node, running_nodes(all_clustered_nodes())), + case {empty_set( + ordsets:del_element(Node, running_nodes(all_clustered_nodes()))), node_type()} of - {Nodes, disc} when ?empty_set(Nodes) -> + {true, disc} -> %% Note that while we check if the nodes was the last to %% go down, apart from the node we're removing from, this %% is still unsafe. Consider the situation in which A and @@ -326,8 +324,8 @@ remove_node_offline_node(Node) -> %% and B goes down. In this case, C is the second-to-last, %% but we don't know that and we'll remove B from A %% anyway, even if that will lead to bad things. - case ?empty_set(ordsets:subtract(running_clustered_nodes(), - ordsets:from_list([node(), Node]))) + case empty_set(ordsets:subtract(running_clustered_nodes(), + ordsets:from_list([node(), Node]))) of true -> start_mnesia(), try [mnesia:force_load_table(T) || @@ -339,7 +337,7 @@ remove_node_offline_node(Node) -> end; false -> e(not_last_node_to_go_down) end; - {_, _} -> + {false, _} -> e(removing_node_from_offline_node) end. @@ -349,8 +347,11 @@ remove_node_offline_node(Node) -> %%---------------------------------------------------------------------------- status() -> - IfNonEmpty = fun (_, Nodes) when ?empty_set(Nodes) -> []; - (Type, Nodes) -> [{Type, ordsets:to_list(Nodes)}] + IfNonEmpty = fun (Type, Nodes) -> + case empty_set(Nodes) of + true -> []; + false -> [{Type, ordsets:to_list(Nodes)}] + end end, [{nodes, (IfNonEmpty(disc, clustered_disc_nodes()) ++ IfNonEmpty(ram, clustered_ram_nodes()))}] ++ @@ -365,7 +366,7 @@ is_db_empty() -> is_clustered() -> AllNodes = all_clustered_nodes(), - not is_only_node(AllNodes) andalso not ?empty_set(AllNodes). + not is_only_node(AllNodes) andalso not empty_set(AllNodes). is_disc_and_clustered() -> node_type() =:= disc andalso is_clustered(). @@ -466,7 +467,7 @@ node_info() -> node_type() -> DiscNodes = clustered_disc_nodes(), - case ?empty_set(DiscNodes) orelse ordsets:is_element(node(), DiscNodes) of + case empty_set(DiscNodes) orelse ordsets:is_element(node(), DiscNodes) of true -> disc; false -> ram end. @@ -704,7 +705,7 @@ on_node_up(Node) -> end. on_node_down(_Node) -> - case ?empty_set(running_clustered_disc_nodes()) of + case empty_set(running_clustered_disc_nodes()) of true -> rabbit_log:info("only running disc node went down~n"); false -> ok end. @@ -1003,16 +1004,16 @@ remove_node_if_mnesia_running(Node) -> end. leave_cluster() -> - case {is_clustered(), - running_nodes(ordsets:del_element(node(), all_clustered_nodes()))} - of - {false, Nodes} when ?empty_set(Nodes) -> + RunningNodes = + running_nodes(ordsets:del_element(node(), all_clustered_nodes())), + case not is_clustered() andalso empty_set(RunningNodes) of + true -> ok; - {_, AllNodes} -> - case lists:any(fun leave_cluster/1, ordsets:to_list(AllNodes)) of - true -> ok; - false -> e(no_running_cluster_nodes) - end + false -> + case lists:any(fun leave_cluster/1, ordsets:to_list(RunningNodes)) + of true -> ok; + false -> e(no_running_cluster_nodes) + end end. leave_cluster(Node) -> @@ -1044,12 +1045,12 @@ stop_mnesia() -> ensure_mnesia_not_running(). change_extra_db_nodes(ClusterNodes0, Force) -> - ClusterNodes = ordsets:del_element(node(), ClusterNodes0), - case mnesia:change_config(extra_db_nodes, ordsets:to_list(ClusterNodes)) of - {ok, []} when not Force andalso not ?empty_set(ClusterNodes) -> + ClusterNodes = ordsets:to_list(ordsets:del_element(node(), ClusterNodes0)), + case {mnesia:change_config(extra_db_nodes, ClusterNodes), ClusterNodes} of + {{ok, []}, [_|_]} when not Force -> throw({error, {failed_to_cluster_with, ClusterNodes, "Mnesia could not connect to any nodes."}}); - {ok, Nodes} -> + {{ok, Nodes}, _} -> Nodes end. @@ -1180,3 +1181,6 @@ is_only_node(Nodes) -> is_only_disc_node() -> is_only_node(clustered_disc_nodes()). + +empty_set(Set) -> + ordsets:size(Set) =:= 0. -- cgit v1.2.1 From 559584e98b6546033a890b0745a7ddef770e4b28 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Tue, 18 Sep 2012 12:42:24 +0100 Subject: reorder functions --- src/rabbit_mnesia.erl | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 4541e9e4..e7578b0b 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -1063,6 +1063,15 @@ running_nodes(Nodes) -> is_running_remote, []), ordsets:from_list([Node || {Running, Node} <- Replies, Running]). +is_only_node(Node, Nodes) -> + ordsets:is_element(Node, Nodes) andalso ordsets:size(Nodes) =:= 1. + +is_only_node(Nodes) -> + is_only_node(node(), Nodes). + +is_only_disc_node() -> + is_only_node(clustered_disc_nodes()). + is_running_remote() -> {proplists:is_defined(rabbit, application:which_applications(infinity)), node()}. @@ -1130,6 +1139,9 @@ find_good_node([Node | Nodes]) -> end end. +empty_set(Set) -> + ordsets:size(Set) =:= 0. + e(Tag) -> throw({error, {Tag, error_description(Tag)}}). error_description(clustering_only_disc_node) -> @@ -1172,15 +1184,3 @@ error_description(removing_node_from_offline_node) -> "from must be a disc node and all the other nodes must be offline."; error_description(no_running_cluster_nodes) -> "You cannot leave a cluster if no online nodes are present.". - -is_only_node(Node, Nodes) -> - ordsets:is_element(Node, Nodes) andalso ordsets:size(Nodes) =:= 1. - -is_only_node(Nodes) -> - is_only_node(node(), Nodes). - -is_only_disc_node() -> - is_only_node(clustered_disc_nodes()). - -empty_set(Set) -> - ordsets:size(Set) =:= 0. -- cgit v1.2.1 From 8e6c5b0cd6a3cb1dcc5b5e188970ef8d70621290 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Tue, 18 Sep 2012 12:46:26 +0100 Subject: `of' at the end --- src/rabbit_mnesia.erl | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index e7578b0b..04f00e1d 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -1010,10 +1010,11 @@ leave_cluster() -> true -> ok; false -> - case lists:any(fun leave_cluster/1, ordsets:to_list(RunningNodes)) - of true -> ok; - false -> e(no_running_cluster_nodes) - end + case lists:any(fun leave_cluster/1, + ordsets:to_list(RunningNodes)) of + true -> ok; + false -> e(no_running_cluster_nodes) + end end. leave_cluster(Node) -> -- cgit v1.2.1 From 841f91ce294c15d8153b32801a509c9878dd2732 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Tue, 18 Sep 2012 12:54:41 +0100 Subject: another list operation --- src/rabbit_mnesia.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 04f00e1d..6cc14cab 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -179,7 +179,7 @@ join_cluster(DiscoveryNode, NodeType) -> E = {error, _} -> throw(E) end, - case lists:member(node(), ClusterNodes) of + case ordsets:is_element(node(), ClusterNodes) of true -> e(already_clustered); false -> ok end, -- cgit v1.2.1 From b07f68fa2565c8f2c7c645e9c4f96e2185a81eab Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 18 Sep 2012 13:08:23 +0100 Subject: some abstraction --- src/rabbit_mnesia.erl | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 6cc14cab..63fb88b1 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -179,7 +179,7 @@ join_cluster(DiscoveryNode, NodeType) -> E = {error, _} -> throw(E) end, - case ordsets:is_element(node(), ClusterNodes) of + case me_in_nodes(ClusterNodes) of true -> e(already_clustered); false -> ok end, @@ -271,7 +271,7 @@ update_cluster_nodes(DiscoveryNode) -> {ok, Status0} -> Status0; {error, _Reason} -> e(cannot_connect_to_node) end, - case ordsets:is_element(node(), AllNodes) of + case me_in_nodes(AllNodes) of true -> %% As in `check_consistency/0', we can safely delete the %% schema here, since it'll be replicated from the other @@ -412,7 +412,7 @@ mnesia_nodes() -> mnesia:table_info(schema, disc_copies)), DiscNodes = case NodeType of - disc -> ordsets:add_element(node(), DiscCopies); + disc -> nodes_incl_me(DiscCopies); ram -> DiscCopies end, {ok, {AllNodes, DiscNodes}}; @@ -435,9 +435,8 @@ cluster_status(WhichNodes, ForceMnesia) -> %% the node is online, but we know for sure that %% the node is offline now, so we can remove it %% from the list of running nodes. - {ok, - {AllNodes, DiscNodes, - fun() -> ordsets:del_element(node(), RunningNodes) end}}; + {ok, {AllNodes, DiscNodes, + fun() -> nodes_excl_me(RunningNodes) end}}; Err = {error, _} -> Err end, @@ -467,7 +466,7 @@ node_info() -> node_type() -> DiscNodes = clustered_disc_nodes(), - case empty_set(DiscNodes) orelse ordsets:is_element(node(), DiscNodes) of + case empty_set(DiscNodes) orelse me_in_nodes(DiscNodes) of true -> disc; false -> ram end. @@ -653,8 +652,7 @@ check_cluster_consistency() -> case lists:foldl( fun (Node, {error, _}) -> check_cluster_consistency(Node); (_Node, {ok, Status}) -> {ok, Status} - end, {error, not_found}, - ordsets:del_element(node(), all_clustered_nodes())) + end, {error, not_found}, nodes_excl_me(all_clustered_nodes())) of {ok, Status = {RemoteAllNodes, _, _}} -> case ordsets:is_subset(all_clustered_nodes(), RemoteAllNodes) of @@ -1004,8 +1002,7 @@ remove_node_if_mnesia_running(Node) -> end. leave_cluster() -> - RunningNodes = - running_nodes(ordsets:del_element(node(), all_clustered_nodes())), + RunningNodes = running_nodes(nodes_excl_me(all_clustered_nodes())), case not is_clustered() andalso empty_set(RunningNodes) of true -> ok; @@ -1046,7 +1043,7 @@ stop_mnesia() -> ensure_mnesia_not_running(). change_extra_db_nodes(ClusterNodes0, Force) -> - ClusterNodes = ordsets:to_list(ordsets:del_element(node(), ClusterNodes0)), + ClusterNodes = ordsets:to_list(nodes_excl_me(ClusterNodes0)), case {mnesia:change_config(extra_db_nodes, ClusterNodes), ClusterNodes} of {{ok, []}, [_|_]} when not Force -> throw({error, {failed_to_cluster_with, ClusterNodes, @@ -1064,19 +1061,22 @@ running_nodes(Nodes) -> is_running_remote, []), ordsets:from_list([Node || {Running, Node} <- Replies, Running]). -is_only_node(Node, Nodes) -> - ordsets:is_element(Node, Nodes) andalso ordsets:size(Nodes) =:= 1. - -is_only_node(Nodes) -> - is_only_node(node(), Nodes). - -is_only_disc_node() -> - is_only_node(clustered_disc_nodes()). - is_running_remote() -> {proplists:is_defined(rabbit, application:which_applications(infinity)), node()}. +is_only_node(Node, Nodes) -> ordsets:to_list(Nodes) == [Node]. + +is_only_node(Nodes) -> is_only_node(node(), Nodes). + +is_only_disc_node() -> is_only_node(clustered_disc_nodes()). + +me_in_nodes(Nodes) -> ordsets:is_element(node(), Nodes). + +nodes_incl_me(Nodes) -> ordsets:add_element(node(), Nodes). + +nodes_excl_me(Nodes) -> ordsets:del_element(node(), Nodes). + check_consistency(OTP, Rabbit) -> rabbit_misc:sequence_error( [check_otp_consistency(OTP), check_rabbit_consistency(Rabbit)]). -- cgit v1.2.1 From ef7ae2d3dfbfba9369ce2b95c63b792f5dde5ea6 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 18 Sep 2012 13:15:25 +0100 Subject: cosmetic --- src/rabbit_mnesia.erl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 63fb88b1..b3de0d16 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -1077,6 +1077,8 @@ nodes_incl_me(Nodes) -> ordsets:add_element(node(), Nodes). nodes_excl_me(Nodes) -> ordsets:del_element(node(), Nodes). +empty_set(Set) -> ordsets:size(Set) =:= 0. + check_consistency(OTP, Rabbit) -> rabbit_misc:sequence_error( [check_otp_consistency(OTP), check_rabbit_consistency(Rabbit)]). @@ -1140,9 +1142,6 @@ find_good_node([Node | Nodes]) -> end end. -empty_set(Set) -> - ordsets:size(Set) =:= 0. - e(Tag) -> throw({error, {Tag, error_description(Tag)}}). error_description(clustering_only_disc_node) -> -- cgit v1.2.1 From aa3b3896a93a86ef0b72b15ead4ae9e3c8c326dc Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 18 Sep 2012 13:18:20 +0100 Subject: cosmetic(ish) --- src/rabbit_mnesia.erl | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index b3de0d16..735a744b 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -1065,20 +1065,6 @@ is_running_remote() -> {proplists:is_defined(rabbit, application:which_applications(infinity)), node()}. -is_only_node(Node, Nodes) -> ordsets:to_list(Nodes) == [Node]. - -is_only_node(Nodes) -> is_only_node(node(), Nodes). - -is_only_disc_node() -> is_only_node(clustered_disc_nodes()). - -me_in_nodes(Nodes) -> ordsets:is_element(node(), Nodes). - -nodes_incl_me(Nodes) -> ordsets:add_element(node(), Nodes). - -nodes_excl_me(Nodes) -> ordsets:del_element(node(), Nodes). - -empty_set(Set) -> ordsets:size(Set) =:= 0. - check_consistency(OTP, Rabbit) -> rabbit_misc:sequence_error( [check_otp_consistency(OTP), check_rabbit_consistency(Rabbit)]). @@ -1090,15 +1076,14 @@ check_consistency(OTP, Rabbit, Node, Status) -> check_nodes_consistency(Node, Status)]). check_nodes_consistency(Node, RemoteStatus = {RemoteAllNodes, _, _}) -> - ThisNode = node(), - case ordsets:is_element(ThisNode, RemoteAllNodes) of + case me_in_nodes(RemoteAllNodes) of true -> {ok, RemoteStatus}; false -> {error, {inconsistent_cluster, rabbit_misc:format("Node ~p thinks it's clustered " "with node ~p, but ~p disagrees", - [ThisNode, Node, Node])}} + [node(), Node, Node])}} end. check_version_consistency(This, Remote, _) when This =:= Remote -> @@ -1142,6 +1127,20 @@ find_good_node([Node | Nodes]) -> end end. +is_only_node(Node, Nodes) -> ordsets:to_list(Nodes) == [Node]. + +is_only_node(Nodes) -> is_only_node(node(), Nodes). + +is_only_disc_node() -> is_only_node(clustered_disc_nodes()). + +me_in_nodes(Nodes) -> ordsets:is_element(node(), Nodes). + +nodes_incl_me(Nodes) -> ordsets:add_element(node(), Nodes). + +nodes_excl_me(Nodes) -> ordsets:del_element(node(), Nodes). + +empty_set(Set) -> ordsets:size(Set) =:= 0. + e(Tag) -> throw({error, {Tag, error_description(Tag)}}). error_description(clustering_only_disc_node) -> -- cgit v1.2.1 From d12b06081a866492ccd742d8c1dea66d59bd0410 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 18 Sep 2012 14:56:32 +0100 Subject: This got swapped round. --- src/rabbit_mnesia.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 735a744b..2280a620 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -531,10 +531,10 @@ init_db_and_upgrade(ClusterNodes, NodeType, Force) -> %% `maybe_upgrade_local' restarts mnesia, so ram nodes will forget %% about the cluster case NodeType of - disc -> start_mnesia(), + ram -> start_mnesia(), change_extra_db_nodes(ClusterNodes, true), wait_for_replicated_tables(); - ram -> ok + disc -> ok end, ok. -- cgit v1.2.1 From 068fa731f2521f39701a72c8ad392ed215a4701f Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 18 Sep 2012 15:31:51 +0100 Subject: Rework booleans around init_db() and friends: * Rename "Force" to "CheckOtherNodes" and invert its meaning, to match "CheckConsistency", and since I hate "Force". * Reduce arity of a couple of functions which are only ever called one way round. * Rearrange order of parameters to init_db_with_mnesia/4 for consistency with everything else. --- src/rabbit_mnesia.erl | 43 ++++++++++++++++++++++++------------------- src/rabbit_upgrade.erl | 2 +- 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 735a744b..d48dc437 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -37,7 +37,7 @@ wait_for_tables/1, cluster_status_from_mnesia/0, - init_db/3, + init_db_unchecked/2, empty_ram_only_tables/0, copy_db/1, wait_for_tables/0, @@ -94,7 +94,7 @@ {'error', any()}). %% Operations on the db and utils, mainly used in `rabbit_upgrade' and `rabbit' --spec(init_db/3 :: (node_set(), node_type(), boolean()) -> 'ok'). +-spec(init_db_unchecked/2 :: (node_set(), node_type()) -> 'ok'). -spec(empty_ram_only_tables/0 :: () -> 'ok'). -spec(create_tables/0 :: () -> 'ok'). -spec(copy_db/1 :: (file:filename()) -> rabbit_types:ok_or_error(any())). @@ -132,7 +132,7 @@ init() -> ok. init(NodeType, AllNodes) -> - init_db_and_upgrade(AllNodes, NodeType, NodeType =:= disc). + init_db_and_upgrade(AllNodes, NodeType, NodeType =:= ram). init_from_config() -> {ok, {TryNodes, NodeType}} = @@ -142,7 +142,7 @@ init_from_config() -> rabbit_log:info("Node '~p' selected for clustering from " "configuration~n", [Node]), {ok, {_, DiscNodes, _}} = discover_cluster(Node), - init_db_and_upgrade(DiscNodes, NodeType, false), + init_db_and_upgrade(DiscNodes, NodeType, true), rabbit_node_monitor:notify_joined_cluster(); none -> rabbit_log:warning("Could not find any suitable node amongst the " @@ -193,7 +193,7 @@ join_cluster(DiscoveryNode, NodeType) -> rabbit_misc:local_info_msg("Clustering with ~p~n", [ClusterNodes]), %% Join the cluster - ok = init_db_with_mnesia(ClusterNodes, NodeType, false), + ok = init_db_with_mnesia(ClusterNodes, NodeType), rabbit_node_monitor:notify_joined_cluster(), @@ -221,7 +221,7 @@ reset(Force) -> %% We don't need to check for consistency because we are %% resetting. Force=true here so that reset still works %% when clustered with a node which is down. - init_db_with_mnesia(AllNodes, node_type(), false, true), + init_db_with_mnesia(AllNodes, node_type(), false, false), case is_disc_and_clustered() andalso is_only_disc_node() of true -> e(resetting_only_disc_node); @@ -278,7 +278,7 @@ update_cluster_nodes(DiscoveryNode) -> %% nodes mnesia:delete_schema([node()]), rabbit_node_monitor:write_cluster_status(Status), - init_db_with_mnesia(AllNodes, node_type(), false); + init_db_with_mnesia(AllNodes, node_type()); false -> e(inconsistent_cluster) end, @@ -483,8 +483,8 @@ table_names() -> [Tab || {Tab, _} <- table_definitions()]. %% schema if there is the need to and catching up if there are other %% nodes in the cluster already. It also updates the cluster status %% file. -init_db(ClusterNodes, NodeType, Force) -> - Nodes = change_extra_db_nodes(ClusterNodes, Force), +init_db(ClusterNodes, NodeType, CheckOtherNodes) -> + Nodes = change_extra_db_nodes(ClusterNodes, CheckOtherNodes), %% Note that we use `system_info' here and not the cluster status %% since when we start rabbit for the first time the cluster %% status will say we are a disc node but the tables won't be @@ -521,8 +521,11 @@ init_db(ClusterNodes, NodeType, Force) -> rabbit_node_monitor:update_cluster_status(), ok. -init_db_and_upgrade(ClusterNodes, NodeType, Force) -> - ok = init_db(ClusterNodes, NodeType, Force), +init_db_unchecked(ClusterNodes, NodeType) -> + init_db(ClusterNodes, NodeType, false). + +init_db_and_upgrade(ClusterNodes, NodeType, CheckOtherNodes) -> + ok = init_db(ClusterNodes, NodeType, CheckOtherNodes), ok = case rabbit_upgrade:maybe_upgrade_local() of ok -> ok; starting_from_scratch -> rabbit_version:record_desired(); @@ -532,23 +535,25 @@ init_db_and_upgrade(ClusterNodes, NodeType, Force) -> %% about the cluster case NodeType of disc -> start_mnesia(), - change_extra_db_nodes(ClusterNodes, true), + change_extra_db_nodes(ClusterNodes, false), wait_for_replicated_tables(); ram -> ok end, ok. -init_db_with_mnesia(ClusterNodes, NodeType, CheckConsistency, Force) -> + +init_db_with_mnesia(ClusterNodes, NodeType) -> + init_db_with_mnesia(ClusterNodes, NodeType, true, true). + +init_db_with_mnesia(ClusterNodes, NodeType, + CheckOtherNodes, CheckConsistency) -> start_mnesia(CheckConsistency), try - init_db_and_upgrade(ClusterNodes, NodeType, Force) + init_db_and_upgrade(ClusterNodes, NodeType, CheckOtherNodes) after stop_mnesia() end. -init_db_with_mnesia(ClusterNodes, NodeType, Force) -> - init_db_with_mnesia(ClusterNodes, NodeType, true, Force). - ensure_mnesia_dir() -> MnesiaDir = dir() ++ "/", case filelib:ensure_dir(MnesiaDir) of @@ -1042,10 +1047,10 @@ stop_mnesia() -> stopped = mnesia:stop(), ensure_mnesia_not_running(). -change_extra_db_nodes(ClusterNodes0, Force) -> +change_extra_db_nodes(ClusterNodes0, CheckOtherNodes) -> ClusterNodes = ordsets:to_list(nodes_excl_me(ClusterNodes0)), case {mnesia:change_config(extra_db_nodes, ClusterNodes), ClusterNodes} of - {{ok, []}, [_|_]} when not Force -> + {{ok, []}, [_|_]} when CheckOtherNodes -> throw({error, {failed_to_cluster_with, ClusterNodes, "Mnesia could not connect to any nodes."}}); {{ok, Nodes}, _} -> diff --git a/src/rabbit_upgrade.erl b/src/rabbit_upgrade.erl index 681d3d3e..2b591c2e 100644 --- a/src/rabbit_upgrade.erl +++ b/src/rabbit_upgrade.erl @@ -220,7 +220,7 @@ secondary_upgrade(AllNodes) -> rabbit_misc:ensure_ok(mnesia:delete_schema([node()]), cannot_delete_schema), rabbit_misc:ensure_ok(mnesia:start(), cannot_start_mnesia), - ok = rabbit_mnesia:init_db(AllNodes, NodeType, true), + ok = rabbit_mnesia:init_db_unchecked(AllNodes, NodeType), ok = rabbit_version:record_desired_for_scope(mnesia), ok. -- cgit v1.2.1 From afbdc85004275eea9785cc16f0ec57e795627f5f Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 18 Sep 2012 15:41:44 +0100 Subject: get rid of a helper --- src/rabbit_mnesia.erl | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 0fb37d79..1fcff223 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -193,7 +193,7 @@ join_cluster(DiscoveryNode, NodeType) -> rabbit_misc:local_info_msg("Clustering with ~p~n", [ClusterNodes]), %% Join the cluster - ok = init_db_with_mnesia(ClusterNodes, NodeType), + ok = init_db_with_mnesia(ClusterNodes, NodeType, true, true), rabbit_node_monitor:notify_joined_cluster(), @@ -278,7 +278,7 @@ update_cluster_nodes(DiscoveryNode) -> %% nodes mnesia:delete_schema([node()]), rabbit_node_monitor:write_cluster_status(Status), - init_db_with_mnesia(AllNodes, node_type()); + init_db_with_mnesia(AllNodes, node_type(), true, true); false -> e(inconsistent_cluster) end, @@ -541,10 +541,6 @@ init_db_and_upgrade(ClusterNodes, NodeType, CheckOtherNodes) -> end, ok. - -init_db_with_mnesia(ClusterNodes, NodeType) -> - init_db_with_mnesia(ClusterNodes, NodeType, true, true). - init_db_with_mnesia(ClusterNodes, NodeType, CheckOtherNodes, CheckConsistency) -> start_mnesia(CheckConsistency), -- cgit v1.2.1 From 6f5870b868366876d9db51a6ae353d9c021e3378 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 18 Sep 2012 15:50:57 +0100 Subject: Swap arguments of transfer/2. --- src/file_handle_cache.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/file_handle_cache.erl b/src/file_handle_cache.erl index e1fa3d70..3260d369 100644 --- a/src/file_handle_cache.erl +++ b/src/file_handle_cache.erl @@ -256,7 +256,7 @@ -spec(release/0 :: () -> 'ok'). -spec(release/1 :: (non_neg_integer()) -> 'ok'). -spec(transfer/1 :: (pid()) -> 'ok'). --spec(transfer/2 :: (non_neg_integer(), pid()) -> 'ok'). +-spec(transfer/2 :: (pid(), non_neg_integer()) -> 'ok'). -spec(set_limit/1 :: (non_neg_integer()) -> 'ok'). -spec(get_limit/0 :: () -> non_neg_integer()). -spec(info_keys/0 :: () -> rabbit_types:info_keys()). @@ -491,7 +491,7 @@ set_maximum_since_use(MaximumAge) -> obtain() -> obtain(1). release() -> release(1). -transfer(Pid) -> transfer(1, Pid). +transfer(Pid) -> transfer(Pid, 1). obtain(Count) when Count > 0 -> %% If the FHC isn't running, obtains succeed immediately. @@ -503,7 +503,7 @@ obtain(Count) when Count > 0 -> release(Count) when Count > 0 -> gen_server2:cast(?SERVER, {release, Count, self()}). -transfer(Count, Pid) when Count > 0 -> +transfer(Pid, Count) when Count > 0 -> gen_server2:cast(?SERVER, {transfer, Count, self(), Pid}). set_limit(Limit) -> -- cgit v1.2.1 From b46488c91b5a9ada37b3889f630e8d0b66bbc8c2 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 18 Sep 2012 15:59:46 +0100 Subject: Oops --- src/rabbit_upgrade.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_upgrade.erl b/src/rabbit_upgrade.erl index 2b591c2e..9c54eb72 100644 --- a/src/rabbit_upgrade.erl +++ b/src/rabbit_upgrade.erl @@ -66,11 +66,11 @@ %% into the boot process by prelaunch before the mnesia application is %% started. By the time Mnesia is started the upgrades have happened %% (on the primary), or Mnesia has been reset (on the secondary) and -%% rabbit_mnesia:init_db/3 can then make the node rejoin the cluster +%% rabbit_mnesia:init_db_unchecked/2 can then make the node rejoin the cluster %% in the normal way. %% %% The non-mnesia upgrades are then triggered by -%% rabbit_mnesia:init_db/3. Of course, it's possible for a given +%% rabbit_mnesia:init_db_unchecked/2. Of course, it's possible for a given %% upgrade process to only require Mnesia upgrades, or only require %% non-Mnesia upgrades. In the latter case no Mnesia resets and %% reclusterings occur. -- cgit v1.2.1 From e24b78cc109bf28b354395bb1f786042c95ddf92 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 18 Sep 2012 18:08:32 +0100 Subject: rabbit_misc:memory/0. --- src/rabbit.erl | 2 +- src/rabbit_misc.erl | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 7a021e37..3809c079 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -356,7 +356,7 @@ status() -> {running_applications, application:which_applications(infinity)}, {os, os:type()}, {erlang_version, erlang:system_info(system_version)}, - {memory, erlang:memory()}], + {memory, rabbit_misc:memory()}], S2 = rabbit_misc:filter_exit_map( fun ({Key, {M, F, A}}) -> {Key, erlang:apply(M, F, A)} end, [{vm_memory_high_watermark, {vm_memory_monitor, diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index a0536a50..996d8ced 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -63,6 +63,7 @@ -export([version/0]). -export([sequence_error/1]). -export([json_encode/1, json_decode/1, json_to_term/1, term_to_json/1]). +-export([memory/0]). %% Horrible macro to use in guards -define(IS_BENIGN_EXIT(R), @@ -987,3 +988,47 @@ term_to_json(L) when is_list(L) -> term_to_json(V) when is_binary(V) orelse is_number(V) orelse V =:= null orelse V =:= true orelse V =:= false -> V. + +%% Like erlang:memory(), but with awareness of rabbit-y things +memory() -> + QPids = lists:append([pids(Q) || Q <- rabbit_amqqueue:list()]), + Conns = sum_proc_memory(rabbit_networking:connections_local()), + Chs = sum_proc_memory(rabbit_channel:list_local()), + Qs = sum_proc_memory(QPids), + Mnesia = mnesia_memory(), + MsgIndex = ets_memory(rabbit_msg_store_ets_index), + [{total, erlang:memory(total)}, + {connection_procs, Conns}, + {channel_procs, Chs}, + {queue_procs, Qs}, + {other_procs, erlang:memory(processes) - Conns - Chs - Qs}, + {mnesia, Mnesia}, + {msg_index, MsgIndex}, + {other_ets, erlang:memory(ets) - Mnesia - MsgIndex}, + {binary, erlang:memory(binary)}, + {system, erlang:memory(system)}, + {atom, erlang:memory(atom)}, + {code, erlang:memory(code)}]. + +sum_proc_memory(Pids) -> + lists:foldl( + fun (Pid, Mem) -> Mem + element(2, process_info(Pid, memory)) end, + 0, Pids). + +pids(#amqqueue{pid = Pid, slave_pids = undefined}) -> + local_pids([Pid]); +pids(#amqqueue{pid = Pid, slave_pids = SPids}) -> + local_pids([Pid | SPids]). + +local_pids(Pids) -> [Pid || Pid <- Pids, node(Pid) =:= node()]. + +mnesia_memory() -> + lists:sum([bytes(mnesia:table_info(Tab, memory)) || + Tab <- mnesia:system_info(tables)]). + +ets_memory(Name) -> + lists:sum([bytes(ets:info(T, memory)) || T <- ets:all(), + N <- [ets:info(T, name)], + N =:= Name]). + +bytes(Words) -> Words * erlang:system_info(wordsize). -- cgit v1.2.1 From 3316ecc0e7b461d3e7e7adf74bd21ed994f3d115 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 18 Sep 2012 18:29:08 +0100 Subject: Try to keep our categories disjoint. --- src/rabbit.erl | 3 ++- src/rabbit_misc.erl | 17 ++++++++++------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 3809c079..ff908b75 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -356,7 +356,8 @@ status() -> {running_applications, application:which_applications(infinity)}, {os, os:type()}, {erlang_version, erlang:system_info(system_version)}, - {memory, rabbit_misc:memory()}], + {memory_used, erlang:memory(total)}, + {memory_details, rabbit_misc:memory()}], S2 = rabbit_misc:filter_exit_map( fun ({Key, {M, F, A}}) -> {Key, erlang:apply(M, F, A)} end, [{vm_memory_high_watermark, {vm_memory_monitor, diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 996d8ced..c928f58c 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -997,18 +997,21 @@ memory() -> Qs = sum_proc_memory(QPids), Mnesia = mnesia_memory(), MsgIndex = ets_memory(rabbit_msg_store_ets_index), - [{total, erlang:memory(total)}, - {connection_procs, Conns}, + ETS = erlang:memory(ets), + Atom = erlang:memory(atom), + Bin = erlang:memory(binary), + Code = erlang:memory(code), + [{connection_procs, Conns}, {channel_procs, Chs}, {queue_procs, Qs}, {other_procs, erlang:memory(processes) - Conns - Chs - Qs}, {mnesia, Mnesia}, {msg_index, MsgIndex}, - {other_ets, erlang:memory(ets) - Mnesia - MsgIndex}, - {binary, erlang:memory(binary)}, - {system, erlang:memory(system)}, - {atom, erlang:memory(atom)}, - {code, erlang:memory(code)}]. + {other_ets, ETS - Mnesia - MsgIndex}, + {binary, Bin}, + {real_system, erlang:memory(system) - ETS - Atom - Bin - Code}, + {atom, Atom}, + {code, Code}]. sum_proc_memory(Pids) -> lists:foldl( -- cgit v1.2.1 From 4bf619f546b1e53c99eb07359b1984fdaf36c30e Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 18 Sep 2012 21:34:07 +0100 Subject: Consistency --- src/rabbit_misc.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index c928f58c..0add655a 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -1009,9 +1009,9 @@ memory() -> {msg_index, MsgIndex}, {other_ets, ETS - Mnesia - MsgIndex}, {binary, Bin}, - {real_system, erlang:memory(system) - ETS - Atom - Bin - Code}, {atom, Atom}, - {code, Code}]. + {code, Code}, + {other_system, erlang:memory(system) - ETS - Atom - Bin - Code}]. sum_proc_memory(Pids) -> lists:foldl( -- cgit v1.2.1 From 6bbfd1038d55df9cc0d83b1a5ee8cff61c74f145 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 18 Sep 2012 22:30:37 +0100 Subject: Move to rabbit.erl, and only invoke erlang:memory/1 once so we do a better job of showing results from a single point in time. --- src/rabbit.erl | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++--- src/rabbit_misc.erl | 48 -------------------------------------------- 2 files changed, 55 insertions(+), 51 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index ff908b75..224a739a 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -21,7 +21,7 @@ -export([start/0, boot/0, stop/0, stop_and_halt/0, await_startup/0, status/0, is_running/0, is_running/1, environment/0, rotate_logs/1, force_event_refresh/0, - start_fhc/0]). + start_fhc/0, memory/0]). -export([start/2, stop/1]). @@ -247,6 +247,7 @@ -spec(maybe_insert_default_data/0 :: () -> 'ok'). -spec(boot_delegate/0 :: () -> 'ok'). -spec(recover/0 :: () -> 'ok'). +-spec(memory/0 :: () -> rabbit_types:infos()). -endif. @@ -356,8 +357,7 @@ status() -> {running_applications, application:which_applications(infinity)}, {os, os:type()}, {erlang_version, erlang:system_info(system_version)}, - {memory_used, erlang:memory(total)}, - {memory_details, rabbit_misc:memory()}], + {memory, memory()}], S2 = rabbit_misc:filter_exit_map( fun ({Key, {M, F, A}}) -> {Key, erlang:apply(M, F, A)} end, [{vm_memory_high_watermark, {vm_memory_monitor, @@ -744,3 +744,55 @@ start_fhc() -> rabbit_sup:start_restartable_child( file_handle_cache, [fun rabbit_alarm:set_alarm/1, fun rabbit_alarm:clear_alarm/1]). + +%% Like erlang:memory(), but with awareness of rabbit-y things +memory() -> + QPids = lists:append([pids(Q) || Q <- rabbit_amqqueue:list()]), + Conns = sum_proc_memory(rabbit_networking:connections_local()), + Chs = sum_proc_memory(rabbit_channel:list_local()), + Qs = sum_proc_memory(QPids), + Mnesia = mnesia_memory(), + MsgIndex = ets_memory(rabbit_msg_store_ets_index), + [{total, Total}, + {processes, Processes}, + {ets, ETS}, + {atom, Atom}, + {binary, Bin}, + {code, Code}, + {system, System}] = + erlang:memory([total, processes, ets, atom, binary, code, system]), + [{total, Total}, + {connection_procs, Conns}, + {channel_procs, Chs}, + {queue_procs, Qs}, + {other_procs, Processes - Conns - Chs - Qs}, + {mnesia, Mnesia}, + {msg_index, MsgIndex}, + {other_ets, ETS - Mnesia - MsgIndex}, + {binary, Bin}, + {atom, Atom}, + {code, Code}, + {other_system, System - ETS - Atom - Bin - Code}]. + +sum_proc_memory(Pids) -> + lists:foldl( + fun (Pid, Mem) -> Mem + element(2, process_info(Pid, memory)) end, + 0, Pids). + +pids(#amqqueue{pid = Pid, slave_pids = undefined}) -> + local_pids([Pid]); +pids(#amqqueue{pid = Pid, slave_pids = SPids}) -> + local_pids([Pid | SPids]). + +local_pids(Pids) -> [Pid || Pid <- Pids, node(Pid) =:= node()]. + +mnesia_memory() -> + lists:sum([bytes(mnesia:table_info(Tab, memory)) || + Tab <- mnesia:system_info(tables)]). + +ets_memory(Name) -> + lists:sum([bytes(ets:info(T, memory)) || T <- ets:all(), + N <- [ets:info(T, name)], + N =:= Name]). + +bytes(Words) -> Words * erlang:system_info(wordsize). diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 0add655a..a0536a50 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -63,7 +63,6 @@ -export([version/0]). -export([sequence_error/1]). -export([json_encode/1, json_decode/1, json_to_term/1, term_to_json/1]). --export([memory/0]). %% Horrible macro to use in guards -define(IS_BENIGN_EXIT(R), @@ -988,50 +987,3 @@ term_to_json(L) when is_list(L) -> term_to_json(V) when is_binary(V) orelse is_number(V) orelse V =:= null orelse V =:= true orelse V =:= false -> V. - -%% Like erlang:memory(), but with awareness of rabbit-y things -memory() -> - QPids = lists:append([pids(Q) || Q <- rabbit_amqqueue:list()]), - Conns = sum_proc_memory(rabbit_networking:connections_local()), - Chs = sum_proc_memory(rabbit_channel:list_local()), - Qs = sum_proc_memory(QPids), - Mnesia = mnesia_memory(), - MsgIndex = ets_memory(rabbit_msg_store_ets_index), - ETS = erlang:memory(ets), - Atom = erlang:memory(atom), - Bin = erlang:memory(binary), - Code = erlang:memory(code), - [{connection_procs, Conns}, - {channel_procs, Chs}, - {queue_procs, Qs}, - {other_procs, erlang:memory(processes) - Conns - Chs - Qs}, - {mnesia, Mnesia}, - {msg_index, MsgIndex}, - {other_ets, ETS - Mnesia - MsgIndex}, - {binary, Bin}, - {atom, Atom}, - {code, Code}, - {other_system, erlang:memory(system) - ETS - Atom - Bin - Code}]. - -sum_proc_memory(Pids) -> - lists:foldl( - fun (Pid, Mem) -> Mem + element(2, process_info(Pid, memory)) end, - 0, Pids). - -pids(#amqqueue{pid = Pid, slave_pids = undefined}) -> - local_pids([Pid]); -pids(#amqqueue{pid = Pid, slave_pids = SPids}) -> - local_pids([Pid | SPids]). - -local_pids(Pids) -> [Pid || Pid <- Pids, node(Pid) =:= node()]. - -mnesia_memory() -> - lists:sum([bytes(mnesia:table_info(Tab, memory)) || - Tab <- mnesia:system_info(tables)]). - -ets_memory(Name) -> - lists:sum([bytes(ets:info(T, memory)) || T <- ets:all(), - N <- [ets:info(T, name)], - N =:= Name]). - -bytes(Words) -> Words * erlang:system_info(wordsize). -- cgit v1.2.1 From 0271cf3b0a527ef7a9298f1e1c5d1a047b3bd9cf Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 18 Sep 2012 23:46:58 +0100 Subject: As far as the user is concerned, atoms and code are really the same thing for memory use. --- src/rabbit.erl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 224a739a..66e459e9 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -770,8 +770,7 @@ memory() -> {msg_index, MsgIndex}, {other_ets, ETS - Mnesia - MsgIndex}, {binary, Bin}, - {atom, Atom}, - {code, Code}, + {code, Code + Atom}, {other_system, System - ETS - Atom - Bin - Code}]. sum_proc_memory(Pids) -> -- cgit v1.2.1 From 91ed2f62a4d8e7f3aafc4cb6bb0d58d0273830fb Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 19 Sep 2012 10:40:21 +0100 Subject: Simplify --- src/rabbit.erl | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 66e459e9..eaf11cfd 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -774,9 +774,7 @@ memory() -> {other_system, System - ETS - Atom - Bin - Code}]. sum_proc_memory(Pids) -> - lists:foldl( - fun (Pid, Mem) -> Mem + element(2, process_info(Pid, memory)) end, - 0, Pids). + lists:sum([Mem || P <- Pids, {memory, Mem} <- [process_info(P, memory)]]). pids(#amqqueue{pid = Pid, slave_pids = undefined}) -> local_pids([Pid]); -- cgit v1.2.1 From ecc198019630424a429a9f4beae20bbc9bd0e6df Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 19 Sep 2012 10:45:10 +0100 Subject: Reinstate atoms --- src/rabbit.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index eaf11cfd..b2a60a55 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -770,7 +770,8 @@ memory() -> {msg_index, MsgIndex}, {other_ets, ETS - Mnesia - MsgIndex}, {binary, Bin}, - {code, Code + Atom}, + {code, Code}, + {atom, Atom}, {other_system, System - ETS - Atom - Bin - Code}]. sum_proc_memory(Pids) -> -- cgit v1.2.1 From 854665a81c5b0eca2e7934a726bb9293a84d1174 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 19 Sep 2012 11:23:52 +0100 Subject: use the correct type --- src/rabbit_auth_backend.erl | 2 +- src/rabbit_auth_mechanism.erl | 2 +- src/rabbit_exchange_decorator.erl | 2 +- src/rabbit_exchange_type.erl | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rabbit_auth_backend.erl b/src/rabbit_auth_backend.erl index e89951e7..c9475efd 100644 --- a/src/rabbit_auth_backend.erl +++ b/src/rabbit_auth_backend.erl @@ -20,7 +20,7 @@ %% A description proplist as with auth mechanisms, %% exchanges. Currently unused. --callback description() -> [proplist:property()]. +-callback description() -> [proplists:property()]. %% Check a user can log in, given a username and a proplist of %% authentication information (e.g. [{password, Password}]). diff --git a/src/rabbit_auth_mechanism.erl b/src/rabbit_auth_mechanism.erl index eda6a743..c7d74dc3 100644 --- a/src/rabbit_auth_mechanism.erl +++ b/src/rabbit_auth_mechanism.erl @@ -19,7 +19,7 @@ -ifdef(use_specs). %% A description. --callback description() -> [proplist:property()]. +-callback description() -> [proplists:property()]. %% If this mechanism is enabled, should it be offered for a given socket? %% (primarily so EXTERNAL can be SSL-only) diff --git a/src/rabbit_exchange_decorator.erl b/src/rabbit_exchange_decorator.erl index b40ceda9..08819427 100644 --- a/src/rabbit_exchange_decorator.erl +++ b/src/rabbit_exchange_decorator.erl @@ -31,7 +31,7 @@ -type(tx() :: 'transaction' | 'none'). -type(serial() :: pos_integer() | tx()). --callback description() -> [proplist:property()]. +-callback description() -> [proplists:property()]. %% Should Rabbit ensure that all binding events that are %% delivered to an individual exchange can be serialised? (they diff --git a/src/rabbit_exchange_type.erl b/src/rabbit_exchange_type.erl index 9a793aab..c5583ffd 100644 --- a/src/rabbit_exchange_type.erl +++ b/src/rabbit_exchange_type.erl @@ -21,7 +21,7 @@ -type(tx() :: 'transaction' | 'none'). -type(serial() :: pos_integer() | tx()). --callback description() -> [proplist:property()]. +-callback description() -> [proplists:property()]. %% Should Rabbit ensure that all binding events that are %% delivered to an individual exchange can be serialised? (they -- cgit v1.2.1 From 3634b80263f000e647870cd4c9649385d7e62c68 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Wed, 19 Sep 2012 11:48:52 +0100 Subject: use `system_info(running_db_nodes)' instead of rpc+application to detect running nodes This makes half of the tests to fail. I rember encountering problems, but I don't remember wheter I had understood what was wrong or if I was happy with the other working solution. --- src/rabbit_mnesia.erl | 36 +++++++++++------------------------- 1 file changed, 11 insertions(+), 25 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index f19046a0..1dfdb1f7 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -316,7 +316,7 @@ forget_cluster_node(Node, RemoveWhenOffline) -> end. remove_node_offline_node(Node) -> - case {ordsets:del_element(Node, running_nodes(all_clustered_nodes())), + case {mnesia:system_info(running_db_nodes) -- [Node], is_disc_node()} of {[], true} -> %% Note that while we check if the nodes was the last to @@ -415,19 +415,17 @@ mnesia_nodes() -> true -> ordsets:add_element(node(), DiscCopies); false -> DiscCopies end, - {ok, {AllNodes, DiscNodes}}; + RunningNodes = mnesia:system_info(running_db_nodes), + {ok, {AllNodes, DiscNodes, RunningNodes}}; false -> {error, tables_not_present} end end. cluster_status(WhichNodes, ForceMnesia) -> - %% I don't want to call `running_nodes/1' unless if necessary, - %% since it can deadlock when stopping applications. Nodes = case mnesia_nodes() of - {ok, {AllNodes, DiscNodes}} -> - {ok, {AllNodes, DiscNodes, - fun() -> running_nodes(AllNodes) end}}; + {ok, Status} -> + {ok, Status}; {error, _Reason} when not ForceMnesia -> {AllNodes, DiscNodes, RunningNodes} = rabbit_node_monitor:read_cluster_status(), @@ -435,19 +433,18 @@ cluster_status(WhichNodes, ForceMnesia) -> %% the node is online, but we know for sure that %% the node is offline now, so we can remove it %% from the list of running nodes. - {ok, - {AllNodes, DiscNodes, - fun() -> ordsets:del_element(node(), RunningNodes) end}}; + {ok, {AllNodes, DiscNodes, + ordsets:del_element(node(), RunningNodes)}}; Err = {error, _} -> Err end, case Nodes of - {ok, {AllNodes1, DiscNodes1, RunningNodesThunk}} -> + {ok, {AllNodes1, DiscNodes1, RunningNodes1}} -> {ok, case WhichNodes of - status -> {AllNodes1, DiscNodes1, RunningNodesThunk()}; + status -> {AllNodes1, DiscNodes1, RunningNodes1}; all -> AllNodes1; disc -> DiscNodes1; - running -> RunningNodesThunk() + running -> RunningNodes1 end}; Err1 = {error, _} -> Err1 @@ -1005,9 +1002,7 @@ remove_node_if_mnesia_running(Node) -> end. leave_cluster() -> - case {is_clustered(), - running_nodes(ordsets:del_element(node(), all_clustered_nodes()))} - of + case {is_clustered(), mnesia:system_info(running_db_nodes) -- [node()]} of {false, []} -> ok; {_, AllNodes} -> case lists:any(fun leave_cluster/1, AllNodes) of true -> ok; @@ -1053,15 +1048,6 @@ change_extra_db_nodes(ClusterNodes0, Force) -> Nodes end. -%% What we really want is nodes running rabbit, not running -%% mnesia. Using `mnesia:system_info(running_db_nodes)' will -%% return false positives when we are actually just doing cluster -%% operations (e.g. joining the cluster). -running_nodes(Nodes) -> - {Replies, _BadNodes} = - rpc:multicall(Nodes, rabbit_mnesia, is_running_remote, []), - [Node || {Running, Node} <- Replies, Running]. - is_running_remote() -> {proplists:is_defined(rabbit, application:which_applications(infinity)), node()}. -- cgit v1.2.1 From 784b2821e93f666fec178b0e421da8bbe407c358 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Wed, 19 Sep 2012 12:16:33 +0100 Subject: revert to manual rpc for running nodes, but use mnesia to check if nodes are running --- src/rabbit_mnesia.erl | 47 ++++++++++++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 1dfdb1f7..e1dee13e 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -316,7 +316,7 @@ forget_cluster_node(Node, RemoveWhenOffline) -> end. remove_node_offline_node(Node) -> - case {mnesia:system_info(running_db_nodes) -- [Node], + case {ordsets:del_element(Node, running_nodes(all_clustered_nodes())), is_disc_node()} of {[], true} -> %% Note that while we check if the nodes was the last to @@ -415,36 +415,38 @@ mnesia_nodes() -> true -> ordsets:add_element(node(), DiscCopies); false -> DiscCopies end, - RunningNodes = mnesia:system_info(running_db_nodes), - {ok, {AllNodes, DiscNodes, RunningNodes}}; + {ok, {AllNodes, DiscNodes}}; false -> {error, tables_not_present} end end. cluster_status(WhichNodes, ForceMnesia) -> + %% I don't want to call `running_nodes/1' unless if necessary, since it's + %% pretty expensive. Nodes = case mnesia_nodes() of - {ok, Status} -> - {ok, Status}; + {ok, {AllNodes, DiscNodes}} -> + {ok, {AllNodes, DiscNodes, + fun() -> running_nodes(AllNodes) end}}; {error, _Reason} when not ForceMnesia -> {AllNodes, DiscNodes, RunningNodes} = rabbit_node_monitor:read_cluster_status(), - %% The cluster status file records the status when - %% the node is online, but we know for sure that - %% the node is offline now, so we can remove it - %% from the list of running nodes. - {ok, {AllNodes, DiscNodes, - ordsets:del_element(node(), RunningNodes)}}; + %% The cluster status file records the status when the node + %% is online, but we know for sure that the node is offline + %% now, so we can remove it from the list of running nodes. + {ok, + {AllNodes, DiscNodes, + fun() -> ordsets:del_element(node(), RunningNodes) end}}; Err = {error, _} -> Err end, case Nodes of - {ok, {AllNodes1, DiscNodes1, RunningNodes1}} -> + {ok, {AllNodes1, DiscNodes1, RunningNodesThunk}} -> {ok, case WhichNodes of - status -> {AllNodes1, DiscNodes1, RunningNodes1}; + status -> {AllNodes1, DiscNodes1, RunningNodesThunk()}; all -> AllNodes1; disc -> DiscNodes1; - running -> RunningNodes1 + running -> RunningNodesThunk() end}; Err1 = {error, _} -> Err1 @@ -1002,7 +1004,9 @@ remove_node_if_mnesia_running(Node) -> end. leave_cluster() -> - case {is_clustered(), mnesia:system_info(running_db_nodes) -- [node()]} of + case {is_clustered(), + running_nodes(ordsets:del_element(node(), all_clustered_nodes()))} + of {false, []} -> ok; {_, AllNodes} -> case lists:any(fun leave_cluster/1, AllNodes) of true -> ok; @@ -1048,9 +1052,18 @@ change_extra_db_nodes(ClusterNodes0, Force) -> Nodes end. +%% We're not using `mnesia:system_info(running_db_nodes)' directly because if +%% the node is a RAM node it won't know about other nodes when mnesia is stopped +running_nodes(Nodes) -> + {Replies, _BadNodes} = + rpc:multicall(Nodes, rabbit_mnesia, is_running_remote, []), + [Node || {Running, Node} <- Replies, Running]. + is_running_remote() -> - {proplists:is_defined(rabbit, application:which_applications(infinity)), - node()}. + {case mnesia:system_info(is_running) of + yes -> true; + no -> false + end, node()}. check_consistency(OTP, Rabbit) -> rabbit_misc:sequence_error( -- cgit v1.2.1 From 92505421fd51c872ad8e619a52460241b30e14a4 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Wed, 19 Sep 2012 12:31:58 +0100 Subject: ordsets, not lists, in `rabbit_node_monitor' --- src/rabbit_node_monitor.erl | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index 846b2ac8..288f3e42 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -93,10 +93,14 @@ prepare_cluster_status_files() -> {ok, _ } -> CorruptFiles(); {error, enoent} -> [] end, + ThisNode = [node()], + %% The running nodes file might contain a set or a list, in case of the + %% legacy file + RunningNodes2 = ordsets:from_list(ThisNode ++ RunningNodes1), {AllNodes1, WantDiscNode} = case try_read_file(cluster_status_filename()) of {ok, [{AllNodes, DiscNodes0}]} -> - {AllNodes, lists:member(node(), DiscNodes0)}; + {AllNodes, ordsets:is_element(node(), DiscNodes0)}; {ok, [AllNodes0]} when is_list(AllNodes0) -> {legacy_cluster_nodes(AllNodes0), legacy_should_be_disc_node(AllNodes0)}; @@ -105,16 +109,11 @@ prepare_cluster_status_files() -> {error, enoent} -> {legacy_cluster_nodes([]), true} end, - - ThisNode = [node()], - - RunningNodes2 = lists:usort(RunningNodes1 ++ ThisNode), - AllNodes2 = lists:usort(AllNodes1 ++ RunningNodes2), + AllNodes2 = ordsets:union(AllNodes1, RunningNodes2), DiscNodes = case WantDiscNode of true -> ThisNode; false -> [] end, - ok = write_cluster_status({AllNodes2, DiscNodes, RunningNodes2}). write_cluster_status({All, Disc, Running}) -> @@ -290,7 +289,7 @@ is_already_monitored(Item) -> legacy_cluster_nodes(Nodes) -> %% We get all the info that we can, including the nodes from mnesia, which %% will be there if the node is a disc node (empty list otherwise) - lists:usort(Nodes ++ mnesia:system_info(db_nodes)). + ordsets:from_list(Nodes ++ mnesia:system_info(db_nodes)). legacy_should_be_disc_node(DiscNodes) -> DiscNodes == [] orelse lists:member(node(), DiscNodes). -- cgit v1.2.1 From a50c2cddf5e9bc5f47fe87561bf1e72e3b8919de Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Wed, 19 Sep 2012 12:41:48 +0100 Subject: ordsets instead of lists in `rabbit_upgrade' --- src/rabbit_upgrade.erl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/rabbit_upgrade.erl b/src/rabbit_upgrade.erl index 9c54eb72..4f34504e 100644 --- a/src/rabbit_upgrade.erl +++ b/src/rabbit_upgrade.erl @@ -126,11 +126,11 @@ maybe_upgrade_mnesia() -> {error, starting_from_scratch} -> ok; {error, version_not_available} -> - case AllNodes of - [_] -> ok; - _ -> die("Cluster upgrade needed but upgrading from " + case ordsets:size(AllNodes) of + 0 -> die("Cluster upgrade needed but upgrading from " "< 2.1.1.~nUnfortunately you will need to " - "rebuild the cluster.", []) + "rebuild the cluster.", []); + _ -> ok end; {error, _} = Err -> throw(Err); @@ -145,7 +145,7 @@ maybe_upgrade_mnesia() -> end. upgrade_mode(AllNodes) -> - case nodes_running(AllNodes) of + case nodes_running(ordsets:to_list(AllNodes)) of [] -> AfterUs = rabbit_mnesia:running_clustered_nodes() -- [node()], case {node_type_legacy(), AfterUs} of @@ -196,7 +196,7 @@ die(Msg, Args) -> halt(1). primary_upgrade(Upgrades, Nodes) -> - Others = Nodes -- [node()], + Others = ordsets:del_element(node(), Nodes), ok = apply_upgrades( mnesia, Upgrades, @@ -206,7 +206,7 @@ primary_upgrade(Upgrades, Nodes) -> [] -> ok; _ -> info("mnesia upgrades: Breaking cluster~n", []), [{atomic, ok} = mnesia:del_table_copy(schema, Node) - || Node <- Others] + || Node <- ordsets:to_list(Others)] end end), ok. -- cgit v1.2.1 From f982067648cd4dc816eeab853cbcd11fe2b87c3c Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Wed, 19 Sep 2012 12:50:49 +0100 Subject: oops. --- src/rabbit_mnesia.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 1fcff223..c60a30a5 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -148,7 +148,7 @@ init_from_config() -> rabbit_log:warning("Could not find any suitable node amongst the " "ones provided in the configuration: ~p~n", [TryNodes]), - init(true, ordsets:from_list([node()])) + init(disc, ordsets:from_list([node()])) end. %% Make the node join a cluster. The node will be reset automatically -- cgit v1.2.1 From 4c3f9bdf2d7446a91255070f1020a2f4e1eb5b9c Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Wed, 19 Sep 2012 13:01:36 +0100 Subject: fix `rabbit_mnesia:is_virgin_node/0', was checking only for one status file --- Makefile | 2 +- src/rabbit_mnesia.erl | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index f3729cfa..c63e3dfd 100644 --- a/Makefile +++ b/Makefile @@ -147,7 +147,7 @@ $(SOURCE_DIR)/rabbit_framing_amqp_0_8.erl: codegen.py $(AMQP_CODEGEN_DIR)/amqp_c dialyze: $(BEAM_TARGETS) $(BASIC_PLT) dialyzer --plt $(BASIC_PLT) --no_native --fullpath \ - -Wrace_conditions $(BEAM_TARGETS) + $(BEAM_TARGETS) # rabbit.plt is used by rabbitmq-erlang-client's dialyze make target create-plt: $(RABBIT_PLT) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index f19046a0..611f7dda 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -1106,16 +1106,17 @@ check_rabbit_consistency(Remote) -> %% mnesia tables aren't there because restarted RAM nodes won't have %% tables while still being non-virgin. What we do instead is to %% check if the mnesia directory is non existant or empty, with the -%% exception of the cluster status file, which will be there thanks to +%% exception of the cluster status files, which will be there thanks to %% `rabbit_node_monitor:prepare_cluster_status_file/0'. is_virgin_node() -> case rabbit_file:list_dir(dir()) of {error, enoent} -> true; - {ok, []} -> true; - {ok, [File]} -> (dir() ++ "/" ++ File) =:= - [rabbit_node_monitor:cluster_status_filename(), - rabbit_node_monitor:running_nodes_filename()]; - {ok, _} -> false + {ok, []} -> true; + {ok, [File1, File2]} -> + lists:usort([dir() ++ "/" ++ File1, dir() ++ "/" ++ File2]) =:= + lists:usort([rabbit_node_monitor:cluster_status_filename(), + rabbit_node_monitor:running_nodes_filename()]); + {ok, _} -> false end. find_good_node([]) -> -- cgit v1.2.1 From 9a56f663cd1e7697161005dc9769bb655fb6ccfc Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 19 Sep 2012 14:28:33 +0100 Subject: Simplify --- src/rabbit_mnesia.erl | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index e1dee13e..cce4f4b9 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -1060,10 +1060,7 @@ running_nodes(Nodes) -> [Node || {Running, Node} <- Replies, Running]. is_running_remote() -> - {case mnesia:system_info(is_running) of - yes -> true; - no -> false - end, node()}. + {mnesia:system_info(is_running) =:= yes, node()}. check_consistency(OTP, Rabbit) -> rabbit_misc:sequence_error( -- cgit v1.2.1 From 7e46234ac9183856ddc1fc063cef780190f6c086 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 19 Sep 2012 14:34:59 +0100 Subject: That hack should not be needed any more. --- src/rabbit_mirror_queue_misc.erl | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 603a490b..295f12da 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -73,7 +73,7 @@ remove_from_queue(QueueName, DeadGMPids) -> remove_from_queue0(QueueName, DeadGMPids) -> DeadNodes = [node(DeadGMPid) || DeadGMPid <- DeadGMPids], - ClusterNodes = clusterable_nodes() -- DeadNodes, + ClusterNodes = rabbit_mnesia:running_clustered_nodes() -- DeadNodes, rabbit_misc:execute_mnesia_transaction( fun () -> %% Someone else could have deleted the queue before we @@ -115,7 +115,7 @@ remove_from_queue0(QueueName, DeadGMPids) -> end). on_node_up() -> - ClusterNodes = clusterable_nodes(), + ClusterNodes = rabbit_mnesia:running_clustered_nodes(), QNames = rabbit_misc:execute_mnesia_transaction( fun () -> @@ -233,10 +233,12 @@ promote_slave([SPid | SPids]) -> %% the one to promote is the oldest. {SPid, SPids}. -suggested_queue_nodes(Q) -> suggested_queue_nodes(Q, clusterable_nodes()). +suggested_queue_nodes(Q) -> + suggested_queue_nodes(Q, rabbit_mnesia:running_clustered_nodes()). -%% This variant exists so we can pull a call to clusterable_nodes() -%% out of a loop or transaction or both. +%% This variant exists so we can pull a call to +%% rabbit_mnesia:running_clustered_nodes() out of a loop or +%% transaction or both. suggested_queue_nodes(Q, ClusterNodes) -> {MNode0, SNodes} = actual_queue_nodes(Q), MNode = case MNode0 of @@ -246,14 +248,6 @@ suggested_queue_nodes(Q, ClusterNodes) -> suggested_queue_nodes(policy(<<"ha-mode">>, Q), policy(<<"ha-params">>, Q), {MNode, SNodes}, ClusterNodes). -%% TODO we should probably just redefine -%% rabbit_mnesia:running_clustered_nodes/0? Waiting on Francesco. -clusterable_nodes() -> - %% We may end up here via on_node_up/0, in which case we are still - %% booting - rabbit_mnesia:running_clustered_nodes/0 will report - %% us as not running. - lists:usort([node() | rabbit_mnesia:running_clustered_nodes()]). - policy(Policy, Q) -> case rabbit_policy:get(Policy, Q) of {ok, P} -> P; -- cgit v1.2.1 From d75b9c20d60ff44ed7a25f8f86700b86f54507b8 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 19 Sep 2012 15:26:08 +0100 Subject: inline --- src/rabbit_mnesia.erl | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index c60a30a5..f4ed8db4 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -383,7 +383,7 @@ clustered_ram_nodes() -> ordsets:subtract(cluster_status(all), running_clustered_nodes() -> cluster_status(running). running_clustered_disc_nodes() -> - {_, DiscNodes, RunningNodes} = cluster_status(), + {_AllNodes, DiscNodes, RunningNodes} = cluster_status(status), ordsets:intersection(DiscNodes, RunningNodes). %% This function is the actual source of information, since it gets @@ -456,8 +456,6 @@ cluster_status(WhichNodes) -> {ok, Status} = cluster_status(WhichNodes, false), Status. -cluster_status() -> cluster_status(status). - cluster_status_from_mnesia() -> cluster_status(status, true). node_info() -> -- cgit v1.2.1 From 3c2091c7bf12183989ca7a0df7d747a8f7caa14c Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 19 Sep 2012 15:39:06 +0100 Subject: eek --- src/rabbit_mnesia.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index f4ed8db4..4b632e5a 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -403,7 +403,7 @@ mnesia_nodes() -> false -> ram end, Tables = mnesia:system_info(tables), - {Table, _} = case table_definitions(NodeType) of [T|_] -> T end, + [{Table, _} | _] = table_definitions(NodeType), case lists:member(Table, Tables) of true -> AllNodes = -- cgit v1.2.1 From e6afafb3df57ea30da8816395904674de7927de9 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 19 Sep 2012 15:40:50 +0100 Subject: cosmetic --- src/rabbit_mnesia.erl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 4b632e5a..72799b65 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -176,7 +176,7 @@ join_cluster(DiscoveryNode, NodeType) -> {ClusterNodes, _, _} = case discover_cluster(DiscoveryNode) of {ok, Res} -> Res; - E = {error, _} -> throw(E) + {error, _} = E -> throw(E) end, case me_in_nodes(ClusterNodes) of @@ -307,7 +307,7 @@ forget_cluster_node(Node, RemoveWhenOffline) -> remove_node_offline_node(Node); {error, mnesia_not_running} -> e(offline_node_no_offline_flag); - Err = {error, _} -> + {error, _} = Err -> throw(Err) end. @@ -437,7 +437,7 @@ cluster_status(WhichNodes, ForceMnesia) -> %% from the list of running nodes. {ok, {AllNodes, DiscNodes, fun() -> nodes_excl_me(RunningNodes) end}}; - Err = {error, _} -> + {error, _} = Err -> Err end, case Nodes of @@ -448,7 +448,7 @@ cluster_status(WhichNodes, ForceMnesia) -> disc -> DiscNodes1; running -> RunningNodesThunk() end}; - Err1 = {error, _} -> + {error, _} = Err1 -> Err1 end. @@ -674,7 +674,7 @@ check_cluster_consistency() -> rabbit_node_monitor:write_cluster_status(Status); {error, not_found} -> ok; - E = {error, _} -> + {error, _} = E -> throw(E) end. @@ -686,7 +686,7 @@ check_cluster_consistency(Node) -> {error, not_found}; {OTP, Rabbit, {ok, Status}} -> case check_consistency(OTP, Rabbit, Node, Status) of - E = {error, _} -> E; + {error, _} = E -> E; {ok, Res} -> {ok, Res} end end. -- cgit v1.2.1 From 65ca4fa90cba2051fb52fbb725ee2d58be11f5e4 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 19 Sep 2012 15:59:03 +0100 Subject: simplify --- src/rabbit_mnesia.erl | 57 ++++++++++++++++++++++----------------------------- 1 file changed, 25 insertions(+), 32 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 72799b65..6da103ba 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -421,42 +421,35 @@ mnesia_nodes() -> end end. -cluster_status(WhichNodes, ForceMnesia) -> +cluster_status(WhichNodes) -> %% I don't want to call `running_nodes/1' unless if necessary, %% since it can deadlock when stopping applications. - Nodes = case mnesia_nodes() of - {ok, {AllNodes, DiscNodes}} -> - {ok, {AllNodes, DiscNodes, - fun() -> running_nodes(AllNodes) end}}; - {error, _Reason} when not ForceMnesia -> - {AllNodes, DiscNodes, RunningNodes} = - rabbit_node_monitor:read_cluster_status(), - %% The cluster status file records the status when - %% the node is online, but we know for sure that - %% the node is offline now, so we can remove it - %% from the list of running nodes. - {ok, {AllNodes, DiscNodes, - fun() -> nodes_excl_me(RunningNodes) end}}; - {error, _} = Err -> - Err - end, - case Nodes of - {ok, {AllNodes1, DiscNodes1, RunningNodesThunk}} -> - {ok, case WhichNodes of - status -> {AllNodes1, DiscNodes1, RunningNodesThunk()}; - all -> AllNodes1; - disc -> DiscNodes1; - running -> RunningNodesThunk() - end}; - {error, _} = Err1 -> - Err1 + {AllNodes1, DiscNodes1, RunningNodesThunk} = + case mnesia_nodes() of + {ok, {AllNodes, DiscNodes}} -> + {AllNodes, DiscNodes, fun() -> running_nodes(AllNodes) end}; + {error, _Reason} -> + {AllNodes, DiscNodes, RunningNodes} = + rabbit_node_monitor:read_cluster_status(), + %% The cluster status file records the status when the + %% node is online, but we know for sure that the node + %% is offline now, so we can remove it from the list + %% of running nodes. + {AllNodes, DiscNodes, fun() -> nodes_excl_me(RunningNodes) end} + end, + case WhichNodes of + status -> {AllNodes1, DiscNodes1, RunningNodesThunk()}; + all -> AllNodes1; + disc -> DiscNodes1; + running -> RunningNodesThunk() end. -cluster_status(WhichNodes) -> - {ok, Status} = cluster_status(WhichNodes, false), - Status. - -cluster_status_from_mnesia() -> cluster_status(status, true). +cluster_status_from_mnesia() -> + case mnesia_nodes() of + {ok, {AllNodes, DiscNodes}} -> {ok, {AllNodes, DiscNodes, + running_nodes(AllNodes)}}; + {error, _} = Err -> Err + end. node_info() -> {erlang:system_info(otp_release), rabbit_misc:version(), -- cgit v1.2.1 From 65d20fdc8c31f2cd263a48699bbf7806b580c57a Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 19 Sep 2012 16:06:22 +0100 Subject: better specs --- src/rabbit_mnesia.erl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 6da103ba..3ffa75b0 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -90,8 +90,8 @@ -spec(node_type/0 :: () -> node_type()). -spec(dir/0 :: () -> file:filename()). -spec(table_names/0 :: () -> [atom()]). --spec(cluster_status_from_mnesia/0 :: () -> {'ok', cluster_status()} | - {'error', any()}). +-spec(cluster_status_from_mnesia/0 :: () -> rabbit_types:ok_or_error2( + cluster_status(), any())). %% Operations on the db and utils, mainly used in `rabbit_upgrade' and `rabbit' -spec(init_db_unchecked/2 :: (node_set(), node_type()) -> 'ok'). @@ -109,8 +109,8 @@ %% Functions used in internal rpc calls -spec(node_info/0 :: () -> {string(), string(), ({'ok', cluster_status()} | 'error')}). --spec(remove_node_if_mnesia_running/1 :: (node()) -> 'ok' | - {'error', term()}). +-spec(remove_node_if_mnesia_running/1 :: + (node()) -> rabbit_types:ok_or_error(term())). -endif. -- cgit v1.2.1 From 2965eaf2eba0db8dc59dd7a73a33f278fc1723e6 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 20 Sep 2012 12:05:07 +0100 Subject: Start new slaves in response to gm deaths a bit later, to prevent deadlock. Also remove {add,drop}_mirror/3 as not used, and don't export drop_mirror/2 as not used outside the module. --- src/rabbit_mirror_queue_coordinator.erl | 3 ++- src/rabbit_mirror_queue_master.erl | 2 +- src/rabbit_mirror_queue_misc.erl | 34 +++++++++++---------------------- src/rabbit_mirror_queue_slave.erl | 20 ++++++++++++------- 4 files changed, 27 insertions(+), 32 deletions(-) diff --git a/src/rabbit_mirror_queue_coordinator.erl b/src/rabbit_mirror_queue_coordinator.erl index 4455b441..5284000b 100644 --- a/src/rabbit_mirror_queue_coordinator.erl +++ b/src/rabbit_mirror_queue_coordinator.erl @@ -344,9 +344,10 @@ handle_cast({gm_deaths, Deaths}, State = #state { q = #amqqueue { name = QueueName, pid = MPid } }) when node(MPid) =:= node() -> case rabbit_mirror_queue_misc:remove_from_queue(QueueName, Deaths) of - {ok, MPid, DeadPids} -> + {ok, MPid, DeadPids, ExtraNodes} -> rabbit_mirror_queue_misc:report_deaths(MPid, true, QueueName, DeadPids), + rabbit_mirror_queue_misc:add_mirrors(QueueName, ExtraNodes), noreply(State); {error, not_found} -> {stop, normal, State} diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index 4c6f9bbd..b7db04fe 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -97,7 +97,7 @@ init_with_existing_bq(#amqqueue { name = QName } = Q, BQ, BQS) -> Q, undefined, sender_death_fun(), length_fun()), GM = rabbit_mirror_queue_coordinator:get_gm(CPid), {_MNode, SNodes} = rabbit_mirror_queue_misc:suggested_queue_nodes(Q), - [rabbit_mirror_queue_misc:add_mirror(QName, Node) || Node <- SNodes], + rabbit_mirror_queue_misc:add_mirrors(QName, SNodes), ok = gm:broadcast(GM, {depth, BQ:depth(BQS)}), #state { gm = GM, coordinator = CPid, diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 295f12da..090948e6 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -16,8 +16,7 @@ -module(rabbit_mirror_queue_misc). --export([remove_from_queue/2, on_node_up/0, - drop_mirror/2, drop_mirror/3, add_mirror/2, add_mirror/3, +-export([remove_from_queue/2, on_node_up/0, add_mirrors/2, add_mirror/2, report_deaths/4, store_updated_slaves/1, suggested_queue_nodes/1, is_mirrored/1, update_mirrors/2]). @@ -32,15 +31,11 @@ -spec(remove_from_queue/2 :: (rabbit_amqqueue:name(), [pid()]) - -> {'ok', pid(), [pid()]} | {'error', 'not_found'}). + -> {'ok', pid(), [pid()], [node()]} | {'error', 'not_found'}). -spec(on_node_up/0 :: () -> 'ok'). --spec(drop_mirror/2 :: - (rabbit_amqqueue:name(), node()) -> rabbit_types:ok_or_error(any())). +-spec(add_mirrors/2 :: (rabbit_amqqueue:name(), [node()]) -> 'ok'). -spec(add_mirror/2 :: (rabbit_amqqueue:name(), node()) -> rabbit_types:ok_or_error(any())). --spec(add_mirror/3 :: - (rabbit_types:vhost(), binary(), atom()) - -> rabbit_types:ok_or_error(any())). -spec(store_updated_slaves/1 :: (rabbit_types:amqqueue()) -> rabbit_types:amqqueue()). -spec(suggested_queue_nodes/1 :: (rabbit_types:amqqueue()) -> @@ -63,15 +58,6 @@ %% Returns {ok, NewMPid, DeadPids} remove_from_queue(QueueName, DeadGMPids) -> - case remove_from_queue0(QueueName, DeadGMPids) of - {ok, NewMPid, DeadQPids, ExtraNodes} -> - [ok = add_mirror(QueueName, Node) || Node <- ExtraNodes], - {ok, NewMPid, DeadQPids}; - Other -> - Other - end. - -remove_from_queue0(QueueName, DeadGMPids) -> DeadNodes = [node(DeadGMPid) || DeadGMPid <- DeadGMPids], ClusterNodes = rabbit_mnesia:running_clustered_nodes() -- DeadNodes, rabbit_misc:execute_mnesia_transaction( @@ -132,8 +118,9 @@ on_node_up() -> [add_mirror(QName, node()) || QName <- QNames], ok. -drop_mirror(VHostPath, QueueName, MirrorNode) -> - drop_mirror(rabbit_misc:r(VHostPath, queue, QueueName), MirrorNode). +drop_mirrors(QName, Nodes) -> + [ok = drop_mirror(QName, Node) || Node <- Nodes], + ok. drop_mirror(QName, MirrorNode) -> if_mirrored_queue( @@ -153,8 +140,9 @@ drop_mirror(QName, MirrorNode) -> end end). -add_mirror(VHostPath, QueueName, MirrorNode) -> - add_mirror(rabbit_misc:r(VHostPath, queue, QueueName), MirrorNode). +add_mirrors(QName, Nodes) -> + [ok = add_mirror(QName, Node) || Node <- Nodes], + ok. add_mirror(QName, MirrorNode) -> if_mirrored_queue( @@ -307,6 +295,6 @@ update_mirrors0(OldQ = #amqqueue{name = QName}, %% slaves that add_mirror/2 will add, and also want to add them %% (even though we are not responding to the death of a %% mirror). Breakage ensues. - [ok = add_mirror(QName, Node) || Node <- NewNodes -- OldNodes], - [ok = drop_mirror(QName, Node) || Node <- OldNodes -- NewNodes], + add_mirrors(QName, NewNodes -- OldNodes), + drop_mirrors(QName, OldNodes -- NewNodes), ok. diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 12e3058a..bdbf972c 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -200,18 +200,25 @@ handle_call({gm_deaths, Deaths}, From, {error, not_found} -> gen_server2:reply(From, ok), {stop, normal, State}; - {ok, Pid, DeadPids} -> + {ok, Pid, DeadPids, ExtraNodes} -> rabbit_mirror_queue_misc:report_deaths(self(), false, QueueName, DeadPids), if node(Pid) =:= node(MPid) -> %% master hasn't changed - reply(ok, State); + gen_server2:reply(From, ok), + rabbit_mirror_queue_misc:add_mirrors(QueueName, ExtraNodes), + noreply(State); node(Pid) =:= node() -> %% we've become master - promote_me(From, State); + QueueState = promote_me(From, State), + rabbit_mirror_queue_misc:add_mirrors(QueueName, ExtraNodes), + {become, rabbit_amqqueue_process, QueueState, hibernate}; true -> %% master has changed to not us. gen_server2:reply(From, ok), + %% assertion, we don't need to add_mirrors/2 in this + %% branch, see last clause in remove_from_queue/2 + [] = ExtraNodes, erlang:monitor(process, Pid), %% GM is lazy. So we know of the death of the %% slave since it is a neighbour of ours, but @@ -556,10 +563,9 @@ promote_me(From, #state { q = Q = #amqqueue { name = QName }, AckTags = [AckTag || {_Num, AckTag} <- lists:sort(NumAckTags)], Deliveries = [Delivery || {_ChPid, {PubQ, _PendCh}} <- dict:to_list(SQ), {Delivery, true} <- queue:to_list(PubQ)], - QueueState = rabbit_amqqueue_process:init_with_backing_queue_state( - Q1, rabbit_mirror_queue_master, MasterState, RateTRef, - AckTags, Deliveries, KS, MTC), - {become, rabbit_amqqueue_process, QueueState, hibernate}. + rabbit_amqqueue_process:init_with_backing_queue_state( + Q1, rabbit_mirror_queue_master, MasterState, RateTRef, AckTags, + Deliveries, KS, MTC). noreply(State) -> {NewState, Timeout} = next_state(State), -- cgit v1.2.1 From 352e716468d82e536ce5e50d8bc34487509f5763 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 20 Sep 2012 13:32:22 +0100 Subject: That case was originally written to cover the unmirrored case - but actually the queue might be mirrored but with no slaves, or about to become mirrored, or whatever. And our is_process_alive() check serves the same purpose. --- src/rabbit_amqqueue.erl | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 452b11b3..9d09742d 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -297,8 +297,6 @@ lookup(Name) -> with(Name, F, E) -> case lookup(Name) of - {ok, Q = #amqqueue{slave_pids = []}} -> - rabbit_misc:with_exit_handler(E, fun () -> F(Q) end); {ok, Q = #amqqueue{pid = QPid}} -> %% We check is_process_alive(QPid) in case we receive a %% nodedown (for example) in F() that has nothing to do -- cgit v1.2.1 From e7552bbd97c63d89b045fd34eece3900ccca7034 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 20 Sep 2012 13:47:33 +0100 Subject: "nodes" policy should not suggest nodes which are not running. Also if nodes policy is completely unconnected with reality, just keep the master alive rather than blow up. --- src/rabbit_mirror_queue_misc.erl | 15 +++++++++++---- src/rabbit_tests.erl | 9 ++++++++- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 090948e6..3b25df6a 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -244,11 +244,18 @@ policy(Policy, Q) -> suggested_queue_nodes(<<"all">>, _Params, {MNode, _SNodes}, All) -> {MNode, All -- [MNode]}; -suggested_queue_nodes(<<"nodes">>, Nodes0, {MNode, _SNodes}, _All) -> +suggested_queue_nodes(<<"nodes">>, Nodes0, {MNode, _SNodes}, All) -> Nodes = [list_to_atom(binary_to_list(Node)) || Node <- Nodes0], - case lists:member(MNode, Nodes) of - true -> {MNode, Nodes -- [MNode]}; - false -> promote_slave(Nodes) + Unavailable = Nodes -- All, + Available = Nodes -- Unavailable, + case Available of + [] -> %% We have never heard of anything? Not much we can do but + %% keep the master alive. + {MNode, []}; + _ -> case lists:member(MNode, Available) of + true -> {MNode, Available -- [MNode]}; + false -> promote_slave(Available) + end end; suggested_queue_nodes(<<"exactly">>, Count, {MNode, SNodes}, All) -> SCount = Count - 1, diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 6cb34b09..b1d549fb 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -898,10 +898,17 @@ test_dynamic_mirroring() -> Test({a,[b,c]},<<"all">>,'_',{a,[b,c]},[a,b,c]), Test({a,[b,c]},<<"all">>,'_',{a,[d]}, [a,b,c]), - Test({a,[b,c]},<<"nodes">>,[<<"a">>,<<"b">>,<<"c">>],{a,[d]},[a,b,c,d]), + %% Add a node Test({a,[b,c]},<<"nodes">>,[<<"a">>,<<"b">>,<<"c">>],{a,[b]},[a,b,c,d]), Test({b,[a,c]},<<"nodes">>,[<<"a">>,<<"b">>,<<"c">>],{b,[a]},[a,b,c,d]), + %% Add two nodes and drop one + Test({a,[b,c]},<<"nodes">>,[<<"a">>,<<"b">>,<<"c">>],{a,[d]},[a,b,c,d]), + %% Promote slave to master by policy Test({a,[b,c]},<<"nodes">>,[<<"a">>,<<"b">>,<<"c">>],{d,[a]},[a,b,c,d]), + %% Don't try to include nodes that are not running + Test({a,[b]}, <<"nodes">>,[<<"a">>,<<"b">>,<<"f">>],{a,[b]},[a,b,c,d]), + %% If we can't find any of the nodes listed then just keep the master + Test({a,[]}, <<"nodes">>,[<<"f">>,<<"g">>,<<"h">>],{a,[b]},[a,b,c,d]), Test({a,[b]}, <<"exactly">>,2,{a,[]}, [a,b,c,d]), Test({a,[b,c]},<<"exactly">>,3,{a,[]}, [a,b,c,d]), -- cgit v1.2.1 From 4590a8ea2504eb60253cddf2c9f2b4ebca172423 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 20 Sep 2012 14:47:52 +0100 Subject: Handle the case where we go from unmirrored(A) to nodes(B, C). --- src/rabbit_mirror_queue_misc.erl | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 3b25df6a..011f0663 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -281,12 +281,19 @@ is_mirrored(Q) -> _ -> false end. + +%% [1] - rabbit_amqqueue:start_mirroring/1 will turn unmirrored to +%% master and start any needed slaves. However, if node(QPid) is not +%% in the nodes for the policy, it won't switch it. So this is for the +%% case where we kill the existing queue and restart elsewhere. TODO: +%% is this TRTTD? All alternatives seem ugly. update_mirrors(OldQ = #amqqueue{pid = QPid}, NewQ = #amqqueue{pid = QPid}) -> case {is_mirrored(OldQ), is_mirrored(NewQ)} of {false, false} -> ok; {true, false} -> rabbit_amqqueue:stop_mirroring(QPid); - {false, true} -> rabbit_amqqueue:start_mirroring(QPid); + {false, true} -> rabbit_amqqueue:start_mirroring(QPid), + update_mirrors0(OldQ, NewQ); %% [1] {true, true} -> update_mirrors0(OldQ, NewQ) end. -- cgit v1.2.1 From 3b6fdaa7a21900a85a43c6f2fb6854fb5a9927eb Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Fri, 21 Sep 2012 11:15:00 +0100 Subject: revert all the ordsets operations to lists --- src/rabbit_mnesia.erl | 124 ++++++++++++++++++++------------------------ src/rabbit_node_monitor.erl | 35 +++++++------ src/rabbit_upgrade.erl | 16 +++--- 3 files changed, 82 insertions(+), 93 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 3ffa75b0..324c5653 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -67,8 +67,7 @@ -export_type([node_type/0, cluster_status/0]). -type(node_type() :: disc | ram). --type(node_set() :: ordsets:ordset(node())). --type(cluster_status() :: {node_set(), node_set(), node_set()}). +-type(cluster_status() :: {[node()], [node()], [node()]}). %% Main interface -spec(init/0 :: () -> 'ok'). @@ -80,13 +79,13 @@ -spec(forget_cluster_node/2 :: (node(), boolean()) -> 'ok'). %% Various queries to get the status of the db --spec(status/0 :: () -> [{'nodes', [{node_type(), node_set()}]} | - {'running_nodes', node_set()}]). +-spec(status/0 :: () -> [{'nodes', [{node_type(), [node()]}]} | + {'running_nodes', [node()]}]). -spec(is_db_empty/0 :: () -> boolean()). -spec(is_clustered/0 :: () -> boolean()). --spec(all_clustered_nodes/0 :: () -> node_set()). --spec(clustered_disc_nodes/0 :: () -> node_set()). --spec(running_clustered_nodes/0 :: () -> node_set()). +-spec(all_clustered_nodes/0 :: () -> [node()]). +-spec(clustered_disc_nodes/0 :: () -> [node()]). +-spec(running_clustered_nodes/0 :: () -> [node()]). -spec(node_type/0 :: () -> node_type()). -spec(dir/0 :: () -> file:filename()). -spec(table_names/0 :: () -> [atom()]). @@ -94,7 +93,7 @@ cluster_status(), any())). %% Operations on the db and utils, mainly used in `rabbit_upgrade' and `rabbit' --spec(init_db_unchecked/2 :: (node_set(), node_type()) -> 'ok'). +-spec(init_db_unchecked/2 :: ([node()], node_type()) -> 'ok'). -spec(empty_ram_only_tables/0 :: () -> 'ok'). -spec(create_tables/0 :: () -> 'ok'). -spec(copy_db/1 :: (file:filename()) -> rabbit_types:ok_or_error(any())). @@ -148,7 +147,7 @@ init_from_config() -> rabbit_log:warning("Could not find any suitable node amongst the " "ones provided in the configuration: ~p~n", [TryNodes]), - init(disc, ordsets:from_list([node()])) + init(disc, [node()]) end. %% Make the node join a cluster. The node will be reset automatically @@ -255,7 +254,7 @@ change_cluster_node_type(Type) -> {ok, Status} -> Status; {error, _Reason} -> e(cannot_connect_to_cluster) end, - Node = case ordsets:to_list(RunningNodes) of + Node = case RunningNodes of [] -> e(no_online_cluster_nodes); [Node0|_] -> Node0 end, @@ -292,7 +291,7 @@ update_cluster_nodes(DiscoveryNode) -> %% the last or second to last after the node we're removing to go %% down forget_cluster_node(Node, RemoveWhenOffline) -> - case ordsets:is_element(Node, all_clustered_nodes()) of + case lists:member(Node, all_clustered_nodes()) of true -> ok; false -> e(not_a_cluster_node) end, @@ -312,10 +311,8 @@ forget_cluster_node(Node, RemoveWhenOffline) -> end. remove_node_offline_node(Node) -> - case {empty_set( - ordsets:del_element(Node, running_nodes(all_clustered_nodes()))), - node_type()} of - {true, disc} -> + case {running_nodes(all_clustered_nodes()) -- [Node], node_type()} of + {[], disc} -> %% Note that while we check if the nodes was the last to %% go down, apart from the node we're removing from, this %% is still unsafe. Consider the situation in which A and @@ -324,20 +321,19 @@ remove_node_offline_node(Node) -> %% and B goes down. In this case, C is the second-to-last, %% but we don't know that and we'll remove B from A %% anyway, even if that will lead to bad things. - case empty_set(ordsets:subtract(running_clustered_nodes(), - ordsets:from_list([node(), Node]))) - of true -> start_mnesia(), - try - [mnesia:force_load_table(T) || - T <- rabbit_mnesia:table_names()], - forget_cluster_node(Node, false), - ensure_mnesia_running() - after + case running_clustered_nodes() -- [node(), Node] of + [] -> start_mnesia(), + try + [mnesia:force_load_table(T) || + T <- rabbit_mnesia:table_names()], + forget_cluster_node(Node, false), + ensure_mnesia_running() + after stop_mnesia() - end; - false -> e(not_last_node_to_go_down) + end; + _ -> e(not_last_node_to_go_down) end; - {false, _} -> + {_, _} -> e(removing_node_from_offline_node) end. @@ -347,11 +343,8 @@ remove_node_offline_node(Node) -> %%---------------------------------------------------------------------------- status() -> - IfNonEmpty = fun (Type, Nodes) -> - case empty_set(Nodes) of - true -> []; - false -> [{Type, ordsets:to_list(Nodes)}] - end + IfNonEmpty = fun (_, []) -> []; + (Type, Nodes) -> [{Type, Nodes}] end, [{nodes, (IfNonEmpty(disc, clustered_disc_nodes()) ++ IfNonEmpty(ram, clustered_ram_nodes()))}] ++ @@ -366,7 +359,7 @@ is_db_empty() -> is_clustered() -> AllNodes = all_clustered_nodes(), - not is_only_node(AllNodes) andalso not empty_set(AllNodes). + not is_only_node(AllNodes) andalso AllNodes =/= []. is_disc_and_clustered() -> node_type() =:= disc andalso is_clustered(). @@ -377,14 +370,14 @@ all_clustered_nodes() -> cluster_status(all). clustered_disc_nodes() -> cluster_status(disc). -clustered_ram_nodes() -> ordsets:subtract(cluster_status(all), - cluster_status(disc)). +clustered_ram_nodes() -> cluster_status(all) -- cluster_status(disc). running_clustered_nodes() -> cluster_status(running). running_clustered_disc_nodes() -> {_AllNodes, DiscNodes, RunningNodes} = cluster_status(status), - ordsets:intersection(DiscNodes, RunningNodes). + ordsets:intersection(ordsets:from_list(DiscNodes), + ordsets:from_list(RunningNodes)). %% This function is the actual source of information, since it gets %% the data from mnesia. Obviously it'll work only when mnesia is @@ -406,15 +399,12 @@ mnesia_nodes() -> [{Table, _} | _] = table_definitions(NodeType), case lists:member(Table, Tables) of true -> - AllNodes = - ordsets:from_list(mnesia:system_info(db_nodes)), - DiscCopies = ordsets:from_list( - mnesia:table_info(schema, disc_copies)), - DiscNodes = - case NodeType of - disc -> nodes_incl_me(DiscCopies); - ram -> DiscCopies - end, + AllNodes = mnesia:system_info(db_nodes), + DiscCopies = mnesia:table_info(schema, disc_copies), + DiscNodes = case NodeType of + disc -> nodes_incl_me(DiscCopies); + ram -> DiscCopies + end, {ok, {AllNodes, DiscNodes}}; false -> {error, tables_not_present} @@ -457,7 +447,7 @@ node_info() -> node_type() -> DiscNodes = clustered_disc_nodes(), - case empty_set(DiscNodes) orelse me_in_nodes(DiscNodes) of + case DiscNodes =:= [] orelse me_in_nodes(DiscNodes) of true -> disc; false -> ram end. @@ -647,7 +637,8 @@ check_cluster_consistency() -> end, {error, not_found}, nodes_excl_me(all_clustered_nodes())) of {ok, Status = {RemoteAllNodes, _, _}} -> - case ordsets:is_subset(all_clustered_nodes(), RemoteAllNodes) of + case ordsets:is_subset(ordsets:from_list(all_clustered_nodes()), + ordsets:from_list(RemoteAllNodes)) of true -> ok; false -> @@ -695,9 +686,9 @@ on_node_up(Node) -> end. on_node_down(_Node) -> - case empty_set(running_clustered_disc_nodes()) of - true -> rabbit_log:info("only running disc node went down~n"); - false -> ok + case running_clustered_disc_nodes() of + [] -> rabbit_log:info("only running disc node went down~n"); + _ -> ok end. %%-------------------------------------------------------------------- @@ -995,15 +986,12 @@ remove_node_if_mnesia_running(Node) -> leave_cluster() -> RunningNodes = running_nodes(nodes_excl_me(all_clustered_nodes())), - case not is_clustered() andalso empty_set(RunningNodes) of - true -> - ok; - false -> - case lists:any(fun leave_cluster/1, - ordsets:to_list(RunningNodes)) of - true -> ok; - false -> e(no_running_cluster_nodes) - end + case not is_clustered() andalso RunningNodes =:= [] of + true -> ok; + false -> case lists:any(fun leave_cluster/1, RunningNodes) of + true -> ok; + false -> e(no_running_cluster_nodes) + end end. leave_cluster(Node) -> @@ -1035,7 +1023,7 @@ stop_mnesia() -> ensure_mnesia_not_running(). change_extra_db_nodes(ClusterNodes0, CheckOtherNodes) -> - ClusterNodes = ordsets:to_list(nodes_excl_me(ClusterNodes0)), + ClusterNodes = nodes_excl_me(ClusterNodes0), case {mnesia:change_config(extra_db_nodes, ClusterNodes), ClusterNodes} of {{ok, []}, [_|_]} when CheckOtherNodes -> throw({error, {failed_to_cluster_with, ClusterNodes, @@ -1049,9 +1037,9 @@ change_extra_db_nodes(ClusterNodes0, CheckOtherNodes) -> %% return false positives when we are actually just doing cluster %% operations (e.g. joining the cluster). running_nodes(Nodes) -> - {Replies, _BadNodes} = rpc:multicall(ordsets:to_list(Nodes), rabbit_mnesia, - is_running_remote, []), - ordsets:from_list([Node || {Running, Node} <- Replies, Running]). + {Replies, _BadNodes} = + rpc:multicall(Nodes, rabbit_mnesia, is_running_remote, []), + [Node || {Running, Node} <- Replies, Running]. is_running_remote() -> {proplists:is_defined(rabbit, application:which_applications(infinity)), @@ -1119,19 +1107,17 @@ find_good_node([Node | Nodes]) -> end end. -is_only_node(Node, Nodes) -> ordsets:to_list(Nodes) == [Node]. +is_only_node(Node, Nodes) -> Nodes =:= [Node]. is_only_node(Nodes) -> is_only_node(node(), Nodes). is_only_disc_node() -> is_only_node(clustered_disc_nodes()). -me_in_nodes(Nodes) -> ordsets:is_element(node(), Nodes). - -nodes_incl_me(Nodes) -> ordsets:add_element(node(), Nodes). +me_in_nodes(Nodes) -> lists:member(node(), Nodes). -nodes_excl_me(Nodes) -> ordsets:del_element(node(), Nodes). +nodes_incl_me(Nodes) -> lists:usort([node()|Nodes]). -empty_set(Set) -> ordsets:size(Set) =:= 0. +nodes_excl_me(Nodes) -> Nodes -- [node()]. e(Tag) -> throw({error, {Tag, error_description(Tag)}}). diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index 288f3e42..f5877c9b 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -96,11 +96,11 @@ prepare_cluster_status_files() -> ThisNode = [node()], %% The running nodes file might contain a set or a list, in case of the %% legacy file - RunningNodes2 = ordsets:from_list(ThisNode ++ RunningNodes1), + RunningNodes2 = lists:usort(ThisNode ++ RunningNodes1), {AllNodes1, WantDiscNode} = case try_read_file(cluster_status_filename()) of {ok, [{AllNodes, DiscNodes0}]} -> - {AllNodes, ordsets:is_element(node(), DiscNodes0)}; + {AllNodes, lists:member(node(), DiscNodes0)}; {ok, [AllNodes0]} when is_list(AllNodes0) -> {legacy_cluster_nodes(AllNodes0), legacy_should_be_disc_node(AllNodes0)}; @@ -109,7 +109,7 @@ prepare_cluster_status_files() -> {error, enoent} -> {legacy_cluster_nodes([]), true} end, - AllNodes2 = ordsets:union(AllNodes1, RunningNodes2), + AllNodes2 = lists:usort(AllNodes1 ++ RunningNodes2), DiscNodes = case WantDiscNode of true -> ThisNode; false -> [] @@ -179,7 +179,7 @@ node_up(Node, IsDiscNode) -> notify_node_up() -> Nodes = cluster_multicall(node_up, [node(), rabbit_mnesia:node_type()]), %% register other active rabbits with this rabbit - [ node_up(N, ordsets:is_element(N, rabbit_mnesia:clustered_disc_nodes())) || + [ node_up(N, lists:member(N, rabbit_mnesia:clustered_disc_nodes())) || N <- Nodes ], ok. @@ -203,31 +203,29 @@ handle_cast({node_up, Node, NodeType}, State) -> true -> {noreply, State}; false -> rabbit_log:info("rabbit on node ~p up~n", [Node]), {AllNodes, DiscNodes, RunningNodes} = read_cluster_status(), - write_cluster_status({ordsets:add_element(Node, AllNodes), + write_cluster_status({add_node(Node, AllNodes), case NodeType of - disc -> ordsets:add_element( - Node, DiscNodes); + disc -> add_node(Node, DiscNodes); ram -> DiscNodes end, - ordsets:add_element(Node, RunningNodes)}), + add_node(Node, RunningNodes)}), erlang:monitor(process, {rabbit, Node}), ok = handle_live_rabbit(Node), {noreply, State} end; handle_cast({joined_cluster, Node, NodeType}, State) -> {AllNodes, DiscNodes, RunningNodes} = read_cluster_status(), - write_cluster_status({ordsets:add_element(Node, AllNodes), + write_cluster_status({add_node(Node, AllNodes), case NodeType of - disc -> ordsets:add_element(Node, DiscNodes); + disc -> add_node(Node, DiscNodes); ram -> DiscNodes end, RunningNodes}), {noreply, State}; handle_cast({left_cluster, Node}, State) -> {AllNodes, DiscNodes, RunningNodes} = read_cluster_status(), - write_cluster_status({ordsets:del_element(Node, AllNodes), - ordsets:del_element(Node, DiscNodes), - ordsets:del_element(Node, RunningNodes)}), + write_cluster_status({del_node(Node, AllNodes), del_node(Node, DiscNodes), + del_node(Node, RunningNodes)}), {noreply, State}; handle_cast(_Msg, State) -> {noreply, State}. @@ -235,8 +233,7 @@ handle_cast(_Msg, State) -> handle_info({'DOWN', _MRef, process, {rabbit, Node}, _Reason}, State) -> rabbit_log:info("rabbit on node ~p down~n", [Node]), {AllNodes, DiscNodes, RunningNodes} = read_cluster_status(), - write_cluster_status({AllNodes, DiscNodes, - ordsets:del_element(Node, RunningNodes)}), + write_cluster_status({AllNodes, DiscNodes, del_node(Node, RunningNodes)}), ok = handle_dead_rabbit(Node), {noreply, State}; handle_info(_Info, State) -> @@ -289,7 +286,13 @@ is_already_monitored(Item) -> legacy_cluster_nodes(Nodes) -> %% We get all the info that we can, including the nodes from mnesia, which %% will be there if the node is a disc node (empty list otherwise) - ordsets:from_list(Nodes ++ mnesia:system_info(db_nodes)). + lists:usort(Nodes ++ mnesia:system_info(db_nodes)). legacy_should_be_disc_node(DiscNodes) -> DiscNodes == [] orelse lists:member(node(), DiscNodes). + +add_node(Node, Nodes) -> + lists:usort([Node|Nodes]). + +del_node(Node, Nodes) -> + Nodes -- [Node]. diff --git a/src/rabbit_upgrade.erl b/src/rabbit_upgrade.erl index 4f34504e..bfc3e006 100644 --- a/src/rabbit_upgrade.erl +++ b/src/rabbit_upgrade.erl @@ -126,11 +126,11 @@ maybe_upgrade_mnesia() -> {error, starting_from_scratch} -> ok; {error, version_not_available} -> - case ordsets:size(AllNodes) of - 0 -> die("Cluster upgrade needed but upgrading from " - "< 2.1.1.~nUnfortunately you will need to " - "rebuild the cluster.", []); - _ -> ok + case AllNodes of + [] -> die("Cluster upgrade needed but upgrading from " + "< 2.1.1.~nUnfortunately you will need to " + "rebuild the cluster.", []); + _ -> ok end; {error, _} = Err -> throw(Err); @@ -145,7 +145,7 @@ maybe_upgrade_mnesia() -> end. upgrade_mode(AllNodes) -> - case nodes_running(ordsets:to_list(AllNodes)) of + case nodes_running(AllNodes) of [] -> AfterUs = rabbit_mnesia:running_clustered_nodes() -- [node()], case {node_type_legacy(), AfterUs} of @@ -196,7 +196,7 @@ die(Msg, Args) -> halt(1). primary_upgrade(Upgrades, Nodes) -> - Others = ordsets:del_element(node(), Nodes), + Others = Nodes -- [node()], ok = apply_upgrades( mnesia, Upgrades, @@ -206,7 +206,7 @@ primary_upgrade(Upgrades, Nodes) -> [] -> ok; _ -> info("mnesia upgrades: Breaking cluster~n", []), [{atomic, ok} = mnesia:del_table_copy(schema, Node) - || Node <- ordsets:to_list(Others)] + || Node <- Others] end end), ok. -- cgit v1.2.1 From 325ceeb402772b8484d4262bdea002dd9f9fc1a0 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Fri, 21 Sep 2012 12:34:08 +0100 Subject: unify the various functions to get the cluster nodes into one --- src/rabbit.erl | 2 +- src/rabbit_mnesia.erl | 62 +++++++++++++++++++++------------------------ src/rabbit_networking.erl | 2 +- src/rabbit_node_monitor.erl | 4 +-- src/rabbit_upgrade.erl | 4 +-- 5 files changed, 35 insertions(+), 39 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 7a021e37..353a5bb9 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -511,7 +511,7 @@ sort_boot_steps(UnsortedSteps) -> end. boot_step_error({error, {timeout_waiting_for_tables, _}}, _Stacktrace) -> - AllNodes = rabbit_mnesia:all_clustered_nodes(), + AllNodes = rabbit_mnesia:cluster_nodes(all), {Err, Nodes} = case AllNodes -- [node()] of [] -> {"Timeout contacting cluster nodes. Since RabbitMQ was" diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 324c5653..a926a9c4 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -28,9 +28,7 @@ status/0, is_db_empty/0, is_clustered/0, - all_clustered_nodes/0, - clustered_disc_nodes/0, - running_clustered_nodes/0, + cluster_nodes/1, node_type/0, dir/0, table_names/0, @@ -83,9 +81,7 @@ {'running_nodes', [node()]}]). -spec(is_db_empty/0 :: () -> boolean()). -spec(is_clustered/0 :: () -> boolean()). --spec(all_clustered_nodes/0 :: () -> [node()]). --spec(clustered_disc_nodes/0 :: () -> [node()]). --spec(running_clustered_nodes/0 :: () -> [node()]). +-spec(cluster_nodes/1 :: ('all' | 'disc' | 'running') -> [node()]). -spec(node_type/0 :: () -> node_type()). -spec(dir/0 :: () -> file:filename()). -spec(table_names/0 :: () -> [atom()]). @@ -122,7 +118,7 @@ init() -> ensure_mnesia_dir(), case is_virgin_node() of true -> init_from_config(); - false -> init(node_type(), all_clustered_nodes()) + false -> init(node_type(), cluster_nodes(all)) end, %% We intuitively expect the global name server to be synced when %% Mnesia is up. In fact that's not guaranteed to be the case - @@ -215,7 +211,7 @@ reset(Force) -> true -> disconnect_nodes(nodes()); false -> - AllNodes = all_clustered_nodes(), + AllNodes = cluster_nodes(all), %% Reconnecting so that we will get an up to date nodes. %% We don't need to check for consistency because we are %% resetting. Force=true here so that reset still works @@ -229,7 +225,7 @@ reset(Force) -> leave_cluster(), rabbit_misc:ensure_ok(mnesia:delete_schema([Node]), cannot_delete_schema), - disconnect_nodes(all_clustered_nodes()), + disconnect_nodes(cluster_nodes(all)), ok end, %% remove persisted messages and any other garbage we find @@ -250,7 +246,7 @@ change_cluster_node_type(Type) -> true -> ok end, {_, _, RunningNodes} = - case discover_cluster(all_clustered_nodes()) of + case discover_cluster(cluster_nodes(all)) of {ok, Status} -> Status; {error, _Reason} -> e(cannot_connect_to_cluster) end, @@ -291,7 +287,7 @@ update_cluster_nodes(DiscoveryNode) -> %% the last or second to last after the node we're removing to go %% down forget_cluster_node(Node, RemoveWhenOffline) -> - case lists:member(Node, all_clustered_nodes()) of + case lists:member(Node, cluster_nodes(all)) of true -> ok; false -> e(not_a_cluster_node) end, @@ -311,7 +307,7 @@ forget_cluster_node(Node, RemoveWhenOffline) -> end. remove_node_offline_node(Node) -> - case {running_nodes(all_clustered_nodes()) -- [Node], node_type()} of + case {running_nodes(cluster_nodes(all)) -- [Node], node_type()} of {[], disc} -> %% Note that while we check if the nodes was the last to %% go down, apart from the node we're removing from, this @@ -321,7 +317,7 @@ remove_node_offline_node(Node) -> %% and B goes down. In this case, C is the second-to-last, %% but we don't know that and we'll remove B from A %% anyway, even if that will lead to bad things. - case running_clustered_nodes() -- [node(), Node] of + case cluster_nodes(running) -- [node(), Node] of [] -> start_mnesia(), try [mnesia:force_load_table(T) || @@ -346,10 +342,10 @@ status() -> IfNonEmpty = fun (_, []) -> []; (Type, Nodes) -> [{Type, Nodes}] end, - [{nodes, (IfNonEmpty(disc, clustered_disc_nodes()) ++ - IfNonEmpty(ram, clustered_ram_nodes()))}] ++ + [{nodes, (IfNonEmpty(disc, cluster_nodes(disc)) ++ + IfNonEmpty(ram, cluster_ram_nodes()))}] ++ case mnesia:system_info(is_running) of - yes -> [{running_nodes, running_clustered_nodes()}]; + yes -> [{running_nodes, cluster_nodes(running)}]; no -> [] end. @@ -358,7 +354,7 @@ is_db_empty() -> table_names()). is_clustered() -> - AllNodes = all_clustered_nodes(), + AllNodes = cluster_nodes(all), not is_only_node(AllNodes) andalso AllNodes =/= []. is_disc_and_clustered() -> node_type() =:= disc andalso is_clustered(). @@ -366,16 +362,10 @@ is_disc_and_clustered() -> node_type() =:= disc andalso is_clustered(). %% Functions that retrieve the nodes in the cluster will rely on the %% status file if offline. -all_clustered_nodes() -> cluster_status(all). +cluster_ram_nodes() -> cluster_nodes(all) -- cluster_nodes(disc). -clustered_disc_nodes() -> cluster_status(disc). - -clustered_ram_nodes() -> cluster_status(all) -- cluster_status(disc). - -running_clustered_nodes() -> cluster_status(running). - -running_clustered_disc_nodes() -> - {_AllNodes, DiscNodes, RunningNodes} = cluster_status(status), +cluster_running_disc_nodes() -> + {_AllNodes, DiscNodes, RunningNodes} = cluster_status(), ordsets:intersection(ordsets:from_list(DiscNodes), ordsets:from_list(RunningNodes)). @@ -434,6 +424,12 @@ cluster_status(WhichNodes) -> running -> RunningNodesThunk() end. +cluster_status() -> cluster_status(status). + +cluster_nodes(WhichNodes) when WhichNodes =:= all orelse WhichNodes =:= disc + orelse WhichNodes =:= running -> + cluster_status(WhichNodes). + cluster_status_from_mnesia() -> case mnesia_nodes() of {ok, {AllNodes, DiscNodes}} -> {ok, {AllNodes, DiscNodes, @@ -446,7 +442,7 @@ node_info() -> cluster_status_from_mnesia()}. node_type() -> - DiscNodes = clustered_disc_nodes(), + DiscNodes = cluster_nodes(disc), case DiscNodes =:= [] orelse me_in_nodes(DiscNodes) of true -> disc; false -> ram @@ -634,10 +630,10 @@ check_cluster_consistency() -> case lists:foldl( fun (Node, {error, _}) -> check_cluster_consistency(Node); (_Node, {ok, Status}) -> {ok, Status} - end, {error, not_found}, nodes_excl_me(all_clustered_nodes())) + end, {error, not_found}, nodes_excl_me(cluster_nodes(all))) of {ok, Status = {RemoteAllNodes, _, _}} -> - case ordsets:is_subset(ordsets:from_list(all_clustered_nodes()), + case ordsets:is_subset(ordsets:from_list(cluster_nodes(all)), ordsets:from_list(RemoteAllNodes)) of true -> ok; @@ -680,13 +676,13 @@ check_cluster_consistency(Node) -> %%-------------------------------------------------------------------- on_node_up(Node) -> - case is_only_node(Node, running_clustered_disc_nodes()) of + case is_only_node(Node, cluster_running_disc_nodes()) of true -> rabbit_log:info("cluster contains disc nodes again~n"); false -> ok end. on_node_down(_Node) -> - case running_clustered_disc_nodes() of + case cluster_running_disc_nodes() of [] -> rabbit_log:info("only running disc node went down~n"); _ -> ok end. @@ -985,7 +981,7 @@ remove_node_if_mnesia_running(Node) -> end. leave_cluster() -> - RunningNodes = running_nodes(nodes_excl_me(all_clustered_nodes())), + RunningNodes = running_nodes(nodes_excl_me(cluster_nodes(all))), case not is_clustered() andalso RunningNodes =:= [] of true -> ok; false -> case lists:any(fun leave_cluster/1, RunningNodes) of @@ -1111,7 +1107,7 @@ is_only_node(Node, Nodes) -> Nodes =:= [Node]. is_only_node(Nodes) -> is_only_node(node(), Nodes). -is_only_disc_node() -> is_only_node(clustered_disc_nodes()). +is_only_disc_node() -> is_only_node(cluster_nodes(disc)). me_in_nodes(Nodes) -> lists:member(node(), Nodes). diff --git a/src/rabbit_networking.erl b/src/rabbit_networking.erl index 2d0ded12..5cf8d1ae 100644 --- a/src/rabbit_networking.erl +++ b/src/rabbit_networking.erl @@ -295,7 +295,7 @@ start_ssl_client(SslOpts, Sock) -> start_client(Sock, ssl_transform_fun(SslOpts)). connections() -> - rabbit_misc:append_rpc_all_nodes(rabbit_mnesia:running_clustered_nodes(), + rabbit_misc:append_rpc_all_nodes(rabbit_mnesia:cluster_nodes(running), rabbit_networking, connections_local, []). connections_local() -> diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index f5877c9b..c1572762 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -179,7 +179,7 @@ node_up(Node, IsDiscNode) -> notify_node_up() -> Nodes = cluster_multicall(node_up, [node(), rabbit_mnesia:node_type()]), %% register other active rabbits with this rabbit - [ node_up(N, lists:member(N, rabbit_mnesia:clustered_disc_nodes())) || + [ node_up(N, lists:member(N, rabbit_mnesia:cluster_nodes(disc))) || N <- Nodes ], ok. @@ -268,7 +268,7 @@ handle_live_rabbit(Node) -> cluster_multicall(Fun, Args) -> Node = node(), - Nodes = rabbit_mnesia:running_clustered_nodes() -- [Node], + Nodes = rabbit_mnesia:cluster_nodes(running) -- [Node], %% notify other rabbits of this cluster case rpc:multicall(Nodes, rabbit_node_monitor, Fun, Args, ?RABBIT_UP_RPC_TIMEOUT) of diff --git a/src/rabbit_upgrade.erl b/src/rabbit_upgrade.erl index bfc3e006..d037f954 100644 --- a/src/rabbit_upgrade.erl +++ b/src/rabbit_upgrade.erl @@ -121,7 +121,7 @@ remove_backup() -> info("upgrades: Mnesia backup removed~n", []). maybe_upgrade_mnesia() -> - AllNodes = rabbit_mnesia:all_clustered_nodes(), + AllNodes = rabbit_mnesia:cluster_nodes(all), case rabbit_version:upgrades_required(mnesia) of {error, starting_from_scratch} -> ok; @@ -147,7 +147,7 @@ maybe_upgrade_mnesia() -> upgrade_mode(AllNodes) -> case nodes_running(AllNodes) of [] -> - AfterUs = rabbit_mnesia:running_clustered_nodes() -- [node()], + AfterUs = rabbit_mnesia:cluster_nodes(running) -- [node()], case {node_type_legacy(), AfterUs} of {disc, []} -> primary; -- cgit v1.2.1 From aca8685a571472041fdd34e6ff5f7f22e86da932 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 21 Sep 2012 15:30:11 +0100 Subject: refactor for clarity (I think) --- src/rabbit_binding.erl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rabbit_binding.erl b/src/rabbit_binding.erl index 2e462354..0d23f716 100644 --- a/src/rabbit_binding.erl +++ b/src/rabbit_binding.erl @@ -169,9 +169,9 @@ add(Binding, InnerFun) -> add(Src, Dst, B) -> [SrcDurable, DstDurable] = [durable(E) || E <- [Src, Dst]], - case (not (SrcDurable andalso DstDurable) orelse - mnesia:read({rabbit_durable_route, B}) =:= []) of - true -> ok = sync_route(#route{binding = B}, SrcDurable, DstDurable, + case (SrcDurable andalso DstDurable andalso + mnesia:read({rabbit_durable_route, B}) =/= []) of + false -> ok = sync_route(#route{binding = B}, SrcDurable, DstDurable, fun mnesia:write/3), x_callback(transaction, Src, add_binding, B), Serial = rabbit_exchange:serial(Src), @@ -179,7 +179,7 @@ add(Src, Dst, B) -> x_callback(Serial, Src, add_binding, B), ok = rabbit_event:notify(binding_created, info(B)) end; - false -> rabbit_misc:const({error, binding_not_found}) + true -> rabbit_misc:const({error, binding_not_found}) end. remove(Binding) -> remove(Binding, fun (_Src, _Dst) -> ok end). -- cgit v1.2.1 From ee524bf6c0c80fd8189257e2fdbd3591773b34c8 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 21 Sep 2012 15:55:38 +0100 Subject: Account for the mgmt DB as another category, since it can get quite big. --- src/rabbit.erl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index b2a60a55..531b5a0c 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -753,6 +753,7 @@ memory() -> Qs = sum_proc_memory(QPids), Mnesia = mnesia_memory(), MsgIndex = ets_memory(rabbit_msg_store_ets_index), + MgmtDB = ets_memory(rabbit_mgmt_db), [{total, Total}, {processes, Processes}, {ets, ETS}, @@ -767,8 +768,9 @@ memory() -> {queue_procs, Qs}, {other_procs, Processes - Conns - Chs - Qs}, {mnesia, Mnesia}, + {mgmt_db, MgmtDB}, {msg_index, MsgIndex}, - {other_ets, ETS - Mnesia - MsgIndex}, + {other_ets, ETS - Mnesia - MsgIndex - MgmtDB}, {binary, Bin}, {code, Code}, {atom, Atom}, -- cgit v1.2.1 From 9ab618e374229e925ea80c870c8d2e48bb027cf1 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Fri, 21 Sep 2012 16:01:08 +0100 Subject: reverse to using `system_info(running_db_nodes)' We noticed that our function is never necessary. Doing these changes, I also noticed that `running_db_nodes' does not do an rpc call if mnesia is running, and instead records the running node in some ETS table. This makes the clustering management tests racy, since we don't know when the information about the nodes which are up/down will propagate. --- src/rabbit_mnesia.erl | 32 +++++++++----------------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index cce4f4b9..6b24854b 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -316,8 +316,7 @@ forget_cluster_node(Node, RemoveWhenOffline) -> end. remove_node_offline_node(Node) -> - case {ordsets:del_element(Node, running_nodes(all_clustered_nodes())), - is_disc_node()} of + case {mnesia:system_info(running_db_nodes) -- [Node], is_disc_node()} of {[], true} -> %% Note that while we check if the nodes was the last to %% go down, apart from the node we're removing from, this @@ -415,19 +414,17 @@ mnesia_nodes() -> true -> ordsets:add_element(node(), DiscCopies); false -> DiscCopies end, - {ok, {AllNodes, DiscNodes}}; + RunningNodes = mnesia:system_info(running_db_nodes), + {ok, {AllNodes, DiscNodes, RunningNodes}}; false -> {error, tables_not_present} end end. cluster_status(WhichNodes, ForceMnesia) -> - %% I don't want to call `running_nodes/1' unless if necessary, since it's - %% pretty expensive. Nodes = case mnesia_nodes() of - {ok, {AllNodes, DiscNodes}} -> - {ok, {AllNodes, DiscNodes, - fun() -> running_nodes(AllNodes) end}}; + {ok, {AllNodes, DiscNodes, RunningNodes}} -> + {ok, {AllNodes, DiscNodes, RunningNodes}}; {error, _Reason} when not ForceMnesia -> {AllNodes, DiscNodes, RunningNodes} = rabbit_node_monitor:read_cluster_status(), @@ -1004,14 +1001,10 @@ remove_node_if_mnesia_running(Node) -> end. leave_cluster() -> - case {is_clustered(), - running_nodes(ordsets:del_element(node(), all_clustered_nodes()))} - of - {false, []} -> ok; - {_, AllNodes} -> case lists:any(fun leave_cluster/1, AllNodes) of - true -> ok; - false -> e(no_running_cluster_nodes) - end + AllNodes = all_clustered_nodes() -- [node()], + case not is_clustered() orelse lists:any(fun leave_cluster/1, AllNodes) of + true -> ok; + false -> e(no_running_cluster_nodes) end. leave_cluster(Node) -> @@ -1052,13 +1045,6 @@ change_extra_db_nodes(ClusterNodes0, Force) -> Nodes end. -%% We're not using `mnesia:system_info(running_db_nodes)' directly because if -%% the node is a RAM node it won't know about other nodes when mnesia is stopped -running_nodes(Nodes) -> - {Replies, _BadNodes} = - rpc:multicall(Nodes, rabbit_mnesia, is_running_remote, []), - [Node || {Running, Node} <- Replies, Running]. - is_running_remote() -> {mnesia:system_info(is_running) =:= yes, node()}. -- cgit v1.2.1 From a6420154c78170d75c106f47fb20dae1d9948471 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Fri, 21 Sep 2012 16:09:32 +0100 Subject: not a thunk anymore --- src/rabbit_mnesia.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 6b24854b..10e4c2a3 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -438,12 +438,12 @@ cluster_status(WhichNodes, ForceMnesia) -> Err end, case Nodes of - {ok, {AllNodes1, DiscNodes1, RunningNodesThunk}} -> + {ok, {AllNodes1, DiscNodes1, RunningNodes1}} -> {ok, case WhichNodes of - status -> {AllNodes1, DiscNodes1, RunningNodesThunk()}; + status -> {AllNodes1, DiscNodes1, RunningNodes1}; all -> AllNodes1; disc -> DiscNodes1; - running -> RunningNodesThunk() + running -> RunningNodes1 end}; Err1 = {error, _} -> Err1 -- cgit v1.2.1 From aa9bbdeb9d4e66a6f6a8cd9bf88987ae88b047c1 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Fri, 21 Sep 2012 16:16:52 +0100 Subject: forgot about the other thunk --- src/rabbit_mnesia.erl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 10e4c2a3..c0edbb1e 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -431,9 +431,8 @@ cluster_status(WhichNodes, ForceMnesia) -> %% The cluster status file records the status when the node %% is online, but we know for sure that the node is offline %% now, so we can remove it from the list of running nodes. - {ok, - {AllNodes, DiscNodes, - fun() -> ordsets:del_element(node(), RunningNodes) end}}; + {ok, {AllNodes, DiscNodes, + ordsets:del_element(node(), RunningNodes)}}; Err = {error, _} -> Err end, -- cgit v1.2.1 From e46ed158fc83e81899457c5d8fd3b73d3b66b0c0 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 21 Sep 2012 16:41:25 +0100 Subject: Rename this, since it can include memory which does not still belong to a process (e.g. after GC). --- src/rabbit.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 531b5a0c..f9cb0d8c 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -766,7 +766,7 @@ memory() -> {connection_procs, Conns}, {channel_procs, Chs}, {queue_procs, Qs}, - {other_procs, Processes - Conns - Chs - Qs}, + {other_proc, Processes - Conns - Chs - Qs}, {mnesia, Mnesia}, {mgmt_db, MgmtDB}, {msg_index, MsgIndex}, -- cgit v1.2.1 From a03476e7383bdf9e7045067cc35103b13aff611c Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Fri, 21 Sep 2012 16:50:59 +0100 Subject: fix in change_cluster_node_type --- src/rabbit_mnesia.erl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index c0edbb1e..2bd68b0e 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -256,7 +256,9 @@ change_cluster_node_type(Type) -> {ok, Status} -> Status; {error, _Reason} -> e(cannot_connect_to_cluster) end, - Node = case RunningNodes of + %% We might still be marked as running by a remote node since the + %% information of us going down might not have propagated yet. + Node = case RunningNodes -- [node()] of [] -> e(no_online_cluster_nodes); [Node0|_] -> Node0 end, -- cgit v1.2.1 From 2156b9ac95585daae655881f2becb969499c028a Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Sat, 22 Sep 2012 11:05:46 +0100 Subject: Go to an entirely supervisor-based way of counting process memory. This has the disadvantage that we need to combine channel and connection memory, but we are far more inclusive, accounting for limiters, writers, supervisors etc. In particular this helps a lot when we have thousands of queues since the queue_sup ends up taking a lot of memory in its own right. This seems to bloat rabbit_mgmt_external_stats less (although still some) in the 100k queue case too. Also measure memory use of mgmt_db and msg_store processes and add them appropriately. --- src/rabbit.erl | 68 +++++++++++++++++++++++++++++++++------------------------- 1 file changed, 39 insertions(+), 29 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index f9cb0d8c..26e4f1f4 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -747,13 +747,15 @@ start_fhc() -> %% Like erlang:memory(), but with awareness of rabbit-y things memory() -> - QPids = lists:append([pids(Q) || Q <- rabbit_amqqueue:list()]), - Conns = sum_proc_memory(rabbit_networking:connections_local()), - Chs = sum_proc_memory(rabbit_channel:list_local()), - Qs = sum_proc_memory(QPids), + ConnChs = sup_memory(rabbit_tcp_client_sup), + Qs = sup_memory(rabbit_amqqueue_sup) + + sup_memory(rabbit_mirror_queue_slave_sup), Mnesia = mnesia_memory(), - MsgIndex = ets_memory(rabbit_msg_store_ets_index), - MgmtDB = ets_memory(rabbit_mgmt_db), + MsgIndexETS = ets_memory(rabbit_msg_store_ets_index), + MsgIndexProc = pid_memory(msg_store_transient) + + pid_memory(msg_store_persistent), + MgmtDbETS = ets_memory(rabbit_mgmt_db), + MgmtDbProc = sup_memory(rabbit_mgmt_sup), [{total, Total}, {processes, Processes}, {ets, ETS}, @@ -762,29 +764,37 @@ memory() -> {code, Code}, {system, System}] = erlang:memory([total, processes, ets, atom, binary, code, system]), - [{total, Total}, - {connection_procs, Conns}, - {channel_procs, Chs}, - {queue_procs, Qs}, - {other_proc, Processes - Conns - Chs - Qs}, - {mnesia, Mnesia}, - {mgmt_db, MgmtDB}, - {msg_index, MsgIndex}, - {other_ets, ETS - Mnesia - MsgIndex - MgmtDB}, - {binary, Bin}, - {code, Code}, - {atom, Atom}, - {other_system, System - ETS - Atom - Bin - Code}]. - -sum_proc_memory(Pids) -> - lists:sum([Mem || P <- Pids, {memory, Mem} <- [process_info(P, memory)]]). - -pids(#amqqueue{pid = Pid, slave_pids = undefined}) -> - local_pids([Pid]); -pids(#amqqueue{pid = Pid, slave_pids = SPids}) -> - local_pids([Pid | SPids]). - -local_pids(Pids) -> [Pid || Pid <- Pids, node(Pid) =:= node()]. + [{total, Total}, + {connection_channel_procs, ConnChs}, + {queue_procs, Qs}, + {other_proc, Processes - ConnChs - Qs - MsgIndexProc - + MgmtDbProc}, + {mnesia, Mnesia}, + {mgmt_db, MgmtDbETS + MgmtDbProc}, + {msg_index, MsgIndexETS + MsgIndexProc}, + {other_ets, ETS - Mnesia - MsgIndexETS - MgmtDbETS}, + {binary, Bin}, + {code, Code}, + {atom, Atom}, + {other_system, System - ETS - Atom - Bin - Code}]. + +sup_memory(Sup) -> + lists:sum([child_memory(P, T) || {_, P, T, _} <- sup_children(Sup)]) + + pid_memory(Sup). + +sup_children(Sup) -> + rabbit_misc:with_exit_handler( + rabbit_misc:const([]), fun () -> supervisor:which_children(Sup) end). + +pid_memory(Pid) when is_pid(Pid) -> element(2, process_info(Pid, memory)); +pid_memory(Name) when is_atom(Name) -> case whereis(Name) of + P when is_pid(P) -> pid_memory(P); + _ -> 0 + end. + +child_memory(Pid, worker) when is_pid (Pid) -> pid_memory(Pid); +child_memory(Pid, supervisor) when is_pid (Pid) -> sup_memory(Pid); +child_memory(_, _) -> 0. mnesia_memory() -> lists:sum([bytes(mnesia:table_info(Tab, memory)) || -- cgit v1.2.1 From 6b5d91f884e339098d2794f469b01af1a77fbab5 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 24 Sep 2012 12:54:19 +0100 Subject: Move this stuff to its own module --- src/rabbit.erl | 67 ++------------------------------------- src/rabbit_vm.erl | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+), 65 deletions(-) create mode 100644 src/rabbit_vm.erl diff --git a/src/rabbit.erl b/src/rabbit.erl index 26e4f1f4..da2a1783 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -21,7 +21,7 @@ -export([start/0, boot/0, stop/0, stop_and_halt/0, await_startup/0, status/0, is_running/0, is_running/1, environment/0, rotate_logs/1, force_event_refresh/0, - start_fhc/0, memory/0]). + start_fhc/0]). -export([start/2, stop/1]). @@ -247,7 +247,6 @@ -spec(maybe_insert_default_data/0 :: () -> 'ok'). -spec(boot_delegate/0 :: () -> 'ok'). -spec(recover/0 :: () -> 'ok'). --spec(memory/0 :: () -> rabbit_types:infos()). -endif. @@ -357,7 +356,7 @@ status() -> {running_applications, application:which_applications(infinity)}, {os, os:type()}, {erlang_version, erlang:system_info(system_version)}, - {memory, memory()}], + {memory, rabbit_vm:memory()}], S2 = rabbit_misc:filter_exit_map( fun ({Key, {M, F, A}}) -> {Key, erlang:apply(M, F, A)} end, [{vm_memory_high_watermark, {vm_memory_monitor, @@ -744,65 +743,3 @@ start_fhc() -> rabbit_sup:start_restartable_child( file_handle_cache, [fun rabbit_alarm:set_alarm/1, fun rabbit_alarm:clear_alarm/1]). - -%% Like erlang:memory(), but with awareness of rabbit-y things -memory() -> - ConnChs = sup_memory(rabbit_tcp_client_sup), - Qs = sup_memory(rabbit_amqqueue_sup) + - sup_memory(rabbit_mirror_queue_slave_sup), - Mnesia = mnesia_memory(), - MsgIndexETS = ets_memory(rabbit_msg_store_ets_index), - MsgIndexProc = pid_memory(msg_store_transient) + - pid_memory(msg_store_persistent), - MgmtDbETS = ets_memory(rabbit_mgmt_db), - MgmtDbProc = sup_memory(rabbit_mgmt_sup), - [{total, Total}, - {processes, Processes}, - {ets, ETS}, - {atom, Atom}, - {binary, Bin}, - {code, Code}, - {system, System}] = - erlang:memory([total, processes, ets, atom, binary, code, system]), - [{total, Total}, - {connection_channel_procs, ConnChs}, - {queue_procs, Qs}, - {other_proc, Processes - ConnChs - Qs - MsgIndexProc - - MgmtDbProc}, - {mnesia, Mnesia}, - {mgmt_db, MgmtDbETS + MgmtDbProc}, - {msg_index, MsgIndexETS + MsgIndexProc}, - {other_ets, ETS - Mnesia - MsgIndexETS - MgmtDbETS}, - {binary, Bin}, - {code, Code}, - {atom, Atom}, - {other_system, System - ETS - Atom - Bin - Code}]. - -sup_memory(Sup) -> - lists:sum([child_memory(P, T) || {_, P, T, _} <- sup_children(Sup)]) + - pid_memory(Sup). - -sup_children(Sup) -> - rabbit_misc:with_exit_handler( - rabbit_misc:const([]), fun () -> supervisor:which_children(Sup) end). - -pid_memory(Pid) when is_pid(Pid) -> element(2, process_info(Pid, memory)); -pid_memory(Name) when is_atom(Name) -> case whereis(Name) of - P when is_pid(P) -> pid_memory(P); - _ -> 0 - end. - -child_memory(Pid, worker) when is_pid (Pid) -> pid_memory(Pid); -child_memory(Pid, supervisor) when is_pid (Pid) -> sup_memory(Pid); -child_memory(_, _) -> 0. - -mnesia_memory() -> - lists:sum([bytes(mnesia:table_info(Tab, memory)) || - Tab <- mnesia:system_info(tables)]). - -ets_memory(Name) -> - lists:sum([bytes(ets:info(T, memory)) || T <- ets:all(), - N <- [ets:info(T, name)], - N =:= Name]). - -bytes(Words) -> Words * erlang:system_info(wordsize). diff --git a/src/rabbit_vm.erl b/src/rabbit_vm.erl new file mode 100644 index 00000000..ce6c9323 --- /dev/null +++ b/src/rabbit_vm.erl @@ -0,0 +1,93 @@ +%% 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 VMware, Inc. +%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% + +-module(rabbit_vm). + +-export([memory/0]). + +%%---------------------------------------------------------------------------- + +-ifdef(use_specs). + +-spec(memory/0 :: () -> rabbit_types:infos()). + +-endif. + +%%---------------------------------------------------------------------------- + +%% Like erlang:memory(), but with awareness of rabbit-y things +memory() -> + ConnChs = sup_memory(rabbit_tcp_client_sup), + Qs = sup_memory(rabbit_amqqueue_sup) + + sup_memory(rabbit_mirror_queue_slave_sup), + Mnesia = mnesia_memory(), + MsgIndexETS = ets_memory(rabbit_msg_store_ets_index), + MsgIndexProc = pid_memory(msg_store_transient) + + pid_memory(msg_store_persistent), + MgmtDbETS = ets_memory(rabbit_mgmt_db), + MgmtDbProc = sup_memory(rabbit_mgmt_sup), + [{total, Total}, + {processes, Processes}, + {ets, ETS}, + {atom, Atom}, + {binary, Bin}, + {code, Code}, + {system, System}] = + erlang:memory([total, processes, ets, atom, binary, code, system]), + [{total, Total}, + {connection_channel_procs, ConnChs}, + {queue_procs, Qs}, + {other_proc, Processes - ConnChs - Qs - MsgIndexProc - + MgmtDbProc}, + {mnesia, Mnesia}, + {mgmt_db, MgmtDbETS + MgmtDbProc}, + {msg_index, MsgIndexETS + MsgIndexProc}, + {other_ets, ETS - Mnesia - MsgIndexETS - MgmtDbETS}, + {binary, Bin}, + {code, Code}, + {atom, Atom}, + {other_system, System - ETS - Atom - Bin - Code}]. + +%%---------------------------------------------------------------------------- + +sup_memory(Sup) -> + lists:sum([child_memory(P, T) || {_, P, T, _} <- sup_children(Sup)]) + + pid_memory(Sup). + +sup_children(Sup) -> + rabbit_misc:with_exit_handler( + rabbit_misc:const([]), fun () -> supervisor:which_children(Sup) end). + +pid_memory(Pid) when is_pid(Pid) -> element(2, process_info(Pid, memory)); +pid_memory(Name) when is_atom(Name) -> case whereis(Name) of + P when is_pid(P) -> pid_memory(P); + _ -> 0 + end. + +child_memory(Pid, worker) when is_pid (Pid) -> pid_memory(Pid); +child_memory(Pid, supervisor) when is_pid (Pid) -> sup_memory(Pid); +child_memory(_, _) -> 0. + +mnesia_memory() -> + lists:sum([bytes(mnesia:table_info(Tab, memory)) || + Tab <- mnesia:system_info(tables)]). + +ets_memory(Name) -> + lists:sum([bytes(ets:info(T, memory)) || T <- ets:all(), + N <- [ets:info(T, name)], + N =:= Name]). + +bytes(Words) -> Words * erlang:system_info(wordsize). -- cgit v1.2.1 From 3697ae8872b9b51efa1506283f3edafcd0966f09 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 24 Sep 2012 12:57:06 +0100 Subject: Ignore dead processes. --- src/rabbit_vm.erl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/rabbit_vm.erl b/src/rabbit_vm.erl index ce6c9323..b49c0a7c 100644 --- a/src/rabbit_vm.erl +++ b/src/rabbit_vm.erl @@ -71,7 +71,10 @@ sup_children(Sup) -> rabbit_misc:with_exit_handler( rabbit_misc:const([]), fun () -> supervisor:which_children(Sup) end). -pid_memory(Pid) when is_pid(Pid) -> element(2, process_info(Pid, memory)); +pid_memory(Pid) when is_pid(Pid) -> case process_info(Pid, memory) of + {memory, M} -> M; + _ -> 0 + end; pid_memory(Name) when is_atom(Name) -> case whereis(Name) of P when is_pid(P) -> pid_memory(P); _ -> 0 -- cgit v1.2.1 From aebb562231b6457c5a3ca58439610f6644e7c0aa Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 24 Sep 2012 13:31:22 +0100 Subject: Add in memory used by SSL and outgoing connections to the connection / channel total. --- src/rabbit_vm.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rabbit_vm.erl b/src/rabbit_vm.erl index b49c0a7c..95ff093e 100644 --- a/src/rabbit_vm.erl +++ b/src/rabbit_vm.erl @@ -30,7 +30,8 @@ %% Like erlang:memory(), but with awareness of rabbit-y things memory() -> - ConnChs = sup_memory(rabbit_tcp_client_sup), + ConnChs = sup_memory(rabbit_tcp_client_sup) + + sup_memory(ssl_connection_sup) + sup_memory(amqp_sup), Qs = sup_memory(rabbit_amqqueue_sup) + sup_memory(rabbit_mirror_queue_slave_sup), Mnesia = mnesia_memory(), -- cgit v1.2.1 From 01f162d8d20fc5f17c68928f9a891193879c4dd8 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 24 Sep 2012 13:43:28 +0100 Subject: cosmetic --- src/rabbit_mnesia.erl | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 991f8c72..0103be7f 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -132,7 +132,7 @@ init(NodeType, AllNodes) -> init_from_config() -> {ok, {TryNodes, NodeType}} = application:get_env(rabbit, cluster_nodes), - case find_good_node(TryNodes -- [node()]) of + case find_good_node(nodes_excl_me(TryNodes)) of {ok, Node} -> rabbit_log:info("Node '~p' selected for clustering from " "configuration~n", [Node]), @@ -173,7 +173,6 @@ join_cluster(DiscoveryNode, NodeType) -> {ok, Res} -> Res; {error, _} = E -> throw(E) end, - case me_in_nodes(ClusterNodes) of true -> e(already_clustered); false -> ok @@ -185,11 +184,10 @@ join_cluster(DiscoveryNode, NodeType) -> %% of reseting the node from the user. reset(false), - rabbit_misc:local_info_msg("Clustering with ~p~n", [ClusterNodes]), - %% Join the cluster + rabbit_misc:local_info_msg("Clustering with ~p as ~p node~n", + [ClusterNodes, NodeType]), ok = init_db_with_mnesia(ClusterNodes, NodeType, true, true), - rabbit_node_monitor:notify_joined_cluster(), ok. @@ -245,11 +243,10 @@ change_cluster_node_type(Type) -> false -> e(not_clustered); true -> ok end, - {_, _, RunningNodes} = - case discover_cluster(cluster_nodes(all)) of - {ok, Status} -> Status; - {error, _Reason} -> e(cannot_connect_to_cluster) - end, + {_, _, RunningNodes} = case discover_cluster(cluster_nodes(all)) of + {ok, Status} -> Status; + {error, _Reason} -> e(cannot_connect_to_cluster) + end, Node = case RunningNodes of [] -> e(no_online_cluster_nodes); [Node0|_] -> Node0 @@ -260,7 +257,6 @@ change_cluster_node_type(Type) -> update_cluster_nodes(DiscoveryNode) -> ensure_mnesia_not_running(), ensure_mnesia_dir(), - Status = {AllNodes, _, _} = case discover_cluster(DiscoveryNode) of {ok, Status0} -> Status0; @@ -320,12 +316,11 @@ remove_node_offline_node(Node) -> case cluster_nodes(running) -- [node(), Node] of [] -> start_mnesia(), try - [mnesia:force_load_table(T) || - T <- rabbit_mnesia:table_names()], + [mnesia:force_load_table(T) || T <- table_names()], forget_cluster_node(Node, false), ensure_mnesia_running() after - stop_mnesia() + stop_mnesia() end; _ -> e(not_last_node_to_go_down) end; @@ -339,7 +334,7 @@ remove_node_offline_node(Node) -> %%---------------------------------------------------------------------------- status() -> - IfNonEmpty = fun (_, []) -> []; + IfNonEmpty = fun (_, []) -> []; (Type, Nodes) -> [{Type, Nodes}] end, [{nodes, (IfNonEmpty(disc, cluster_nodes(disc)) ++ @@ -1034,8 +1029,7 @@ running_nodes(Nodes) -> rpc:multicall(Nodes, rabbit_mnesia, is_running_remote, []), [Node || {Running, Node} <- Replies, Running]. -is_running_remote() -> - {mnesia:system_info(is_running) =:= yes, node()}. +is_running_remote() -> {mnesia:system_info(is_running) =:= yes, node()}. check_consistency(OTP, Rabbit) -> rabbit_misc:sequence_error( @@ -1080,13 +1074,16 @@ check_rabbit_consistency(Remote) -> %% `rabbit_node_monitor:prepare_cluster_status_file/0'. is_virgin_node() -> case rabbit_file:list_dir(dir()) of - {error, enoent} -> true; - {ok, []} -> true; + {error, enoent} -> + true; + {ok, []} -> + true; {ok, [File1, File2]} -> lists:usort([dir() ++ "/" ++ File1, dir() ++ "/" ++ File2]) =:= lists:usort([rabbit_node_monitor:cluster_status_filename(), rabbit_node_monitor:running_nodes_filename()]); - {ok, _} -> false + {ok, _} -> + false end. find_good_node([]) -> -- cgit v1.2.1 From 72593df8c7591875e4ef66d29e93dc7488a9278f Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 24 Sep 2012 13:56:57 +0100 Subject: cosmetic --- src/rabbit_mnesia.erl | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 0103be7f..93b8fed5 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -1022,11 +1022,12 @@ change_extra_db_nodes(ClusterNodes0, CheckOtherNodes) -> Nodes end. -%% We're not using `mnesia:system_info(running_db_nodes)' directly because if -%% the node is a RAM node it won't know about other nodes when mnesia is stopped +%% We're not using `mnesia:system_info(running_db_nodes)' directly +%% because if the node is a RAM node it won't know about other nodes +%% when mnesia is stopped running_nodes(Nodes) -> - {Replies, _BadNodes} = - rpc:multicall(Nodes, rabbit_mnesia, is_running_remote, []), + {Replies, _BadNodes} = rpc:multicall(Nodes, + rabbit_mnesia, is_running_remote, []), [Node || {Running, Node} <- Replies, Running]. is_running_remote() -> {mnesia:system_info(is_running) =:= yes, node()}. -- cgit v1.2.1 From b9500aca846d06524c6d383414cad408c178a424 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 24 Sep 2012 13:57:37 +0100 Subject: tidy up exports & specs --- src/rabbit_mnesia.erl | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 93b8fed5..12573fd2 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -32,13 +32,12 @@ node_type/0, dir/0, table_names/0, - wait_for_tables/1, cluster_status_from_mnesia/0, init_db_unchecked/2, empty_ram_only_tables/0, copy_db/1, - wait_for_tables/0, + wait_for_tables/1, check_cluster_consistency/0, ensure_mnesia_dir/0, @@ -101,12 +100,6 @@ -spec(on_node_up/1 :: (node()) -> 'ok'). -spec(on_node_down/1 :: (node()) -> 'ok'). -%% Functions used in internal rpc calls --spec(node_info/0 :: () -> {string(), string(), - ({'ok', cluster_status()} | 'error')}). --spec(remove_node_if_mnesia_running/1 :: - (node()) -> rabbit_types:ok_or_error(term())). - -endif. %%---------------------------------------------------------------------------- -- cgit v1.2.1 From 5dc2bc925925de789b1b287d3184eb8680c0f8b4 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 24 Sep 2012 13:59:48 +0100 Subject: inline init/2 --- src/rabbit_mnesia.erl | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 12573fd2..05af43e0 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -111,7 +111,9 @@ init() -> ensure_mnesia_dir(), case is_virgin_node() of true -> init_from_config(); - false -> init(node_type(), cluster_nodes(all)) + false -> NodeType = node_type(), + init_db_and_upgrade(cluster_nodes(all), NodeType, + NodeType =:= ram) end, %% We intuitively expect the global name server to be synced when %% Mnesia is up. In fact that's not guaranteed to be the case - @@ -119,9 +121,6 @@ init() -> ok = global:sync(), ok. -init(NodeType, AllNodes) -> - init_db_and_upgrade(AllNodes, NodeType, NodeType =:= ram). - init_from_config() -> {ok, {TryNodes, NodeType}} = application:get_env(rabbit, cluster_nodes), @@ -136,7 +135,7 @@ init_from_config() -> rabbit_log:warning("Could not find any suitable node amongst the " "ones provided in the configuration: ~p~n", [TryNodes]), - init(disc, [node()]) + init_db_and_upgrade([node()], disc, false) end. %% Make the node join a cluster. The node will be reset automatically -- cgit v1.2.1 From fa981c1417cb98a96508d6a32a712ee8436c7923 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 24 Sep 2012 14:06:47 +0100 Subject: logging and error reporting consistency --- src/rabbit_mnesia.erl | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 05af43e0..0a8fc06a 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -187,14 +187,15 @@ join_cluster(DiscoveryNode, NodeType) -> %% return node to its virgin state, where it is not member of any %% cluster, has no cluster configuration, no local database, and no %% persisted messages -reset() -> reset(false). -force_reset() -> reset(true). +reset() -> + rabbit_misc:local_info_msg("Resetting Rabbit~n", []), + reset(false). + +force_reset() -> + rabbit_misc:local_info_msg("Resetting Rabbit forcefully~n", []), + reset(true). reset(Force) -> - rabbit_misc:local_info_msg("Resetting Rabbit~s~n", - [if Force -> " forcefully"; - true -> "" - end]), ensure_mnesia_not_running(), Node = node(), case Force of @@ -229,8 +230,8 @@ reset(Force) -> disconnect_nodes(Nodes) -> [erlang:disconnect_node(N) || N <- Nodes]. change_cluster_node_type(Type) -> - ensure_mnesia_dir(), ensure_mnesia_not_running(), + ensure_mnesia_dir(), case is_clustered() of false -> e(not_clustered); true -> ok @@ -243,7 +244,7 @@ change_cluster_node_type(Type) -> [] -> e(no_online_cluster_nodes); [Node0|_] -> Node0 end, - ok = reset(false), + ok = reset(), ok = join_cluster(Node, Type). update_cluster_nodes(DiscoveryNode) -> @@ -261,6 +262,8 @@ update_cluster_nodes(DiscoveryNode) -> %% nodes mnesia:delete_schema([node()]), rabbit_node_monitor:write_cluster_status(Status), + rabbit_misc:local_info_msg("Updating cluster nodes from ~p~n", + [DiscoveryNode]), init_db_with_mnesia(AllNodes, node_type(), true, true); false -> e(inconsistent_cluster) -- cgit v1.2.1 From 9d8f811dda0dfd46bf370b7cff82357b55c368bc Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 24 Sep 2012 14:15:03 +0100 Subject: simplification around various node list functions - cluster_nodes/1 accepts 'ram', eliminating cluster_ram_nodes/0 - introduce is_only_clustered_disc_node/0 helper and get rid of is_disc_and_clustered/0 and is_only_disc_node/0 in the process Also inline disconnect_nodes/1. --- src/rabbit_mnesia.erl | 97 ++++++++++++++++++++++----------------------------- 1 file changed, 41 insertions(+), 56 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 0a8fc06a..4e819041 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -80,7 +80,7 @@ {'running_nodes', [node()]}]). -spec(is_db_empty/0 :: () -> boolean()). -spec(is_clustered/0 :: () -> boolean()). --spec(cluster_nodes/1 :: ('all' | 'disc' | 'running') -> [node()]). +-spec(cluster_nodes/1 :: ('all' | 'disc' | 'ram' | 'running') -> [node()]). -spec(node_type/0 :: () -> node_type()). -spec(dir/0 :: () -> file:filename()). -spec(table_names/0 :: () -> [atom()]). @@ -153,14 +153,12 @@ init_from_config() -> %% all in the same cluster, we simply pick the first online node and %% we cluster to its cluster. join_cluster(DiscoveryNode, NodeType) -> - case is_disc_and_clustered() andalso is_only_disc_node() of - true -> e(clustering_only_disc_node); - _ -> ok - end, - ensure_mnesia_not_running(), ensure_mnesia_dir(), - + case is_only_clustered_disc_node() of + true -> e(clustering_only_disc_node); + false -> ok + end, {ClusterNodes, _, _} = case discover_cluster(DiscoveryNode) of {ok, Res} -> Res; {error, _} = E -> throw(E) @@ -197,38 +195,35 @@ force_reset() -> reset(Force) -> ensure_mnesia_not_running(), - Node = node(), - case Force of - true -> - disconnect_nodes(nodes()); - false -> - AllNodes = cluster_nodes(all), - %% Reconnecting so that we will get an up to date nodes. - %% We don't need to check for consistency because we are - %% resetting. Force=true here so that reset still works - %% when clustered with a node which is down. - init_db_with_mnesia(AllNodes, node_type(), false, false), - case is_disc_and_clustered() andalso is_only_disc_node() - of - true -> e(resetting_only_disc_node); - false -> ok + Nodes = case Force of + true -> + nodes(); + false -> + AllNodes = cluster_nodes(all), + %% Reconnecting so that we will get an up to date + %% nodes. We don't need to check for consistency + %% because we are resetting. Force=true here so + %% that reset still works when clustered with a + %% node which is down. + init_db_with_mnesia(AllNodes, node_type(), false, false), + case is_only_clustered_disc_node() of + true -> e(resetting_only_disc_node); + false -> ok + end, + leave_cluster(), + rabbit_misc:ensure_ok(mnesia:delete_schema([node()]), + cannot_delete_schema), + cluster_nodes(all) end, - leave_cluster(), - rabbit_misc:ensure_ok(mnesia:delete_schema([Node]), - cannot_delete_schema), - disconnect_nodes(cluster_nodes(all)), - ok - end, + %% We need to make sure that we don't end up in a distributed + %% Erlang system with nodes while not being in an Mnesia cluster + %% with them. We don't handle that well. + [erlang:disconnect_node(N) || N <- Nodes], %% remove persisted messages and any other garbage we find ok = rabbit_file:recursive_delete(filelib:wildcard(dir() ++ "/*")), ok = rabbit_node_monitor:reset_cluster_status(), ok. -%% We need to make sure that we don't end up in a distributed Erlang -%% system with nodes while not being in an Mnesia cluster with -%% them. We don't handle that well. -disconnect_nodes(Nodes) -> [erlang:disconnect_node(N) || N <- Nodes]. - change_cluster_node_type(Type) -> ensure_mnesia_not_running(), ensure_mnesia_dir(), @@ -333,7 +328,7 @@ status() -> (Type, Nodes) -> [{Type, Nodes}] end, [{nodes, (IfNonEmpty(disc, cluster_nodes(disc)) ++ - IfNonEmpty(ram, cluster_ram_nodes()))}] ++ + IfNonEmpty(ram, cluster_nodes(ram)))}] ++ case mnesia:system_info(is_running) of yes -> [{running_nodes, cluster_nodes(running)}]; no -> [] @@ -345,19 +340,7 @@ is_db_empty() -> is_clustered() -> AllNodes = cluster_nodes(all), - not is_only_node(AllNodes) andalso AllNodes =/= []. - -is_disc_and_clustered() -> node_type() =:= disc andalso is_clustered(). - -%% Functions that retrieve the nodes in the cluster will rely on the -%% status file if offline. - -cluster_ram_nodes() -> cluster_nodes(all) -- cluster_nodes(disc). - -cluster_running_disc_nodes() -> - {_AllNodes, DiscNodes, RunningNodes} = cluster_status(), - ordsets:intersection(ordsets:from_list(DiscNodes), - ordsets:from_list(RunningNodes)). + AllNodes =/= [] andalso not is_only_node(AllNodes). %% This function is the actual source of information, since it gets %% the data from mnesia. Obviously it'll work only when mnesia is @@ -391,7 +374,7 @@ mnesia_nodes() -> end end. -cluster_status(WhichNodes) -> +cluster_nodes(WhichNodes) -> %% I don't want to call `running_nodes/1' unless if necessary, since it's %% pretty expensive. {AllNodes1, DiscNodes1, RunningNodesThunk} = @@ -410,15 +393,10 @@ cluster_status(WhichNodes) -> status -> {AllNodes1, DiscNodes1, RunningNodesThunk()}; all -> AllNodes1; disc -> DiscNodes1; + ram -> AllNodes1 -- DiscNodes1; running -> RunningNodesThunk() end. -cluster_status() -> cluster_status(status). - -cluster_nodes(WhichNodes) when WhichNodes =:= all orelse WhichNodes =:= disc - orelse WhichNodes =:= running -> - cluster_status(WhichNodes). - cluster_status_from_mnesia() -> case mnesia_nodes() of {ok, {AllNodes, DiscNodes}} -> {ok, {AllNodes, DiscNodes, @@ -676,6 +654,11 @@ on_node_down(_Node) -> _ -> ok end. +cluster_running_disc_nodes() -> + {_AllNodes, DiscNodes, RunningNodes} = cluster_nodes(status), + ordsets:to_list(ordsets:intersection(ordsets:from_list(DiscNodes), + ordsets:from_list(RunningNodes))). + %%-------------------------------------------------------------------- %% Internal helpers %%-------------------------------------------------------------------- @@ -1093,12 +1076,14 @@ find_good_node([Node | Nodes]) -> end end. +is_only_clustered_disc_node() -> + node_type() =:= disc andalso is_clustered() andalso + is_only_node(cluster_nodes(disc)). + is_only_node(Node, Nodes) -> Nodes =:= [Node]. is_only_node(Nodes) -> is_only_node(node(), Nodes). -is_only_disc_node() -> is_only_node(cluster_nodes(disc)). - me_in_nodes(Nodes) -> lists:member(node(), Nodes). nodes_incl_me(Nodes) -> lists:usort([node()|Nodes]). -- cgit v1.2.1 From 2ecbf3a1c87267a5be21051d3a55c2903514b54e Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 24 Sep 2012 14:16:47 +0100 Subject: simplify forget_cluster_node and get logging right --- src/rabbit_mnesia.erl | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 4e819041..997ce0e9 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -277,19 +277,20 @@ forget_cluster_node(Node, RemoveWhenOffline) -> true -> ok; false -> e(not_a_cluster_node) end, - case {mnesia:system_info(is_running), RemoveWhenOffline} of - {yes, true} -> e(online_node_offline_flag); - _ -> ok - end, - case remove_node_if_mnesia_running(Node) of - ok -> - ok; - {error, mnesia_not_running} when RemoveWhenOffline -> + case mnesia:system_info(is_running) of + no when RemoveWhenOffline -> remove_node_offline_node(Node); - {error, mnesia_not_running} -> + yes when RemoveWhenOffline -> + e(online_node_offline_flag); + no -> e(offline_node_no_offline_flag); - {error, _} = Err -> - throw(Err) + yes -> + rabbit_misc:local_info_msg("Removing node ~p from cluster~n", + [Node]), + case remove_node_if_mnesia_running(Node) of + ok -> ok; + {error, _} = Err -> throw(Err) + end end. remove_node_offline_node(Node) -> -- cgit v1.2.1 From 9cc163e0d08f9a288b87d7d435e52b3042fd4d09 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 24 Sep 2012 14:17:11 +0100 Subject: cosmetic --- src/rabbit_mnesia.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 997ce0e9..16f347ce 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -644,18 +644,18 @@ check_cluster_consistency(Node) -> %%-------------------------------------------------------------------- on_node_up(Node) -> - case is_only_node(Node, cluster_running_disc_nodes()) of + case is_only_node(Node, running_disc_nodes()) of true -> rabbit_log:info("cluster contains disc nodes again~n"); false -> ok end. on_node_down(_Node) -> - case cluster_running_disc_nodes() of + case running_disc_nodes() of [] -> rabbit_log:info("only running disc node went down~n"); _ -> ok end. -cluster_running_disc_nodes() -> +running_disc_nodes() -> {_AllNodes, DiscNodes, RunningNodes} = cluster_nodes(status), ordsets:to_list(ordsets:intersection(ordsets:from_list(DiscNodes), ordsets:from_list(RunningNodes))). -- cgit v1.2.1 From d4e9ec7f74334732c06dd13521b0cdca06086666 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 24 Sep 2012 14:26:13 +0100 Subject: Plugins --- src/rabbit_vm.erl | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/rabbit_vm.erl b/src/rabbit_vm.erl index 95ff093e..42b514db 100644 --- a/src/rabbit_vm.erl +++ b/src/rabbit_vm.erl @@ -18,6 +18,8 @@ -export([memory/0]). +-define(MAGIC_PLUGINS, [mochiweb, webmachine, cowboy, sockjs, rfc4627_jsonrpc]). + %%---------------------------------------------------------------------------- -ifdef(use_specs). @@ -40,6 +42,7 @@ memory() -> pid_memory(msg_store_persistent), MgmtDbETS = ets_memory(rabbit_mgmt_db), MgmtDbProc = sup_memory(rabbit_mgmt_sup), + Plugins = plugins_memory() - MgmtDbProc, [{total, Total}, {processes, Processes}, {ets, ETS}, @@ -51,8 +54,9 @@ memory() -> [{total, Total}, {connection_channel_procs, ConnChs}, {queue_procs, Qs}, + {plugins, Plugins}, {other_proc, Processes - ConnChs - Qs - MsgIndexProc - - MgmtDbProc}, + MgmtDbProc - Plugins}, {mnesia, Mnesia}, {mgmt_db, MgmtDbETS + MgmtDbProc}, {msg_index, MsgIndexETS + MsgIndexProc}, @@ -95,3 +99,19 @@ ets_memory(Name) -> N =:= Name]). bytes(Words) -> Words * erlang:system_info(wordsize). + +plugins_memory() -> + lists:sum([plugin_memory(App) || + {App, _, _} <- application:which_applications(), + is_plugin(App)]). + +plugin_memory(App) -> + case catch application_master:get_child( + application_controller:get_master(App)) of + {Pid, _} -> sup_memory(Pid); + _ -> 0 + end. + +is_plugin(App) -> + lists:member(App, ?MAGIC_PLUGINS) orelse + string:left(atom_to_list(App), 9) =:= "rabbitmq_". -- cgit v1.2.1 From 607375dbca5148bfab0d22bb35f2f1fb78eac8d0 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 24 Sep 2012 14:41:04 +0100 Subject: Never display negative memory. --- src/rabbit_vm.erl | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/rabbit_vm.erl b/src/rabbit_vm.erl index 42b514db..5115c82e 100644 --- a/src/rabbit_vm.erl +++ b/src/rabbit_vm.erl @@ -43,6 +43,7 @@ memory() -> MgmtDbETS = ets_memory(rabbit_mgmt_db), MgmtDbProc = sup_memory(rabbit_mgmt_sup), Plugins = plugins_memory() - MgmtDbProc, + OtherProc = Processes - ConnChs - Qs - MsgIndexProc - MgmtDbProc - Plugins, [{total, Total}, {processes, Processes}, {ets, ETS}, @@ -55,8 +56,7 @@ memory() -> {connection_channel_procs, ConnChs}, {queue_procs, Qs}, {plugins, Plugins}, - {other_proc, Processes - ConnChs - Qs - MsgIndexProc - - MgmtDbProc - Plugins}, + {other_proc, erlang:max(0, OtherProc)}, %% [1] {mnesia, Mnesia}, {mgmt_db, MgmtDbETS + MgmtDbProc}, {msg_index, MsgIndexETS + MsgIndexProc}, @@ -66,6 +66,11 @@ memory() -> {atom, Atom}, {other_system, System - ETS - Atom - Bin - Code}]. +%% [1] - erlang:memory(processes) can be less than the sum of its +%% parts. Rather than display something nonsensical, just silence any +%% claims about negative memory. See +%% http://erlang.org/pipermail/erlang-questions/2012-September/069320.html + %%---------------------------------------------------------------------------- sup_memory(Sup) -> -- cgit v1.2.1 From f5a7992675e764f45f75141d4381401b6ba8647d Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 24 Sep 2012 14:58:38 +0100 Subject: cosmetic --- src/rabbit_mnesia.erl | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 16f347ce..63f128bc 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -362,16 +362,14 @@ mnesia_nodes() -> Tables = mnesia:system_info(tables), [{Table, _} | _] = table_definitions(NodeType), case lists:member(Table, Tables) of - true -> - AllNodes = mnesia:system_info(db_nodes), - DiscCopies = mnesia:table_info(schema, disc_copies), - DiscNodes = case NodeType of - disc -> nodes_incl_me(DiscCopies); - ram -> DiscCopies - end, - {ok, {AllNodes, DiscNodes}}; - false -> - {error, tables_not_present} + true -> AllNodes = mnesia:system_info(db_nodes), + DiscCopies = mnesia:table_info(schema, disc_copies), + DiscNodes = case NodeType of + disc -> nodes_incl_me(DiscCopies); + ram -> DiscCopies + end, + {ok, {AllNodes, DiscNodes}}; + false -> {error, tables_not_present} end end. -- cgit v1.2.1 From fb546cf5902454dd0e1d135a432cd775a2ec82aa Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 24 Sep 2012 14:58:49 +0100 Subject: inline is_only_node --- src/rabbit_mnesia.erl | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 63f128bc..30bfa61f 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -339,9 +339,8 @@ is_db_empty() -> lists:all(fun (Tab) -> mnesia:dirty_first(Tab) == '$end_of_table' end, table_names()). -is_clustered() -> - AllNodes = cluster_nodes(all), - AllNodes =/= [] andalso not is_only_node(AllNodes). +is_clustered() -> AllNodes = cluster_nodes(all), + AllNodes =/= [] andalso AllNodes =/= [node()]. %% This function is the actual source of information, since it gets %% the data from mnesia. Obviously it'll work only when mnesia is @@ -642,7 +641,7 @@ check_cluster_consistency(Node) -> %%-------------------------------------------------------------------- on_node_up(Node) -> - case is_only_node(Node, running_disc_nodes()) of + case running_disc_nodes() =:= [Node] of true -> rabbit_log:info("cluster contains disc nodes again~n"); false -> ok end. @@ -1077,11 +1076,7 @@ find_good_node([Node | Nodes]) -> is_only_clustered_disc_node() -> node_type() =:= disc andalso is_clustered() andalso - is_only_node(cluster_nodes(disc)). - -is_only_node(Node, Nodes) -> Nodes =:= [Node]. - -is_only_node(Nodes) -> is_only_node(node(), Nodes). + cluster_nodes(disc) =:= [node()]. me_in_nodes(Nodes) -> lists:member(node(), Nodes). -- cgit v1.2.1 From 0f0df649626d1f33e4a7f451a345ccbbde599b99 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 24 Sep 2012 15:37:36 +0100 Subject: some more inlining --- src/rabbit_mnesia.erl | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 30bfa61f..c4920dc4 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -539,7 +539,7 @@ check_schema_integrity() -> true -> check_table_attributes(Tab, TabDef) end end) of - ok -> ok = wait_for_tables(), + ok -> ok = wait_for_tables(table_names()), check_tables(fun check_table_content/2); Other -> Other end. @@ -574,9 +574,9 @@ copy_db(Destination) -> ok = ensure_mnesia_not_running(), rabbit_file:recursive_copy(dir(), Destination). -wait_for_replicated_tables() -> wait_for_tables(replicated_table_names()). - -wait_for_tables() -> wait_for_tables(table_names()). +wait_for_replicated_tables() -> + wait_for_tables([Tab || {Tab, TabDef} <- table_definitions(), + not lists:member({local_content, true}, TabDef)]). wait_for_tables(TableNames) -> case mnesia:wait_for_tables(TableNames, 30000) of @@ -801,11 +801,6 @@ queue_name_match() -> resource_match(Kind) -> #resource{kind = Kind, _='_'}. -replicated_table_names() -> - [Tab || {Tab, TabDef} <- table_definitions(), - not lists:member({local_content, true}, TabDef) - ]. - check_table_attributes(Tab, TabDef) -> {_, ExpAttrs} = proplists:lookup(attributes, TabDef), case mnesia:table_info(Tab, attributes) of -- cgit v1.2.1 From c745b04b9e8931dbd823dabe769bb5b508da6a7c Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 24 Sep 2012 15:42:00 +0100 Subject: You would think I would at least compile it. --- src/rabbit_vm.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_vm.erl b/src/rabbit_vm.erl index 5115c82e..c39d92ae 100644 --- a/src/rabbit_vm.erl +++ b/src/rabbit_vm.erl @@ -43,7 +43,6 @@ memory() -> MgmtDbETS = ets_memory(rabbit_mgmt_db), MgmtDbProc = sup_memory(rabbit_mgmt_sup), Plugins = plugins_memory() - MgmtDbProc, - OtherProc = Processes - ConnChs - Qs - MsgIndexProc - MgmtDbProc - Plugins, [{total, Total}, {processes, Processes}, {ets, ETS}, @@ -52,6 +51,7 @@ memory() -> {code, Code}, {system, System}] = erlang:memory([total, processes, ets, atom, binary, code, system]), + OtherProc = Processes - ConnChs - Qs - MsgIndexProc - MgmtDbProc - Plugins, [{total, Total}, {connection_channel_procs, ConnChs}, {queue_procs, Qs}, -- cgit v1.2.1 From c372383769afd21d27bf9be00fd521d83bf664bb Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 24 Sep 2012 17:24:59 +0100 Subject: move table handling code from rabbit_mnesia to rabbit_table --- src/rabbit.erl | 4 +- src/rabbit_mnesia.erl | 292 +----------------------------------- src/rabbit_table.erl | 311 +++++++++++++++++++++++++++++++++++++++ src/rabbit_upgrade.erl | 5 +- src/rabbit_upgrade_functions.erl | 4 +- 5 files changed, 324 insertions(+), 292 deletions(-) create mode 100644 src/rabbit_table.erl diff --git a/src/rabbit.erl b/src/rabbit.erl index 353a5bb9..afa97ddc 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -419,7 +419,7 @@ stop(_State) -> ok = rabbit_alarm:stop(), ok = case rabbit_mnesia:is_clustered() of true -> rabbit_amqqueue:on_node_down(node()); - false -> rabbit_mnesia:empty_ram_only_tables() + false -> rabbit_table:clear_ram_only_tables() end, ok. @@ -546,7 +546,7 @@ recover() -> rabbit_binding:recover(rabbit_exchange:recover(), rabbit_amqqueue:start()). maybe_insert_default_data() -> - case rabbit_mnesia:is_db_empty() of + case rabbit_table:is_empty() of true -> insert_default_data(); false -> ok end. diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index c4920dc4..f7a355be 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -14,7 +14,6 @@ %% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. %% - -module(rabbit_mnesia). -export([init/0, @@ -26,18 +25,14 @@ forget_cluster_node/2, status/0, - is_db_empty/0, is_clustered/0, cluster_nodes/1, node_type/0, dir/0, - table_names/0, cluster_status_from_mnesia/0, init_db_unchecked/2, - empty_ram_only_tables/0, copy_db/1, - wait_for_tables/1, check_cluster_consistency/0, ensure_mnesia_dir/0, @@ -51,10 +46,6 @@ is_running_remote/0 ]). -%% create_tables/0 exported for helping embed RabbitMQ in or alongside -%% other mnesia-using Erlang applications, such as ejabberd --export([create_tables/0]). - -include("rabbit.hrl"). %%---------------------------------------------------------------------------- @@ -78,21 +69,16 @@ %% Various queries to get the status of the db -spec(status/0 :: () -> [{'nodes', [{node_type(), [node()]}]} | {'running_nodes', [node()]}]). --spec(is_db_empty/0 :: () -> boolean()). -spec(is_clustered/0 :: () -> boolean()). -spec(cluster_nodes/1 :: ('all' | 'disc' | 'ram' | 'running') -> [node()]). -spec(node_type/0 :: () -> node_type()). -spec(dir/0 :: () -> file:filename()). --spec(table_names/0 :: () -> [atom()]). -spec(cluster_status_from_mnesia/0 :: () -> rabbit_types:ok_or_error2( cluster_status(), any())). %% Operations on the db and utils, mainly used in `rabbit_upgrade' and `rabbit' -spec(init_db_unchecked/2 :: ([node()], node_type()) -> 'ok'). --spec(empty_ram_only_tables/0 :: () -> 'ok'). --spec(create_tables/0 :: () -> 'ok'). -spec(copy_db/1 :: (file:filename()) -> rabbit_types:ok_or_error(any())). --spec(wait_for_tables/1 :: ([atom()]) -> 'ok'). -spec(check_cluster_consistency/0 :: () -> 'ok'). -spec(ensure_mnesia_dir/0 :: () -> 'ok'). @@ -307,7 +293,7 @@ remove_node_offline_node(Node) -> case cluster_nodes(running) -- [node(), Node] of [] -> start_mnesia(), try - [mnesia:force_load_table(T) || T <- table_names()], + rabbit_table:force_load(), forget_cluster_node(Node, false), ensure_mnesia_running() after @@ -335,10 +321,6 @@ status() -> no -> [] end. -is_db_empty() -> - lists:all(fun (Tab) -> mnesia:dirty_first(Tab) == '$end_of_table' end, - table_names()). - is_clustered() -> AllNodes = cluster_nodes(all), AllNodes =/= [] andalso AllNodes =/= [node()]. @@ -358,9 +340,7 @@ mnesia_nodes() -> true -> disc; false -> ram end, - Tables = mnesia:system_info(tables), - [{Table, _} | _] = table_definitions(NodeType), - case lists:member(Table, Tables) of + case rabbit_table:is_present() of true -> AllNodes = mnesia:system_info(db_nodes), DiscCopies = mnesia:table_info(schema, disc_copies), DiscNodes = case NodeType of @@ -415,8 +395,6 @@ node_type() -> dir() -> mnesia:system_info(directory). -table_names() -> [Tab || {Tab, _} <- table_definitions()]. - %%---------------------------------------------------------------------------- %% Operations on the db %%---------------------------------------------------------------------------- @@ -446,18 +424,8 @@ init_db(ClusterNodes, NodeType, CheckOtherNodes) -> %% Subsequent node in cluster, catch up ensure_version_ok( rpc:call(AnotherNode, rabbit_version, recorded, [])), - ok = wait_for_replicated_tables(), - %% The sequence in which we delete the schema and then the - %% other tables is important: if we delete the schema - %% first when moving to RAM mnesia will loudly complain - %% since it doesn't make much sense to do that. But when - %% moving to disc, we need to move the schema first. - case NodeType of - disc -> create_local_table_copy(schema, disc_copies), - create_local_table_copies(disc); - ram -> create_local_table_copies(ram), - create_local_table_copy(schema, ram_copies) - end + ok = rabbit_table:wait_for_replicated(), + ok = rabbit_table:create_local_copy(NodeType) end, ensure_schema_integrity(), rabbit_node_monitor:update_cluster_status(), @@ -478,7 +446,7 @@ init_db_and_upgrade(ClusterNodes, NodeType, CheckOtherNodes) -> case NodeType of ram -> start_mnesia(), change_extra_db_nodes(ClusterNodes, false), - wait_for_replicated_tables(); + rabbit_table:wait_for_replicated(); disc -> ok end, ok. @@ -524,70 +492,17 @@ ensure_mnesia_not_running() -> end. ensure_schema_integrity() -> - case check_schema_integrity() of + case rabbit_table:check_schema_integrity() of ok -> ok; {error, Reason} -> throw({error, {schema_integrity_check_failed, Reason}}) end. -check_schema_integrity() -> - Tables = mnesia:system_info(tables), - case check_tables(fun (Tab, TabDef) -> - case lists:member(Tab, Tables) of - false -> {error, {table_missing, Tab}}; - true -> check_table_attributes(Tab, TabDef) - end - end) of - ok -> ok = wait_for_tables(table_names()), - check_tables(fun check_table_content/2); - Other -> Other - end. - -empty_ram_only_tables() -> - Node = node(), - lists:foreach( - fun (TabName) -> - case lists:member(Node, mnesia:table_info(TabName, ram_copies)) of - true -> {atomic, ok} = mnesia:clear_table(TabName); - false -> ok - end - end, table_names()), - ok. - -create_tables() -> create_tables(disc). - -create_tables(Type) -> - lists:foreach(fun ({Tab, TabDef}) -> - TabDef1 = proplists:delete(match, TabDef), - case mnesia:create_table(Tab, TabDef1) of - {atomic, ok} -> ok; - {aborted, Reason} -> - throw({error, {table_creation_failed, - Tab, TabDef1, Reason}}) - end - end, - table_definitions(Type)), - ok. - copy_db(Destination) -> ok = ensure_mnesia_not_running(), rabbit_file:recursive_copy(dir(), Destination). -wait_for_replicated_tables() -> - wait_for_tables([Tab || {Tab, TabDef} <- table_definitions(), - not lists:member({local_content, true}, TabDef)]). - -wait_for_tables(TableNames) -> - case mnesia:wait_for_tables(TableNames, 30000) of - ok -> - ok; - {timeout, BadTabs} -> - throw({error, {timeout_waiting_for_tables, BadTabs}}); - {error, Reason} -> - throw({error, {failed_waiting_for_tables, Reason}}) - end. - %% This does not guarantee us much, but it avoids some situations that %% will definitely end up badly check_cluster_consistency() -> @@ -682,158 +597,8 @@ discover_cluster(Node) -> end end. -%% The tables aren't supposed to be on disk on a ram node -table_definitions(disc) -> - table_definitions(); -table_definitions(ram) -> - [{Tab, copy_type_to_ram(TabDef)} || {Tab, TabDef} <- table_definitions()]. - -table_definitions() -> - [{rabbit_user, - [{record_name, internal_user}, - {attributes, record_info(fields, internal_user)}, - {disc_copies, [node()]}, - {match, #internal_user{_='_'}}]}, - {rabbit_user_permission, - [{record_name, user_permission}, - {attributes, record_info(fields, user_permission)}, - {disc_copies, [node()]}, - {match, #user_permission{user_vhost = #user_vhost{_='_'}, - permission = #permission{_='_'}, - _='_'}}]}, - {rabbit_vhost, - [{record_name, vhost}, - {attributes, record_info(fields, vhost)}, - {disc_copies, [node()]}, - {match, #vhost{_='_'}}]}, - {rabbit_listener, - [{record_name, listener}, - {attributes, record_info(fields, listener)}, - {type, bag}, - {match, #listener{_='_'}}]}, - {rabbit_durable_route, - [{record_name, route}, - {attributes, record_info(fields, route)}, - {disc_copies, [node()]}, - {match, #route{binding = binding_match(), _='_'}}]}, - {rabbit_semi_durable_route, - [{record_name, route}, - {attributes, record_info(fields, route)}, - {type, ordered_set}, - {match, #route{binding = binding_match(), _='_'}}]}, - {rabbit_route, - [{record_name, route}, - {attributes, record_info(fields, route)}, - {type, ordered_set}, - {match, #route{binding = binding_match(), _='_'}}]}, - {rabbit_reverse_route, - [{record_name, reverse_route}, - {attributes, record_info(fields, reverse_route)}, - {type, ordered_set}, - {match, #reverse_route{reverse_binding = reverse_binding_match(), - _='_'}}]}, - {rabbit_topic_trie_node, - [{record_name, topic_trie_node}, - {attributes, record_info(fields, topic_trie_node)}, - {type, ordered_set}, - {match, #topic_trie_node{trie_node = trie_node_match(), _='_'}}]}, - {rabbit_topic_trie_edge, - [{record_name, topic_trie_edge}, - {attributes, record_info(fields, topic_trie_edge)}, - {type, ordered_set}, - {match, #topic_trie_edge{trie_edge = trie_edge_match(), _='_'}}]}, - {rabbit_topic_trie_binding, - [{record_name, topic_trie_binding}, - {attributes, record_info(fields, topic_trie_binding)}, - {type, ordered_set}, - {match, #topic_trie_binding{trie_binding = trie_binding_match(), - _='_'}}]}, - {rabbit_durable_exchange, - [{record_name, exchange}, - {attributes, record_info(fields, exchange)}, - {disc_copies, [node()]}, - {match, #exchange{name = exchange_name_match(), _='_'}}]}, - {rabbit_exchange, - [{record_name, exchange}, - {attributes, record_info(fields, exchange)}, - {match, #exchange{name = exchange_name_match(), _='_'}}]}, - {rabbit_exchange_serial, - [{record_name, exchange_serial}, - {attributes, record_info(fields, exchange_serial)}, - {match, #exchange_serial{name = exchange_name_match(), _='_'}}]}, - {rabbit_runtime_parameters, - [{record_name, runtime_parameters}, - {attributes, record_info(fields, runtime_parameters)}, - {disc_copies, [node()]}, - {match, #runtime_parameters{_='_'}}]}, - {rabbit_durable_queue, - [{record_name, amqqueue}, - {attributes, record_info(fields, amqqueue)}, - {disc_copies, [node()]}, - {match, #amqqueue{name = queue_name_match(), _='_'}}]}, - {rabbit_queue, - [{record_name, amqqueue}, - {attributes, record_info(fields, amqqueue)}, - {match, #amqqueue{name = queue_name_match(), _='_'}}]}] - ++ gm:table_definitions() - ++ mirrored_supervisor:table_definitions(). - -binding_match() -> - #binding{source = exchange_name_match(), - destination = binding_destination_match(), - _='_'}. -reverse_binding_match() -> - #reverse_binding{destination = binding_destination_match(), - source = exchange_name_match(), - _='_'}. -binding_destination_match() -> - resource_match('_'). -trie_node_match() -> - #trie_node{ exchange_name = exchange_name_match(), _='_'}. -trie_edge_match() -> - #trie_edge{ exchange_name = exchange_name_match(), _='_'}. -trie_binding_match() -> - #trie_binding{exchange_name = exchange_name_match(), _='_'}. -exchange_name_match() -> - resource_match(exchange). -queue_name_match() -> - resource_match(queue). -resource_match(Kind) -> - #resource{kind = Kind, _='_'}. - -check_table_attributes(Tab, TabDef) -> - {_, ExpAttrs} = proplists:lookup(attributes, TabDef), - case mnesia:table_info(Tab, attributes) of - ExpAttrs -> ok; - Attrs -> {error, {table_attributes_mismatch, Tab, ExpAttrs, Attrs}} - end. - -check_table_content(Tab, TabDef) -> - {_, Match} = proplists:lookup(match, TabDef), - case mnesia:dirty_first(Tab) of - '$end_of_table' -> - ok; - Key -> - ObjList = mnesia:dirty_read(Tab, Key), - MatchComp = ets:match_spec_compile([{Match, [], ['$_']}]), - case ets:match_spec_run(ObjList, MatchComp) of - ObjList -> ok; - _ -> {error, {table_content_invalid, Tab, Match, ObjList}} - end - end. - -check_tables(Fun) -> - case [Error || {Tab, TabDef} <- table_definitions(node_type()), - case Fun(Tab, TabDef) of - ok -> Error = none, false; - {error, Error} -> true - end] of - [] -> ok; - Errors -> {error, Errors} - end. - schema_ok_or_move() -> - case check_schema_integrity() of + case rabbit_table:check_schema_integrity() of ok -> ok; {error, Reason} -> @@ -862,7 +627,7 @@ create_schema() -> stop_mnesia(), rabbit_misc:ensure_ok(mnesia:create_schema([node()]), cannot_create_schema), start_mnesia(), - ok = create_tables(disc), + ok = rabbit_table:create(), ensure_schema_integrity(), ok = rabbit_version:record_desired(). @@ -887,47 +652,6 @@ move_db() -> start_mnesia(), ok. -copy_type_to_ram(TabDef) -> - [{disc_copies, []}, {ram_copies, [node()]} - | proplists:delete(ram_copies, proplists:delete(disc_copies, TabDef))]. - -table_has_copy_type(TabDef, DiscType) -> - lists:member(node(), proplists:get_value(DiscType, TabDef, [])). - -create_local_table_copies(Type) -> - lists:foreach( - fun ({Tab, TabDef}) -> - HasDiscCopies = table_has_copy_type(TabDef, disc_copies), - HasDiscOnlyCopies = table_has_copy_type(TabDef, disc_only_copies), - LocalTab = proplists:get_bool(local_content, TabDef), - StorageType = - if - Type =:= disc orelse LocalTab -> - if - HasDiscCopies -> disc_copies; - HasDiscOnlyCopies -> disc_only_copies; - true -> ram_copies - end; - Type =:= ram -> - ram_copies - end, - ok = create_local_table_copy(Tab, StorageType) - end, - table_definitions(Type)), - ok. - -create_local_table_copy(Tab, Type) -> - StorageType = mnesia:table_info(Tab, storage_type), - {atomic, ok} = - if - StorageType == unknown -> - mnesia:add_table_copy(Tab, node(), Type); - StorageType /= Type -> - mnesia:change_table_copy_type(Tab, node(), Type); - true -> {atomic, ok} - end, - ok. - remove_node_if_mnesia_running(Node) -> case mnesia:system_info(is_running) of yes -> diff --git a/src/rabbit_table.erl b/src/rabbit_table.erl new file mode 100644 index 00000000..fa1c5bbd --- /dev/null +++ b/src/rabbit_table.erl @@ -0,0 +1,311 @@ +%% 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 VMware, Inc. +%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% + +-module(rabbit_table). + +-export([create/0, create_local_copy/1, wait_for_replicated/0, wait/1, + force_load/0, is_present/0, is_empty/0, + check_schema_integrity/0, clear_ram_only_tables/0]). + +-include("rabbit.hrl"). + +%%---------------------------------------------------------------------------- + +-ifdef(use_specs). + +-spec(create/0 :: () -> 'ok'). +-spec(create_local_copy/1 :: ('disc' | 'ram') -> 'ok'). +-spec(wait_for_replicated/0 :: () -> 'ok'). +-spec(wait/1 :: ([atom()]) -> 'ok'). +-spec(force_load/0 :: () -> 'ok'). +-spec(is_present/0 :: () -> boolean()). +-spec(is_empty/0 :: () -> boolean()). +-spec(check_schema_integrity/0 :: () -> rabbit_types:ok_or_error(any())). +-spec(clear_ram_only_tables/0 :: () -> 'ok'). + +-endif. + +%%---------------------------------------------------------------------------- +%% Main interface +%%---------------------------------------------------------------------------- + +create() -> + lists:foreach(fun ({Tab, TabDef}) -> + TabDef1 = proplists:delete(match, TabDef), + case mnesia:create_table(Tab, TabDef1) of + {atomic, ok} -> ok; + {aborted, Reason} -> + throw({error, {table_creation_failed, + Tab, TabDef1, Reason}}) + end + end, definitions()), + ok. + +%% The sequence in which we delete the schema and then the other +%% tables is important: if we delete the schema first when moving to +%% RAM mnesia will loudly complain since it doesn't make much sense to +%% do that. But when moving to disc, we need to move the schema first. +create_local_copy(disc) -> + create_local_copy(schema, disc_copies), + create_local_copies(disc); +create_local_copy(ram) -> + create_local_copies(ram), + create_local_copy(schema, ram_copies). + +wait_for_replicated() -> + wait([Tab || {Tab, TabDef} <- definitions(), + not lists:member({local_content, true}, TabDef)]). + +wait(TableNames) -> + case mnesia:wait_for_tables(TableNames, 30000) of + ok -> + ok; + {timeout, BadTabs} -> + throw({error, {timeout_waiting_for_tables, BadTabs}}); + {error, Reason} -> + throw({error, {failed_waiting_for_tables, Reason}}) + end. + +force_load() -> [mnesia:force_load_table(T) || T <- names()], ok. + +is_present() -> names() -- mnesia:system_info(tables) =:= []. + +is_empty() -> + lists:all(fun (Tab) -> mnesia:dirty_first(Tab) == '$end_of_table' end, + names()). + +check_schema_integrity() -> + Tables = mnesia:system_info(tables), + case check(fun (Tab, TabDef) -> + case lists:member(Tab, Tables) of + false -> {error, {table_missing, Tab}}; + true -> check_attributes(Tab, TabDef) + end + end) of + ok -> ok = wait(names()), + check(fun check_content/2); + Other -> Other + end. + +clear_ram_only_tables() -> + Node = node(), + lists:foreach( + fun (TabName) -> + case lists:member(Node, mnesia:table_info(TabName, ram_copies)) of + true -> {atomic, ok} = mnesia:clear_table(TabName); + false -> ok + end + end, names()), + ok. + +%%-------------------------------------------------------------------- +%% Internal helpers +%%-------------------------------------------------------------------- + +create_local_copies(Type) -> + lists:foreach( + fun ({Tab, TabDef}) -> + HasDiscCopies = has_copy_type(TabDef, disc_copies), + HasDiscOnlyCopies = has_copy_type(TabDef, disc_only_copies), + LocalTab = proplists:get_bool(local_content, TabDef), + StorageType = + if + Type =:= disc orelse LocalTab -> + if + HasDiscCopies -> disc_copies; + HasDiscOnlyCopies -> disc_only_copies; + true -> ram_copies + end; + Type =:= ram -> + ram_copies + end, + ok = create_local_copy(Tab, StorageType) + end, definitions(Type)), + ok. + +create_local_copy(Tab, Type) -> + StorageType = mnesia:table_info(Tab, storage_type), + {atomic, ok} = + if + StorageType == unknown -> + mnesia:add_table_copy(Tab, node(), Type); + StorageType /= Type -> + mnesia:change_table_copy_type(Tab, node(), Type); + true -> {atomic, ok} + end, + ok. + +has_copy_type(TabDef, DiscType) -> + lists:member(node(), proplists:get_value(DiscType, TabDef, [])). + +check_attributes(Tab, TabDef) -> + {_, ExpAttrs} = proplists:lookup(attributes, TabDef), + case mnesia:table_info(Tab, attributes) of + ExpAttrs -> ok; + Attrs -> {error, {table_attributes_mismatch, Tab, ExpAttrs, Attrs}} + end. + +check_content(Tab, TabDef) -> + {_, Match} = proplists:lookup(match, TabDef), + case mnesia:dirty_first(Tab) of + '$end_of_table' -> + ok; + Key -> + ObjList = mnesia:dirty_read(Tab, Key), + MatchComp = ets:match_spec_compile([{Match, [], ['$_']}]), + case ets:match_spec_run(ObjList, MatchComp) of + ObjList -> ok; + _ -> {error, {table_content_invalid, Tab, Match, ObjList}} + end + end. + +check(Fun) -> + case [Error || {Tab, TabDef} <- definitions(), + case Fun(Tab, TabDef) of + ok -> Error = none, false; + {error, Error} -> true + end] of + [] -> ok; + Errors -> {error, Errors} + end. + +%%-------------------------------------------------------------------- +%% Table definitions +%%-------------------------------------------------------------------- + +names() -> [Tab || {Tab, _} <- definitions()]. + +%% The tables aren't supposed to be on disk on a ram node +definitions(disc) -> + definitions(); +definitions(ram) -> + [{Tab, [{disc_copies, []}, {ram_copies, [node()]} | + proplists:delete( + ram_copies, proplists:delete(disc_copies, TabDef))]} || + {Tab, TabDef} <- definitions()]. + +definitions() -> + [{rabbit_user, + [{record_name, internal_user}, + {attributes, record_info(fields, internal_user)}, + {disc_copies, [node()]}, + {match, #internal_user{_='_'}}]}, + {rabbit_user_permission, + [{record_name, user_permission}, + {attributes, record_info(fields, user_permission)}, + {disc_copies, [node()]}, + {match, #user_permission{user_vhost = #user_vhost{_='_'}, + permission = #permission{_='_'}, + _='_'}}]}, + {rabbit_vhost, + [{record_name, vhost}, + {attributes, record_info(fields, vhost)}, + {disc_copies, [node()]}, + {match, #vhost{_='_'}}]}, + {rabbit_listener, + [{record_name, listener}, + {attributes, record_info(fields, listener)}, + {type, bag}, + {match, #listener{_='_'}}]}, + {rabbit_durable_route, + [{record_name, route}, + {attributes, record_info(fields, route)}, + {disc_copies, [node()]}, + {match, #route{binding = binding_match(), _='_'}}]}, + {rabbit_semi_durable_route, + [{record_name, route}, + {attributes, record_info(fields, route)}, + {type, ordered_set}, + {match, #route{binding = binding_match(), _='_'}}]}, + {rabbit_route, + [{record_name, route}, + {attributes, record_info(fields, route)}, + {type, ordered_set}, + {match, #route{binding = binding_match(), _='_'}}]}, + {rabbit_reverse_route, + [{record_name, reverse_route}, + {attributes, record_info(fields, reverse_route)}, + {type, ordered_set}, + {match, #reverse_route{reverse_binding = reverse_binding_match(), + _='_'}}]}, + {rabbit_topic_trie_node, + [{record_name, topic_trie_node}, + {attributes, record_info(fields, topic_trie_node)}, + {type, ordered_set}, + {match, #topic_trie_node{trie_node = trie_node_match(), _='_'}}]}, + {rabbit_topic_trie_edge, + [{record_name, topic_trie_edge}, + {attributes, record_info(fields, topic_trie_edge)}, + {type, ordered_set}, + {match, #topic_trie_edge{trie_edge = trie_edge_match(), _='_'}}]}, + {rabbit_topic_trie_binding, + [{record_name, topic_trie_binding}, + {attributes, record_info(fields, topic_trie_binding)}, + {type, ordered_set}, + {match, #topic_trie_binding{trie_binding = trie_binding_match(), + _='_'}}]}, + {rabbit_durable_exchange, + [{record_name, exchange}, + {attributes, record_info(fields, exchange)}, + {disc_copies, [node()]}, + {match, #exchange{name = exchange_name_match(), _='_'}}]}, + {rabbit_exchange, + [{record_name, exchange}, + {attributes, record_info(fields, exchange)}, + {match, #exchange{name = exchange_name_match(), _='_'}}]}, + {rabbit_exchange_serial, + [{record_name, exchange_serial}, + {attributes, record_info(fields, exchange_serial)}, + {match, #exchange_serial{name = exchange_name_match(), _='_'}}]}, + {rabbit_runtime_parameters, + [{record_name, runtime_parameters}, + {attributes, record_info(fields, runtime_parameters)}, + {disc_copies, [node()]}, + {match, #runtime_parameters{_='_'}}]}, + {rabbit_durable_queue, + [{record_name, amqqueue}, + {attributes, record_info(fields, amqqueue)}, + {disc_copies, [node()]}, + {match, #amqqueue{name = queue_name_match(), _='_'}}]}, + {rabbit_queue, + [{record_name, amqqueue}, + {attributes, record_info(fields, amqqueue)}, + {match, #amqqueue{name = queue_name_match(), _='_'}}]}] + ++ gm:table_definitions() + ++ mirrored_supervisor:table_definitions(). + +binding_match() -> + #binding{source = exchange_name_match(), + destination = binding_destination_match(), + _='_'}. +reverse_binding_match() -> + #reverse_binding{destination = binding_destination_match(), + source = exchange_name_match(), + _='_'}. +binding_destination_match() -> + resource_match('_'). +trie_node_match() -> + #trie_node{ exchange_name = exchange_name_match(), _='_'}. +trie_edge_match() -> + #trie_edge{ exchange_name = exchange_name_match(), _='_'}. +trie_binding_match() -> + #trie_binding{exchange_name = exchange_name_match(), _='_'}. +exchange_name_match() -> + resource_match(exchange). +queue_name_match() -> + resource_match(queue). +resource_match(Kind) -> + #resource{kind = Kind, _='_'}. diff --git a/src/rabbit_upgrade.erl b/src/rabbit_upgrade.erl index d037f954..455134da 100644 --- a/src/rabbit_upgrade.erl +++ b/src/rabbit_upgrade.erl @@ -201,7 +201,7 @@ primary_upgrade(Upgrades, Nodes) -> mnesia, Upgrades, fun () -> - force_tables(), + rabbit_table:force_load(), case Others of [] -> ok; _ -> info("mnesia upgrades: Breaking cluster~n", []), @@ -211,9 +211,6 @@ primary_upgrade(Upgrades, Nodes) -> end), ok. -force_tables() -> - [mnesia:force_load_table(T) || T <- rabbit_mnesia:table_names()]. - secondary_upgrade(AllNodes) -> %% must do this before we wipe out schema NodeType = node_type_legacy(), diff --git a/src/rabbit_upgrade_functions.erl b/src/rabbit_upgrade_functions.erl index 47b22b98..7c054993 100644 --- a/src/rabbit_upgrade_functions.erl +++ b/src/rabbit_upgrade_functions.erl @@ -258,12 +258,12 @@ sync_slave_pids() -> %%-------------------------------------------------------------------- transform(TableName, Fun, FieldList) -> - rabbit_mnesia:wait_for_tables([TableName]), + rabbit_table:wait([TableName]), {atomic, ok} = mnesia:transform_table(TableName, Fun, FieldList), ok. transform(TableName, Fun, FieldList, NewRecordName) -> - rabbit_mnesia:wait_for_tables([TableName]), + rabbit_table:wait([TableName]), {atomic, ok} = mnesia:transform_table(TableName, Fun, FieldList, NewRecordName), ok. -- cgit v1.2.1 From 04870f6a91abf989ec781fe5de1e0c93bb381488 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 24 Sep 2012 17:38:36 +0100 Subject: improve workingness --- src/rabbit_channel.erl | 2 +- src/rabbit_control_main.erl | 2 +- src/rabbit_direct.erl | 2 +- src/rabbit_mirror_queue_master.erl | 11 +++++------ 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index e50e823c..c0bad4fa 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -136,7 +136,7 @@ flushed(Pid, QPid) -> gen_server2:cast(Pid, {flushed, QPid}). list() -> - rabbit_misc:append_rpc_all_nodes(rabbit_mnesia:running_clustered_nodes(), + rabbit_misc:append_rpc_all_nodes(rabbit_mnesia:cluster_nodes(running), rabbit_channel, list_local, []). list_local() -> diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index a6c4fe67..e75e1f6f 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -461,7 +461,7 @@ action(list_parameters, Node, [], Opts, Inform) -> action(report, Node, _Args, _Opts, Inform) -> Inform("Reporting server status on ~p~n~n", [erlang:universaltime()]), [begin ok = action(Action, N, [], [], Inform), io:nl() end || - N <- unsafe_rpc(Node, rabbit_mnesia, running_clustered_nodes, []), + N <- unsafe_rpc(Node, rabbit_mnesia, cluster_nodes, [running]), Action <- [status, cluster_status, environment]], VHosts = unsafe_rpc(Node, rabbit_vhost, list, []), [print_report(Node, Q) || Q <- ?GLOBAL_QUERIES], diff --git a/src/rabbit_direct.erl b/src/rabbit_direct.erl index a669a2b3..758b37d1 100644 --- a/src/rabbit_direct.erl +++ b/src/rabbit_direct.erl @@ -59,7 +59,7 @@ list_local() -> pg_local:get_members(rabbit_direct). list() -> - rabbit_misc:append_rpc_all_nodes(rabbit_mnesia:running_clustered_nodes(), + rabbit_misc:append_rpc_all_nodes(rabbit_mnesia:cluster_nodes(running), rabbit_direct, list_local, []). %%---------------------------------------------------------------------------- diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index c11a8ff7..41389815 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -87,12 +87,11 @@ init(#amqqueue { name = QName, mirror_nodes = MNodes } = Q, Recover, {ok, CPid} = rabbit_mirror_queue_coordinator:start_link( Q, undefined, sender_death_fun(), length_fun()), GM = rabbit_mirror_queue_coordinator:get_gm(CPid), - MNodes1 = - (case MNodes of - all -> rabbit_mnesia:all_clustered_nodes(); - undefined -> []; - _ -> MNodes - end) -- [node()], + MNodes1 = (case MNodes of + all -> rabbit_mnesia:cluster_nodes(all); + undefined -> []; + _ -> MNodes + end) -- [node()], [rabbit_mirror_queue_misc:add_mirror(QName, Node) || Node <- MNodes1], {ok, BQ} = application:get_env(backing_queue_module), BQS = BQ:init(Q, Recover, AsyncCallback), -- cgit v1.2.1 From 67e1c4f2374a759cca2f018b8366a05950844d03 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 24 Sep 2012 17:39:07 +0100 Subject: appease dialyzer --- src/rabbit_mnesia.erl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index c4920dc4..1a2c70fa 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -342,6 +342,8 @@ is_db_empty() -> is_clustered() -> AllNodes = cluster_nodes(all), AllNodes =/= [] andalso AllNodes =/= [node()]. +cluster_nodes(WhichNodes) -> cluster_status(WhichNodes). + %% This function is the actual source of information, since it gets %% the data from mnesia. Obviously it'll work only when mnesia is %% running. @@ -372,7 +374,7 @@ mnesia_nodes() -> end end. -cluster_nodes(WhichNodes) -> +cluster_status(WhichNodes) -> %% I don't want to call `running_nodes/1' unless if necessary, since it's %% pretty expensive. {AllNodes1, DiscNodes1, RunningNodesThunk} = @@ -653,7 +655,7 @@ on_node_down(_Node) -> end. running_disc_nodes() -> - {_AllNodes, DiscNodes, RunningNodes} = cluster_nodes(status), + {_AllNodes, DiscNodes, RunningNodes} = cluster_status(status), ordsets:to_list(ordsets:intersection(ordsets:from_list(DiscNodes), ordsets:from_list(RunningNodes))). -- cgit v1.2.1 From 63e855d5070bee346571b0751953d5d2cbbda1e2 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 24 Sep 2012 17:53:33 +0100 Subject: No longer need confirmed_broadcast if immediate no longer exists. --- src/rabbit_mirror_queue_master.erl | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index c11a8ff7..10f60c53 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -170,10 +170,7 @@ publish_delivered(AckRequired, Msg = #basic_message { id = MsgId }, MsgProps, backing_queue_state = BQS, ack_msg_id = AM }) -> false = dict:is_key(MsgId, SS), %% ASSERTION - %% Must use confirmed_broadcast here in order to guarantee that - %% all slaves are forced to interpret this publish_delivered at - %% the same point, especially if we die and a slave is promoted. - ok = gm:confirmed_broadcast( + ok = gm:broadcast( GM, {publish, {true, AckRequired}, ChPid, MsgProps, Msg}), {AckTag, BQS1} = BQ:publish_delivered(AckRequired, Msg, MsgProps, ChPid, BQS), -- cgit v1.2.1 From e78ba15640a5edd308a49a72dc2db41f133b39c9 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 24 Sep 2012 22:48:23 +0100 Subject: cosmetic --- src/rabbit_mnesia.erl | 26 ++++++++++++-------------- src/rabbit_node_monitor.erl | 6 ++---- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 1a2c70fa..b3546483 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -643,9 +643,9 @@ check_cluster_consistency(Node) -> %%-------------------------------------------------------------------- on_node_up(Node) -> - case running_disc_nodes() =:= [Node] of - true -> rabbit_log:info("cluster contains disc nodes again~n"); - false -> ok + case running_disc_nodes() of + [Node] -> rabbit_log:info("cluster contains disc nodes again~n"); + _ -> ok end. on_node_down(_Node) -> @@ -670,18 +670,16 @@ discover_cluster(Nodes) when is_list(Nodes) -> discover_cluster(Node) -> OfflineError = {error, {cannot_discover_cluster, - "The nodes provided is either offline or not running"}}, + "The nodes provided are either offline or not running"}}, case node() of - Node-> - {error, {cannot_discover_cluster, - "You provided the current node as node to cluster with"}}; - _ -> - case rpc:call(Node, - rabbit_mnesia, cluster_status_from_mnesia, []) of - {badrpc, _Reason} -> OfflineError; - {error, mnesia_not_running} -> OfflineError; - {ok, Res} -> {ok, Res} - end + Node -> {error, {cannot_discover_cluster, + "Cannot cluster node with itself"}}; + _ -> case rpc:call(Node, + rabbit_mnesia, cluster_status_from_mnesia, []) of + {badrpc, _Reason} -> OfflineError; + {error, mnesia_not_running} -> OfflineError; + {ok, Res} -> {ok, Res} + end end. %% The tables aren't supposed to be on disk on a ram node diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index c1572762..efbe57c0 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -291,8 +291,6 @@ legacy_cluster_nodes(Nodes) -> legacy_should_be_disc_node(DiscNodes) -> DiscNodes == [] orelse lists:member(node(), DiscNodes). -add_node(Node, Nodes) -> - lists:usort([Node|Nodes]). +add_node(Node, Nodes) -> lists:usort([Node | Nodes]). -del_node(Node, Nodes) -> - Nodes -- [Node]. +del_node(Node, Nodes) -> Nodes -- [Node]. -- cgit v1.2.1 From 448f61f27f97561c1f497791e914a9274a8eea52 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 24 Sep 2012 23:10:01 +0100 Subject: cosmetic --- src/rabbit_node_monitor.erl | 65 +++++++++++++++++++-------------------------- 1 file changed, 28 insertions(+), 37 deletions(-) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index efbe57c0..38493db1 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -18,29 +18,19 @@ -behaviour(gen_server). +-export([start_link/0]). -export([running_nodes_filename/0, - cluster_status_filename/0, - prepare_cluster_status_files/0, - write_cluster_status/1, - read_cluster_status/0, - update_cluster_status/0, - reset_cluster_status/0, - - joined_cluster/2, - notify_joined_cluster/0, - left_cluster/1, - notify_left_cluster/1, - node_up/2, - notify_node_up/0, - - start_link/0, - init/1, - handle_call/3, - handle_cast/2, - handle_info/2, - terminate/2, - code_change/3 - ]). + cluster_status_filename/0, prepare_cluster_status_files/0, + write_cluster_status/1, read_cluster_status/0, + update_cluster_status/0, reset_cluster_status/0]). +-export([notify_joined_cluster/0, notify_left_cluster/1, notify_node_up/0]). + +%% internal +-export([joined_cluster/2, left_cluster/1, node_up/2]). + +%% gen_server callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, + code_change/3]). -define(SERVER, ?MODULE). -define(RABBIT_UP_RPC_TIMEOUT, 2000). @@ -49,6 +39,8 @@ -ifdef(use_specs). +-spec(start_link/0 :: () -> rabbit_types:ok_pid_or_error()). + -spec(running_nodes_filename/0 :: () -> string()). -spec(cluster_status_filename/0 :: () -> string()). -spec(prepare_cluster_status_files/0 :: () -> 'ok'). @@ -57,15 +49,18 @@ -spec(update_cluster_status/0 :: () -> 'ok'). -spec(reset_cluster_status/0 :: () -> 'ok'). --spec(joined_cluster/2 :: (node(), boolean()) -> 'ok'). -spec(notify_joined_cluster/0 :: () -> 'ok'). --spec(left_cluster/1 :: (node()) -> 'ok'). -spec(notify_left_cluster/1 :: (node()) -> 'ok'). --spec(node_up/2 :: (node(), boolean()) -> 'ok'). -spec(notify_node_up/0 :: () -> 'ok'). -endif. +%%---------------------------------------------------------------------------- +%% Start +%%---------------------------------------------------------------------------- + +start_link() -> gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). + %%---------------------------------------------------------------------------- %% Cluster file operations %%---------------------------------------------------------------------------- @@ -131,13 +126,6 @@ write_cluster_status({All, Disc, Running}) -> {FN, {error, E2}} -> throw({error, {could_not_write_file, FN, E2}}) end. -try_read_file(FileName) -> - case rabbit_file:read_term_file(FileName) of - {ok, Term} -> {ok, Term}; - {error, enoent} -> {error, enoent}; - {error, E} -> throw({error, {cannot_read_file, FileName, E}}) - end. - read_cluster_status() -> case {try_read_file(cluster_status_filename()), try_read_file(running_nodes_filename())} of @@ -187,11 +175,7 @@ notify_node_up() -> %% gen_server callbacks %%---------------------------------------------------------------------------- -start_link() -> - gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). - -init([]) -> - {ok, no_state}. +init([]) -> {ok, no_state}. handle_call(_Request, _From, State) -> {noreply, State}. @@ -266,6 +250,13 @@ handle_live_rabbit(Node) -> %% Internal utils %%-------------------------------------------------------------------- +try_read_file(FileName) -> + case rabbit_file:read_term_file(FileName) of + {ok, Term} -> {ok, Term}; + {error, enoent} -> {error, enoent}; + {error, E} -> throw({error, {cannot_read_file, FileName, E}}) + end. + cluster_multicall(Fun, Args) -> Node = node(), Nodes = rabbit_mnesia:cluster_nodes(running) -- [Node], -- cgit v1.2.1 From 005d4304e2d0320995520525ed29ce611741c9c8 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 24 Sep 2012 23:15:39 +0100 Subject: cosmetic --- src/rabbit_node_monitor.erl | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index 38493db1..f906e9f1 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -146,24 +146,15 @@ reset_cluster_status() -> %% Cluster notifications %%---------------------------------------------------------------------------- -joined_cluster(Node, IsDiscNode) -> - gen_server:cast(?SERVER, {rabbit_join, Node, IsDiscNode}). - notify_joined_cluster() -> cluster_multicall(joined_cluster, [node(), rabbit_mnesia:node_type()]), ok. -left_cluster(Node) -> - gen_server:cast(?SERVER, {left_cluster, Node}). - notify_left_cluster(Node) -> left_cluster(Node), cluster_multicall(left_cluster, [Node]), ok. -node_up(Node, IsDiscNode) -> - gen_server:cast(?SERVER, {node_up, Node, IsDiscNode}). - notify_node_up() -> Nodes = cluster_multicall(node_up, [node(), rabbit_mnesia:node_type()]), %% register other active rabbits with this rabbit @@ -171,6 +162,15 @@ notify_node_up() -> N <- Nodes ], ok. +joined_cluster(Node, NodeType) -> + gen_server:cast(?SERVER, {rabbit_join, Node, NodeType}). + +left_cluster(Node) -> + gen_server:cast(?SERVER, {left_cluster, Node}). + +node_up(Node, IsDiscNode) -> + gen_server:cast(?SERVER, {node_up, Node, IsDiscNode}). + %%---------------------------------------------------------------------------- %% gen_server callbacks %%---------------------------------------------------------------------------- -- cgit v1.2.1 From 369b0a82d73f0df50a1b068d34d612ea1bd1b312 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 24 Sep 2012 23:21:28 +0100 Subject: more workingness (and efficiency) --- src/rabbit_node_monitor.erl | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index f906e9f1..e1f7b817 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -158,8 +158,11 @@ notify_left_cluster(Node) -> notify_node_up() -> Nodes = cluster_multicall(node_up, [node(), rabbit_mnesia:node_type()]), %% register other active rabbits with this rabbit - [ node_up(N, lists:member(N, rabbit_mnesia:cluster_nodes(disc))) || - N <- Nodes ], + DiskNodes = rabbit_mnesia:cluster_nodes(disc), + [node_up(N, case lists:member(N, DiskNodes) of + true -> disk; + false -> ram + end) || N <- Nodes], ok. joined_cluster(Node, NodeType) -> @@ -168,8 +171,8 @@ joined_cluster(Node, NodeType) -> left_cluster(Node) -> gen_server:cast(?SERVER, {left_cluster, Node}). -node_up(Node, IsDiscNode) -> - gen_server:cast(?SERVER, {node_up, Node, IsDiscNode}). +node_up(Node, NodeType) -> + gen_server:cast(?SERVER, {node_up, Node, NodeType}). %%---------------------------------------------------------------------------- %% gen_server callbacks -- cgit v1.2.1 From c17f50bf568f4263ee3ba06f61370fca699d8cb0 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 25 Sep 2012 12:12:35 +0100 Subject: it's a disc --- src/rabbit_node_monitor.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index e1f7b817..3bab10af 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -160,7 +160,7 @@ notify_node_up() -> %% register other active rabbits with this rabbit DiskNodes = rabbit_mnesia:cluster_nodes(disc), [node_up(N, case lists:member(N, DiskNodes) of - true -> disk; + true -> disc; false -> ram end) || N <- Nodes], ok. -- cgit v1.2.1 From ca157cb17c62a28fe807e7f50c4cb6e7263a1fa5 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 25 Sep 2012 13:42:53 +0100 Subject: fix a bug --- src/rabbit_node_monitor.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index 64c801f2..88037953 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -160,7 +160,7 @@ reset_cluster_status() -> %%---------------------------------------------------------------------------- joined_cluster(Node, IsDiscNode) -> - gen_server:cast(?SERVER, {rabbit_join, Node, IsDiscNode}). + gen_server:cast(?SERVER, {joined_cluster, Node, IsDiscNode}). notify_joined_cluster() -> cluster_multicall(joined_cluster, [node(), rabbit_mnesia:is_disc_node()]), -- cgit v1.2.1 From a1df1c022addd809a2af508e8460a174d7445a93 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 25 Sep 2012 14:04:34 +0100 Subject: replace cluster_multicall with gen_server:abcast --- src/rabbit_node_monitor.erl | 43 ++++++++++++------------------------------- 1 file changed, 12 insertions(+), 31 deletions(-) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index 096df848..b372d4e7 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -25,9 +25,6 @@ update_cluster_status/0, reset_cluster_status/0]). -export([notify_joined_cluster/0, notify_left_cluster/1, notify_node_up/0]). -%% internal --export([joined_cluster/2, left_cluster/1, node_up/2]). - %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). @@ -147,33 +144,28 @@ reset_cluster_status() -> %%---------------------------------------------------------------------------- notify_joined_cluster() -> - cluster_multicall(joined_cluster, [node(), rabbit_mnesia:node_type()]), + Nodes = rabbit_mnesia:cluster_nodes(running) -- [node()], + gen_server:abcast(Nodes, ?SERVER, + {joined_cluster, node(), rabbit_mnesia:node_type()}), ok. notify_left_cluster(Node) -> - left_cluster(Node), - cluster_multicall(left_cluster, [Node]), + Nodes = rabbit_mnesia:cluster_nodes(running), + gen_server:abcast(Nodes, ?SERVER, {left_cluster, Node}), ok. notify_node_up() -> - Nodes = cluster_multicall(node_up, [node(), rabbit_mnesia:node_type()]), + Nodes = rabbit_mnesia:cluster_nodes(running) -- [node()], + gen_server:abcast(Nodes, ?SERVER, + {node_up, node(), rabbit_mnesia:node_type()}), %% register other active rabbits with this rabbit DiskNodes = rabbit_mnesia:cluster_nodes(disc), - [node_up(N, case lists:member(N, DiskNodes) of - true -> disc; - false -> ram - end) || N <- Nodes], + [gen_server:cast(?SERVER, {node_up, N, case lists:member(N, DiskNodes) of + true -> disc; + false -> ram + end}) || N <- Nodes], ok. -joined_cluster(Node, NodeType) -> - gen_server:cast(?SERVER, {joined_cluster, Node, NodeType}). - -left_cluster(Node) -> - gen_server:cast(?SERVER, {left_cluster, Node}). - -node_up(Node, NodeType) -> - gen_server:cast(?SERVER, {node_up, Node, NodeType}). - %%---------------------------------------------------------------------------- %% gen_server callbacks %%---------------------------------------------------------------------------- @@ -260,17 +252,6 @@ try_read_file(FileName) -> {error, E} -> throw({error, {cannot_read_file, FileName, E}}) end. -cluster_multicall(Fun, Args) -> - Node = node(), - Nodes = rabbit_mnesia:cluster_nodes(running) -- [Node], - %% notify other rabbits of this cluster - case rpc:multicall(Nodes, rabbit_node_monitor, Fun, Args, - ?RABBIT_UP_RPC_TIMEOUT) of - {_, [] } -> ok; - {_, Bad} -> rabbit_log:info("failed to contact nodes ~p~n", [Bad]) - end, - Nodes. - is_already_monitored(Item) -> {monitors, Monitors} = process_info(self(), monitors), lists:any(fun ({_, Item1}) when Item =:= Item1 -> true; -- cgit v1.2.1 From 13f77b4cb9151ba56e07c96174d3aba3eec0b23b Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 25 Sep 2012 14:06:23 +0100 Subject: cosmetic --- src/rabbit_node_monitor.erl | 55 ++++++++++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index b372d4e7..aaee583f 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -23,7 +23,7 @@ cluster_status_filename/0, prepare_cluster_status_files/0, write_cluster_status/1, read_cluster_status/0, update_cluster_status/0, reset_cluster_status/0]). --export([notify_joined_cluster/0, notify_left_cluster/1, notify_node_up/0]). +-export([notify_node_up/0, notify_joined_cluster/0, notify_left_cluster/1]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, @@ -46,9 +46,9 @@ -spec(update_cluster_status/0 :: () -> 'ok'). -spec(reset_cluster_status/0 :: () -> 'ok'). +-spec(notify_node_up/0 :: () -> 'ok'). -spec(notify_joined_cluster/0 :: () -> 'ok'). -spec(notify_left_cluster/1 :: (node()) -> 'ok'). --spec(notify_node_up/0 :: () -> 'ok'). -endif. @@ -62,14 +62,15 @@ start_link() -> gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). %% Cluster file operations %%---------------------------------------------------------------------------- -%% The cluster file information is kept in two files. The "cluster status file" -%% contains all the clustered nodes and the disc nodes. The "running nodes -%% file" contains the currently running nodes or the running nodes at shutdown -%% when the node is down. +%% The cluster file information is kept in two files. The "cluster +%% status file" contains all the clustered nodes and the disc nodes. +%% The "running nodes file" contains the currently running nodes or +%% the running nodes at shutdown when the node is down. %% -%% We strive to keep the files up to date and we rely on this assumption in -%% various situations. Obviously when mnesia is offline the information we have -%% will be outdated, but it can't be otherwise. +%% We strive to keep the files up to date and we rely on this +%% assumption in various situations. Obviously when mnesia is offline +%% the information we have will be outdated, but it cannot be +%% otherwise. running_nodes_filename() -> filename:join(rabbit_mnesia:dir(), "nodes_running_at_shutdown"). @@ -86,8 +87,8 @@ prepare_cluster_status_files() -> {error, enoent} -> [] end, ThisNode = [node()], - %% The running nodes file might contain a set or a list, in case of the - %% legacy file + %% The running nodes file might contain a set or a list, in case + %% of the legacy file RunningNodes2 = lists:usort(ThisNode ++ RunningNodes1), {AllNodes1, WantDiscNode} = case try_read_file(cluster_status_filename()) of @@ -143,17 +144,6 @@ reset_cluster_status() -> %% Cluster notifications %%---------------------------------------------------------------------------- -notify_joined_cluster() -> - Nodes = rabbit_mnesia:cluster_nodes(running) -- [node()], - gen_server:abcast(Nodes, ?SERVER, - {joined_cluster, node(), rabbit_mnesia:node_type()}), - ok. - -notify_left_cluster(Node) -> - Nodes = rabbit_mnesia:cluster_nodes(running), - gen_server:abcast(Nodes, ?SERVER, {left_cluster, Node}), - ok. - notify_node_up() -> Nodes = rabbit_mnesia:cluster_nodes(running) -- [node()], gen_server:abcast(Nodes, ?SERVER, @@ -166,6 +156,17 @@ notify_node_up() -> end}) || N <- Nodes], ok. +notify_joined_cluster() -> + Nodes = rabbit_mnesia:cluster_nodes(running) -- [node()], + gen_server:abcast(Nodes, ?SERVER, + {joined_cluster, node(), rabbit_mnesia:node_type()}), + ok. + +notify_left_cluster(Node) -> + Nodes = rabbit_mnesia:cluster_nodes(running), + gen_server:abcast(Nodes, ?SERVER, {left_cluster, Node}), + ok. + %%---------------------------------------------------------------------------- %% gen_server callbacks %%---------------------------------------------------------------------------- @@ -175,8 +176,9 @@ init([]) -> {ok, no_state}. handle_call(_Request, _From, State) -> {noreply, State}. -%% Note: when updating the status file, we can't simply write the mnesia -%% information since the message can (and will) overtake the mnesia propagation. +%% Note: when updating the status file, we can't simply write the +%% mnesia information since the message can (and will) overtake the +%% mnesia propagation. handle_cast({node_up, Node, NodeType}, State) -> case is_already_monitored({rabbit, Node}) of true -> {noreply, State}; @@ -259,8 +261,9 @@ is_already_monitored(Item) -> end, Monitors). legacy_cluster_nodes(Nodes) -> - %% We get all the info that we can, including the nodes from mnesia, which - %% will be there if the node is a disc node (empty list otherwise) + %% We get all the info that we can, including the nodes from + %% mnesia, which will be there if the node is a disc node (empty + %% list otherwise) lists:usort(Nodes ++ mnesia:system_info(db_nodes)). legacy_should_be_disc_node(DiscNodes) -> -- cgit v1.2.1 From a1ff10f18797242049fdeb8222c0ef6e9eecbbe9 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 25 Sep 2012 14:18:13 +0100 Subject: generalise pmon --- src/pmon.erl | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/pmon.erl b/src/pmon.erl index 45786577..1aeebb72 100644 --- a/src/pmon.erl +++ b/src/pmon.erl @@ -27,37 +27,39 @@ -opaque(?MODULE() :: dict()). +-type(item() :: pid() | {atom(), node()}). + -spec(new/0 :: () -> ?MODULE()). --spec(monitor/2 :: (pid(), ?MODULE()) -> ?MODULE()). --spec(monitor_all/2 :: ([pid()], ?MODULE()) -> ?MODULE()). --spec(demonitor/2 :: (pid(), ?MODULE()) -> ?MODULE()). --spec(is_monitored/2 :: (pid(), ?MODULE()) -> boolean()). --spec(erase/2 :: (pid(), ?MODULE()) -> ?MODULE()). --spec(monitored/1 :: (?MODULE()) -> [pid()]). +-spec(monitor/2 :: (item(), ?MODULE()) -> ?MODULE()). +-spec(monitor_all/2 :: ([item()], ?MODULE()) -> ?MODULE()). +-spec(demonitor/2 :: (item(), ?MODULE()) -> ?MODULE()). +-spec(is_monitored/2 :: (item(), ?MODULE()) -> boolean()). +-spec(erase/2 :: (item(), ?MODULE()) -> ?MODULE()). +-spec(monitored/1 :: (?MODULE()) -> [item()]). -spec(is_empty/1 :: (?MODULE()) -> boolean()). -endif. new() -> dict:new(). -monitor(Pid, M) -> - case dict:is_key(Pid, M) of +monitor(Item, M) -> + case dict:is_key(Item, M) of true -> M; - false -> dict:store(Pid, erlang:monitor(process, Pid), M) + false -> dict:store(Item, erlang:monitor(process, Item), M) end. -monitor_all(Pids, M) -> lists:foldl(fun monitor/2, M, Pids). +monitor_all(Items, M) -> lists:foldl(fun monitor/2, M, Items). -demonitor(Pid, M) -> - case dict:find(Pid, M) of +demonitor(Item, M) -> + case dict:find(Item, M) of {ok, MRef} -> erlang:demonitor(MRef), - dict:erase(Pid, M); + dict:erase(Item, M); error -> M end. -is_monitored(Pid, M) -> dict:is_key(Pid, M). +is_monitored(Item, M) -> dict:is_key(Item, M). -erase(Pid, M) -> dict:erase(Pid, M). +erase(Item, M) -> dict:erase(Item, M). monitored(M) -> dict:fetch_keys(M). -- cgit v1.2.1 From 8423dd2fcde33deabe887988f3d70c68074ed279 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 25 Sep 2012 14:19:55 +0100 Subject: track monitors explicitly --- src/rabbit_node_monitor.erl | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index aaee583f..026aa362 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -171,7 +171,7 @@ notify_left_cluster(Node) -> %% gen_server callbacks %%---------------------------------------------------------------------------- -init([]) -> {ok, no_state}. +init([]) -> {ok, pmon:new()}. handle_call(_Request, _From, State) -> {noreply, State}. @@ -179,9 +179,9 @@ handle_call(_Request, _From, State) -> %% Note: when updating the status file, we can't simply write the %% mnesia information since the message can (and will) overtake the %% mnesia propagation. -handle_cast({node_up, Node, NodeType}, State) -> - case is_already_monitored({rabbit, Node}) of - true -> {noreply, State}; +handle_cast({node_up, Node, NodeType}, Monitors) -> + case pmon:is_monitored({rabbit, Node}, Monitors) of + true -> {noreply, Monitors}; false -> rabbit_log:info("rabbit on node ~p up~n", [Node]), {AllNodes, DiscNodes, RunningNodes} = read_cluster_status(), write_cluster_status({add_node(Node, AllNodes), @@ -190,9 +190,8 @@ handle_cast({node_up, Node, NodeType}, State) -> ram -> DiscNodes end, add_node(Node, RunningNodes)}), - erlang:monitor(process, {rabbit, Node}), ok = handle_live_rabbit(Node), - {noreply, State} + {noreply, pmon:monitor({rabbit, Node}, Monitors)} end; handle_cast({joined_cluster, Node, NodeType}, State) -> {AllNodes, DiscNodes, RunningNodes} = read_cluster_status(), @@ -211,12 +210,12 @@ handle_cast({left_cluster, Node}, State) -> handle_cast(_Msg, State) -> {noreply, State}. -handle_info({'DOWN', _MRef, process, {rabbit, Node}, _Reason}, State) -> +handle_info({'DOWN', _MRef, process, {rabbit, Node}, _Reason}, Monitors) -> rabbit_log:info("rabbit on node ~p down~n", [Node]), {AllNodes, DiscNodes, RunningNodes} = read_cluster_status(), write_cluster_status({AllNodes, DiscNodes, del_node(Node, RunningNodes)}), ok = handle_dead_rabbit(Node), - {noreply, State}; + {noreply, pmon:erase({rabbit, Node}, Monitors)}; handle_info(_Info, State) -> {noreply, State}. @@ -254,12 +253,6 @@ try_read_file(FileName) -> {error, E} -> throw({error, {cannot_read_file, FileName, E}}) end. -is_already_monitored(Item) -> - {monitors, Monitors} = process_info(self(), monitors), - lists:any(fun ({_, Item1}) when Item =:= Item1 -> true; - (_) -> false - end, Monitors). - legacy_cluster_nodes(Nodes) -> %% We get all the info that we can, including the nodes from %% mnesia, which will be there if the node is a disc node (empty -- cgit v1.2.1 From f5e77c697a33161543c7505bc1a62ba60e4e4b0c Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 25 Sep 2012 15:37:21 +0100 Subject: remove unnecessary calls to set_synchronised/2 in 'fetch' --- src/rabbit_mirror_queue_slave.erl | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 1f6567e0..b28ff6e2 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -806,21 +806,17 @@ process_instruction({fetch, AckRequired, MsgId, Remaining}, State = #state { backing_queue = BQ, backing_queue_state = BQS }) -> QLen = BQ:len(BQS), - {State1, Delta} = - case QLen - 1 of - Remaining -> - {{#basic_message{id = MsgId}, _IsDelivered, - AckTag, Remaining}, BQS1} = BQ:fetch(AckRequired, BQS), - {maybe_store_ack(AckRequired, MsgId, AckTag, - State #state { backing_queue_state = BQS1 }), - 0}; + {ok, case QLen - 1 of + Remaining -> + {{#basic_message{id = MsgId}, _IsDelivered, + AckTag, Remaining}, BQS1} = BQ:fetch(AckRequired, BQS), + maybe_store_ack(AckRequired, MsgId, AckTag, + State #state { backing_queue_state = BQS1 }); + _ when QLen =< Remaining andalso AckRequired -> + State; _ when QLen =< Remaining -> - {State, case AckRequired of - true -> 0; - false -> -1 - end} - end, - {ok, set_synchronised(Delta, State1)}; + set_synchronised(-1, State) + end}; process_instruction({ack, MsgIds}, State = #state { backing_queue = BQ, backing_queue_state = BQS, -- cgit v1.2.1 From f15ccf9f4289b61a48020241764894188d7a4c8a Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 25 Sep 2012 16:37:36 +0100 Subject: take a stab at making set_synchronised less obscure --- src/rabbit_mirror_queue_slave.erl | 67 ++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 37 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index b28ff6e2..472eff47 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -853,10 +853,15 @@ process_instruction({sender_death, ChPid}, known_senders = pmon:demonitor(ChPid, KS) } end}; process_instruction({depth, Depth}, - State = #state { backing_queue = BQ, + State = #state { depth_delta = D, + backing_queue = BQ, backing_queue_state = BQS }) -> - {ok, set_synchronised( - 0, true, State #state { depth_delta = Depth - BQ:depth(BQS) })}; + D1 = case D of + undefinded -> 1; %% anything but 0 will do here + _ -> D + end, + {ok, set_synchronised(Depth - BQ:depth(BQS) - D1, + State #state { depth_delta = D1 })}; process_instruction({delete_and_terminate, Reason}, State = #state { backing_queue = BQ, backing_queue_state = BQS }) -> @@ -882,38 +887,26 @@ maybe_store_ack(true, MsgId, AckTag, State = #state { msg_id_ack = MA, State #state { msg_id_ack = dict:store(MsgId, {Num, AckTag}, MA), ack_num = Num + 1 }. -set_synchronised(Delta, State) -> - set_synchronised(Delta, false, State). - -set_synchronised(_Delta, _AddAnyway, - State = #state { depth_delta = undefined }) -> +set_synchronised(_Delta, State = #state { depth_delta = undefined }) -> State; -set_synchronised(Delta, AddAnyway, - State = #state { depth_delta = DepthDelta, - q = #amqqueue { name = QName }}) -> - DepthDelta1 = DepthDelta + Delta, - %% We intentionally leave out the head where a slave becomes - %% unsynchronised: we assert that can never happen. - %% The `AddAnyway' param is there since in the `depth' instruction we - %% receive the master depth for the first time, and we want to set the sync - %% state anyway if we are synced. - case DepthDelta1 =:= 0 of - true when not (DepthDelta =:= 0) orelse AddAnyway -> - Self = self(), - rabbit_misc:execute_mnesia_transaction( - fun () -> - case mnesia:read({rabbit_queue, QName}) of - [] -> - ok; - [Q1 = #amqqueue{sync_slave_pids = SSPids}] -> - %% We might be there already, in the `AddAnyway' - %% case - SSPids1 = SSPids -- [Self], - rabbit_mirror_queue_misc:store_updated_slaves( - Q1#amqqueue{sync_slave_pids = [Self | SSPids1]}) - end - end); - _ when DepthDelta1 >= 0 -> - ok - end, - State #state { depth_delta = DepthDelta1 }. +set_synchronised(Delta, State = #state { depth_delta = D }) -> + case D + Delta of + 0 when D == 0 -> State; + 0 when D =/= 0 -> ok = record_synchronised(State#state.q), + State #state { depth_delta = 0 }; + N when D =/= 0 andalso N > 0 -> State #state { depth_delta = N } + end. + +record_synchronised(#amqqueue { name = QName }) -> + Self = self(), + rabbit_misc:execute_mnesia_transaction( + fun () -> + case mnesia:read({rabbit_queue, QName}) of + [] -> + ok; + [Q = #amqqueue { sync_slave_pids = SSPids }] -> + rabbit_mirror_queue_misc:store_updated_slaves( + Q #amqqueue { sync_slave_pids = [Self | SSPids] }), + ok + end + end). -- cgit v1.2.1 From 813d9ab83e883034e4270c1b54606895866166bd Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 25 Sep 2012 17:00:03 +0100 Subject: oops --- src/rabbit_mirror_queue_slave.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 472eff47..f30264e1 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -857,8 +857,8 @@ process_instruction({depth, Depth}, backing_queue = BQ, backing_queue_state = BQS }) -> D1 = case D of - undefinded -> 1; %% anything but 0 will do here - _ -> D + undefined -> 1; %% anything but 0 will do here + _ -> D end, {ok, set_synchronised(Depth - BQ:depth(BQS) - D1, State #state { depth_delta = D1 })}; -- cgit v1.2.1 From 6cdf3e7e0a198f969808268f396ebb0a98440068 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 25 Sep 2012 18:07:03 +0100 Subject: Refactor which I find clearer. Yes "DeltaChange". But it's a delta of a delta, and "deltadelta" sounds silly. --- src/rabbit_mirror_queue_slave.erl | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index f30264e1..4d53bd1b 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -887,14 +887,16 @@ maybe_store_ack(true, MsgId, AckTag, State = #state { msg_id_ack = MA, State #state { msg_id_ack = dict:store(MsgId, {Num, AckTag}, MA), ack_num = Num + 1 }. -set_synchronised(_Delta, State = #state { depth_delta = undefined }) -> +set_synchronised(_DeltaChange, State = #state { depth_delta = undefined }) -> State; -set_synchronised(Delta, State = #state { depth_delta = D }) -> - case D + Delta of - 0 when D == 0 -> State; - 0 when D =/= 0 -> ok = record_synchronised(State#state.q), - State #state { depth_delta = 0 }; - N when D =/= 0 andalso N > 0 -> State #state { depth_delta = N } +set_synchronised(DeltaChange, State = #state { depth_delta = Delta }) -> + %% We intentionally leave out the head where a slave becomes + %% unsynchronised: we assert that can never happen. + case {Delta, Delta + DeltaChange} of + {0, 0} -> State; + {_, 0} -> ok = record_synchronised(State#state.q), + State #state { depth_delta = 0 }; + {_, N} when N > 0 -> State #state { depth_delta = N } end. record_synchronised(#amqqueue { name = QName }) -> -- cgit v1.2.1 From 42e2c535eaaa385c3175d7b6aff37e2b83f2c721 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 25 Sep 2012 18:27:09 +0100 Subject: Rename set_synchronised to update_delta. And remove that nasty little arithmetic-based hack. We should be explicit about the fact that we treat depth messages from the master differently when we don't have a delta. --- src/rabbit_mirror_queue_slave.erl | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 4d53bd1b..7c3aef7a 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -800,7 +800,7 @@ process_instruction({drop, Length, Dropped, AckRequired}, end, State, lists:duplicate(ToDrop, const)), {ok, case AckRequired of true -> State1; - false -> set_synchronised(ToDrop - Dropped, State1) + false -> update_delta(ToDrop - Dropped, State1) end}; process_instruction({fetch, AckRequired, MsgId, Remaining}, State = #state { backing_queue = BQ, @@ -815,7 +815,7 @@ process_instruction({fetch, AckRequired, MsgId, Remaining}, _ when QLen =< Remaining andalso AckRequired -> State; _ when QLen =< Remaining -> - set_synchronised(-1, State) + update_delta(-1, State) end}; process_instruction({ack, MsgIds}, State = #state { backing_queue = BQ, @@ -824,9 +824,9 @@ process_instruction({ack, MsgIds}, {AckTags, MA1} = msg_ids_to_acktags(MsgIds, MA), {MsgIds1, BQS1} = BQ:ack(AckTags, BQS), [] = MsgIds1 -- MsgIds, %% ASSERTION - {ok, set_synchronised(length(MsgIds1) - length(MsgIds), - State #state { msg_id_ack = MA1, - backing_queue_state = BQS1 })}; + {ok, update_delta(length(MsgIds1) - length(MsgIds), + State #state { msg_id_ack = MA1, + backing_queue_state = BQS1 })}; process_instruction({requeue, MsgIds}, State = #state { backing_queue = BQ, backing_queue_state = BQS, @@ -853,15 +853,10 @@ process_instruction({sender_death, ChPid}, known_senders = pmon:demonitor(ChPid, KS) } end}; process_instruction({depth, Depth}, - State = #state { depth_delta = D, - backing_queue = BQ, + State = #state { backing_queue = BQ, backing_queue_state = BQS }) -> - D1 = case D of - undefined -> 1; %% anything but 0 will do here - _ -> D - end, - {ok, set_synchronised(Depth - BQ:depth(BQS) - D1, - State #state { depth_delta = D1 })}; + {ok, update_delta_from_master(Depth - BQ:depth(BQS), State)}; + process_instruction({delete_and_terminate, Reason}, State = #state { backing_queue = BQ, backing_queue_state = BQS }) -> @@ -887,9 +882,18 @@ maybe_store_ack(true, MsgId, AckTag, State = #state { msg_id_ack = MA, State #state { msg_id_ack = dict:store(MsgId, {Num, AckTag}, MA), ack_num = Num + 1 }. -set_synchronised(_DeltaChange, State = #state { depth_delta = undefined }) -> +update_delta_from_master(NewDelta, State = #state{depth_delta = undefined}) -> + case NewDelta of + 0 -> ok = record_synchronised(State#state.q), + State #state { depth_delta = 0 }; + N when N > 0 -> State #state { depth_delta = N } + end; +update_delta_from_master(DeltaChange, State) -> + update_delta(DeltaChange, State). + +update_delta(_DeltaChange, State = #state { depth_delta = undefined }) -> State; -set_synchronised(DeltaChange, State = #state { depth_delta = Delta }) -> +update_delta(DeltaChange, State = #state { depth_delta = Delta }) -> %% We intentionally leave out the head where a slave becomes %% unsynchronised: we assert that can never happen. case {Delta, Delta + DeltaChange} of -- cgit v1.2.1 From f9fb42cee26f7f349d89342acc69795c5f9538df Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 25 Sep 2012 18:47:13 +0100 Subject: improve correctness --- src/rabbit_mirror_queue_slave.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 7c3aef7a..e52c570e 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -888,8 +888,8 @@ update_delta_from_master(NewDelta, State = #state{depth_delta = undefined}) -> State #state { depth_delta = 0 }; N when N > 0 -> State #state { depth_delta = N } end; -update_delta_from_master(DeltaChange, State) -> - update_delta(DeltaChange, State). +update_delta_from_master(NewDelta, State = #state { depth_delta = Delta }) -> + update_delta(NewDelta - Delta, State). update_delta(_DeltaChange, State = #state { depth_delta = undefined }) -> State; -- cgit v1.2.1 From cbb21d811a3b503a32dbaa35fea69cbf7d33d57c Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 25 Sep 2012 19:08:27 +0100 Subject: put the assertion back. This time more explictly. --- src/rabbit_mirror_queue_slave.erl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index e52c570e..dcc7e5f6 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -894,10 +894,9 @@ update_delta_from_master(NewDelta, State = #state { depth_delta = Delta }) -> update_delta(_DeltaChange, State = #state { depth_delta = undefined }) -> State; update_delta(DeltaChange, State = #state { depth_delta = Delta }) -> - %% We intentionally leave out the head where a slave becomes - %% unsynchronised: we assert that can never happen. case {Delta, Delta + DeltaChange} of - {0, 0} -> State; + {0, N} -> 0 = N, %% assertion: we cannot become unsync'ed + State; {_, 0} -> ok = record_synchronised(State#state.q), State #state { depth_delta = 0 }; {_, N} when N > 0 -> State #state { depth_delta = N } -- cgit v1.2.1 From e160a9edc08f109b32426145c6ad8ced10b9f911 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 25 Sep 2012 19:10:30 +0100 Subject: cosmetic --- src/rabbit_mirror_queue_slave.erl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index dcc7e5f6..92a8ed9f 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -855,7 +855,7 @@ process_instruction({sender_death, ChPid}, process_instruction({depth, Depth}, State = #state { backing_queue = BQ, backing_queue_state = BQS }) -> - {ok, update_delta_from_master(Depth - BQ:depth(BQS), State)}; + {ok, set_delta(Depth - BQ:depth(BQS), State)}; process_instruction({delete_and_terminate, Reason}, State = #state { backing_queue = BQ, @@ -882,24 +882,24 @@ maybe_store_ack(true, MsgId, AckTag, State = #state { msg_id_ack = MA, State #state { msg_id_ack = dict:store(MsgId, {Num, AckTag}, MA), ack_num = Num + 1 }. -update_delta_from_master(NewDelta, State = #state{depth_delta = undefined}) -> +set_delta(NewDelta, State = #state { depth_delta = undefined }) -> case NewDelta of 0 -> ok = record_synchronised(State#state.q), State #state { depth_delta = 0 }; - N when N > 0 -> State #state { depth_delta = N } + D when D > 0 -> State #state { depth_delta = D } end; -update_delta_from_master(NewDelta, State = #state { depth_delta = Delta }) -> +set_delta(NewDelta, State = #state { depth_delta = Delta }) -> update_delta(NewDelta - Delta, State). update_delta(_DeltaChange, State = #state { depth_delta = undefined }) -> State; update_delta(DeltaChange, State = #state { depth_delta = Delta }) -> case {Delta, Delta + DeltaChange} of - {0, N} -> 0 = N, %% assertion: we cannot become unsync'ed + {0, D} -> 0 = D, %% assertion: we cannot become unsync'ed State; {_, 0} -> ok = record_synchronised(State#state.q), State #state { depth_delta = 0 }; - {_, N} when N > 0 -> State #state { depth_delta = N } + {_, D} when D > 0 -> State #state { depth_delta = D } end. record_synchronised(#amqqueue { name = QName }) -> -- cgit v1.2.1 From 5105a8ae3f3d2901f2161cd4cf3229e297990cfa Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 25 Sep 2012 22:00:52 +0100 Subject: Is this better? I'm not sure. --- src/rabbit_mirror_queue_slave.erl | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 92a8ed9f..5d20aae4 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -894,12 +894,11 @@ set_delta(NewDelta, State = #state { depth_delta = Delta }) -> update_delta(_DeltaChange, State = #state { depth_delta = undefined }) -> State; update_delta(DeltaChange, State = #state { depth_delta = Delta }) -> - case {Delta, Delta + DeltaChange} of - {0, D} -> 0 = D, %% assertion: we cannot become unsync'ed - State; - {_, 0} -> ok = record_synchronised(State#state.q), - State #state { depth_delta = 0 }; - {_, D} when D > 0 -> State #state { depth_delta = D } + NewDelta = Delta + DeltaChange, + case Delta of + 0 -> 0 = NewDelta, %% assertion: we cannot become unsync'ed + State; + _ -> set_delta(NewDelta, State #state { depth_delta = undefined }) end. record_synchronised(#amqqueue { name = QName }) -> -- cgit v1.2.1 From b151d6930710aea7ae7220678208cea69c968a8c Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 25 Sep 2012 22:14:54 +0100 Subject: neater --- src/rabbit_mirror_queue_slave.erl | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 5d20aae4..07a1d9e5 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -882,24 +882,22 @@ maybe_store_ack(true, MsgId, AckTag, State = #state { msg_id_ack = MA, State #state { msg_id_ack = dict:store(MsgId, {Num, AckTag}, MA), ack_num = Num + 1 }. +set_delta(0, State = #state { depth_delta = undefined }) -> + ok = record_synchronised(State#state.q), + State #state { depth_delta = 0 }; set_delta(NewDelta, State = #state { depth_delta = undefined }) -> - case NewDelta of - 0 -> ok = record_synchronised(State#state.q), - State #state { depth_delta = 0 }; - D when D > 0 -> State #state { depth_delta = D } - end; -set_delta(NewDelta, State = #state { depth_delta = Delta }) -> + true = NewDelta > 0, %% assertion + State #state { depth_delta = NewDelta }; +set_delta(NewDelta, State = #state { depth_delta = Delta }) -> update_delta(NewDelta - Delta, State). update_delta(_DeltaChange, State = #state { depth_delta = undefined }) -> State; -update_delta(DeltaChange, State = #state { depth_delta = Delta }) -> - NewDelta = Delta + DeltaChange, - case Delta of - 0 -> 0 = NewDelta, %% assertion: we cannot become unsync'ed - State; - _ -> set_delta(NewDelta, State #state { depth_delta = undefined }) - end. +update_delta( DeltaChange, State = #state { depth_delta = 0 }) -> + 0 = DeltaChange, %% assertion: we cannot become unsync'ed + State; +update_delta( DeltaChange, State = #state { depth_delta = Delta }) -> + set_delta(Delta + DeltaChange, State #state { depth_delta = undefined }). record_synchronised(#amqqueue { name = QName }) -> Self = self(), -- cgit v1.2.1 From dd8b5390d61f07a9b8a2da89533831a881f3e1f5 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 25 Sep 2012 22:19:52 +0100 Subject: another assertion --- src/rabbit_mirror_queue_slave.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 07a1d9e5..039b2749 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -897,6 +897,7 @@ update_delta( DeltaChange, State = #state { depth_delta = 0 }) -> 0 = DeltaChange, %% assertion: we cannot become unsync'ed State; update_delta( DeltaChange, State = #state { depth_delta = Delta }) -> + true = DeltaChange =< 0, %% assertion: we cannot become 'less' sync'ed set_delta(Delta + DeltaChange, State #state { depth_delta = undefined }). record_synchronised(#amqqueue { name = QName }) -> -- cgit v1.2.1 From 61612719dc17dcd5e495132c543cc15a1dd06904 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 26 Sep 2012 10:50:29 +0100 Subject: Bring default back to life --- README | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README b/README index 67e3a66a..90e99e62 100644 --- a/README +++ b/README @@ -1 +1 @@ -Please see http://www.rabbitmq.com/build-server.html for build instructions. +Please see http://www.rabbitmq.com/build-server.html for build instructions. \ No newline at end of file -- cgit v1.2.1 From 7df087a1d09c71ae8c2e0292a13c63cb8fb182a1 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 26 Sep 2012 11:12:57 +0100 Subject: Fake rebase: 8ebdcd9a5d2e (which bug25182 was branched from) got junked. "rebase" from 5859a8e6d682. --- src/rabbit_mirror_queue_master.erl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index 10f60c53..c11a8ff7 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -170,7 +170,10 @@ publish_delivered(AckRequired, Msg = #basic_message { id = MsgId }, MsgProps, backing_queue_state = BQS, ack_msg_id = AM }) -> false = dict:is_key(MsgId, SS), %% ASSERTION - ok = gm:broadcast( + %% Must use confirmed_broadcast here in order to guarantee that + %% all slaves are forced to interpret this publish_delivered at + %% the same point, especially if we die and a slave is promoted. + ok = gm:confirmed_broadcast( GM, {publish, {true, AckRequired}, ChPid, MsgProps, Msg}), {AckTag, BQS1} = BQ:publish_delivered(AckRequired, Msg, MsgProps, ChPid, BQS), -- cgit v1.2.1 From 750dcde2aab7e79a01b1da8959b0038f78354374 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 26 Sep 2012 12:05:57 +0100 Subject: simplify forget_cluster_node/2 --- src/rabbit_mnesia.erl | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index b3546483..ae36febb 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -277,20 +277,16 @@ forget_cluster_node(Node, RemoveWhenOffline) -> true -> ok; false -> e(not_a_cluster_node) end, - case mnesia:system_info(is_running) of - no when RemoveWhenOffline -> - remove_node_offline_node(Node); - yes when RemoveWhenOffline -> - e(online_node_offline_flag); - no -> - e(offline_node_no_offline_flag); - yes -> - rabbit_misc:local_info_msg("Removing node ~p from cluster~n", - [Node]), - case remove_node_if_mnesia_running(Node) of - ok -> ok; - {error, _} = Err -> throw(Err) - end + case {RemoveWhenOffline, mnesia:system_info(is_running)} of + {true, no} -> remove_node_offline_node(Node); + {true, yes} -> e(online_node_offline_flag); + {false, no} -> e(offline_node_no_offline_flag); + {false, yes} -> rabbit_misc:local_info_msg( + "Removing node ~p from cluster~n", [Node]), + case remove_node_if_mnesia_running(Node) of + ok -> ok; + {error, _} = Err -> throw(Err) + end end. remove_node_offline_node(Node) -> -- cgit v1.2.1 From 78f055926cc685cd2e94b8b899d0eb6d18203671 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 26 Sep 2012 16:33:42 +0100 Subject: Mark enqueued-on-promotion messages as redelivered --- src/rabbit_amqqueue_process.erl | 20 ++++++++++++-------- src/rabbit_backing_queue.erl | 5 +++-- src/rabbit_mirror_queue_master.erl | 10 +++++----- src/rabbit_mirror_queue_slave.erl | 10 +++++++--- src/rabbit_variable_queue.erl | 26 +++++++++++++------------- 5 files changed, 40 insertions(+), 31 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 0e3f0bac..d0810564 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -78,7 +78,7 @@ -spec(info_keys/0 :: () -> rabbit_types:info_keys()). -spec(init_with_backing_queue_state/8 :: (rabbit_types:amqqueue(), atom(), tuple(), any(), [any()], - [rabbit_types:delivery()], pmon:pmon(), dict()) -> #q{}). + [{rabbit_types:delivery(), boolean()}], pmon:pmon(), dict()) -> #q{}). -endif. @@ -170,7 +170,9 @@ init_with_backing_queue_state(Q = #amqqueue{exclusive_owner = Owner}, BQ, BQS, rabbit_event:init_stats_timer( State, #q.stats_timer))), lists:foldl( - fun (Delivery, StateN) -> deliver_or_enqueue(Delivery, StateN) end, + fun ({Delivery, Redelivered}, StateN) -> + deliver_or_enqueue(Delivery, Redelivered, StateN) + end, State1, Deliveries). terminate(shutdown = R, State = #q{backing_queue = BQ}) -> @@ -535,6 +537,7 @@ run_message_queue(State) -> State2. attempt_delivery(#delivery{sender = SenderPid, message = Message}, Confirm, + Redelivered, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> case BQ:is_duplicate(Message, BQS) of {false, BQS1} -> @@ -544,7 +547,7 @@ attempt_delivery(#delivery{sender = SenderPid, message = Message}, Confirm, {AckTag, BQS3} = BQ:publish_delivered( AckRequired, Message, Props, SenderPid, BQS2), - {{Message, false, AckTag}, true, + {{Message, Redelivered, AckTag}, true, State1#q{backing_queue_state = BQS3}} end, false, State#q{backing_queue_state = BQS1}); {Duplicate, BQS1} -> @@ -560,9 +563,10 @@ attempt_delivery(#delivery{sender = SenderPid, message = Message}, Confirm, end. deliver_or_enqueue(Delivery = #delivery{message = Message, - sender = SenderPid}, State) -> + sender = SenderPid}, Redelivered, + State) -> Confirm = should_confirm_message(Delivery, State), - case attempt_delivery(Delivery, Confirm, State) of + case attempt_delivery(Delivery, Confirm, Redelivered, State) of {true, State1} -> maybe_record_confirm_message(Confirm, State1); %% the next one is an optimisations @@ -573,7 +577,7 @@ deliver_or_enqueue(Delivery = #delivery{message = Message, State2 = #q{backing_queue = BQ, backing_queue_state = BQS} = maybe_record_confirm_message(Confirm, State1), Props = message_properties(Confirm, State2), - BQS1 = BQ:publish(Message, Props, SenderPid, BQS), + BQS1 = BQ:publish(Message, Props, SenderPid, Redelivered, BQS), ensure_ttl_timer(Props#message_properties.expiry, State2#q{backing_queue_state = BQS1}) end. @@ -1032,7 +1036,7 @@ handle_call(consumers, _From, State) -> handle_call({deliver, Delivery}, From, State) -> %% Synchronous, "mandatory" deliver mode. gen_server2:reply(From, ok), - noreply(deliver_or_enqueue(Delivery, State)); + noreply(deliver_or_enqueue(Delivery, false, State)); handle_call({notify_down, ChPid}, From, State) -> %% we want to do this synchronously, so that auto_deleted queues @@ -1184,7 +1188,7 @@ handle_cast({deliver, Delivery = #delivery{sender = Sender}, Flow}, noflow -> Senders end, State1 = State#q{senders = Senders1}, - noreply(deliver_or_enqueue(Delivery, State1)); + noreply(deliver_or_enqueue(Delivery, false, State1)); handle_cast({ack, AckTags, ChPid}, State) -> noreply(subtract_acks( diff --git a/src/rabbit_backing_queue.erl b/src/rabbit_backing_queue.erl index d69a6c3b..9510ae23 100644 --- a/src/rabbit_backing_queue.erl +++ b/src/rabbit_backing_queue.erl @@ -77,7 +77,8 @@ %% Publish a message. -callback publish(rabbit_types:basic_message(), - rabbit_types:message_properties(), pid(), state()) -> + rabbit_types:message_properties(), pid(), boolean(), + state()) -> state(). %% Called for messages which have already been passed straight @@ -212,7 +213,7 @@ behaviour_info(callbacks) -> [{start, 1}, {stop, 0}, {init, 3}, {terminate, 2}, - {delete_and_terminate, 2}, {purge, 1}, {publish, 4}, + {delete_and_terminate, 2}, {purge, 1}, {publish, 5}, {publish_delivered, 5}, {drain_confirmed, 1}, {dropwhile, 3}, {fetch, 2}, {ack, 2}, {fold, 3}, {requeue, 2}, {len, 1}, {is_empty, 1}, {depth, 1}, {set_ram_duration_target, 2}, diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index c11a8ff7..9e98c726 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -17,7 +17,7 @@ -module(rabbit_mirror_queue_master). -export([init/3, terminate/2, delete_and_terminate/2, - purge/1, publish/4, publish_delivered/5, fetch/2, ack/2, + purge/1, publish/5, publish_delivered/5, fetch/2, ack/2, requeue/2, len/1, is_empty/1, depth/1, drain_confirmed/1, dropwhile/3, set_ram_duration_target/2, ram_duration/1, needs_timeout/1, timeout/1, handle_pre_hibernate/1, @@ -153,14 +153,14 @@ purge(State = #state { gm = GM, {Count, State #state { backing_queue_state = BQS1, set_delivered = 0 }}. -publish(Msg = #basic_message { id = MsgId }, MsgProps, ChPid, +publish(Msg = #basic_message { id = MsgId }, MsgProps, ChPid, Redelivered, State = #state { gm = GM, seen_status = SS, backing_queue = BQ, backing_queue_state = BQS }) -> false = dict:is_key(MsgId, SS), %% ASSERTION - ok = gm:broadcast(GM, {publish, false, ChPid, MsgProps, Msg}), - BQS1 = BQ:publish(Msg, MsgProps, ChPid, BQS), + ok = gm:broadcast(GM, {publish, false, ChPid, MsgProps, Msg, Redelivered}), + BQS1 = BQ:publish(Msg, MsgProps, ChPid, Redelivered, BQS), ensure_monitoring(ChPid, State #state { backing_queue_state = BQS1 }). publish_delivered(AckRequired, Msg = #basic_message { id = MsgId }, MsgProps, @@ -174,7 +174,7 @@ publish_delivered(AckRequired, Msg = #basic_message { id = MsgId }, MsgProps, %% all slaves are forced to interpret this publish_delivered at %% the same point, especially if we die and a slave is promoted. ok = gm:confirmed_broadcast( - GM, {publish, {true, AckRequired}, ChPid, MsgProps, Msg}), + GM, {publish, {true, AckRequired}, ChPid, MsgProps, Msg, false}), {AckTag, BQS1} = BQ:publish_delivered(AckRequired, Msg, MsgProps, ChPid, BQS), AM1 = maybe_store_acktag(AckTag, MsgId, AM), diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 039b2749..b11cd199 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -536,8 +536,10 @@ promote_me(From, #state { q = Q = #amqqueue { name = QName }, end, gb_trees:empty(), MSList), NumAckTags = [NumAckTag || {_MsgId, NumAckTag} <- dict:to_list(MA)], AckTags = [AckTag || {_Num, AckTag} <- lists:sort(NumAckTags)], - Deliveries = [Delivery || {_ChPid, {PubQ, _PendCh}} <- dict:to_list(SQ), + Deliveries = [{Delivery, true} || + {_ChPid, {PubQ, _PendCh}} <- dict:to_list(SQ), Delivery <- queue:to_list(PubQ)], + rabbit_log:warning("Promotion deliveries: ~p~n", [Deliveries]), QueueState = rabbit_amqqueue_process:init_with_backing_queue_state( Q1, rabbit_mirror_queue_master, MasterState, RateTRef, AckTags, Deliveries, KS, MTC), @@ -693,7 +695,8 @@ remove_from_pending_ch(MsgId, ChPid, SQ) -> end. process_instruction( - {publish, Deliver, ChPid, MsgProps, Msg = #basic_message { id = MsgId }}, + {publish, Deliver, ChPid, MsgProps, Msg = #basic_message { id = MsgId }, + Redelivered}, State = #state { sender_queues = SQ, backing_queue = BQ, backing_queue_state = BQS, @@ -743,9 +746,10 @@ process_instruction( {ok, case Deliver of false -> - BQS1 = BQ:publish(Msg, MsgProps, ChPid, BQS), + BQS1 = BQ:publish(Msg, MsgProps, ChPid, Redelivered, BQS), State2 #state { backing_queue_state = BQS1 }; {true, AckRequired} -> + false = Redelivered, %% master:publish_delivered/5 only sends this {AckTag, BQS1} = BQ:publish_delivered(AckRequired, Msg, MsgProps, ChPid, BQS), maybe_store_ack(AckRequired, MsgId, AckTag, diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 98c45717..59c0bbeb 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -17,7 +17,7 @@ -module(rabbit_variable_queue). -export([init/3, terminate/2, delete_and_terminate/2, purge/1, - publish/4, publish_delivered/5, drain_confirmed/1, + publish/5, publish_delivered/5, drain_confirmed/1, dropwhile/3, fetch/2, ack/2, requeue/2, len/1, is_empty/1, depth/1, set_ram_duration_target/2, ram_duration/1, needs_timeout/1, timeout/1, handle_pre_hibernate/1, status/1, invoke/3, @@ -521,16 +521,16 @@ purge(State = #vqstate { q4 = Q4, publish(Msg = #basic_message { is_persistent = IsPersistent, id = MsgId }, MsgProps = #message_properties { needs_confirming = NeedsConfirming }, - _ChPid, State = #vqstate { q1 = Q1, q3 = Q3, q4 = Q4, - next_seq_id = SeqId, - len = Len, - in_counter = InCount, - persistent_count = PCount, - durable = IsDurable, - ram_msg_count = RamMsgCount, - unconfirmed = UC }) -> + _ChPid, Redelivered, State = #vqstate { q1 = Q1, q3 = Q3, q4 = Q4, + next_seq_id = SeqId, + len = Len, + in_counter = InCount, + persistent_count = PCount, + durable = IsDurable, + ram_msg_count = RamMsgCount, + unconfirmed = UC }) -> IsPersistent1 = IsDurable andalso IsPersistent, - MsgStatus = msg_status(IsPersistent1, SeqId, Msg, MsgProps), + MsgStatus = msg_status(IsPersistent1, SeqId, Msg, MsgProps, Redelivered), {MsgStatus1, State1} = maybe_write_to_disk(false, false, MsgStatus, State), State2 = case ?QUEUE:is_empty(Q3) of false -> State1 #vqstate { q1 = ?QUEUE:in(m(MsgStatus1), Q1) }; @@ -566,7 +566,7 @@ publish_delivered(true, Msg = #basic_message { is_persistent = IsPersistent, durable = IsDurable, unconfirmed = UC }) -> IsPersistent1 = IsDurable andalso IsPersistent, - MsgStatus = (msg_status(IsPersistent1, SeqId, Msg, MsgProps)) + MsgStatus = (msg_status(IsPersistent1, SeqId, Msg, MsgProps, false)) #msg_status { is_delivered = true }, {MsgStatus1, State1} = maybe_write_to_disk(false, false, MsgStatus, State), State2 = record_pending_ack(m(MsgStatus1), State1), @@ -874,9 +874,9 @@ gb_sets_maybe_insert(false, _Val, Set) -> Set; gb_sets_maybe_insert(true, Val, Set) -> gb_sets:add(Val, Set). msg_status(IsPersistent, SeqId, Msg = #basic_message { id = MsgId }, - MsgProps) -> + MsgProps, Redelivered) -> #msg_status { seq_id = SeqId, msg_id = MsgId, msg = Msg, - is_persistent = IsPersistent, is_delivered = false, + is_persistent = IsPersistent, is_delivered = Redelivered, msg_on_disk = false, index_on_disk = false, msg_props = MsgProps }. -- cgit v1.2.1 From 7b8dc2e55534fb8053caaf6f0994f704591edb2c Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 26 Sep 2012 16:34:30 +0100 Subject: Let's try that again: we no longer need to use confirmed_broadcast here due to lack of immediate. --- src/rabbit_mirror_queue_master.erl | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index 9e98c726..d447f1f3 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -170,10 +170,7 @@ publish_delivered(AckRequired, Msg = #basic_message { id = MsgId }, MsgProps, backing_queue_state = BQS, ack_msg_id = AM }) -> false = dict:is_key(MsgId, SS), %% ASSERTION - %% Must use confirmed_broadcast here in order to guarantee that - %% all slaves are forced to interpret this publish_delivered at - %% the same point, especially if we die and a slave is promoted. - ok = gm:confirmed_broadcast( + ok = gm:broadcast( GM, {publish, {true, AckRequired}, ChPid, MsgProps, Msg, false}), {AckTag, BQS1} = BQ:publish_delivered(AckRequired, Msg, MsgProps, ChPid, BQS), -- cgit v1.2.1 From 27f8ce04ae8198c73968001cd31786e69bcacb24 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 26 Sep 2012 16:51:40 +0100 Subject: API change --- src/rabbit_tests.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 08535e7d..64007992 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -2142,7 +2142,7 @@ variable_queue_publish(IsPersistent, Count, PropFun, VQ) -> true -> 2; false -> 1 end}, <<>>), - PropFun(N, #message_properties{}), self(), VQN) + PropFun(N, #message_properties{}), self(), false, VQN) end, VQ, lists:seq(1, Count)). variable_queue_fetch(Count, IsPersistent, IsDelivered, Len, VQ) -> -- cgit v1.2.1 From b9d5c4d41195b0bdb315c5df4eb138a3a33e353b Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 26 Sep 2012 18:09:06 +0100 Subject: Stick redelivered in #message_properties, which makes the diff rather smaller. --- include/rabbit.hrl | 3 ++- src/rabbit_amqqueue_process.erl | 19 +++++++++---------- src/rabbit_backing_queue.erl | 5 ++--- src/rabbit_mirror_queue_master.erl | 10 +++++----- src/rabbit_mirror_queue_slave.erl | 7 ++----- src/rabbit_variable_queue.erl | 25 +++++++++++++------------ 6 files changed, 33 insertions(+), 36 deletions(-) diff --git a/include/rabbit.hrl b/include/rabbit.hrl index fff92205..19fc8ff7 100644 --- a/include/rabbit.hrl +++ b/include/rabbit.hrl @@ -78,7 +78,8 @@ -record(event, {type, props, timestamp}). --record(message_properties, {expiry, needs_confirming = false}). +-record(message_properties, {expiry, needs_confirming = false, + redelivered = false}). -record(plugin, {name, %% atom() version, %% string() diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index d0810564..a6f49473 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -536,19 +536,17 @@ run_message_queue(State) -> BQ:is_empty(BQS), State1), State2. -attempt_delivery(#delivery{sender = SenderPid, message = Message}, Confirm, - Redelivered, +attempt_delivery(#delivery{sender = SenderPid, message = Message}, Props, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> case BQ:is_duplicate(Message, BQS) of {false, BQS1} -> deliver_msgs_to_consumers( fun (AckRequired, State1 = #q{backing_queue_state = BQS2}) -> - Props = message_properties(Confirm, State1), {AckTag, BQS3} = BQ:publish_delivered( AckRequired, Message, Props, SenderPid, BQS2), - {{Message, Redelivered, AckTag}, true, - State1#q{backing_queue_state = BQS3}} + {{Message, Props#message_properties.redelivered, AckTag}, + true, State1#q{backing_queue_state = BQS3}} end, false, State#q{backing_queue_state = BQS1}); {Duplicate, BQS1} -> %% if the message has previously been seen by the BQ then @@ -566,7 +564,8 @@ deliver_or_enqueue(Delivery = #delivery{message = Message, sender = SenderPid}, Redelivered, State) -> Confirm = should_confirm_message(Delivery, State), - case attempt_delivery(Delivery, Confirm, Redelivered, State) of + Props = message_properties(Confirm, Redelivered, State), + case attempt_delivery(Delivery, Props, State) of {true, State1} -> maybe_record_confirm_message(Confirm, State1); %% the next one is an optimisations @@ -576,8 +575,7 @@ deliver_or_enqueue(Delivery = #delivery{message = Message, {false, State1} -> State2 = #q{backing_queue = BQ, backing_queue_state = BQS} = maybe_record_confirm_message(Confirm, State1), - Props = message_properties(Confirm, State2), - BQS1 = BQ:publish(Message, Props, SenderPid, Redelivered, BQS), + BQS1 = BQ:publish(Message, Props, SenderPid, BQS), ensure_ttl_timer(Props#message_properties.expiry, State2#q{backing_queue_state = BQS1}) end. @@ -706,9 +704,10 @@ discard_delivery(#delivery{sender = SenderPid, backing_queue_state = BQS}) -> State#q{backing_queue_state = BQ:discard(Message, SenderPid, BQS)}. -message_properties(Confirm, #q{ttl = TTL}) -> +message_properties(Confirm, Redelivered, #q{ttl = TTL}) -> #message_properties{expiry = calculate_msg_expiry(TTL), - needs_confirming = needs_confirming(Confirm)}. + needs_confirming = needs_confirming(Confirm), + redelivered = Redelivered}. calculate_msg_expiry(undefined) -> undefined; calculate_msg_expiry(TTL) -> now_micros() + (TTL * 1000). diff --git a/src/rabbit_backing_queue.erl b/src/rabbit_backing_queue.erl index 9510ae23..d69a6c3b 100644 --- a/src/rabbit_backing_queue.erl +++ b/src/rabbit_backing_queue.erl @@ -77,8 +77,7 @@ %% Publish a message. -callback publish(rabbit_types:basic_message(), - rabbit_types:message_properties(), pid(), boolean(), - state()) -> + rabbit_types:message_properties(), pid(), state()) -> state(). %% Called for messages which have already been passed straight @@ -213,7 +212,7 @@ behaviour_info(callbacks) -> [{start, 1}, {stop, 0}, {init, 3}, {terminate, 2}, - {delete_and_terminate, 2}, {purge, 1}, {publish, 5}, + {delete_and_terminate, 2}, {purge, 1}, {publish, 4}, {publish_delivered, 5}, {drain_confirmed, 1}, {dropwhile, 3}, {fetch, 2}, {ack, 2}, {fold, 3}, {requeue, 2}, {len, 1}, {is_empty, 1}, {depth, 1}, {set_ram_duration_target, 2}, diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index 9e98c726..c11a8ff7 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -17,7 +17,7 @@ -module(rabbit_mirror_queue_master). -export([init/3, terminate/2, delete_and_terminate/2, - purge/1, publish/5, publish_delivered/5, fetch/2, ack/2, + purge/1, publish/4, publish_delivered/5, fetch/2, ack/2, requeue/2, len/1, is_empty/1, depth/1, drain_confirmed/1, dropwhile/3, set_ram_duration_target/2, ram_duration/1, needs_timeout/1, timeout/1, handle_pre_hibernate/1, @@ -153,14 +153,14 @@ purge(State = #state { gm = GM, {Count, State #state { backing_queue_state = BQS1, set_delivered = 0 }}. -publish(Msg = #basic_message { id = MsgId }, MsgProps, ChPid, Redelivered, +publish(Msg = #basic_message { id = MsgId }, MsgProps, ChPid, State = #state { gm = GM, seen_status = SS, backing_queue = BQ, backing_queue_state = BQS }) -> false = dict:is_key(MsgId, SS), %% ASSERTION - ok = gm:broadcast(GM, {publish, false, ChPid, MsgProps, Msg, Redelivered}), - BQS1 = BQ:publish(Msg, MsgProps, ChPid, Redelivered, BQS), + ok = gm:broadcast(GM, {publish, false, ChPid, MsgProps, Msg}), + BQS1 = BQ:publish(Msg, MsgProps, ChPid, BQS), ensure_monitoring(ChPid, State #state { backing_queue_state = BQS1 }). publish_delivered(AckRequired, Msg = #basic_message { id = MsgId }, MsgProps, @@ -174,7 +174,7 @@ publish_delivered(AckRequired, Msg = #basic_message { id = MsgId }, MsgProps, %% all slaves are forced to interpret this publish_delivered at %% the same point, especially if we die and a slave is promoted. ok = gm:confirmed_broadcast( - GM, {publish, {true, AckRequired}, ChPid, MsgProps, Msg, false}), + GM, {publish, {true, AckRequired}, ChPid, MsgProps, Msg}), {AckTag, BQS1} = BQ:publish_delivered(AckRequired, Msg, MsgProps, ChPid, BQS), AM1 = maybe_store_acktag(AckTag, MsgId, AM), diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index b11cd199..1e10e8a9 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -539,7 +539,6 @@ promote_me(From, #state { q = Q = #amqqueue { name = QName }, Deliveries = [{Delivery, true} || {_ChPid, {PubQ, _PendCh}} <- dict:to_list(SQ), Delivery <- queue:to_list(PubQ)], - rabbit_log:warning("Promotion deliveries: ~p~n", [Deliveries]), QueueState = rabbit_amqqueue_process:init_with_backing_queue_state( Q1, rabbit_mirror_queue_master, MasterState, RateTRef, AckTags, Deliveries, KS, MTC), @@ -695,8 +694,7 @@ remove_from_pending_ch(MsgId, ChPid, SQ) -> end. process_instruction( - {publish, Deliver, ChPid, MsgProps, Msg = #basic_message { id = MsgId }, - Redelivered}, + {publish, Deliver, ChPid, MsgProps, Msg = #basic_message { id = MsgId }}, State = #state { sender_queues = SQ, backing_queue = BQ, backing_queue_state = BQS, @@ -746,10 +744,9 @@ process_instruction( {ok, case Deliver of false -> - BQS1 = BQ:publish(Msg, MsgProps, ChPid, Redelivered, BQS), + BQS1 = BQ:publish(Msg, MsgProps, ChPid, BQS), State2 #state { backing_queue_state = BQS1 }; {true, AckRequired} -> - false = Redelivered, %% master:publish_delivered/5 only sends this {AckTag, BQS1} = BQ:publish_delivered(AckRequired, Msg, MsgProps, ChPid, BQS), maybe_store_ack(AckRequired, MsgId, AckTag, diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 59c0bbeb..7af29279 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -17,7 +17,7 @@ -module(rabbit_variable_queue). -export([init/3, terminate/2, delete_and_terminate/2, purge/1, - publish/5, publish_delivered/5, drain_confirmed/1, + publish/4, publish_delivered/5, drain_confirmed/1, dropwhile/3, fetch/2, ack/2, requeue/2, len/1, is_empty/1, depth/1, set_ram_duration_target/2, ram_duration/1, needs_timeout/1, timeout/1, handle_pre_hibernate/1, status/1, invoke/3, @@ -521,16 +521,16 @@ purge(State = #vqstate { q4 = Q4, publish(Msg = #basic_message { is_persistent = IsPersistent, id = MsgId }, MsgProps = #message_properties { needs_confirming = NeedsConfirming }, - _ChPid, Redelivered, State = #vqstate { q1 = Q1, q3 = Q3, q4 = Q4, - next_seq_id = SeqId, - len = Len, - in_counter = InCount, - persistent_count = PCount, - durable = IsDurable, - ram_msg_count = RamMsgCount, - unconfirmed = UC }) -> + _ChPid, State = #vqstate { q1 = Q1, q3 = Q3, q4 = Q4, + next_seq_id = SeqId, + len = Len, + in_counter = InCount, + persistent_count = PCount, + durable = IsDurable, + ram_msg_count = RamMsgCount, + unconfirmed = UC }) -> IsPersistent1 = IsDurable andalso IsPersistent, - MsgStatus = msg_status(IsPersistent1, SeqId, Msg, MsgProps, Redelivered), + MsgStatus = msg_status(IsPersistent1, SeqId, Msg, MsgProps), {MsgStatus1, State1} = maybe_write_to_disk(false, false, MsgStatus, State), State2 = case ?QUEUE:is_empty(Q3) of false -> State1 #vqstate { q1 = ?QUEUE:in(m(MsgStatus1), Q1) }; @@ -566,7 +566,7 @@ publish_delivered(true, Msg = #basic_message { is_persistent = IsPersistent, durable = IsDurable, unconfirmed = UC }) -> IsPersistent1 = IsDurable andalso IsPersistent, - MsgStatus = (msg_status(IsPersistent1, SeqId, Msg, MsgProps, false)) + MsgStatus = (msg_status(IsPersistent1, SeqId, Msg, MsgProps)) #msg_status { is_delivered = true }, {MsgStatus1, State1} = maybe_write_to_disk(false, false, MsgStatus, State), State2 = record_pending_ack(m(MsgStatus1), State1), @@ -874,7 +874,8 @@ gb_sets_maybe_insert(false, _Val, Set) -> Set; gb_sets_maybe_insert(true, Val, Set) -> gb_sets:add(Val, Set). msg_status(IsPersistent, SeqId, Msg = #basic_message { id = MsgId }, - MsgProps, Redelivered) -> + MsgProps = #message_properties{redelivered = Redelivered}) -> + %% TODO would it make sense to remove #msg_status.is_delivered? #msg_status { seq_id = SeqId, msg_id = MsgId, msg = Msg, is_persistent = IsPersistent, is_delivered = Redelivered, msg_on_disk = false, index_on_disk = false, -- cgit v1.2.1 From d09d500f7c32b712a533921b3ea0b2007a946702 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 26 Sep 2012 18:12:03 +0100 Subject: I put that on the wrong branch. And it's obsolete now anyway. --- src/rabbit_tests.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 64007992..08535e7d 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -2142,7 +2142,7 @@ variable_queue_publish(IsPersistent, Count, PropFun, VQ) -> true -> 2; false -> 1 end}, <<>>), - PropFun(N, #message_properties{}), self(), false, VQN) + PropFun(N, #message_properties{}), self(), VQN) end, VQ, lists:seq(1, Count)). variable_queue_fetch(Count, IsPersistent, IsDelivered, Len, VQ) -> -- cgit v1.2.1 From 4748a55bf9261d1a5781cabbf0702bf7f0752b7c Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 26 Sep 2012 18:16:00 +0100 Subject: s/redelivered/delivered/g --- include/rabbit.hrl | 2 +- src/rabbit_amqqueue_process.erl | 10 +++++----- src/rabbit_variable_queue.erl | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/rabbit.hrl b/include/rabbit.hrl index 19fc8ff7..f2389587 100644 --- a/include/rabbit.hrl +++ b/include/rabbit.hrl @@ -79,7 +79,7 @@ -record(event, {type, props, timestamp}). -record(message_properties, {expiry, needs_confirming = false, - redelivered = false}). + delivered = false}). -record(plugin, {name, %% atom() version, %% string() diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index a6f49473..3bf291e7 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -545,7 +545,7 @@ attempt_delivery(#delivery{sender = SenderPid, message = Message}, Props, {AckTag, BQS3} = BQ:publish_delivered( AckRequired, Message, Props, SenderPid, BQS2), - {{Message, Props#message_properties.redelivered, AckTag}, + {{Message, Props#message_properties.delivered, AckTag}, true, State1#q{backing_queue_state = BQS3}} end, false, State#q{backing_queue_state = BQS1}); {Duplicate, BQS1} -> @@ -561,10 +561,10 @@ attempt_delivery(#delivery{sender = SenderPid, message = Message}, Props, end. deliver_or_enqueue(Delivery = #delivery{message = Message, - sender = SenderPid}, Redelivered, + sender = SenderPid}, Delivered, State) -> Confirm = should_confirm_message(Delivery, State), - Props = message_properties(Confirm, Redelivered, State), + Props = message_properties(Confirm, Delivered, State), case attempt_delivery(Delivery, Props, State) of {true, State1} -> maybe_record_confirm_message(Confirm, State1); @@ -704,10 +704,10 @@ discard_delivery(#delivery{sender = SenderPid, backing_queue_state = BQS}) -> State#q{backing_queue_state = BQ:discard(Message, SenderPid, BQS)}. -message_properties(Confirm, Redelivered, #q{ttl = TTL}) -> +message_properties(Confirm, Delivered, #q{ttl = TTL}) -> #message_properties{expiry = calculate_msg_expiry(TTL), needs_confirming = needs_confirming(Confirm), - redelivered = Redelivered}. + delivered = Delivered}. calculate_msg_expiry(undefined) -> undefined; calculate_msg_expiry(TTL) -> now_micros() + (TTL * 1000). diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 7af29279..68c659df 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -874,10 +874,10 @@ gb_sets_maybe_insert(false, _Val, Set) -> Set; gb_sets_maybe_insert(true, Val, Set) -> gb_sets:add(Val, Set). msg_status(IsPersistent, SeqId, Msg = #basic_message { id = MsgId }, - MsgProps = #message_properties{redelivered = Redelivered}) -> + MsgProps = #message_properties{delivered = Delivered}) -> %% TODO would it make sense to remove #msg_status.is_delivered? #msg_status { seq_id = SeqId, msg_id = MsgId, msg = Msg, - is_persistent = IsPersistent, is_delivered = Redelivered, + is_persistent = IsPersistent, is_delivered = Delivered, msg_on_disk = false, index_on_disk = false, msg_props = MsgProps }. -- cgit v1.2.1 From 4416f7befd1b4584bdda53f4969b6aff7b4da1c3 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 26 Sep 2012 20:26:27 +0100 Subject: correct comment following removal of 'immediate' --- src/rabbit_mirror_queue_slave.erl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 039b2749..625bcdff 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -457,8 +457,7 @@ promote_me(From, #state { q = Q = #amqqueue { name = QName }, ok = rabbit_mirror_queue_coordinator:ensure_monitoring(CPid, MPids), %% We find all the messages that we've received from channels but - %% not from gm, and if they're due to be enqueued on promotion - %% then we pass them to the + %% not from gm, and pass them to the %% queue_process:init_with_backing_queue_state to be enqueued. %% %% We also have to requeue messages which are pending acks: the -- cgit v1.2.1 From 8d52df02cc34781ab28303ee7d1013f36604d641 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 27 Sep 2012 12:15:16 +0100 Subject: Don't change the signature of init_with_backing_queue_state --- src/rabbit_amqqueue_process.erl | 6 +++--- src/rabbit_mirror_queue_slave.erl | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 3bf291e7..487c06b7 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -78,7 +78,7 @@ -spec(info_keys/0 :: () -> rabbit_types:info_keys()). -spec(init_with_backing_queue_state/8 :: (rabbit_types:amqqueue(), atom(), tuple(), any(), [any()], - [{rabbit_types:delivery(), boolean()}], pmon:pmon(), dict()) -> #q{}). + [rabbit_types:delivery()], pmon:pmon(), dict()) -> #q{}). -endif. @@ -170,8 +170,8 @@ init_with_backing_queue_state(Q = #amqqueue{exclusive_owner = Owner}, BQ, BQS, rabbit_event:init_stats_timer( State, #q.stats_timer))), lists:foldl( - fun ({Delivery, Redelivered}, StateN) -> - deliver_or_enqueue(Delivery, Redelivered, StateN) + fun (Delivery, StateN) -> + deliver_or_enqueue(Delivery, true, StateN) end, State1, Deliveries). diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 1e10e8a9..cc41ac01 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -536,7 +536,7 @@ promote_me(From, #state { q = Q = #amqqueue { name = QName }, end, gb_trees:empty(), MSList), NumAckTags = [NumAckTag || {_MsgId, NumAckTag} <- dict:to_list(MA)], AckTags = [AckTag || {_Num, AckTag} <- lists:sort(NumAckTags)], - Deliveries = [{Delivery, true} || + Deliveries = [Delivery || {_ChPid, {PubQ, _PendCh}} <- dict:to_list(SQ), Delivery <- queue:to_list(PubQ)], QueueState = rabbit_amqqueue_process:init_with_backing_queue_state( -- cgit v1.2.1 From 7839b217164b9a9f089fed001253c74d8c0954cf Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 27 Sep 2012 12:49:21 +0100 Subject: cosmetic --- src/rabbit_amqqueue_process.erl | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 487c06b7..10ac5bea 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -169,11 +169,9 @@ init_with_backing_queue_state(Q = #amqqueue{exclusive_owner = Owner}, BQ, BQS, State1 = requeue_and_run(AckTags, process_args( rabbit_event:init_stats_timer( State, #q.stats_timer))), - lists:foldl( - fun (Delivery, StateN) -> - deliver_or_enqueue(Delivery, true, StateN) - end, - State1, Deliveries). + lists:foldl(fun (Delivery, StateN) -> + deliver_or_enqueue(Delivery, true, StateN) + end, State1, Deliveries). terminate(shutdown = R, State = #q{backing_queue = BQ}) -> terminate_shutdown(fun (BQS) -> BQ:terminate(R, BQS) end, State); -- cgit v1.2.1 From 2eceee754f95301eca78f12a1f33f20b3206f0cc Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 27 Sep 2012 14:58:11 +0100 Subject: undo spurious change --- src/rabbit_mirror_queue_slave.erl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index cc41ac01..039b2749 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -536,8 +536,7 @@ promote_me(From, #state { q = Q = #amqqueue { name = QName }, end, gb_trees:empty(), MSList), NumAckTags = [NumAckTag || {_MsgId, NumAckTag} <- dict:to_list(MA)], AckTags = [AckTag || {_Num, AckTag} <- lists:sort(NumAckTags)], - Deliveries = [Delivery || - {_ChPid, {PubQ, _PendCh}} <- dict:to_list(SQ), + Deliveries = [Delivery || {_ChPid, {PubQ, _PendCh}} <- dict:to_list(SQ), Delivery <- queue:to_list(PubQ)], QueueState = rabbit_amqqueue_process:init_with_backing_queue_state( Q1, rabbit_mirror_queue_master, MasterState, RateTRef, -- cgit v1.2.1 From e92bbcbb0602aaa87e3aea0c5f514219c6122f6a Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 27 Sep 2012 15:45:37 +0100 Subject: Work on older Erlangs. --- src/rabbit_vm.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_vm.erl b/src/rabbit_vm.erl index c39d92ae..cc9d9635 100644 --- a/src/rabbit_vm.erl +++ b/src/rabbit_vm.erl @@ -56,7 +56,7 @@ memory() -> {connection_channel_procs, ConnChs}, {queue_procs, Qs}, {plugins, Plugins}, - {other_proc, erlang:max(0, OtherProc)}, %% [1] + {other_proc, lists:max([0, OtherProc])}, %% [1] {mnesia, Mnesia}, {mgmt_db, MgmtDbETS + MgmtDbProc}, {msg_index, MsgIndexETS + MsgIndexProc}, -- cgit v1.2.1 From 30f0965f25789fac24bbee6f6e35a3d256ba32cd Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 27 Sep 2012 15:45:48 +0100 Subject: interval_operation/3 --- src/rabbit_vm.erl | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/rabbit_vm.erl b/src/rabbit_vm.erl index cc9d9635..9f9b9fbe 100644 --- a/src/rabbit_vm.erl +++ b/src/rabbit_vm.erl @@ -16,7 +16,7 @@ -module(rabbit_vm). --export([memory/0]). +-export([memory/0, interval_operation/3]). -define(MAGIC_PLUGINS, [mochiweb, webmachine, cowboy, sockjs, rfc4627_jsonrpc]). @@ -25,7 +25,9 @@ -ifdef(use_specs). -spec(memory/0 :: () -> rabbit_types:infos()). - +-spec(interval_operation/3 :: + (fun (() -> any()), non_neg_integer(), non_neg_integer()) + -> {any(), non_neg_integer()}). -endif. %%---------------------------------------------------------------------------- @@ -71,6 +73,17 @@ memory() -> %% claims about negative memory. See %% http://erlang.org/pipermail/erlang-questions/2012-September/069320.html + +%% Ideally, you'd want Fun to run every IdealInterval. but you don't +%% want it to take more than MaxTime per IdealInterval. So if it takes +%% more then you want to run it less often. So we time how long it +%% takes to run, and then suggest how long you should wait before +%% running it again. Times are in millis. +interval_operation(Fun, MaxTime, IdealInterval) -> + {Micros, Res} = timer:tc(Fun), + Ratio = lists:max([1, Micros / MaxTime / 1000]), + {Res, round(IdealInterval * Ratio)}. + %%---------------------------------------------------------------------------- sup_memory(Sup) -> -- cgit v1.2.1 From 2123d74f5d1360fca9c1f51c960ff5484d10eb2a Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Fri, 28 Sep 2012 12:39:13 +0100 Subject: Lintian warnings --- packaging/debs/Debian/debian/control | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packaging/debs/Debian/debian/control b/packaging/debs/Debian/debian/control index 943ed48f..d4526d87 100644 --- a/packaging/debs/Debian/debian/control +++ b/packaging/debs/Debian/debian/control @@ -5,12 +5,12 @@ Maintainer: RabbitMQ Team Uploaders: Emile Joubert DM-Upload-Allowed: yes Build-Depends: cdbs, debhelper (>= 5), erlang-dev, python-simplejson, xmlto, xsltproc, erlang-nox (>= 1:12.b.3), erlang-src (>= 1:12.b.3), unzip, zip -Standards-Version: 3.8.0 +Standards-Version: 3.9.2 Package: rabbitmq-server Architecture: all Depends: erlang-nox (>= 1:12.b.3), adduser, logrotate, ${misc:Depends} -Description: An AMQP server written in Erlang +Description: AMQP server written in Erlang RabbitMQ is an implementation of AMQP, the emerging standard for high performance enterprise messaging. The RabbitMQ server is a robust and scalable implementation of an AMQP broker. -- cgit v1.2.1 From 2f0729042ad6f7adf81ea509809c29f9bb24c8f6 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 28 Sep 2012 15:21:42 +0100 Subject: active_consumers info item. --- docs/rabbitmqctl.1.xml | 8 ++++++++ src/rabbit_amqqueue_process.erl | 3 +++ 2 files changed, 11 insertions(+) diff --git a/docs/rabbitmqctl.1.xml b/docs/rabbitmqctl.1.xml index 11d85e9e..6c4ee7d5 100644 --- a/docs/rabbitmqctl.1.xml +++ b/docs/rabbitmqctl.1.xml @@ -985,6 +985,14 @@ consumers Number of consumers. + + active_consumers + Number of active consumers. An active + consumer is one which could immediately receive any + messages sent to the queue. At least one of + messages_ready and active_consumers must always be + zero. + memory Bytes of memory consumed by the Erlang process associated with the diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 10ac5bea..55bd970c 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -92,6 +92,7 @@ messages_unacknowledged, messages, consumers, + active_consumers, memory, slave_pids, synchronised_slave_pids, @@ -914,6 +915,8 @@ i(messages, State) -> messages_unacknowledged]]); i(consumers, _) -> consumer_count(); +i(active_consumers, _) -> + active_consumer_count(); i(memory, _) -> {memory, M} = process_info(self(), memory), M; -- cgit v1.2.1 From 94511c36e377a52bebdd8b84f297e1b89ed39d54 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 28 Sep 2012 16:29:53 +0100 Subject: We want to catch here, if sup:start_child exits the rabbit_amqqueue:with we are in will convert any exit to not_found. Which is unlikely to be helpful --- src/rabbit_mirror_queue_misc.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 2e26ebfc..8aa860a7 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -163,7 +163,7 @@ add_mirror(QName, MirrorNode) -> end). start_child(Name, MirrorNode, Q) -> - case rabbit_mirror_queue_slave_sup:start_child(MirrorNode, [Q]) of + case catch rabbit_mirror_queue_slave_sup:start_child(MirrorNode, [Q]) of {ok, undefined} -> %% this means the mirror process was %% already running on the given node. -- cgit v1.2.1 From ef32fd17a363ec7a1170acd2f39614aab38704f6 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Fri, 28 Sep 2012 16:33:19 +0100 Subject: Change RPM service launcher --- packaging/RPMS/Fedora/Makefile | 7 +++++-- packaging/RPMS/Fedora/rabbitmq-server.init | 2 ++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packaging/RPMS/Fedora/Makefile b/packaging/RPMS/Fedora/Makefile index 03e513f8..6c75f6ad 100644 --- a/packaging/RPMS/Fedora/Makefile +++ b/packaging/RPMS/Fedora/Makefile @@ -13,13 +13,15 @@ RPM_OS=fedora endif ifeq "$(RPM_OS)" "suse" +FUNCTION_LIBRARY= REQUIRES=/sbin/chkconfig /sbin/service OS_DEFINES=--define '_initrddir /etc/init.d' --define 'dist .suse' -START_PROG=setsid +START_PROG=startproc else +FUNCTION_LIBRARY=\# Source function library.\n. /etc/init.d/functions REQUIRES=chkconfig initscripts OS_DEFINES=--define '_initrddir /etc/rc.d/init.d' -START_PROG=runuser rabbitmq --session-command +START_PROG=daemon endif rpms: clean server @@ -35,6 +37,7 @@ prepare: cp rabbitmq-server.init SOURCES/rabbitmq-server.init sed -i \ -e 's|^START_PROG=.*$$|START_PROG="$(START_PROG)"|' \ + -e 's|^@FUNCTION_LIBRARY@|$(FUNCTION_LIBRARY)|' \ SOURCES/rabbitmq-server.init ifeq "$(RPM_OS)" "fedora" # Fedora says that only vital services should have Default-Start diff --git a/packaging/RPMS/Fedora/rabbitmq-server.init b/packaging/RPMS/Fedora/rabbitmq-server.init index 2d2680e3..4bf778b3 100644 --- a/packaging/RPMS/Fedora/rabbitmq-server.init +++ b/packaging/RPMS/Fedora/rabbitmq-server.init @@ -16,6 +16,8 @@ # Short-Description: Enable AMQP service provided by RabbitMQ broker ### END INIT INFO +@FUNCTION_LIBRARY@ + PATH=/sbin:/usr/sbin:/bin:/usr/bin NAME=rabbitmq-server DAEMON=/usr/sbin/${NAME} -- cgit v1.2.1 From d93e42d597cd93f9f469634f2ad4469bb0f7c41e Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 28 Sep 2012 16:52:53 +0100 Subject: Assert a bit here too. --- src/rabbit_mirror_queue_misc.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 8aa860a7..8c75ab02 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -115,7 +115,7 @@ on_node_up() -> end end, [], rabbit_queue) end), - [add_mirror(QName, node()) || QName <- QNames], + [ok = add_mirror(QName, node()) || QName <- QNames], ok. drop_mirrors(QName, Nodes) -> -- cgit v1.2.1 From a695a4d2e3f711fe7b2407cb8bac356b0c7d87a9 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 28 Sep 2012 16:53:25 +0100 Subject: Ignore if the node is down. --- src/rabbit_mirror_queue_misc.erl | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 8c75ab02..a0b03166 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -163,11 +163,19 @@ add_mirror(QName, MirrorNode) -> end). start_child(Name, MirrorNode, Q) -> - case catch rabbit_mirror_queue_slave_sup:start_child(MirrorNode, [Q]) of + case rabbit_misc:with_exit_handler( + rabbit_misc:const({ok, down}), + fun () -> + rabbit_mirror_queue_slave_sup:start_child(MirrorNode, [Q]) + end) of {ok, undefined} -> %% this means the mirror process was %% already running on the given node. ok; + {ok, down} -> + %% Node went down between us deciding to start a mirror + %% and actually starting it. Which is fine. + ok; {ok, SPid} -> rabbit_log:info("Adding mirror of ~s on node ~p: ~p~n", [rabbit_misc:rs(Name), MirrorNode, SPid]), -- cgit v1.2.1 From 995def5af7bc95d652fa38b092195f63375e60b5 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 28 Sep 2012 16:58:40 +0100 Subject: Don't throw, rabbit_amqqueue:with/2 will eat it. --- src/rabbit_mirror_queue_misc.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index a0b03166..453f2f2c 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -186,7 +186,7 @@ start_child(Name, MirrorNode, Q) -> [rabbit_misc:rs(Name), MirrorNode, StalePid]), ok; {error, {{duplicate_live_master, _}=Err, _}} -> - throw(Err); + Err; Other -> Other end. -- cgit v1.2.1 From 3d87dab43b9833cc654715239098fdd0348619a6 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 1 Oct 2012 11:28:32 +0100 Subject: stylistic consistency --- src/rabbit_variable_queue.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 68c659df..ddb136a7 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -874,7 +874,7 @@ gb_sets_maybe_insert(false, _Val, Set) -> Set; gb_sets_maybe_insert(true, Val, Set) -> gb_sets:add(Val, Set). msg_status(IsPersistent, SeqId, Msg = #basic_message { id = MsgId }, - MsgProps = #message_properties{delivered = Delivered}) -> + MsgProps = #message_properties { delivered = Delivered }) -> %% TODO would it make sense to remove #msg_status.is_delivered? #msg_status { seq_id = SeqId, msg_id = MsgId, msg = Msg, is_persistent = IsPersistent, is_delivered = Delivered, -- cgit v1.2.1 From afac1ac66ae592e018bb26c430d242bf6b6fa1dc Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 1 Oct 2012 16:21:22 +0100 Subject: Clearer? --- docs/rabbitmqctl.1.xml | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/docs/rabbitmqctl.1.xml b/docs/rabbitmqctl.1.xml index 6c4ee7d5..174f5bc2 100644 --- a/docs/rabbitmqctl.1.xml +++ b/docs/rabbitmqctl.1.xml @@ -987,11 +987,23 @@ active_consumers - Number of active consumers. An active - consumer is one which could immediately receive any - messages sent to the queue. At least one of - messages_ready and active_consumers must always be - zero. + + + Number of active consumers. An active consumer is + one which could immediately receive any messages + sent to the queue - i.e. it is not limited by its + prefetch count or TCP congestion. At least one of + messages_ready and active_consumers must always be + zero. + + + Note that this value is an instantaneous snapshot + - when consumers are restricted by their prefetch + count they may only appear to be active for small + fractions of a second until more messages are sent + out. + + memory -- cgit v1.2.1 From d70eb76abf5266b503996f26c25c4117fc2b0344 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 1 Oct 2012 17:02:26 +0100 Subject: get rid of all but one use of run_backing_queue and inline the latter --- src/rabbit_amqqueue_process.erl | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 10ac5bea..33fd36db 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -578,14 +578,13 @@ deliver_or_enqueue(Delivery = #delivery{message = Message, State2#q{backing_queue_state = BQS1}) end. -requeue_and_run(AckTags, State = #q{backing_queue = BQ}) -> - run_backing_queue(BQ, fun (M, BQS) -> - {_MsgIds, BQS1} = M:requeue(AckTags, BQS), - BQS1 - end, State). - -fetch(AckRequired, State = #q{backing_queue_state = BQS, - backing_queue = BQ}) -> +requeue_and_run(AckTags, State = #q{backing_queue = BQ, + backing_queue_state = BQS}) -> + {_MsgIds, BQS1} = BQ:requeue(AckTags, BQS), + run_message_queue(State#q{backing_queue_state = BQS1}). + +fetch(AckRequired, State = #q{backing_queue = BQ, + backing_queue_state = BQS}) -> {Result, BQS1} = BQ:fetch(AckRequired, BQS), {Result, State#q{backing_queue_state = BQS1}}. @@ -679,12 +678,9 @@ maybe_send_reply(ChPid, Msg) -> ok = rabbit_channel:send_command(ChPid, Msg). qname(#q{q = #amqqueue{name = QName}}) -> QName. -backing_queue_timeout(State = #q{backing_queue = BQ}) -> - run_backing_queue(BQ, fun (M, BQS) -> M:timeout(BQS) end, State). - -run_backing_queue(Mod, Fun, State = #q{backing_queue = BQ, - backing_queue_state = BQS}) -> - run_message_queue(State#q{backing_queue_state = BQ:invoke(Mod, Fun, BQS)}). +backing_queue_timeout(State = #q{backing_queue = BQ, + backing_queue_state = BQS}) -> + State#q{backing_queue_state = BQ:timeout(BQS)}. subtract_acks(ChPid, AckTags, State, Fun) -> case lookup_ch(ChPid) of @@ -1173,8 +1169,10 @@ handle_cast({confirm, MsgSeqNos, QPid}, State = #q{unconfirmed = UC}) -> handle_cast(_, State = #q{delayed_stop = DS}) when DS =/= undefined -> noreply(State); -handle_cast({run_backing_queue, Mod, Fun}, State) -> - noreply(run_backing_queue(Mod, Fun, State)); +handle_cast({run_backing_queue, Mod, Fun}, + State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> + noreply(run_message_queue( + State#q{backing_queue_state = BQ:invoke(Mod, Fun, BQS)})); handle_cast({deliver, Delivery = #delivery{sender = Sender}, Flow}, State = #q{senders = Senders}) -> -- cgit v1.2.1 From 14ae461635d848a5b6dce9b23d63c43d0abffa5e Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 1 Oct 2012 18:33:50 +0100 Subject: First pass at reverse DNS lookups for clients. --- src/rabbit_networking.erl | 2 +- src/rabbit_reader.erl | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/rabbit_networking.erl b/src/rabbit_networking.erl index 5cf8d1ae..31eeef73 100644 --- a/src/rabbit_networking.erl +++ b/src/rabbit_networking.erl @@ -21,7 +21,7 @@ node_listeners/1, connections/0, connection_info_keys/0, connection_info/1, connection_info/2, connection_info_all/0, connection_info_all/1, - close_connection/2, force_connection_event_refresh/0]). + close_connection/2, force_connection_event_refresh/0, tcp_host/1]). %%used by TCP-based transports, e.g. STOMP adapter -export([tcp_listener_addresses/1, tcp_listener_spec/6, diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index aef48b20..d3e8d4f6 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -39,11 +39,11 @@ connection_state, queue_collector, heartbeater, stats_timer, channel_sup_sup_pid, start_heartbeat_fun, buf, buf_len, auth_mechanism, auth_state, conserve_resources, - last_blocked_by, last_blocked_at}). + last_blocked_by, last_blocked_at, peer_host}). -define(STATISTICS_KEYS, [pid, recv_oct, recv_cnt, send_oct, send_cnt, send_pend, state, last_blocked_by, last_blocked_age, - channels]). + channels, peer_host]). -define(CREATION_EVENT_KEYS, [pid, name, address, port, peer_address, peer_port, ssl, peer_cert_subject, peer_cert_issuer, @@ -224,7 +224,13 @@ start_connection(Parent, ChannelSupSupPid, Collector, StartHeartbeatFun, Deb, auth_state = none, conserve_resources = false, last_blocked_by = none, - last_blocked_at = never}, + last_blocked_at = never, + peer_host = unknown}, + Self = self(), + spawn(fun() -> + Res = rabbit_networking:tcp_host(i(peer_address, State)), + Self ! {rdns, Res} + end), try ok = inet_op(fun () -> rabbit_net:tune_buffer_size(ClientSock) end), recvloop(Deb, switch_callback(rabbit_event:init_stats_timer( @@ -348,6 +354,8 @@ handle_other({system, From, Request}, Deb, State = #v1{parent = Parent}) -> handle_other({bump_credit, Msg}, Deb, State) -> credit_flow:handle_bump_msg(Msg), recvloop(Deb, control_throttle(State)); +handle_other({rdns, Host}, Deb, State) -> + mainloop(Deb, State#v1{peer_host = list_to_binary(Host)}); handle_other(Other, _Deb, _State) -> %% internal error -> something worth dying for exit({unexpected_message, Other}). @@ -875,6 +883,8 @@ i(pid, #v1{}) -> self(); i(name, #v1{sock = Sock}) -> list_to_binary(name(Sock)); +i(peer_host, #v1{peer_host = Host}) -> + Host; i(address, #v1{sock = Sock}) -> socket_info(fun rabbit_net:sockname/1, fun ({A, _}) -> A end, Sock); i(port, #v1{sock = Sock}) -> -- cgit v1.2.1 From da8646c42ac1507f68062588041c5a38ac49bed7 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 1 Oct 2012 19:13:15 +0100 Subject: cosmetic: remove comment that no longer applies This should have been done when consumer cancellation notifications were introduced. --- src/rabbit_amqqueue_process.erl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 10ac5bea..a0e74b42 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -179,7 +179,6 @@ terminate({shutdown, _} = R, State = #q{backing_queue = BQ}) -> terminate_shutdown(fun (BQS) -> BQ:terminate(R, BQS) end, State); terminate(Reason, State = #q{q = #amqqueue{name = QName}, backing_queue = BQ}) -> - %% FIXME: How do we cancel active subscriptions? terminate_shutdown( fun (BQS) -> BQS1 = BQ:delete_and_terminate(Reason, BQS), -- cgit v1.2.1 From 4b6a2777580d67ab069c86349f32f832feff8fc5 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 1 Oct 2012 19:35:26 +0100 Subject: remove superfluous comment We already say that stuff from MA gets requeued. Furthermore, the master doesn't have an 'MA' field as such - the information is kept in the backing queue. --- src/rabbit_mirror_queue_slave.erl | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 625bcdff..8e541db1 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -513,10 +513,6 @@ promote_me(From, #state { q = Q = #amqqueue { name = QName }, %% those messages are then requeued. However, as discussed above, %% this does not affect MS, nor which bits go through to SS in %% Master, or MTC in queue_process. - %% - %% Everything that's in MA gets requeued. Consequently the new - %% master should start with a fresh AM as there are no messages - %% pending acks. MSList = dict:to_list(MS), SS = dict:from_list( -- cgit v1.2.1 From d0ccf58dec4511707ad10e6a7b8970c34c2e52ad Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 1 Oct 2012 20:04:48 +0100 Subject: requeue unacked messages earlier during promotion ...so that the set_delivered marker is set correctly. Note that - there is no need to sort AckTags - BQ:requeue doesn't care about the order in which it is given the tags. - we don't need to 'run the backing queue' / 'run the message queue' when requeuing since there are no consumers. But we *do* need to drop expired messages; process_args takes care of that already. --- src/rabbit_amqqueue_process.erl | 12 +++++------- src/rabbit_mirror_queue_master.erl | 18 +++++++++++------- src/rabbit_mirror_queue_slave.erl | 9 ++++----- 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index a0e74b42..a79a2ee0 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -26,7 +26,7 @@ -export([start_link/1, info_keys/0]). --export([init_with_backing_queue_state/8]). +-export([init_with_backing_queue_state/7]). -export([init/1, terminate/2, code_change/3, handle_call/3, handle_cast/2, handle_info/2, handle_pre_hibernate/1, prioritise_call/3, @@ -76,8 +76,8 @@ -spec(start_link/1 :: (rabbit_types:amqqueue()) -> rabbit_types:ok_pid_or_error()). -spec(info_keys/0 :: () -> rabbit_types:info_keys()). --spec(init_with_backing_queue_state/8 :: - (rabbit_types:amqqueue(), atom(), tuple(), any(), [any()], +-spec(init_with_backing_queue_state/7 :: + (rabbit_types:amqqueue(), atom(), tuple(), any(), [rabbit_types:delivery()], pmon:pmon(), dict()) -> #q{}). -endif. @@ -144,7 +144,7 @@ init(Q) -> {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}. init_with_backing_queue_state(Q = #amqqueue{exclusive_owner = Owner}, BQ, BQS, - RateTRef, AckTags, Deliveries, Senders, MTC) -> + RateTRef, Deliveries, Senders, MTC) -> case Owner of none -> ok; _ -> erlang:monitor(process, Owner) @@ -166,9 +166,7 @@ init_with_backing_queue_state(Q = #amqqueue{exclusive_owner = Owner}, BQ, BQS, delayed_stop = undefined, queue_monitors = pmon:new(), msg_id_to_channel = MTC}, - State1 = requeue_and_run(AckTags, process_args( - rabbit_event:init_stats_timer( - State, #q.stats_timer))), + State1 = process_args(rabbit_event:init_stats_timer(State, #q.stats_timer)), lists:foldl(fun (Delivery, StateN) -> deliver_or_enqueue(Delivery, true, StateN) end, State1, Deliveries). diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index 4cfb3dcb..5d302329 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -25,7 +25,7 @@ -export([start/1, stop/0]). --export([promote_backing_queue_state/6, sender_death_fun/0, length_fun/0]). +-export([promote_backing_queue_state/7, sender_death_fun/0, length_fun/0]). -behaviour(rabbit_backing_queue). @@ -59,8 +59,9 @@ known_senders :: set() }). --spec(promote_backing_queue_state/6 :: - (pid(), atom(), any(), pid(), dict(), [pid()]) -> master_state()). +-spec(promote_backing_queue_state/7 :: + (pid(), atom(), any(), pid(), [any()], dict(), [pid()]) -> + master_state()). -spec(sender_death_fun/0 :: () -> death_fun()). -spec(length_fun/0 :: () -> length_fun()). @@ -372,13 +373,16 @@ discard(Msg = #basic_message { id = MsgId }, ChPid, %% Other exported functions %% --------------------------------------------------------------------------- -promote_backing_queue_state(CPid, BQ, BQS, GM, SeenStatus, KS) -> - Len = BQ:len(BQS), - ok = gm:broadcast(GM, {depth, BQ:depth(BQS)}), +promote_backing_queue_state(CPid, BQ, BQS, GM, AckTags, SeenStatus, KS) -> + {_MsgIds, BQS1} = BQ:requeue(AckTags, BQS), + Len = BQ:len(BQS1), + Depth = BQ:depth(BQS1), + true = Len == Depth, %% ASSERTION: everything must have been requeued + ok = gm:broadcast(GM, {depth, Depth}), #state { gm = GM, coordinator = CPid, backing_queue = BQ, - backing_queue_state = BQS, + backing_queue_state = BQS1, set_delivered = Len, seen_status = SeenStatus, confirmed = [], diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 8e541db1..1832049d 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -287,7 +287,7 @@ terminate(Reason, #state { q = Q, rate_timer_ref = RateTRef }) -> ok = gm:leave(GM), QueueState = rabbit_amqqueue_process:init_with_backing_queue_state( - Q, BQ, BQS, RateTRef, [], [], pmon:new(), dict:new()), + Q, BQ, BQS, RateTRef, [], pmon:new(), dict:new()), rabbit_amqqueue_process:terminate(Reason, QueueState); terminate([_SPid], _Reason) -> %% gm case @@ -520,22 +520,21 @@ promote_me(From, #state { q = Q = #amqqueue { name = QName }, [{MsgId, Status} || {MsgId, {Status, _ChPid}} <- MSList, Status =:= published orelse Status =:= confirmed]), + AckTags = [AckTag || {_MsgId, {_Num, AckTag}} <- dict:to_list(MA)], MasterState = rabbit_mirror_queue_master:promote_backing_queue_state( - CPid, BQ, BQS, GM, SS, MPids), + CPid, BQ, BQS, GM, AckTags, SS, MPids), MTC = lists:foldl(fun ({MsgId, {published, ChPid, MsgSeqNo}}, MTC0) -> gb_trees:insert(MsgId, {ChPid, MsgSeqNo}, MTC0); (_, MTC0) -> MTC0 end, gb_trees:empty(), MSList), - NumAckTags = [NumAckTag || {_MsgId, NumAckTag} <- dict:to_list(MA)], - AckTags = [AckTag || {_Num, AckTag} <- lists:sort(NumAckTags)], Deliveries = [Delivery || {_ChPid, {PubQ, _PendCh}} <- dict:to_list(SQ), Delivery <- queue:to_list(PubQ)], QueueState = rabbit_amqqueue_process:init_with_backing_queue_state( Q1, rabbit_mirror_queue_master, MasterState, RateTRef, - AckTags, Deliveries, KS, MTC), + Deliveries, KS, MTC), {become, rabbit_amqqueue_process, QueueState, hibernate}. noreply(State) -> -- cgit v1.2.1 From 5384745ad76ead540b9b893ad366a0e6f20da8f6 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 1 Oct 2012 22:19:35 +0100 Subject: cosmetic(ish) --- src/rabbit_mirror_queue_master.erl | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index 4cfb3dcb..71d316f3 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -131,19 +131,12 @@ delete_and_terminate(Reason, State = #state { gm = GM, node(Pid) =/= node()], MRefs = [erlang:monitor(process, S) || S <- Slaves], ok = gm:broadcast(GM, {delete_and_terminate, Reason}), - monitor_wait(MRefs), + [receive {'DOWN', MRef, process, _Pid, _Info} -> ok end || + MRef <- MRefs], ok = gm:forget_group(proplists:get_value(group_name, Info)), State #state { backing_queue_state = BQ:delete_and_terminate(Reason, BQS), set_delivered = 0 }. -monitor_wait([]) -> - ok; -monitor_wait([MRef | MRefs]) -> - receive({'DOWN', MRef, process, _Pid, _Info}) -> - ok - end, - monitor_wait(MRefs). - purge(State = #state { gm = GM, backing_queue = BQ, backing_queue_state = BQS }) -> @@ -410,10 +403,8 @@ length_fun() -> end) end. -maybe_store_acktag(undefined, _MsgId, AM) -> - AM; -maybe_store_acktag(AckTag, MsgId, AM) -> - dict:store(AckTag, MsgId, AM). +maybe_store_acktag(undefined, _MsgId, AM) -> AM; +maybe_store_acktag(AckTag, MsgId, AM) -> dict:store(AckTag, MsgId, AM). ensure_monitoring(ChPid, State = #state { coordinator = CPid, known_senders = KS }) -> -- cgit v1.2.1 From 8494b83766c2d310e631d95fc15916fb18e60ad2 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 1 Oct 2012 22:20:24 +0100 Subject: cosmetic --- src/rabbit_mirror_queue_master.erl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index 71d316f3..48650206 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -131,8 +131,7 @@ delete_and_terminate(Reason, State = #state { gm = GM, node(Pid) =/= node()], MRefs = [erlang:monitor(process, S) || S <- Slaves], ok = gm:broadcast(GM, {delete_and_terminate, Reason}), - [receive {'DOWN', MRef, process, _Pid, _Info} -> ok end || - MRef <- MRefs], + [receive {'DOWN', MRef, process, _Pid, _Info} -> ok end || MRef <- MRefs], ok = gm:forget_group(proplists:get_value(group_name, Info)), State #state { backing_queue_state = BQ:delete_and_terminate(Reason, BQS), set_delivered = 0 }. -- cgit v1.2.1 From ce8c17a472b3480b1bafb9c1683367e998688487 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Tue, 2 Oct 2012 10:17:17 +0100 Subject: First draft of policy validation --- src/rabbit_control_main.erl | 24 ++++++++++++++++++++++ src/rabbit_policy.erl | 22 +++++++++++++++++++- src/rabbit_policy_validator.erl | 37 ++++++++++++++++++++++++++++++++++ src/rabbit_registry.erl | 3 ++- src/rabbit_runtime_parameters.erl | 11 ++++++++-- src/rabbit_runtime_parameters_test.erl | 22 ++++++++++++++++++++ src/rabbit_tests.erl | 17 ++++++++++++++++ 7 files changed, 132 insertions(+), 4 deletions(-) create mode 100644 src/rabbit_policy_validator.erl diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index e75e1f6f..1efde136 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -70,6 +70,10 @@ {clear_parameter, [?VHOST_DEF]}, {list_parameters, [?VHOST_DEF]}, + {set_policy, [?VHOST_DEF]}, + {clear_policy, [?VHOST_DEF]}, + {list_policies, [?VHOST_DEF]}, + {list_queues, [?VHOST_DEF]}, {list_exchanges, [?VHOST_DEF]}, {list_bindings, [?VHOST_DEF]}, @@ -458,6 +462,26 @@ 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, Value], Opts, Inform) -> + VHostArg = list_to_binary(proplists:get_value(?VHOST_OPT, Opts)), + Inform("Setting policy ~p to ~p", [Key, Value]), + rpc_call(Node, rabbit_runtime_parameters, parse_set, + [VHostArg, <<"policy">>, list_to_binary(Key), Value]); + +action(clear_policy, Node, [Key], Opts, Inform) -> + VHostArg = list_to_binary(proplists:get_value(?VHOST_OPT, Opts)), + Inform("Clearing policy ~p", [Key]), + rpc_call(Node, rabbit_runtime_parameters, clear, [VHostArg, + <<"policy">>, + list_to_binary(Key)]); + +action(list_policies, Node, [], Opts, Inform) -> + VHostArg = list_to_binary(proplists:get_value(?VHOST_OPT, Opts)), + Inform("Listing policies", []), + display_info_list( + rpc_call(Node, rabbit_runtime_parameters, list_formatted_policies, [VHostArg]), + rabbit_runtime_parameters:info_keys() -- [component]); + action(report, Node, _Args, _Opts, Inform) -> Inform("Reporting server status on ~p~n~n", [erlang:universaltime()]), [begin ok = action(Action, N, [], [], Inform), io:nl() end || diff --git a/src/rabbit_policy.erl b/src/rabbit_policy.erl index 69480c9c..f8e06914 100644 --- a/src/rabbit_policy.erl +++ b/src/rabbit_policy.erl @@ -80,6 +80,7 @@ notify_clear(VHost, <<"policy">>, _Name) -> %%---------------------------------------------------------------------------- + list(VHost) -> [[{<<"name">>, pget(key, P)} | pget(value, P)] || P <- rabbit_runtime_parameters:list(VHost, <<"policy">>)]. @@ -139,4 +140,23 @@ sort_pred(A, B) -> policy_validation() -> [{<<"priority">>, fun rabbit_parameter_validation:number/2, optional}, {<<"pattern">>, fun rabbit_parameter_validation:regex/2, mandatory}, - {<<"policy">>, fun rabbit_parameter_validation:list/2, mandatory}]. + {<<"policy">>, fun validation/2, mandatory}]. + +validation(_Name, Terms) when is_list(Terms) -> + [validation0(T) || T <- Terms ]; +validation(Name, Term) -> + {error, "~s should be list, actually was ~p", [Name, Term]}. + +validation0({Key, Value}) when is_binary(Key) -> + case rabbit_registry:lookup_module(policy_validator, + list_to_atom(binary_to_list(Key))) of + {ok, Mod} -> + Mod:validate_policy(Key, Value); + {error, not_found} -> + {error, "~p is not a recognised policy option", [Key]}; + Error -> + Error + end; +validation0(Term) -> + {error, "parse error while reading policy: ~p", [Term]}. + diff --git a/src/rabbit_policy_validator.erl b/src/rabbit_policy_validator.erl new file mode 100644 index 00000000..3cc02ecc --- /dev/null +++ b/src/rabbit_policy_validator.erl @@ -0,0 +1,37 @@ +%% 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 VMware, Inc. +%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% + +-module(rabbit_policy_validator). + +-ifdef(use_specs). + +-type(validate_results() :: + 'ok' | {error, string(), [term()]} | [validate_results()]). + +-callback validate_policy(binary(), term()) -> validate_results(). + +-else. + +-export([behaviour_info/1]). + +behaviour_info(callbacks) -> + [ + {validate_policy, 2}, + ]; +behaviour_info(_Other) -> + undefined. + +-endif. diff --git a/src/rabbit_registry.erl b/src/rabbit_registry.erl index e14bbba0..32709d24 100644 --- a/src/rabbit_registry.erl +++ b/src/rabbit_registry.erl @@ -107,7 +107,8 @@ sanity_check_module(ClassModule, Module) -> 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(exchange_decorator) -> rabbit_exchange_decorator; +class_module(policy_validator) -> rabbit_policy_validator. %%--------------------------------------------------------------------------- diff --git a/src/rabbit_runtime_parameters.erl b/src/rabbit_runtime_parameters.erl index b58b459a..c4608b42 100644 --- a/src/rabbit_runtime_parameters.erl +++ b/src/rabbit_runtime_parameters.erl @@ -20,7 +20,7 @@ -export([parse_set/4, set/4, clear/3, list/0, list/1, list_strict/1, list/2, list_strict/2, list_formatted/1, - lookup/3, value/3, value/4, info_keys/0]). + list_formatted_policies/1, lookup/3, value/3, value/4, info_keys/0]). %%---------------------------------------------------------------------------- @@ -41,6 +41,8 @@ -spec(list_strict/2 :: (rabbit_types:vhost(), binary()) -> [rabbit_types:infos()] | 'not_found'). -spec(list_formatted/1 :: (rabbit_types:vhost()) -> [rabbit_types:infos()]). +-spec(list_formatted_policies/1 :: (rabbit_types:vhost()) -> + [rabbit_types:infos()]). -spec(lookup/3 :: (rabbit_types:vhost(), binary(), binary()) -> rabbit_types:infos()). -spec(value/3 :: (rabbit_types:vhost(), binary(), binary()) -> term()). @@ -141,7 +143,12 @@ list(VHost, Component, Default) -> end. list_formatted(VHost) -> - [pset(value, format(pget(value, P)), P) || P <- list(VHost)]. + [pset(value, format(pget(value, P)), P) + || P <- list(VHost), pget(component, P) /= <<"policy">>]. + +list_formatted_policies(VHost) -> + [proplists:delete(component, pset(value, format(pget(value, P)), P)) + || P <- list(VHost), pget(component, P) == <<"policy">>]. lookup(VHost, Component, Key) -> case lookup0(VHost, Component, Key, rabbit_misc:const(not_found)) of diff --git a/src/rabbit_runtime_parameters_test.erl b/src/rabbit_runtime_parameters_test.erl index 5224ccaa..4ac19ff1 100644 --- a/src/rabbit_runtime_parameters_test.erl +++ b/src/rabbit_runtime_parameters_test.erl @@ -16,9 +16,14 @@ -module(rabbit_runtime_parameters_test). -behaviour(rabbit_runtime_parameter). +-behaviour(rabbit_policy_validator). -export([validate/4, validate_clear/3, notify/4, notify_clear/3]). -export([register/0, unregister/0]). +-export([validate_policy/2]). +-export([register_policy_validator/0, unregister_policy_validator/0]). + +%---------------------------------------------------------------------------- register() -> rabbit_registry:register(runtime_parameter, <<"test">>, ?MODULE). @@ -36,3 +41,20 @@ validate_clear(_, <<"test">>, _) -> {error, "meh", []}. notify(_, _, _, _) -> ok. notify_clear(_, _, _) -> ok. + +%---------------------------------------------------------------------------- + +register_policy_validator() -> + rabbit_registry:register(policy_validator, <<"testpolicy">>, ?MODULE). + +unregister_policy_validator() -> + rabbit_registry:unregister(policy_validator, <<"testpolicy">>). + +validate_policy(<<"testpolicy">>, Terms) when is_list(Terms) -> + rabbit_log:info("pol val ~p~n", [Terms]), + case length(Terms) rem 2 =:= 0 of + true -> ok; + false -> {error, "meh", []} + end; +validate_policy(<<"testpolicy">>, _) -> + {error, "meh", []}. diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index df0ee721..9c015b47 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -56,6 +56,7 @@ all_tests() -> passed = test_arguments_parser(), passed = test_user_management(), passed = test_runtime_parameters(), + passed = test_policy_validation(), passed = test_server_status(), passed = test_confirms(), passed = @@ -992,6 +993,22 @@ test_runtime_parameters() -> rabbit_runtime_parameters_test:unregister(), passed. +test_policy_validation() -> + rabbit_runtime_parameters_test:register_policy_validator(), + SetPol = fun (Pol, Val) -> + control_action( + set_policy, + ["name", lists:flatten( + io_lib:format("{\"pattern\":\"pat\", \"policy\":" + "{\"~s\":~p}}", [Pol, Val]))]) + end, + ok = SetPol("testpolicy", []), + ok = SetPol("testpolicy", [1, 2]), + ok = SetPol("testpolicy", [1, 2, 3, 4]), + {error_string, _} = SetPol("testpolicy", [1, 2, 3]), + {error_string, _} = SetPol("not_registered", []), + rabbit_runtime_parameters_test:unregister_policy_validator(). + test_server_status() -> %% create a few things so there is some useful information to list Writer = spawn(fun () -> receive shutdown -> ok end end), -- cgit v1.2.1 From 72e5fb833f2f0c16d4e13e6dd00641a7a44bb32a Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 2 Oct 2012 11:35:11 +0100 Subject: remove gratuitous ack numbering the sole purpose of which was to sort the acktags by that number, which serves no purpose whatsoever. --- src/rabbit_mirror_queue_slave.erl | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 1832049d..2a58e897 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -72,7 +72,6 @@ sender_queues, %% :: Pid -> {Q Msg, Set MsgId} msg_id_ack, %% :: MsgId -> AckTag - ack_num, msg_id_status, known_senders, @@ -125,7 +124,6 @@ init(#amqqueue { name = QueueName } = Q) -> sender_queues = dict:new(), msg_id_ack = dict:new(), - ack_num = 0, msg_id_status = dict:new(), known_senders = pmon:new(), @@ -520,7 +518,7 @@ promote_me(From, #state { q = Q = #amqqueue { name = QName }, [{MsgId, Status} || {MsgId, {Status, _ChPid}} <- MSList, Status =:= published orelse Status =:= confirmed]), - AckTags = [AckTag || {_MsgId, {_Num, AckTag}} <- dict:to_list(MA)], + AckTags = [AckTag || {_MsgId, AckTag} <- dict:to_list(MA)], MasterState = rabbit_mirror_queue_master:promote_backing_queue_state( CPid, BQ, BQS, GM, AckTags, SS, MPids), @@ -862,19 +860,16 @@ msg_ids_to_acktags(MsgIds, MA) -> lists:foldl( fun (MsgId, {Acc, MAN}) -> case dict:find(MsgId, MA) of - error -> {Acc, MAN}; - {ok, {_Num, AckTag}} -> {[AckTag | Acc], - dict:erase(MsgId, MAN)} + error -> {Acc, MAN}; + {ok, AckTag} -> {[AckTag | Acc], dict:erase(MsgId, MAN)} end end, {[], MA}, MsgIds), {lists:reverse(AckTags), MA1}. maybe_store_ack(false, _MsgId, _AckTag, State) -> State; -maybe_store_ack(true, MsgId, AckTag, State = #state { msg_id_ack = MA, - ack_num = Num }) -> - State #state { msg_id_ack = dict:store(MsgId, {Num, AckTag}, MA), - ack_num = Num + 1 }. +maybe_store_ack(true, MsgId, AckTag, State = #state { msg_id_ack = MA }) -> + State #state { msg_id_ack = dict:store(MsgId, AckTag, MA) }. set_delta(0, State = #state { depth_delta = undefined }) -> ok = record_synchronised(State#state.q), -- cgit v1.2.1 From 9b3a5bc27649c231eb9d97e3f38b5f759641e9e5 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Tue, 2 Oct 2012 13:16:40 +0100 Subject: simplify `leave_cluster/0' --- src/rabbit_mnesia.erl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index bfecf06a..7ec678b5 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -666,10 +666,10 @@ remove_node_if_mnesia_running(Node) -> end. leave_cluster() -> - RunningNodes = running_nodes(nodes_excl_me(cluster_nodes(all))), - case not is_clustered() andalso RunningNodes =:= [] of - true -> ok; - false -> case lists:any(fun leave_cluster/1, RunningNodes) of + AllNodes = nodes_excl_me(cluster_nodes(all)), + case AllNodes of + [] -> ok; + _ -> case lists:any(fun leave_cluster/1, AllNodes) of true -> ok; false -> e(no_running_cluster_nodes) end -- cgit v1.2.1 From 5d927e59e024adef594a39414de9d98b8e3760f2 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Tue, 2 Oct 2012 13:31:03 +0100 Subject: wait for tables, not for mnesia --- src/rabbit_mnesia.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 7ec678b5..a442f995 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -291,7 +291,7 @@ remove_node_offline_node(Node) -> try rabbit_table:force_load(), forget_cluster_node(Node, false), - ensure_mnesia_running() + rabbit_table:wait_for_replicated() after stop_mnesia() end; -- cgit v1.2.1 From cd358dc464999336b2f5e561fedc02f799683d52 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 2 Oct 2012 13:39:06 +0100 Subject: Don't let the GM hang around. We exit(normal) it rather than tell it to leave/1 since it really shouldn't be there, we want it to stop ASAP. --- src/rabbit_mirror_queue_slave.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index b4b0d4d3..6f4a7ee7 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -143,6 +143,7 @@ init(#amqqueue { name = QueueName } = Q) -> duplicate_live_master -> {stop, {duplicate_live_master, Node}}; existing -> + exit(GM, normal), ignore end. -- cgit v1.2.1 From bde9f8d0642b4b9b2cc46ce523d9fba412c98e99 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Tue, 2 Oct 2012 13:43:53 +0100 Subject: cosmetics --- src/rabbit_mnesia.erl | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index a442f995..c0321299 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -666,13 +666,12 @@ remove_node_if_mnesia_running(Node) -> end. leave_cluster() -> - AllNodes = nodes_excl_me(cluster_nodes(all)), - case AllNodes of - [] -> ok; - _ -> case lists:any(fun leave_cluster/1, AllNodes) of - true -> ok; - false -> e(no_running_cluster_nodes) - end + case nodes_excl_me(cluster_nodes(all)) of + [] -> ok; + AllNodes -> case lists:any(fun leave_cluster/1, AllNodes) of + true -> ok; + false -> e(no_running_cluster_nodes) + end end. leave_cluster(Node) -> -- cgit v1.2.1 From 3e79a2cf23e145de7768ef1544b7e55bfa90f971 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Tue, 2 Oct 2012 13:51:52 +0100 Subject: comments, also wait for tables right after you load them --- src/rabbit_mnesia.erl | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index c0321299..87069228 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -276,6 +276,9 @@ forget_cluster_node(Node, RemoveWhenOffline) -> end. remove_node_offline_node(Node) -> + %% We want the running nodes *now*, so we don't call + %% `cluster_nodes(running)' which will just get what's in the cluster status + %% file. case {running_nodes(cluster_nodes(all)) -- [Node], node_type()} of {[], disc} -> %% Note that while we check if the nodes was the last to @@ -289,9 +292,13 @@ remove_node_offline_node(Node) -> case cluster_nodes(running) -- [node(), Node] of [] -> start_mnesia(), try + %% What we want to do here is replace the last node to + %% go down with the current node. The way we do this + %% is by force loading the table, and making sure that + %% they are loaded. rabbit_table:force_load(), - forget_cluster_node(Node, false), - rabbit_table:wait_for_replicated() + rabbit_table:wait_for_replicated(), + forget_cluster_node(Node, false) after stop_mnesia() end; -- cgit v1.2.1 From 959c2cdbd1622e0cfdd95866af0f1344d48eb3b0 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 2 Oct 2012 16:53:06 +0100 Subject: Various tweaks. --- src/rabbit_misc.erl | 14 +++++++++++++ src/rabbit_vm.erl | 57 +++++++++++++++++++++-------------------------------- 2 files changed, 36 insertions(+), 35 deletions(-) diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index a0536a50..7e79ef8c 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -63,6 +63,7 @@ -export([version/0]). -export([sequence_error/1]). -export([json_encode/1, json_decode/1, json_to_term/1, term_to_json/1]). +-export([interval_operation/3]). %% Horrible macro to use in guards -define(IS_BENIGN_EXIT(R), @@ -227,6 +228,9 @@ -spec(json_decode/1 :: (string()) -> {'ok', any()} | 'error'). -spec(json_to_term/1 :: (any()) -> any()). -spec(term_to_json/1 :: (any()) -> any()). +-spec(interval_operation/3 :: + (fun (() -> any()), non_neg_integer(), non_neg_integer()) + -> {any(), non_neg_integer()}). -endif. @@ -987,3 +991,13 @@ term_to_json(L) when is_list(L) -> term_to_json(V) when is_binary(V) orelse is_number(V) orelse V =:= null orelse V =:= true orelse V =:= false -> V. + +%% Ideally, you'd want Fun to run every IdealInterval. but you don't +%% want it to take more than MaxRatio of IdealInterval. So if it takes +%% more then you want to run it less often. So we time how long it +%% takes to run, and then suggest how long you should wait before +%% running it again. Times are in millis. +interval_operation(Fun, MaxRatio, IdealInterval) -> + {Micros, Res} = timer:tc(Fun), + Ratio = lists:max([1, Micros / (MaxRatio * IdealInterval) / 1000]), + {Res, round(IdealInterval * Ratio)}. diff --git a/src/rabbit_vm.erl b/src/rabbit_vm.erl index 9f9b9fbe..1323512b 100644 --- a/src/rabbit_vm.erl +++ b/src/rabbit_vm.erl @@ -16,25 +16,24 @@ -module(rabbit_vm). --export([memory/0, interval_operation/3]). +-export([memory/0]). --define(MAGIC_PLUGINS, [mochiweb, webmachine, cowboy, sockjs, rfc4627_jsonrpc]). +-define(MAGIC_PLUGINS, ["mochiweb", "webmachine", "cowboy", "sockjs", + "rfc4627_jsonrpc"]). %%---------------------------------------------------------------------------- -ifdef(use_specs). -spec(memory/0 :: () -> rabbit_types:infos()). --spec(interval_operation/3 :: - (fun (() -> any()), non_neg_integer(), non_neg_integer()) - -> {any(), non_neg_integer()}). + -endif. %%---------------------------------------------------------------------------- %% Like erlang:memory(), but with awareness of rabbit-y things memory() -> - ConnChs = sup_memory(rabbit_tcp_client_sup) + + Conns = sup_memory(rabbit_tcp_client_sup) + sup_memory(ssl_connection_sup) + sup_memory(amqp_sup), Qs = sup_memory(rabbit_amqqueue_sup) + sup_memory(rabbit_mirror_queue_slave_sup), @@ -53,37 +52,26 @@ memory() -> {code, Code}, {system, System}] = erlang:memory([total, processes, ets, atom, binary, code, system]), - OtherProc = Processes - ConnChs - Qs - MsgIndexProc - MgmtDbProc - Plugins, - [{total, Total}, - {connection_channel_procs, ConnChs}, - {queue_procs, Qs}, - {plugins, Plugins}, - {other_proc, lists:max([0, OtherProc])}, %% [1] - {mnesia, Mnesia}, - {mgmt_db, MgmtDbETS + MgmtDbProc}, - {msg_index, MsgIndexETS + MsgIndexProc}, - {other_ets, ETS - Mnesia - MsgIndexETS - MgmtDbETS}, - {binary, Bin}, - {code, Code}, - {atom, Atom}, - {other_system, System - ETS - Atom - Bin - Code}]. + OtherProc = Processes - Conns - Qs - MsgIndexProc - MgmtDbProc - Plugins, + [{total, Total}, + {connection_procs, Conns}, + {queue_procs, Qs}, + {plugins, Plugins}, + {other_proc, lists:max([0, OtherProc])}, %% [1] + {mnesia, Mnesia}, + {mgmt_db, MgmtDbETS + MgmtDbProc}, + {msg_index, MsgIndexETS + MsgIndexProc}, + {other_ets, ETS - Mnesia - MsgIndexETS - MgmtDbETS}, + {binary, Bin}, + {code, Code}, + {atom, Atom}, + {other_system, System - ETS - Atom - Bin - Code}]. %% [1] - erlang:memory(processes) can be less than the sum of its %% parts. Rather than display something nonsensical, just silence any %% claims about negative memory. See %% http://erlang.org/pipermail/erlang-questions/2012-September/069320.html - -%% Ideally, you'd want Fun to run every IdealInterval. but you don't -%% want it to take more than MaxTime per IdealInterval. So if it takes -%% more then you want to run it less often. So we time how long it -%% takes to run, and then suggest how long you should wait before -%% running it again. Times are in millis. -interval_operation(Fun, MaxTime, IdealInterval) -> - {Micros, Res} = timer:tc(Fun), - Ratio = lists:max([1, Micros / MaxTime / 1000]), - {Res, round(IdealInterval * Ratio)}. - %%---------------------------------------------------------------------------- sup_memory(Sup) -> @@ -121,7 +109,7 @@ bytes(Words) -> Words * erlang:system_info(wordsize). plugins_memory() -> lists:sum([plugin_memory(App) || {App, _, _} <- application:which_applications(), - is_plugin(App)]). + is_plugin(atom_to_list(App))]). plugin_memory(App) -> case catch application_master:get_child( @@ -130,6 +118,5 @@ plugin_memory(App) -> _ -> 0 end. -is_plugin(App) -> - lists:member(App, ?MAGIC_PLUGINS) orelse - string:left(atom_to_list(App), 9) =:= "rabbitmq_". +is_plugin("rabbitmq_" ++ _) -> true; +is_plugin(App) -> lists:member(App, ?MAGIC_PLUGINS). -- cgit v1.2.1 From 1acaaffbbb9b8749b180a6f0b10b74cf6c252cba Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 2 Oct 2012 23:01:33 +0100 Subject: cosmetic --- src/rabbit_policy.erl | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/rabbit_policy.erl b/src/rabbit_policy.erl index 69480c9c..f4c1f42b 100644 --- a/src/rabbit_policy.erl +++ b/src/rabbit_policy.erl @@ -98,19 +98,17 @@ update_policies(VHost) -> ok. update_exchange(X = #exchange{name = XName, policy = OldPolicy}, Policies) -> - NewPolicy = match(XName, Policies), - case NewPolicy of + case match(XName, Policies) of OldPolicy -> no_change; - _ -> rabbit_exchange:update( + NewPolicy -> rabbit_exchange:update( XName, fun(X1) -> X1#exchange{policy = NewPolicy} end), {X, X#exchange{policy = NewPolicy}} end. update_queue(Q = #amqqueue{name = QName, policy = OldPolicy}, Policies) -> - NewPolicy = match(QName, Policies), - case NewPolicy of + case match(QName, Policies) of OldPolicy -> no_change; - _ -> rabbit_amqqueue:update( + NewPolicy -> rabbit_amqqueue:update( QName, fun(Q1) -> Q1#amqqueue{policy = NewPolicy} end), {Q, Q#amqqueue{policy = NewPolicy}} end. @@ -131,12 +129,11 @@ match(Name, Policies) -> matches(#resource{name = Name}, Policy) -> match =:= re:run(Name, pget(<<"pattern">>, Policy), [{capture, none}]). -sort_pred(A, B) -> - pget(<<"priority">>, A, 0) >= pget(<<"priority">>, B, 0). +sort_pred(A, B) -> pget(<<"priority">>, A, 0) >= pget(<<"priority">>, B, 0). %%---------------------------------------------------------------------------- policy_validation() -> [{<<"priority">>, fun rabbit_parameter_validation:number/2, optional}, - {<<"pattern">>, fun rabbit_parameter_validation:regex/2, mandatory}, - {<<"policy">>, fun rabbit_parameter_validation:list/2, mandatory}]. + {<<"pattern">>, fun rabbit_parameter_validation:regex/2, mandatory}, + {<<"policy">>, fun rabbit_parameter_validation:list/2, mandatory}]. -- cgit v1.2.1 From 55a89d13cb3f6d7d7cfe2abe8737c8b0296eed1e Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 3 Oct 2012 09:20:39 +0100 Subject: distinguish between deliveries to masters and slaves ...thus allowing us to set the 'delivered' flag correctly for messages that were in flight during a promotion --- src/rabbit_amqqueue.erl | 42 ++++++++++++++++++++++++++------------- src/rabbit_amqqueue_process.erl | 8 ++++---- src/rabbit_mirror_queue_slave.erl | 5 +++-- 3 files changed, 35 insertions(+), 20 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 8fc103e4..7e857fc7 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -622,29 +622,43 @@ deliver(Qs, Delivery = #delivery{mandatory = false}, Flow) -> %% returned. It is therefore safe to use a fire-and-forget cast %% here and return the QPids - the semantics is preserved. This %% scales much better than the case below. - QPids = qpids(Qs), + {MPids, SPids} = qpids(Qs), + QPids = MPids ++ SPids, case Flow of flow -> [credit_flow:send(QPid) || QPid <- QPids]; noflow -> ok end, - delegate:invoke_no_result( - QPids, fun (QPid) -> - gen_server2:cast(QPid, {deliver, Delivery, Flow}) - end), + MMsg = {deliver, Delivery, false, Flow}, + SMsg = {deliver, Delivery, true, Flow}, + delegate:invoke_no_result(MPids, + fun (QPid) -> gen_server2:cast(QPid, MMsg) end), + delegate:invoke_no_result(SPids, + fun (QPid) -> gen_server2:cast(QPid, SMsg) end), {routed, QPids}; deliver(Qs, Delivery, _Flow) -> - case delegate:invoke( - qpids(Qs), fun (QPid) -> - ok = gen_server2:call(QPid, {deliver, Delivery}, - infinity) - end) of - {[], _} -> {unroutable, []}; - {R , _} -> {routed, [QPid || {QPid, ok} <- R]} + {MPids, SPids} = qpids(Qs), + MMsg = {deliver, Delivery, false}, + SMsg = {deliver, Delivery, true}, + {MRouted, _} = delegate:invoke( + MPids, fun (QPid) -> + ok = gen_server2:call(QPid, MMsg, infinity) + end), + {SRouted, _} = delegate:invoke( + SPids, fun (QPid) -> + ok = gen_server2:call(QPid, SMsg, infinity) + end), + case MRouted ++ SRouted of + [] -> {unroutable, []}; + R -> {routed, [QPid || {QPid, ok} <- R]} end. -qpids(Qs) -> lists:append([[QPid | SPids] || - #amqqueue{pid = QPid, slave_pids = SPids} <- Qs]). +qpids(Qs) -> + {MPids, SPids} = lists:foldl(fun (#amqqueue{pid = QPid, slave_pids = SPids}, + {MPidAcc, SPidAcc}) -> + {[QPid | MPidAcc], [SPids | SPidAcc]} + end, {[], []}, Qs), + {MPids, lists:append(SPids)}. safe_delegate_call_ok(F, Pids) -> {_, Bads} = delegate:invoke(Pids, fun (Pid) -> diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index dfd0ab7e..a22e32b0 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1030,10 +1030,10 @@ handle_call({info, Items}, _From, State) -> handle_call(consumers, _From, State) -> reply(consumers(State), State); -handle_call({deliver, Delivery}, From, State) -> +handle_call({deliver, Delivery, Delivered}, From, State) -> %% Synchronous, "mandatory" deliver mode. gen_server2:reply(From, ok), - noreply(deliver_or_enqueue(Delivery, false, State)); + noreply(deliver_or_enqueue(Delivery, Delivered, State)); handle_call({notify_down, ChPid}, From, State) -> %% we want to do this synchronously, so that auto_deleted queues @@ -1193,7 +1193,7 @@ handle_cast(_, State = #q{delayed_stop = DS}) when DS =/= undefined -> handle_cast({run_backing_queue, Mod, Fun}, State) -> noreply(run_backing_queue(Mod, Fun, State)); -handle_cast({deliver, Delivery = #delivery{sender = Sender}, Flow}, +handle_cast({deliver, Delivery = #delivery{sender = Sender}, Delivered, Flow}, State = #q{senders = Senders}) -> %% Asynchronous, non-"mandatory" deliver mode. Senders1 = case Flow of @@ -1202,7 +1202,7 @@ handle_cast({deliver, Delivery = #delivery{sender = Sender}, Flow}, noflow -> Senders end, State1 = State#q{senders = Senders1}, - noreply(deliver_or_enqueue(Delivery, false, State1)); + noreply(deliver_or_enqueue(Delivery, Delivered, State1)); handle_cast({ack, AckTags, ChPid}, State) -> noreply(subtract_acks( diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index c74470f6..e4ff79ee 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -166,7 +166,7 @@ init_it(Self, Node, QueueName) -> add_slave(Q, New, MPids) -> rabbit_mirror_queue_misc:store_updated_slaves( Q#amqqueue{slave_pids = MPids ++ [New]}). -handle_call({deliver, Delivery}, From, State) -> +handle_call({deliver, Delivery, true}, From, State) -> %% Synchronous, "mandatory" deliver mode. gen_server2:reply(From, ok), noreply(maybe_enqueue_message(Delivery, State)); @@ -220,7 +220,8 @@ handle_cast({run_backing_queue, Mod, Fun}, State) -> handle_cast({gm, Instruction}, State) -> handle_process_result(process_instruction(Instruction, State)); -handle_cast({deliver, Delivery = #delivery{sender = Sender}, Flow}, State) -> +handle_cast({deliver, Delivery = #delivery{sender = Sender}, true, Flow}, + State) -> %% Asynchronous, non-"mandatory", deliver mode. case Flow of flow -> credit_flow:ack(Sender); -- cgit v1.2.1 From 17e49f71fa1411d352845a0bdbceb31017ec3232 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 3 Oct 2012 11:31:01 +0100 Subject: More reasons. --- docs/rabbitmqctl.1.xml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/rabbitmqctl.1.xml b/docs/rabbitmqctl.1.xml index 174f5bc2..73347cea 100644 --- a/docs/rabbitmqctl.1.xml +++ b/docs/rabbitmqctl.1.xml @@ -992,9 +992,10 @@ Number of active consumers. An active consumer is one which could immediately receive any messages sent to the queue - i.e. it is not limited by its - prefetch count or TCP congestion. At least one of - messages_ready and active_consumers must always be - zero. + prefetch count, TCP congestion, flow control, or + because it has issued channel.flow. At least one + of messages_ready and active_consumers must always + be zero. Note that this value is an instantaneous snapshot -- cgit v1.2.1 From 4f06ff2e237827a364ea17ea723078d526658a11 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Wed, 3 Oct 2012 12:52:17 +0100 Subject: sanitize the contents of `cluster_nodes' --- src/rabbit_mnesia.erl | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index bfecf06a..aea455b4 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -110,6 +110,15 @@ init() -> init_from_config() -> {ok, {TryNodes, NodeType}} = application:get_env(rabbit, cluster_nodes), + {TryNodes, NodeType} = + case application:get_env(rabbit, cluster_nodes) of + {ok, {TryNodes, disc} = C} when is_list(TryNodes) -> + C; + {ok, {TryNodes, ram } = C} when is_list(TryNodes) -> + C; + _ -> + e(invalid_cluster_config) + end, case find_good_node(nodes_excl_me(TryNodes)) of {ok, Node} -> rabbit_log:info("Node '~p' selected for clustering from " @@ -840,4 +849,7 @@ error_description(removing_node_from_offline_node) -> "To remove a node remotely from an offline node, the node you're removing " "from must be a disc node and all the other nodes must be offline."; error_description(no_running_cluster_nodes) -> - "You cannot leave a cluster if no online nodes are present.". + "You cannot leave a cluster if no online nodes are present."; +error_description(invalid_cluster_config) -> + "Invalid or missing cluster configuration. Check the 'cluster_nodes' field " + "in your config file.". -- cgit v1.2.1 From 9207ac7614247cc09b9d6785307c9ead042fb3bc Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 3 Oct 2012 12:54:38 +0100 Subject: Explain --- src/rabbit_amqqueue.erl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 7e857fc7..adab5d03 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -628,6 +628,12 @@ deliver(Qs, Delivery = #delivery{mandatory = false}, Flow) -> flow -> [credit_flow:send(QPid) || QPid <- QPids]; noflow -> ok end, + + %% We let slaves know that they were being addressed as slaves at + %% the time - if they receive such a message from the channel + %% after they have become master they should mark the message as + %% redelivered since they do not know what the master may have + %% done with it. MMsg = {deliver, Delivery, false, Flow}, SMsg = {deliver, Delivery, true, Flow}, delegate:invoke_no_result(MPids, -- cgit v1.2.1 From 4e02a2376ebc2d7e88c704e5f6e90f92e562f331 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 3 Oct 2012 13:20:28 +0100 Subject: cosmetic --- src/rabbit_amqqueue.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index adab5d03..a8b0ea24 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -632,7 +632,7 @@ deliver(Qs, Delivery = #delivery{mandatory = false}, Flow) -> %% We let slaves know that they were being addressed as slaves at %% the time - if they receive such a message from the channel %% after they have become master they should mark the message as - %% redelivered since they do not know what the master may have + %% 'delivered' since they do not know what the master may have %% done with it. MMsg = {deliver, Delivery, false, Flow}, SMsg = {deliver, Delivery, true, Flow}, @@ -644,6 +644,7 @@ deliver(Qs, Delivery = #delivery{mandatory = false}, Flow) -> deliver(Qs, Delivery, _Flow) -> {MPids, SPids} = qpids(Qs), + %% see comment above MMsg = {deliver, Delivery, false}, SMsg = {deliver, Delivery, true}, {MRouted, _} = delegate:invoke( -- cgit v1.2.1 From a39693e655bbf987f7a04688bd6b09762e39e266 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 3 Oct 2012 18:06:27 +0100 Subject: Progress towards storing a GM pid -> queue pid mapping. This is dependent on not feeding the process_death message into remove_from_queue - bug 25195 suggests this might be TRTTD. --- include/rabbit.hrl | 3 ++- src/rabbit_amqqueue.erl | 3 ++- src/rabbit_mirror_queue_master.erl | 11 +++++++++-- src/rabbit_mirror_queue_misc.erl | 23 +++++++++++++++++------ src/rabbit_mirror_queue_slave.erl | 29 ++++++++++++++++++++--------- src/rabbit_upgrade_functions.erl | 15 +++++++++++++++ 6 files changed, 65 insertions(+), 19 deletions(-) diff --git a/include/rabbit.hrl b/include/rabbit.hrl index 41cce0a3..3db2b68a 100644 --- a/include/rabbit.hrl +++ b/include/rabbit.hrl @@ -47,7 +47,8 @@ -record(exchange_serial, {name, next}). -record(amqqueue, {name, durable, auto_delete, exclusive_owner = none, - arguments, pid, slave_pids, sync_slave_pids, policy}). + arguments, pid, slave_pids, sync_slave_pids, policy, + gm_pids}). %% mnesia doesn't like unary records, so we add a dummy 'value' field -record(route, {binding, value = const}). diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index adab5d03..466d3b8a 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -219,7 +219,8 @@ declare(QueueName, Durable, AutoDelete, Args, Owner) -> exclusive_owner = Owner, pid = none, slave_pids = [], - sync_slave_pids = []}), + sync_slave_pids = [], + gm_pids = []}), {Node, _MNodes} = rabbit_mirror_queue_misc:suggested_queue_nodes(Q0), Q1 = start_queue_process(Node, Q0), case gen_server2:call(Q1#amqqueue.pid, {init, false}, infinity) of diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index c2bbcf92..02b96629 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -93,11 +93,17 @@ init(Q, Recover, AsyncCallback) -> BQS = BQ:init(Q, Recover, AsyncCallback), init_with_existing_bq(Q, BQ, BQS). -init_with_existing_bq(#amqqueue { name = QName } = Q, BQ, BQS) -> +init_with_existing_bq(#amqqueue { name = QName, + gm_pids = []} = Q, BQ, BQS) -> {ok, CPid} = rabbit_mirror_queue_coordinator:start_link( Q, undefined, sender_death_fun(), length_fun()), GM = rabbit_mirror_queue_coordinator:get_gm(CPid), - {_MNode, SNodes} = rabbit_mirror_queue_misc:suggested_queue_nodes(Q), + Q1 = Q#amqqueue{gm_pids = [{GM, self()}]}, + ok = rabbit_misc:execute_mnesia_transaction( + fun () -> + ok = rabbit_amqqueue:store_queue(Q1) + end), + {_MNode, SNodes} = rabbit_mirror_queue_misc:suggested_queue_nodes(Q1), rabbit_mirror_queue_misc:add_mirrors(QName, SNodes), ok = gm:broadcast(GM, {depth, BQ:depth(BQS)}), #state { gm = GM, @@ -114,6 +120,7 @@ stop_mirroring(State = #state { coordinator = CPid, backing_queue = BQ, backing_queue_state = BQS }) -> unlink(CPid), + %% TODO remove GM from mnesia stop_all_slaves(shutdown, State), {BQ, BQS}. diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 453f2f2c..1011a4ff 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -58,8 +58,8 @@ %% Returns {ok, NewMPid, DeadPids} remove_from_queue(QueueName, DeadGMPids) -> - DeadNodes = [node(DeadGMPid) || DeadGMPid <- DeadGMPids], - ClusterNodes = rabbit_mnesia:cluster_nodes(running) -- DeadNodes, + ClusterNodes = rabbit_mnesia:cluster_nodes(running) -- + [node(DeadGMPid) || DeadGMPid <- DeadGMPids], rabbit_misc:execute_mnesia_transaction( fun () -> %% Someone else could have deleted the queue before we @@ -67,10 +67,20 @@ remove_from_queue(QueueName, DeadGMPids) -> case mnesia:read({rabbit_queue, QueueName}) of [] -> {error, not_found}; [Q = #amqqueue { pid = QPid, - slave_pids = SPids }] -> - Alive = [Pid || Pid <- [QPid | SPids], - not lists:member(node(Pid), DeadNodes)], + slave_pids = SPids, + gm_pids = GMPids }] -> + + {Dead, GMPids1} = lists:partition( + fun ({GM, _}) -> + lists:member(GM, DeadGMPids) + end, GMPids), + DeadPids = [Pid || {_GM, Pid} <- Dead, Pid =/= existing], + {_, Alive} = lists:partition( + fun (Pid) -> + lists:member(Pid, DeadPids) + end, [QPid | SPids]), {QPid1, SPids1} = promote_slave(Alive), + case {{QPid, SPids}, {QPid1, SPids1}} of {Same, Same} -> {ok, QPid1, [], []}; @@ -80,7 +90,8 @@ remove_from_queue(QueueName, DeadGMPids) -> %% become the master. Q1 = store_updated_slaves( Q #amqqueue { pid = QPid1, - slave_pids = SPids1 }), + slave_pids = SPids1, + gm_pids = GMPids1 }), %% Sometimes a slave dying means we need %% to start more on other nodes - %% "exactly" mode can cause this to diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index fff02140..7c799dd8 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -105,7 +105,7 @@ init(#amqqueue { name = QueueName } = Q) -> Self = self(), Node = node(), case rabbit_misc:execute_mnesia_transaction( - fun() -> init_it(Self, Node, QueueName) end) of + fun() -> init_it(Self, GM, Node, QueueName) end) of {new, MPid} -> erlang:monitor(process, MPid), ok = file_handle_cache:register_callback( @@ -145,27 +145,37 @@ init(#amqqueue { name = QueueName } = Q) -> ignore end. -init_it(Self, Node, QueueName) -> - [Q1 = #amqqueue { pid = QPid, slave_pids = MPids }] = +init_it(Self, GM, Node, QueueName) -> + [Q1 = #amqqueue { pid = QPid, slave_pids = MPids, gm_pids = GMPids }] = mnesia:read({rabbit_queue, QueueName}), case [Pid || Pid <- [QPid | MPids], node(Pid) =:= Node] of - [] -> add_slave(Q1, Self, MPids), + [] -> add_slave(Q1, Self, GM), {new, QPid}; [QPid] -> case rabbit_misc:is_process_alive(QPid) of true -> duplicate_live_master; false -> {stale, QPid} end; [SPid] -> case rabbit_misc:is_process_alive(SPid) of - true -> existing; - false -> add_slave(Q1, Self, MPids -- [SPid]), + true -> Q2 = Q1#amqqueue{gm_pids = [{GM, existing} | + GMPids]}, + ok = rabbit_amqqueue:store_queue(Q2), + existing; + false -> add_slave(forget_slave(SPid, Q1), Self, GM), {new, QPid} end end. %% Add to the end, so they are in descending order of age, see %% rabbit_mirror_queue_misc:promote_slave/1 -add_slave(Q, New, MPids) -> rabbit_mirror_queue_misc:store_updated_slaves( - Q#amqqueue{slave_pids = MPids ++ [New]}). +add_slave(Q = #amqqueue{gm_pids = GMPids, slave_pids = SPids}, New, GM) -> + rabbit_mirror_queue_misc:store_updated_slaves( + Q#amqqueue{slave_pids = SPids ++ [New], + gm_pids = [{GM, New} | GMPids]}). + +forget_slave(SPid, Q = #amqqueue{slave_pids = SPids, + gm_pids = GMPids}) -> + Q#amqqueue{slave_pids = SPids -- [SPid], + gm_pids = [T || T = {S, _} <- GMPids, S =/= SPid]}. handle_call({deliver, Delivery, true}, From, State) -> %% Synchronous, "mandatory" deliver mode. @@ -355,7 +365,8 @@ handle_msg([_SPid], _From, {ensure_monitoring, _Pid}) -> %% This is only of value to the master ok; handle_msg([SPid], _From, {process_death, Pid}) -> - inform_deaths(SPid, [Pid]); + %%inform_deaths(SPid, [Pid]); TODO see bug25195 (?) + ok; handle_msg([CPid], _From, {delete_and_terminate, _Reason} = Msg) -> ok = gen_server2:cast(CPid, {gm, Msg}), {stop, {shutdown, ring_shutdown}}; diff --git a/src/rabbit_upgrade_functions.erl b/src/rabbit_upgrade_functions.erl index ddc9c565..21fdcd66 100644 --- a/src/rabbit_upgrade_functions.erl +++ b/src/rabbit_upgrade_functions.erl @@ -42,6 +42,7 @@ [exchange_scratches, ha_mirrors]}). -rabbit_upgrade({sync_slave_pids, mnesia, [policy]}). -rabbit_upgrade({no_mirror_nodes, mnesia, [sync_slave_pids]}). +-rabbit_upgrade({gm_pids, mnesia, [no_mirror_nodes]}). %% ------------------------------------------------------------------- @@ -66,6 +67,7 @@ -spec(policy/0 :: () -> 'ok'). -spec(sync_slave_pids/0 :: () -> 'ok'). -spec(no_mirror_nodes/0 :: () -> 'ok'). +-spec(gm_pids/0 :: () -> 'ok'). -endif. @@ -268,6 +270,19 @@ no_mirror_nodes() -> || T <- Tables], ok. +gm_pids() -> + Tables = [rabbit_queue, rabbit_durable_queue], + AddGMPidsFun = + fun ({amqqueue, N, D, AD, O, A, Pid, SPids, SSPids, Pol}) -> + {amqqueue, N, D, AD, O, A, Pid, SPids, SSPids, Pol, []} + end, + [ok = transform(T, AddGMPidsFun, + [name, durable, auto_delete, exclusive_owner, arguments, + pid, slave_pids, sync_slave_pids, policy, gm_pids]) + || T <- Tables], + ok. + + %%-------------------------------------------------------------------- -- cgit v1.2.1 From 457890b467765d57133e63b13db86690b32cc26b Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 3 Oct 2012 21:10:17 +0100 Subject: consistency --- src/rabbit_mirror_queue_coordinator.erl | 24 ++++++++++++------------ src/rabbit_mirror_queue_master.erl | 12 ++++++------ src/rabbit_mirror_queue_slave.erl | 6 +++--- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/rabbit_mirror_queue_coordinator.erl b/src/rabbit_mirror_queue_coordinator.erl index 5284000b..40359da3 100644 --- a/src/rabbit_mirror_queue_coordinator.erl +++ b/src/rabbit_mirror_queue_coordinator.erl @@ -33,14 +33,14 @@ gm, monitors, death_fun, - length_fun + depth_fun }). -ifdef(use_specs). -spec(start_link/4 :: (rabbit_types:amqqueue(), pid() | 'undefined', rabbit_mirror_queue_master:death_fun(), - rabbit_mirror_queue_master:length_fun()) -> + rabbit_mirror_queue_master:depth_fun()) -> rabbit_types:ok_pid_or_error()). -spec(get_gm/1 :: (pid()) -> pid()). -spec(ensure_monitoring/2 :: (pid(), [pid()]) -> 'ok'). @@ -154,8 +154,8 @@ %% be able to work out when their head does not differ from the master %% (and is much simpler and cheaper than getting the master to hang on %% to the guid of the msg at the head of its queue). When a slave is -%% promoted to a master, it unilaterally broadcasts its length, in -%% order to solve the problem of length requests from new slaves being +%% promoted to a master, it unilaterally broadcasts its depth, in +%% order to solve the problem of depth requests from new slaves being %% unanswered by a dead master. %% %% Obviously, due to the async nature of communication across gm, the @@ -297,15 +297,15 @@ %% if they have no mirrored content at all. This is not surprising: to %% achieve anything more sophisticated would require the master and %% recovering slave to be able to check to see whether they agree on -%% the last seen state of the queue: checking length alone is not +%% the last seen state of the queue: checking depth alone is not %% sufficient in this case. %% %% For more documentation see the comments in bug 23554. %% %%---------------------------------------------------------------------------- -start_link(Queue, GM, DeathFun, LengthFun) -> - gen_server2:start_link(?MODULE, [Queue, GM, DeathFun, LengthFun], []). +start_link(Queue, GM, DeathFun, DepthFun) -> + gen_server2:start_link(?MODULE, [Queue, GM, DeathFun, DepthFun], []). get_gm(CPid) -> gen_server2:call(CPid, get_gm, infinity). @@ -317,7 +317,7 @@ ensure_monitoring(CPid, Pids) -> %% gen_server %% --------------------------------------------------------------------------- -init([#amqqueue { name = QueueName } = Q, GM, DeathFun, LengthFun]) -> +init([#amqqueue { name = QueueName } = Q, GM, DeathFun, DepthFun]) -> GM1 = case GM of undefined -> {ok, GM2} = gm:start_link(QueueName, ?MODULE, [self()]), @@ -333,7 +333,7 @@ init([#amqqueue { name = QueueName } = Q, GM, DeathFun, LengthFun]) -> gm = GM1, monitors = pmon:new(), death_fun = DeathFun, - length_fun = LengthFun }, + depth_fun = DepthFun }, hibernate, {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}. @@ -353,8 +353,8 @@ handle_cast({gm_deaths, Deaths}, {stop, normal, State} end; -handle_cast(request_length, State = #state { length_fun = LengthFun }) -> - ok = LengthFun(), +handle_cast(request_depth, State = #state { depth_fun = DepthFun }) -> + ok = DepthFun(), noreply(State); handle_cast({ensure_monitoring, Pids}, State = #state { monitors = Mons }) -> @@ -400,7 +400,7 @@ members_changed([CPid], _Births, Deaths) -> handle_msg([_CPid], _From, master_changed) -> ok; -handle_msg([CPid], _From, request_length = Msg) -> +handle_msg([CPid], _From, request_depth = Msg) -> ok = gen_server2:cast(CPid, Msg); handle_msg([CPid], _From, {ensure_monitoring, _Pids} = Msg) -> ok = gen_server2:cast(CPid, Msg); diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index c2bbcf92..15ab9424 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -25,7 +25,7 @@ -export([start/1, stop/0]). --export([promote_backing_queue_state/7, sender_death_fun/0, length_fun/0]). +-export([promote_backing_queue_state/7, sender_death_fun/0, depth_fun/0]). -export([init_with_existing_bq/3, stop_mirroring/1]). @@ -46,10 +46,10 @@ -ifdef(use_specs). --export_type([death_fun/0, length_fun/0]). +-export_type([death_fun/0, depth_fun/0]). -type(death_fun() :: fun ((pid()) -> 'ok')). --type(length_fun() :: fun (() -> 'ok')). +-type(depth_fun() :: fun (() -> 'ok')). -type(master_state() :: #state { gm :: pid(), coordinator :: pid(), backing_queue :: atom(), @@ -65,7 +65,7 @@ (pid(), atom(), any(), pid(), [any()], dict(), [pid()]) -> master_state()). -spec(sender_death_fun/0 :: () -> death_fun()). --spec(length_fun/0 :: () -> length_fun()). +-spec(depth_fun/0 :: () -> depth_fun()). -spec(init_with_existing_bq/3 :: (rabbit_types:amqqueue(), atom(), any()) -> master_state()). -spec(stop_mirroring/1 :: (master_state()) -> {atom(), any()}). @@ -95,7 +95,7 @@ init(Q, Recover, AsyncCallback) -> init_with_existing_bq(#amqqueue { name = QName } = Q, BQ, BQS) -> {ok, CPid} = rabbit_mirror_queue_coordinator:start_link( - Q, undefined, sender_death_fun(), length_fun()), + Q, undefined, sender_death_fun(), depth_fun()), GM = rabbit_mirror_queue_coordinator:get_gm(CPid), {_MNode, SNodes} = rabbit_mirror_queue_misc:suggested_queue_nodes(Q), rabbit_mirror_queue_misc:add_mirrors(QName, SNodes), @@ -406,7 +406,7 @@ sender_death_fun() -> end) end. -length_fun() -> +depth_fun() -> Self = self(), fun () -> rabbit_amqqueue:run_backing_queue( diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index e4ff79ee..307f2b4f 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -132,7 +132,7 @@ init(#amqqueue { name = QueueName } = Q) -> }, rabbit_event:notify(queue_slave_created, infos(?CREATION_EVENT_KEYS, State)), - ok = gm:broadcast(GM, request_length), + ok = gm:broadcast(GM, request_depth), {ok, State, hibernate, {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}; @@ -347,7 +347,7 @@ members_changed([ SPid], _Births, Deaths) -> inform_deaths(SPid, Deaths). handle_msg([_SPid], _From, master_changed) -> ok; -handle_msg([_SPid], _From, request_length) -> +handle_msg([_SPid], _From, request_depth) -> %% This is only of value to the master ok; handle_msg([_SPid], _From, {ensure_monitoring, _Pid}) -> @@ -451,7 +451,7 @@ promote_me(From, #state { q = Q = #amqqueue { name = QName }, Q1 = Q #amqqueue { pid = self() }, {ok, CPid} = rabbit_mirror_queue_coordinator:start_link( Q1, GM, rabbit_mirror_queue_master:sender_death_fun(), - rabbit_mirror_queue_master:length_fun()), + rabbit_mirror_queue_master:depth_fun()), true = unlink(GM), gen_server2:reply(From, {promote, CPid}), %% TODO this has been in here since the beginning, but it's not -- cgit v1.2.1 From b1053015c890cc303f93a2d59b5d7132c0baf342 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 4 Oct 2012 08:52:36 +0100 Subject: cosmetic --- src/rabbit_vm.erl | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/rabbit_vm.erl b/src/rabbit_vm.erl index 1323512b..2b9b827b 100644 --- a/src/rabbit_vm.erl +++ b/src/rabbit_vm.erl @@ -33,17 +33,19 @@ %% Like erlang:memory(), but with awareness of rabbit-y things memory() -> - Conns = sup_memory(rabbit_tcp_client_sup) + - sup_memory(ssl_connection_sup) + sup_memory(amqp_sup), - Qs = sup_memory(rabbit_amqqueue_sup) + - sup_memory(rabbit_mirror_queue_slave_sup), - Mnesia = mnesia_memory(), - MsgIndexETS = ets_memory(rabbit_msg_store_ets_index), - MsgIndexProc = pid_memory(msg_store_transient) + - pid_memory(msg_store_persistent), - MgmtDbETS = ets_memory(rabbit_mgmt_db), - MgmtDbProc = sup_memory(rabbit_mgmt_sup), - Plugins = plugins_memory() - MgmtDbProc, + Conns = (sup_memory(rabbit_tcp_client_sup) + + sup_memory(ssl_connection_sup) + + sup_memory(amqp_sup)), + Qs = (sup_memory(rabbit_amqqueue_sup) + + sup_memory(rabbit_mirror_queue_slave_sup)), + Mnesia = mnesia_memory(), + MsgIndexETS = ets_memory(rabbit_msg_store_ets_index), + MsgIndexProc = (pid_memory(msg_store_transient) + + pid_memory(msg_store_persistent)), + MgmtDbETS = ets_memory(rabbit_mgmt_db), + MgmtDbProc = sup_memory(rabbit_mgmt_sup), + Plugins = plugins_memory() - MgmtDbProc, + [{total, Total}, {processes, Processes}, {ets, ETS}, @@ -52,7 +54,9 @@ memory() -> {code, Code}, {system, System}] = erlang:memory([total, processes, ets, atom, binary, code, system]), + OtherProc = Processes - Conns - Qs - MsgIndexProc - MgmtDbProc - Plugins, + [{total, Total}, {connection_procs, Conns}, {queue_procs, Qs}, -- cgit v1.2.1 From 4b912ba6b41fe1387841ac36a3a9965456c96125 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 4 Oct 2012 08:56:30 +0100 Subject: correct type signature --- src/rabbit_misc.erl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 7e79ef8c..83ac1004 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -229,8 +229,7 @@ -spec(json_to_term/1 :: (any()) -> any()). -spec(term_to_json/1 :: (any()) -> any()). -spec(interval_operation/3 :: - (fun (() -> any()), non_neg_integer(), non_neg_integer()) - -> {any(), non_neg_integer()}). + (thunk(A), float(), non_neg_integer()) -> {A, non_neg_integer()}). -endif. -- cgit v1.2.1 From e7bc7b820702b26f6ab0ac3de5859dc21d4b36a4 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 4 Oct 2012 11:35:03 +0100 Subject: Add a policy info item for queues, and make sure we return '' when there is no policy for both queues and exchanges. --- src/rabbit_amqqueue_process.erl | 7 +++++++ src/rabbit_exchange.erl | 5 ++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 30df2b5c..f137ae91 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -86,6 +86,7 @@ -define(STATISTICS_KEYS, [pid, + policy, exclusive_consumer_pid, exclusive_consumer_tag, messages_ready, @@ -890,6 +891,12 @@ 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), + case rabbit_policy:name(Q) of + none -> ''; + Policy -> Policy + end; i(exclusive_consumer_pid, #q{exclusive_consumer = none}) -> ''; i(exclusive_consumer_pid, #q{exclusive_consumer = {ChPid, _ConsumerTag}}) -> diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index 4cc96ef5..a205b23d 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -298,7 +298,10 @@ i(durable, #exchange{durable = Durable}) -> Durable; i(auto_delete, #exchange{auto_delete = AutoDelete}) -> AutoDelete; i(internal, #exchange{internal = Internal}) -> Internal; i(arguments, #exchange{arguments = Arguments}) -> Arguments; -i(policy, X) -> rabbit_policy:name(X); +i(policy, X) -> case rabbit_policy:name(X) of + none -> ''; + Policy -> Policy + end; i(Item, _) -> throw({bad_argument, Item}). info(X = #exchange{}) -> infos(?INFO_KEYS, X). -- cgit v1.2.1 From 34873be839a7797cea8bf1c71a7cb13850ac536d Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Thu, 4 Oct 2012 15:32:53 +0100 Subject: Further spec correctness for RPM --- packaging/RPMS/Fedora/Makefile | 4 +++- packaging/RPMS/Fedora/rabbitmq-server.init | 2 +- packaging/RPMS/Fedora/rabbitmq-server.spec | 10 ++++++---- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/packaging/RPMS/Fedora/Makefile b/packaging/RPMS/Fedora/Makefile index 6c75f6ad..4f5f1327 100644 --- a/packaging/RPMS/Fedora/Makefile +++ b/packaging/RPMS/Fedora/Makefile @@ -16,11 +16,13 @@ ifeq "$(RPM_OS)" "suse" FUNCTION_LIBRARY= REQUIRES=/sbin/chkconfig /sbin/service OS_DEFINES=--define '_initrddir /etc/init.d' --define 'dist .suse' +SPEC_DEFINES=--define 'group_tag Productivity/Networking/Other' START_PROG=startproc else FUNCTION_LIBRARY=\# Source function library.\n. /etc/init.d/functions REQUIRES=chkconfig initscripts OS_DEFINES=--define '_initrddir /etc/rc.d/init.d' +SPEC_DEFINES=--define 'group_tag Development/Libraries' START_PROG=daemon endif @@ -50,7 +52,7 @@ endif cp rabbitmq-server.logrotate SOURCES/rabbitmq-server.logrotate server: prepare - rpmbuild -ba --nodeps SPECS/rabbitmq-server.spec $(DEFINES) $(OS_DEFINES) + rpmbuild -ba --nodeps SPECS/rabbitmq-server.spec $(DEFINES) $(OS_DEFINES) $(SPEC_DEFINES) clean: rm -rf SOURCES SPECS RPMS SRPMS BUILD tmp diff --git a/packaging/RPMS/Fedora/rabbitmq-server.init b/packaging/RPMS/Fedora/rabbitmq-server.init index 4bf778b3..3e48147b 100644 --- a/packaging/RPMS/Fedora/rabbitmq-server.init +++ b/packaging/RPMS/Fedora/rabbitmq-server.init @@ -10,7 +10,7 @@ # Provides: rabbitmq-server # Required-Start: $remote_fs $network # Required-Stop: $remote_fs $network -# Default-Start: 3 4 5 +# Default-Start: 3 5 # Default-Stop: 0 1 2 6 # Description: RabbitMQ broker # Short-Description: Enable AMQP service provided by RabbitMQ broker diff --git a/packaging/RPMS/Fedora/rabbitmq-server.spec b/packaging/RPMS/Fedora/rabbitmq-server.spec index a6899005..d73c5634 100644 --- a/packaging/RPMS/Fedora/rabbitmq-server.spec +++ b/packaging/RPMS/Fedora/rabbitmq-server.spec @@ -3,8 +3,8 @@ Name: rabbitmq-server Version: %%VERSION%% Release: 1%{?dist} -License: MPLv1.1 -Group: Development/Libraries +License: MPLv1.1 and MIT and ASL 2.0 and BSD +Group: %{group_tag} Source: http://www.rabbitmq.com/releases/rabbitmq-server/v%{version}/%{name}-%{version}.tar.gz Source1: rabbitmq-server.init Source2: rabbitmq-script-wrapper @@ -31,8 +31,10 @@ scalable implementation of an AMQP broker. %define _rabbit_server_ocf %{_builddir}/`basename %{S:4}` %define _plugins_state_dir %{_localstatedir}/lib/rabbitmq/plugins + %define _maindir %{buildroot}%{_rabbit_erllibdir} + %prep %setup -q @@ -110,8 +112,8 @@ done %files -f ../%{name}.files %defattr(-,root,root,-) -%attr(0750, rabbitmq, rabbitmq) %dir %{_localstatedir}/lib/rabbitmq -%attr(0750, rabbitmq, rabbitmq) %dir %{_localstatedir}/log/rabbitmq +%attr(0755, rabbitmq, rabbitmq) %dir %{_localstatedir}/lib/rabbitmq +%attr(0755, rabbitmq, rabbitmq) %dir %{_localstatedir}/log/rabbitmq %dir %{_sysconfdir}/rabbitmq %{_initrddir}/rabbitmq-server %config(noreplace) %{_sysconfdir}/logrotate.d/rabbitmq-server -- cgit v1.2.1 From e50020be2aa4361eff31bbd23295f0c3d1f4ffa3 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 4 Oct 2012 17:57:05 +0100 Subject: Shuffle candidate nodes when we want to add some in "exactly" mode. --- src/rabbit_mirror_queue_misc.erl | 10 ++++++++- src/rabbit_tests.erl | 45 +++++++++++++++++++++++++--------------- 2 files changed, 37 insertions(+), 18 deletions(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 453f2f2c..150b0899 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -265,16 +265,24 @@ suggested_queue_nodes(<<"nodes">>, Nodes0, {MNode, _SNodes}, All) -> false -> promote_slave(Available) end end; +%% When we need to add nodes, we randomise our candidate list as a +%% crude form of load-balancing. TODO it would also be nice to +%% randomise when we have too many - but that would fail to take +%% account of synchronisation... suggested_queue_nodes(<<"exactly">>, Count, {MNode, SNodes}, All) -> SCount = Count - 1, {MNode, case SCount > length(SNodes) of - true -> Cand = (All -- [MNode]) -- SNodes, + true -> Cand = shuffle((All -- [MNode]) -- SNodes), SNodes ++ lists:sublist(Cand, SCount - length(SNodes)); false -> lists:sublist(SNodes, SCount) end}; suggested_queue_nodes(_, _, {MNode, _}, _) -> {MNode, []}. +shuffle(L) -> + {_, L1} = lists:unzip(lists:keysort(1, [{random:uniform(), N} || N <- L])), + L1. + actual_queue_nodes(#amqqueue{pid = MPid, slave_pids = SPids}) -> {case MPid of none -> none; diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 11f280bb..df89de89 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -886,38 +886,49 @@ test_arguments_parser() -> test_dynamic_mirroring() -> %% Just unit tests of the node selection logic, see multi node %% tests for the rest... - Test = fun ({NewM, NewSs}, Policy, Params, {OldM, OldSs}, All) -> + Test = fun ({NewM, NewSs, ExtraSs}, Policy, Params, {OldM, OldSs}, All) -> {NewM, NewSs0} = rabbit_mirror_queue_misc:suggested_queue_nodes( Policy, Params, {OldM, OldSs}, All), - NewSs = lists:sort(NewSs0) + NewSs1 = lists:sort(NewSs0), + case dm_list_match(NewSs, NewSs1, ExtraSs) of + ok -> ok; + error -> exit({no_match, NewSs, NewSs1, ExtraSs}) + end end, - Test({a,[b,c]},<<"all">>,'_',{a,[]}, [a,b,c]), - Test({a,[b,c]},<<"all">>,'_',{a,[b,c]},[a,b,c]), - Test({a,[b,c]},<<"all">>,'_',{a,[d]}, [a,b,c]), + Test({a,[b,c],0},<<"all">>,'_',{a,[]}, [a,b,c]), + Test({a,[b,c],0},<<"all">>,'_',{a,[b,c]},[a,b,c]), + Test({a,[b,c],0},<<"all">>,'_',{a,[d]}, [a,b,c]), %% Add a node - Test({a,[b,c]},<<"nodes">>,[<<"a">>,<<"b">>,<<"c">>],{a,[b]},[a,b,c,d]), - Test({b,[a,c]},<<"nodes">>,[<<"a">>,<<"b">>,<<"c">>],{b,[a]},[a,b,c,d]), + Test({a,[b,c],0},<<"nodes">>,[<<"a">>,<<"b">>,<<"c">>],{a,[b]},[a,b,c,d]), + Test({b,[a,c],0},<<"nodes">>,[<<"a">>,<<"b">>,<<"c">>],{b,[a]},[a,b,c,d]), %% Add two nodes and drop one - Test({a,[b,c]},<<"nodes">>,[<<"a">>,<<"b">>,<<"c">>],{a,[d]},[a,b,c,d]), + Test({a,[b,c],0},<<"nodes">>,[<<"a">>,<<"b">>,<<"c">>],{a,[d]},[a,b,c,d]), %% Promote slave to master by policy - Test({a,[b,c]},<<"nodes">>,[<<"a">>,<<"b">>,<<"c">>],{d,[a]},[a,b,c,d]), + Test({a,[b,c],0},<<"nodes">>,[<<"a">>,<<"b">>,<<"c">>],{d,[a]},[a,b,c,d]), %% Don't try to include nodes that are not running - Test({a,[b]}, <<"nodes">>,[<<"a">>,<<"b">>,<<"f">>],{a,[b]},[a,b,c,d]), + Test({a,[b], 0},<<"nodes">>,[<<"a">>,<<"b">>,<<"f">>],{a,[b]},[a,b,c,d]), %% If we can't find any of the nodes listed then just keep the master - Test({a,[]}, <<"nodes">>,[<<"f">>,<<"g">>,<<"h">>],{a,[b]},[a,b,c,d]), + Test({a,[], 0},<<"nodes">>,[<<"f">>,<<"g">>,<<"h">>],{a,[b]},[a,b,c,d]), - Test({a,[b]}, <<"exactly">>,2,{a,[]}, [a,b,c,d]), - Test({a,[b,c]},<<"exactly">>,3,{a,[]}, [a,b,c,d]), - Test({a,[c]}, <<"exactly">>,2,{a,[c]}, [a,b,c,d]), - Test({a,[b,c]},<<"exactly">>,3,{a,[c]}, [a,b,c,d]), - Test({a,[c]}, <<"exactly">>,2,{a,[c,d]},[a,b,c,d]), - Test({a,[c,d]},<<"exactly">>,3,{a,[c,d]},[a,b,c,d]), + Test({a,[], 1},<<"exactly">>,2,{a,[]}, [a,b,c,d]), + Test({a,[], 2},<<"exactly">>,3,{a,[]}, [a,b,c,d]), + Test({a,[c], 0},<<"exactly">>,2,{a,[c]}, [a,b,c,d]), + Test({a,[c], 1},<<"exactly">>,3,{a,[c]}, [a,b,c,d]), + Test({a,[c], 0},<<"exactly">>,2,{a,[c,d]},[a,b,c,d]), + Test({a,[c,d],0},<<"exactly">>,3,{a,[c,d]},[a,b,c,d]), passed. +%% Does the first list match the second where the second is required +%% to have exactly Extra superfluous items? +dm_list_match([], [], 0) -> ok; +dm_list_match(_, [], _Extra) -> error; +dm_list_match([H|T1], [H |T2], Extra) -> dm_list_match(T1, T2, Extra); +dm_list_match(L1, [H2|T2], Extra) -> dm_list_match(L1, T2, Extra - 1). + test_user_management() -> %% lots if stuff that should fail -- cgit v1.2.1 From 1dc23eceda34925a4d968e32150ad2858db53daf Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 4 Oct 2012 17:58:49 +0100 Subject: Clearer --- src/rabbit_mirror_queue_misc.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 150b0899..00cb170a 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -267,8 +267,8 @@ suggested_queue_nodes(<<"nodes">>, Nodes0, {MNode, _SNodes}, All) -> end; %% When we need to add nodes, we randomise our candidate list as a %% crude form of load-balancing. TODO it would also be nice to -%% randomise when we have too many - but that would fail to take -%% account of synchronisation... +%% randomise the list of ones to remove when we have too many - but +%% that would fail to take account of synchronisation... suggested_queue_nodes(<<"exactly">>, Count, {MNode, SNodes}, All) -> SCount = Count - 1, {MNode, case SCount > length(SNodes) of -- cgit v1.2.1 From 97627f1267503de270de1a42c7f955243663cc80 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 4 Oct 2012 18:18:35 +0100 Subject: That wasn't actually very random --- src/rabbit_mirror_queue_misc.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 00cb170a..d634e6eb 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -280,6 +280,7 @@ suggested_queue_nodes(_, _, {MNode, _}, _) -> {MNode, []}. shuffle(L) -> + random:seed(now()), {_, L1} = lists:unzip(lists:keysort(1, [{random:uniform(), N} || N <- L])), L1. -- cgit v1.2.1 From 99210638a83565ae112ea8a73fbcc243c948e927 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Fri, 5 Oct 2012 13:37:05 +0100 Subject: treat cluster consistency check failures as boot errors --- src/rabbit.erl | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 7b417b00..7c669599 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -301,7 +301,7 @@ start() -> %% mnesia after just restarting the app ok = ensure_application_loaded(), ok = rabbit_node_monitor:prepare_cluster_status_files(), - ok = rabbit_mnesia:check_cluster_consistency(), + run_cluster_consistency_check(), ok = ensure_working_log_handlers(), ok = app_utils:start_applications( app_startup_order(), fun handle_app_error/2), @@ -318,7 +318,7 @@ boot() -> %% It's important that the consistency check happens after %% the upgrade, since if we are a secondary node the %% primary node will have forgotten us - ok = rabbit_mnesia:check_cluster_consistency(), + run_cluster_consistency_check(), Plugins = rabbit_plugins:setup(), ToBeLoaded = Plugins ++ ?APPS, ok = app_utils:load_applications(ToBeLoaded), @@ -329,6 +329,13 @@ boot() -> ok = print_plugin_info(Plugins) end). +run_cluster_consistency_check() -> + try + ok = rabbit_mnesia:check_cluster_consistency() + catch + _:Reason -> boot_error(Reason, erlang:get_stacktrace()) + end. + handle_app_error(App, {bad_return, {_MFA, {'EXIT', {Reason, _}}}}) -> boot_error({could_not_start, App, Reason}, not_available); @@ -531,8 +538,16 @@ boot_error({error, {timeout_waiting_for_tables, _}}, _Stacktrace) -> end, basic_boot_error(Err ++ rabbit_nodes:diagnostics(Nodes) ++ "~n~n", []); +boot_error({Tag, [H|_]=Message}=Reason, Stacktrace) when is_atom(Tag) andalso + is_integer(H) -> + ErrorFmt = "~s: ~s", + boot_error(ErrorFmt, Reason, Stacktrace); boot_error(Reason, Stacktrace) -> - Fmt = "Error description:~n ~p~n~n" + ErrorFmt = "~p", + boot_error(ErrorFmt, Reason, Stacktrace). + +boot_error(ErrorFmt, Reason, Stacktrace) -> + Fmt = "Error description:~n " ++ ErrorFmt ++ "~n~n" ++ "Log files (may contain more information):~n ~s~n ~s~n~n", Args = [Reason, log_location(kernel), log_location(sasl)], case Stacktrace of -- cgit v1.2.1 From 2ba276d3b9a5042eeaf6fb903af036519949618a Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Fri, 5 Oct 2012 13:43:27 +0100 Subject: oops --- src/rabbit.erl | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 7c669599..4d3146c9 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -538,18 +538,19 @@ boot_error({error, {timeout_waiting_for_tables, _}}, _Stacktrace) -> end, basic_boot_error(Err ++ rabbit_nodes:diagnostics(Nodes) ++ "~n~n", []); -boot_error({Tag, [H|_]=Message}=Reason, Stacktrace) when is_atom(Tag) andalso +boot_error({Tag, [H|_]=Message}, Stacktrace) when is_atom(Tag) andalso is_integer(H) -> - ErrorFmt = "~s: ~s", - boot_error(ErrorFmt, Reason, Stacktrace); + Fmt = "Error description:~n ~s: ~s~n~n" ++ + "Log files (may contain more information):~n ~s~n ~s~n~n", + Args = [Tag, Message, log_location(kernel), log_location(sasl)], + boot_error(Fmt, Args, Stacktrace); boot_error(Reason, Stacktrace) -> - ErrorFmt = "~p", - boot_error(ErrorFmt, Reason, Stacktrace). - -boot_error(ErrorFmt, Reason, Stacktrace) -> - Fmt = "Error description:~n " ++ ErrorFmt ++ "~n~n" ++ + Fmt = "Error description:~n ~p~n~n" ++ "Log files (may contain more information):~n ~s~n ~s~n~n", Args = [Reason, log_location(kernel), log_location(sasl)], + boot_error(Fmt, Args, Stacktrace). + +boot_error(Fmt, Args, Stacktrace) -> case Stacktrace of not_available -> basic_boot_error(Fmt, Args); _ -> basic_boot_error(Fmt ++ "Stack trace:~n ~p~n~n", -- cgit v1.2.1 From 7ce9e388db84f591f539e0245fa05c8563f8daed Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 5 Oct 2012 14:13:10 +0100 Subject: Do not start slaves in init_with_existing_bq/3 since we will only get here via rabbit_mirror_queue_misc:update_mirrors/2 which will start them anyway. --- src/rabbit_mirror_queue_master.erl | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index 15ab9424..ea98430c 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -88,18 +88,19 @@ stop() -> %% Same as start/1. exit({not_valid_for_generic_backing_queue, ?MODULE}). -init(Q, Recover, AsyncCallback) -> +init(Q = #amqqueue{name = QName}, Recover, AsyncCallback) -> {ok, BQ} = application:get_env(backing_queue_module), BQS = BQ:init(Q, Recover, AsyncCallback), - init_with_existing_bq(Q, BQ, BQS). + State = #state{gm = GM} = init_with_existing_bq(Q, BQ, BQS), + {_MNode, SNodes} = rabbit_mirror_queue_misc:suggested_queue_nodes(Q), + rabbit_mirror_queue_misc:add_mirrors(QName, SNodes), + ok = gm:broadcast(GM, {depth, BQ:depth(BQS)}), + State. -init_with_existing_bq(#amqqueue { name = QName } = Q, BQ, BQS) -> +init_with_existing_bq(Q, BQ, BQS) -> {ok, CPid} = rabbit_mirror_queue_coordinator:start_link( Q, undefined, sender_death_fun(), depth_fun()), GM = rabbit_mirror_queue_coordinator:get_gm(CPid), - {_MNode, SNodes} = rabbit_mirror_queue_misc:suggested_queue_nodes(Q), - rabbit_mirror_queue_misc:add_mirrors(QName, SNodes), - ok = gm:broadcast(GM, {depth, BQ:depth(BQS)}), #state { gm = GM, coordinator = CPid, backing_queue = BQ, -- cgit v1.2.1 From c07bcfd2c01bc5ae71977cf181b31547cacb34c9 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Fri, 5 Oct 2012 14:25:29 +0100 Subject: also handle cluster status file prep failures --- src/rabbit.erl | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 4d3146c9..1f2a3e4e 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -300,8 +300,10 @@ start() -> %% We do not want to HiPE compile or upgrade %% mnesia after just restarting the app ok = ensure_application_loaded(), - ok = rabbit_node_monitor:prepare_cluster_status_files(), - run_cluster_consistency_check(), + apply_post_boot_step( + fun rabbit_node_monitor:prepare_cluster_status_files/0), + apply_post_boot_step( + fun rabbit_mnesia:check_cluster_consistency/0), ok = ensure_working_log_handlers(), ok = app_utils:start_applications( app_startup_order(), fun handle_app_error/2), @@ -312,13 +314,15 @@ boot() -> start_it(fun() -> ok = ensure_application_loaded(), maybe_hipe_compile(), - ok = rabbit_node_monitor:prepare_cluster_status_files(), + apply_post_boot_step( + fun rabbit_node_monitor:prepare_cluster_status_files/0), ok = ensure_working_log_handlers(), ok = rabbit_upgrade:maybe_upgrade_mnesia(), %% It's important that the consistency check happens after %% the upgrade, since if we are a secondary node the %% primary node will have forgotten us - run_cluster_consistency_check(), + apply_post_boot_step( + fun rabbit_mnesia:check_cluster_consistency/0), Plugins = rabbit_plugins:setup(), ToBeLoaded = Plugins ++ ?APPS, ok = app_utils:load_applications(ToBeLoaded), @@ -329,9 +333,9 @@ boot() -> ok = print_plugin_info(Plugins) end). -run_cluster_consistency_check() -> +apply_post_boot_step(Step) -> try - ok = rabbit_mnesia:check_cluster_consistency() + ok = Step() catch _:Reason -> boot_error(Reason, erlang:get_stacktrace()) end. -- cgit v1.2.1 From 728867cfcde914119f681ffa4ead657f28b89b1e Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 5 Oct 2012 14:26:19 +0100 Subject: Don't fail if the queue is already mirrored, tweak return values a bit. --- src/rabbit_mirror_queue_misc.erl | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 453f2f2c..b0226bcb 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -115,7 +115,7 @@ on_node_up() -> end end, [], rabbit_queue) end), - [ok = add_mirror(QName, node()) || QName <- QNames], + [{ok, _} = add_mirror(QName, node()) || QName <- QNames], ok. drop_mirrors(QName, Nodes) -> @@ -141,7 +141,7 @@ drop_mirror(QName, MirrorNode) -> end). add_mirrors(QName, Nodes) -> - [ok = add_mirror(QName, Node) || Node <- Nodes], + [{ok, _} = add_mirror(QName, Node) || Node <- Nodes], ok. add_mirror(QName, MirrorNode) -> @@ -154,8 +154,7 @@ add_mirror(QName, MirrorNode) -> [SPid] -> case rabbit_misc:is_process_alive(SPid) of true -> - {error,{queue_already_mirrored_on_node, - MirrorNode}}; + {ok, already_mirrored}; false -> start_child(Name, MirrorNode, Q) end @@ -171,20 +170,20 @@ start_child(Name, MirrorNode, Q) -> {ok, undefined} -> %% this means the mirror process was %% already running on the given node. - ok; + {ok, already_mirrored}; {ok, down} -> %% Node went down between us deciding to start a mirror %% and actually starting it. Which is fine. - ok; + {ok, node_down}; {ok, SPid} -> rabbit_log:info("Adding mirror of ~s on node ~p: ~p~n", [rabbit_misc:rs(Name), MirrorNode, SPid]), - ok; + {ok, started}; {error, {{stale_master_pid, StalePid}, _}} -> rabbit_log:warning("Detected stale HA master while adding " "mirror of ~s on node ~p: ~p~n", [rabbit_misc:rs(Name), MirrorNode, StalePid]), - ok; + {ok, stale_master}; {error, {{duplicate_live_master, _}=Err, _}} -> Err; Other -> -- cgit v1.2.1 From 03a464ba0f74c146101f5ad917fdbbaa7fcaa7df Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Fri, 5 Oct 2012 14:27:45 +0100 Subject: start log handlers first, so we see the output only once --- src/rabbit.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 1f2a3e4e..96c1c1bf 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -300,11 +300,11 @@ start() -> %% We do not want to HiPE compile or upgrade %% mnesia after just restarting the app ok = ensure_application_loaded(), + ok = ensure_working_log_handlers(), apply_post_boot_step( fun rabbit_node_monitor:prepare_cluster_status_files/0), apply_post_boot_step( fun rabbit_mnesia:check_cluster_consistency/0), - ok = ensure_working_log_handlers(), ok = app_utils:start_applications( app_startup_order(), fun handle_app_error/2), ok = print_plugin_info(rabbit_plugins:active()) @@ -314,9 +314,9 @@ boot() -> start_it(fun() -> ok = ensure_application_loaded(), maybe_hipe_compile(), + ok = ensure_working_log_handlers(), apply_post_boot_step( fun rabbit_node_monitor:prepare_cluster_status_files/0), - ok = ensure_working_log_handlers(), ok = rabbit_upgrade:maybe_upgrade_mnesia(), %% It's important that the consistency check happens after %% the upgrade, since if we are a secondary node the -- cgit v1.2.1 From a84fdd6359480b3b0ab80fe7e1bf3e603acc69d4 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Fri, 5 Oct 2012 14:50:23 +0100 Subject: don't try too hard to get the output errors looking nice --- src/rabbit.erl | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 96c1c1bf..ddaaeb25 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -541,13 +541,6 @@ boot_error({error, {timeout_waiting_for_tables, _}}, _Stacktrace) -> Ns} end, basic_boot_error(Err ++ rabbit_nodes:diagnostics(Nodes) ++ "~n~n", []); - -boot_error({Tag, [H|_]=Message}, Stacktrace) when is_atom(Tag) andalso - is_integer(H) -> - Fmt = "Error description:~n ~s: ~s~n~n" ++ - "Log files (may contain more information):~n ~s~n ~s~n~n", - Args = [Tag, Message, log_location(kernel), log_location(sasl)], - boot_error(Fmt, Args, Stacktrace); boot_error(Reason, Stacktrace) -> Fmt = "Error description:~n ~p~n~n" ++ "Log files (may contain more information):~n ~s~n ~s~n~n", -- cgit v1.2.1 From 4e8e0fa18061a09a074e7975fd39a4003cb9b8ca Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Fri, 5 Oct 2012 18:20:10 +0100 Subject: Updated policy validation --- docs/rabbitmqctl.1.xml | 79 +++++++++++++++++++++++++++++++++- src/rabbit_control_main.erl | 9 ++-- src/rabbit_mirror_queue_misc.erl | 46 +++++++++++++++++++- src/rabbit_policy.erl | 42 ++++++++++++------ src/rabbit_policy_validator.erl | 4 +- src/rabbit_runtime_parameters.erl | 39 +++++++++++++++-- src/rabbit_runtime_parameters_test.erl | 35 ++++++++++++--- src/rabbit_tests.erl | 38 ++++++++++------ 8 files changed, 248 insertions(+), 44 deletions(-) diff --git a/docs/rabbitmqctl.1.xml b/docs/rabbitmqctl.1.xml index 73347cea..eea42484 100644 --- a/docs/rabbitmqctl.1.xml +++ b/docs/rabbitmqctl.1.xml @@ -637,7 +637,7 @@ Deleting a virtual host deletes all its exchanges, - queues, bindings, user permissions and parameters. + queues, bindings, user permissions, parameters and policies. For example: rabbitmqctl delete_vhost test @@ -894,6 +894,83 @@ + + Policy Management + + Policies are used to control and modify the behaviour of queues + and exchanges on a cluster-wide basis. Policies apply within a + given vhost, and consist of a key and a value. The key must be a + string and the value must be a JSON string. Policies can be set, + cleared and listed. + + + + set_policy -p vhostpath key value + + + Sets a policy. + + + + key + + The name of the policy. + + + + value + + The definition of the policy, as a + JSON string. In most shells you are very likely to + need to quote this. + + + + For example: + rabbitmqctl set_policy federate-me '{"pattern":"^amq.",\ + "policy":{"federation-upstream-set":"all"}}' + + This command sets the policy federate-me in the default virtual host so that built-in exchanges are federated. + + + + + clear_policy -p vhostpath key + + + Clears a policy. + + + + key + + The name of the policy being cleared. + + + + For example: + rabbitmqctl clear_policy federate-me + + This command clears the federate-me policy in the default virtual host. + + + + + list_policies -p vhostpath + + + Lists all policies for a virtual host. + + For example: + rabbitmqctl list_policies + + This command lists all policies in the default virtual host. + + + + + + Server Status diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index 1efde136..15fa1fd5 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -465,15 +465,14 @@ action(list_parameters, Node, [], Opts, Inform) -> action(set_policy, Node, [Key, Value], Opts, Inform) -> VHostArg = list_to_binary(proplists:get_value(?VHOST_OPT, Opts)), Inform("Setting policy ~p to ~p", [Key, Value]), - rpc_call(Node, rabbit_runtime_parameters, parse_set, - [VHostArg, <<"policy">>, list_to_binary(Key), Value]); + rpc_call(Node, rabbit_runtime_parameters, parse_set_policy, + [VHostArg, list_to_binary(Key), Value]); action(clear_policy, Node, [Key], Opts, Inform) -> VHostArg = list_to_binary(proplists:get_value(?VHOST_OPT, Opts)), Inform("Clearing policy ~p", [Key]), - rpc_call(Node, rabbit_runtime_parameters, clear, [VHostArg, - <<"policy">>, - list_to_binary(Key)]); + rpc_call(Node, rabbit_runtime_parameters, clear_policy, + [VHostArg, list_to_binary(Key)]); action(list_policies, Node, [], Opts, Inform) -> VHostArg = list_to_binary(proplists:get_value(?VHOST_OPT, Opts)), diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 453f2f2c..f02308a1 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -15,16 +15,26 @@ %% -module(rabbit_mirror_queue_misc). +-behaviour(rabbit_policy_validator). -export([remove_from_queue/2, on_node_up/0, add_mirrors/2, add_mirror/2, report_deaths/4, store_updated_slaves/1, suggested_queue_nodes/1, - is_mirrored/1, update_mirrors/2]). + is_mirrored/1, update_mirrors/2, validate_policy/1]). %% for testing only -export([suggested_queue_nodes/4]). -include("rabbit.hrl"). +-rabbit_boot_step({?MODULE, + [{description, "HA policy validator hook"}, + {mfa, {rabbit_registry, register, + [policy_validator, <<"ha-mode">>, ?MODULE]}}, + {mfa, {rabbit_registry, register, + [policy_validator, <<"ha-params">>, ?MODULE]}}, + {requires, rabbit_registry}, + {enables, recovery}]}). + %%---------------------------------------------------------------------------- -ifdef(use_specs). @@ -320,3 +330,37 @@ update_mirrors0(OldQ = #amqqueue{name = QName}, add_mirrors(QName, NewNodes -- OldNodes), drop_mirrors(QName, OldNodes -- NewNodes), ok. + +%%---------------------------------------------------------------------------- + +validate_policy(TagList) -> + Mode = proplists:get_all_values(<<"ha-mode">>, TagList), + Params = proplists:get_all_values(<<"ha-params">>, TagList), + case Mode of + [<<"all">>] -> + ok; + [<<"nodes">>] -> + validate_params(fun erlang:is_binary/1, lists:append(Params), + "~p has invalid node names when ha-mode=nodes"); + [<<"exactly">>] -> + case Params of + [_] -> validate_params( + fun (N) -> is_integer(N) andalso N >= 0 end, + Params, "~p must be a positive integer"); + X -> {error, "ha-params must be supplied with one number " + "when ha-mode=exactly. found ~p arguments", + [length(X)]} + end; + [_, _|_] -> + {error, "ha-mode may appear once at most", []}; + [Other] -> + {error, "~p is not a valid ha-mode value", [Other]} + end. + +validate_params(FilterFun, Params, Msg) when is_list(Params) -> + case lists:filter(fun (P) -> not FilterFun(P) end, Params) of + [] -> ok; + X -> {error, Msg, [X]} + end; +validate_params(_, Params, _) -> + {error, "~p was expected to be a list", [Params]}. diff --git a/src/rabbit_policy.erl b/src/rabbit_policy.erl index c9af46e9..5540de83 100644 --- a/src/rabbit_policy.erl +++ b/src/rabbit_policy.erl @@ -139,21 +139,35 @@ policy_validation() -> {<<"pattern">>, fun rabbit_parameter_validation:regex/2, mandatory}, {<<"policy">>, fun validation/2, mandatory}]. +validation(_Name, []) -> + ok; validation(_Name, Terms) when is_list(Terms) -> - [validation0(T) || T <- Terms ]; -validation(Name, Term) -> - {error, "~s should be list, actually was ~p", [Name, Term]}. - -validation0({Key, Value}) when is_binary(Key) -> - case rabbit_registry:lookup_module(policy_validator, - list_to_atom(binary_to_list(Key))) of - {ok, Mod} -> - Mod:validate_policy(Key, Value); - {error, not_found} -> - {error, "~p is not a recognised policy option", [Key]}; - Error -> - Error + {Tags, Modules} = lists:unzip( + rabbit_registry:lookup_all(policy_validator)), + case lists:usort(Tags -- lists:usort(Tags)) of + [] -> ok; + Dup -> rabbit_log:warning("Duplicate policy validators: ~p~n", [Dup]) + end, + Validators = lists:zipwith(fun (M, T) -> {M, a2b(T)} end, Modules, Tags), + case lists:foldl( + fun (_, {Error, _} = Acc) when Error /= ok -> + Acc; + (Mod, {ok, TermsLeft}) -> + ModTags = proplists:get_all_values(Mod, Validators), + case [T || {Tag, _} = T <- TermsLeft, + lists:member(Tag, ModTags)] of + [] -> {ok, TermsLeft}; + Scope -> {Mod:validate_policy(Scope), TermsLeft -- Scope} + end + end, {ok, Terms}, proplists:get_keys(Validators)) of + {ok, []} -> + ok; + {ok, Unvalidated} -> + {error, "~p are not recognised policy settings", Unvalidated}; + {Error, _} -> + Error end; -validation0(Term) -> +validation(_Name, Term) -> {error, "parse error while reading policy: ~p", [Term]}. +a2b(A) -> list_to_binary(atom_to_list(A)). diff --git a/src/rabbit_policy_validator.erl b/src/rabbit_policy_validator.erl index 3cc02ecc..624c3d54 100644 --- a/src/rabbit_policy_validator.erl +++ b/src/rabbit_policy_validator.erl @@ -21,7 +21,7 @@ -type(validate_results() :: 'ok' | {error, string(), [term()]} | [validate_results()]). --callback validate_policy(binary(), term()) -> validate_results(). +-callback validate_policy([{binary(), term()}]) -> validate_results(). -else. @@ -29,7 +29,7 @@ behaviour_info(callbacks) -> [ - {validate_policy, 2}, + {validate_policy, 1}, ]; behaviour_info(_Other) -> undefined. diff --git a/src/rabbit_runtime_parameters.erl b/src/rabbit_runtime_parameters.erl index c4608b42..70428e96 100644 --- a/src/rabbit_runtime_parameters.erl +++ b/src/rabbit_runtime_parameters.erl @@ -18,9 +18,10 @@ -include("rabbit.hrl"). --export([parse_set/4, set/4, clear/3, - list/0, list/1, list_strict/1, list/2, list_strict/2, list_formatted/1, - list_formatted_policies/1, lookup/3, value/3, value/4, info_keys/0]). +-export([parse_set/4, parse_set_policy/3, set/4, set_policy/3, clear/3, + clear_policy/2, list/0, list/1, list_strict/1, list/2, list_strict/2, + list_formatted/1, list_formatted_policies/1, lookup/3, value/3, + value/4, info_keys/0]). %%---------------------------------------------------------------------------- @@ -30,10 +31,16 @@ -spec(parse_set/4 :: (rabbit_types:vhost(), binary(), binary(), string()) -> ok_or_error_string()). +-spec(parse_set_policy/3 :: (rabbit_types:vhost(), binary(), string()) + -> ok_or_error_string()). -spec(set/4 :: (rabbit_types:vhost(), binary(), binary(), term()) -> ok_or_error_string()). +-spec(set_policy/3 :: (rabbit_types:vhost(), binary(), term()) + -> ok_or_error_string()). -spec(clear/3 :: (rabbit_types:vhost(), binary(), binary()) -> ok_or_error_string()). +-spec(clear_policy/2 :: (rabbit_types:vhost(), binary()) + -> ok_or_error_string()). -spec(list/0 :: () -> [rabbit_types:infos()]). -spec(list/1 :: (rabbit_types:vhost()) -> [rabbit_types:infos()]). -spec(list_strict/1 :: (binary()) -> [rabbit_types:infos()] | 'not_found'). @@ -59,18 +66,36 @@ %%--------------------------------------------------------------------------- +parse_set(_, <<"policy">>, _, _) -> + {error_string, "policies may not be set using this method"}; parse_set(VHost, Component, Key, String) -> case rabbit_misc:json_decode(String) of {ok, JSON} -> set(VHost, Component, Key, rabbit_misc:json_to_term(JSON)); error -> {error_string, "JSON decoding error"} end. +parse_set_policy(VHost, Key, String) -> + case rabbit_misc:json_decode(String) of + {ok, JSON} -> + set_policy(VHost, Key, rabbit_misc:json_to_term(JSON)); + error -> + {error_string, "JSON decoding error"} + end. + +set(_, <<"policy">>, _, _) -> + {error_string, "policies may not be set using this method"}; set(VHost, Component, Key, Term) -> case set0(VHost, Component, Key, Term) of ok -> ok; {errors, L} -> format_error(L) end. +set_policy(VHost, Key, Term) -> + case set0(VHost, <<"policy">>, Key, Term) of + ok -> ok; + {errors, L} -> format_error(L) + end. + format_error(L) -> {error_string, rabbit_misc:format_many([{"Validation failed~n", []} | L])}. @@ -102,12 +127,20 @@ mnesia_update(VHost, Component, Key, Term) -> Res end). +clear(_, <<"policy">> , _) -> + {error_string, "policies may not be cleared using this method"}; clear(VHost, Component, Key) -> case clear0(VHost, Component, Key) of ok -> ok; {errors, L} -> format_error(L) end. +clear_policy(VHost, Key) -> + case clear0(VHost, <<"policy">>, Key) of + ok -> ok; + {errors, L} -> format_error(L) + end. + clear0(VHost, Component, Key) -> case lookup_component(Component) of {ok, Mod} -> case flatten_errors( diff --git a/src/rabbit_runtime_parameters_test.erl b/src/rabbit_runtime_parameters_test.erl index 4ac19ff1..b39a98bc 100644 --- a/src/rabbit_runtime_parameters_test.erl +++ b/src/rabbit_runtime_parameters_test.erl @@ -20,7 +20,7 @@ -export([validate/4, validate_clear/3, notify/4, notify_clear/3]). -export([register/0, unregister/0]). --export([validate_policy/2]). +-export([validate_policy/1]). -export([register_policy_validator/0, unregister_policy_validator/0]). %---------------------------------------------------------------------------- @@ -45,16 +45,39 @@ notify_clear(_, _, _) -> ok. %---------------------------------------------------------------------------- register_policy_validator() -> - rabbit_registry:register(policy_validator, <<"testpolicy">>, ?MODULE). + rabbit_registry:register(policy_validator, <<"testeven">>, ?MODULE), + rabbit_registry:register(policy_validator, <<"testpos">>, ?MODULE). unregister_policy_validator() -> - rabbit_registry:unregister(policy_validator, <<"testpolicy">>). + rabbit_registry:unregister(policy_validator, <<"testeven">>), + rabbit_registry:unregister(policy_validator, <<"testpos">>). -validate_policy(<<"testpolicy">>, Terms) when is_list(Terms) -> - rabbit_log:info("pol val ~p~n", [Terms]), +validate_policy([{<<"testeven">>, Terms}]) when is_list(Terms) -> case length(Terms) rem 2 =:= 0 of true -> ok; false -> {error, "meh", []} end; -validate_policy(<<"testpolicy">>, _) -> + +validate_policy([{<<"testpos">>, Terms}]) when is_list(Terms) -> + case lists:all(fun (N) -> is_integer(N) andalso N > 0 end, Terms) of + true -> ok; + false -> {error, "meh", []} + end; + +validate_policy([{Tag1, Arg1}, {Tag2, Arg2}]) + when is_list(Arg1), is_list(Arg2) -> + case [Tag1, Tag2] -- [<<"testpos">>, <<"testeven">>] of + [] -> + case {lists:all(fun (N) -> + is_integer(N) andalso + N > 0 + end, Arg1 ++ Arg2), + length(Arg1) rem 2, length(Arg2) rem 2} of + {true, 0, 0} -> ok; + _ -> {error, "meh", []} + end; + _ -> {error, "meh", []} + end; + +validate_policy(_) -> {error, "meh", []}. diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index a7eab2d5..9550a482 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -1031,18 +1031,31 @@ test_runtime_parameters() -> test_policy_validation() -> rabbit_runtime_parameters_test:register_policy_validator(), - SetPol = fun (Pol, Val) -> - control_action( - set_policy, - ["name", lists:flatten( - io_lib:format("{\"pattern\":\"pat\", \"policy\":" - "{\"~s\":~p}}", [Pol, Val]))]) - end, - ok = SetPol("testpolicy", []), - ok = SetPol("testpolicy", [1, 2]), - ok = SetPol("testpolicy", [1, 2, 3, 4]), - {error_string, _} = SetPol("testpolicy", [1, 2, 3]), - {error_string, _} = SetPol("not_registered", []), + SetPol = + fun (TagValList) -> + Frag = lists:foldl( + fun ({Pol, Val}, Acc) -> + [rabbit_misc:format("\"~s\":~p", [Pol, Val]) | + Acc] + end, "", TagValList), + control_action( + set_policy, + ["name", rabbit_misc:format("{\"pattern\":\".*\", \"policy\":" + "{~s}}", [string:join(Frag, ",")])]) + end, + + ok = SetPol([{"testeven", []}]), + ok = SetPol([{"testeven", [1, 2]}]), + ok = SetPol([{"testeven", [1, 2, 3, 4]}]), + ok = SetPol([{"testpos", [2, 3, 5, 562]}]), + + {error_string, _} = SetPol([{"testpos", [-1, 0, 1]}]), + {error_string, _} = SetPol([{"testeven", [ 1, 2, 3]}]), + + ok = SetPol([{"testpos", [2, 16]}, {"testeven", [12, 24]}]), + {error_string, _} = SetPol([{"testpos", [2, 16, 32]}, {"testeven", [12, 24]}]), + {error_string, _} = SetPol([{"testpos", [2, 16]}, {"testeven", [12, -2]}]), + {error_string, _} = SetPol([{"not_registered", []}]), rabbit_runtime_parameters_test:unregister_policy_validator(). test_server_status() -> @@ -1481,6 +1494,7 @@ test_declare_on_dead_queue(SecondaryNode) -> %%--------------------------------------------------------------------- control_action(Command, Args) -> +rabbit_log:info("control args ~p~n", [Args]), control_action(Command, node(), Args, default_options()). control_action(Command, Args, NewOpts) -> -- cgit v1.2.1 From 2f51465dd826a646928db7c2b3f50214cad32d4e Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 8 Oct 2012 12:24:23 +0100 Subject: wrap the whole of start/boot in the same error check --- src/rabbit.erl | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index ddaaeb25..1eba733b 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -301,10 +301,8 @@ start() -> %% mnesia after just restarting the app ok = ensure_application_loaded(), ok = ensure_working_log_handlers(), - apply_post_boot_step( - fun rabbit_node_monitor:prepare_cluster_status_files/0), - apply_post_boot_step( - fun rabbit_mnesia:check_cluster_consistency/0), + rabbit_node_monitor:prepare_cluster_status_files(), + rabbit_mnesia:check_cluster_consistency(), ok = app_utils:start_applications( app_startup_order(), fun handle_app_error/2), ok = print_plugin_info(rabbit_plugins:active()) @@ -315,14 +313,12 @@ boot() -> ok = ensure_application_loaded(), maybe_hipe_compile(), ok = ensure_working_log_handlers(), - apply_post_boot_step( - fun rabbit_node_monitor:prepare_cluster_status_files/0), + rabbit_node_monitor:prepare_cluster_status_files(), ok = rabbit_upgrade:maybe_upgrade_mnesia(), %% It's important that the consistency check happens after %% the upgrade, since if we are a secondary node the %% primary node will have forgotten us - apply_post_boot_step( - fun rabbit_mnesia:check_cluster_consistency/0), + rabbit_mnesia:check_cluster_consistency(), Plugins = rabbit_plugins:setup(), ToBeLoaded = Plugins ++ ?APPS, ok = app_utils:load_applications(ToBeLoaded), @@ -333,22 +329,20 @@ boot() -> ok = print_plugin_info(Plugins) end). -apply_post_boot_step(Step) -> - try - ok = Step() - catch - _:Reason -> boot_error(Reason, erlang:get_stacktrace()) - end. - handle_app_error(App, {bad_return, {_MFA, {'EXIT', {Reason, _}}}}) -> - boot_error({could_not_start, App, Reason}, not_available); + throw({could_not_start, App, Reason}); handle_app_error(App, Reason) -> - boot_error({could_not_start, App, Reason}, not_available). + throw({could_not_start, App, Reason}). start_it(StartFun) -> try StartFun() + catch + throw:{could_not_start, _App, _Reason}=Err -> + boot_error(Err, not_available); + _:Reason -> + boot_error(Reason, erlang:get_stacktrace()) after %% give the error loggers some time to catch up timer:sleep(100) -- cgit v1.2.1 From c0b7cbaf64ebf76f57cadd24b6890485aa514d8f Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 8 Oct 2012 12:24:30 +0100 Subject: cosmetic: trains station --- src/rabbit_vm.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_vm.erl b/src/rabbit_vm.erl index 2b9b827b..c28584c4 100644 --- a/src/rabbit_vm.erl +++ b/src/rabbit_vm.erl @@ -44,7 +44,7 @@ memory() -> pid_memory(msg_store_persistent)), MgmtDbETS = ets_memory(rabbit_mgmt_db), MgmtDbProc = sup_memory(rabbit_mgmt_sup), - Plugins = plugins_memory() - MgmtDbProc, + Plugins = plugin_memory() - MgmtDbProc, [{total, Total}, {processes, Processes}, @@ -110,7 +110,7 @@ ets_memory(Name) -> bytes(Words) -> Words * erlang:system_info(wordsize). -plugins_memory() -> +plugin_memory() -> lists:sum([plugin_memory(App) || {App, _, _} <- application:which_applications(), is_plugin(atom_to_list(App))]). -- cgit v1.2.1 From 39490ea6d727429572f56a6cd8cf4858ca857e1d Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 8 Oct 2012 12:47:03 +0100 Subject: R12B-3 compatibility. --- src/rabbit_mirror_queue_misc.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index d634e6eb..9d701422 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -280,7 +280,8 @@ suggested_queue_nodes(_, _, {MNode, _}, _) -> {MNode, []}. shuffle(L) -> - random:seed(now()), + {A1,A2,A3} = now(), + random:seed(A1, A2, A3), {_, L1} = lists:unzip(lists:keysort(1, [{random:uniform(), N} || N <- L])), L1. -- cgit v1.2.1 From 7dde8e9f86777c48a0ece1056f8fe6e8a70cc55a Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 8 Oct 2012 15:51:29 +0100 Subject: include the error term in start/boot exits and refactor the tests to handle 'EXIT' instead of 'throw' for start/boot failures --- src/rabbit.erl | 23 ++++++++++++++--------- src/rabbit_tests.erl | 15 +++++++++++---- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 1eba733b..4876d679 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -503,13 +503,16 @@ sort_boot_steps(UnsortedSteps) -> not erlang:function_exported(M, F, length(A))] of [] -> SortedSteps; MissingFunctions -> basic_boot_error( + {missing_functions, MissingFunctions}, "Boot step functions not exported: ~p~n", [MissingFunctions]) end; {error, {vertex, duplicate, StepName}} -> - basic_boot_error("Duplicate boot step name: ~w~n", [StepName]); + basic_boot_error({duplicate_boot_step, StepName}, + "Duplicate boot step name: ~w~n", [StepName]); {error, {edge, Reason, From, To}} -> basic_boot_error( + {invalid_boot_step_dependency, From, To}, "Could not add boot step dependency of ~w on ~w:~n~s", [To, From, case Reason of @@ -523,7 +526,7 @@ sort_boot_steps(UnsortedSteps) -> end]) end. -boot_error({error, {timeout_waiting_for_tables, _}}, _Stacktrace) -> +boot_error(Term={error, {timeout_waiting_for_tables, _}}, _Stacktrace) -> AllNodes = rabbit_mnesia:cluster_nodes(all), {Err, Nodes} = case AllNodes -- [node()] of @@ -534,25 +537,27 @@ boot_error({error, {timeout_waiting_for_tables, _}}, _Stacktrace) -> "Timeout contacting cluster nodes: ~p.~n", [Ns]), Ns} end, - basic_boot_error(Err ++ rabbit_nodes:diagnostics(Nodes) ++ "~n~n", []); + basic_boot_error(Term, + Err ++ rabbit_nodes:diagnostics(Nodes) ++ "~n~n", []); boot_error(Reason, Stacktrace) -> Fmt = "Error description:~n ~p~n~n" ++ "Log files (may contain more information):~n ~s~n ~s~n~n", Args = [Reason, log_location(kernel), log_location(sasl)], - boot_error(Fmt, Args, Stacktrace). + boot_error(Reason, Fmt, Args, Stacktrace). -boot_error(Fmt, Args, Stacktrace) -> +boot_error(Reason, Fmt, Args, Stacktrace) -> case Stacktrace of - not_available -> basic_boot_error(Fmt, Args); - _ -> basic_boot_error(Fmt ++ "Stack trace:~n ~p~n~n", + not_available -> basic_boot_error(Reason, Fmt, Args); + _ -> basic_boot_error(Reason, Fmt ++ + "Stack trace:~n ~p~n~n", Args ++ [Stacktrace]) end. -basic_boot_error(Format, Args) -> +basic_boot_error(Reason, Format, Args) -> io:format("~n~nBOOT FAILED~n===========~n~n" ++ Format, Args), error_logger:error_msg(Format, Args), timer:sleep(1000), - exit({?MODULE, failure_during_boot}). + exit({?MODULE, failure_during_boot, Reason}). %%--------------------------------------------------------------------------- %% boot step functions diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 11f280bb..a7ac25c2 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -774,7 +774,9 @@ test_log_management_during_startup() -> ok = case catch control_action(start_app, []) of ok -> exit({got_success_but_expected_failure, log_rotation_tty_no_handlers_test}); - {error, {cannot_log_to_tty, _, _}} -> ok + {badrpc, {'EXIT', {rabbit,failure_during_boot, + {error,{cannot_log_to_tty, + _, not_installed}}}}} -> ok end, %% fix sasl logging @@ -798,7 +800,9 @@ test_log_management_during_startup() -> ok = case control_action(start_app, []) of ok -> exit({got_success_but_expected_failure, log_rotation_no_write_permission_dir_test}); - {error, {cannot_log_to_file, _, _}} -> ok + {badrpc, {'EXIT', + {rabbit, failure_during_boot, + {error, {cannot_log_to_file, _, _}}}}} -> ok end, %% start application with logging to a subdirectory which @@ -809,8 +813,11 @@ test_log_management_during_startup() -> ok = case control_action(start_app, []) of ok -> exit({got_success_but_expected_failure, log_rotatation_parent_dirs_test}); - {error, {cannot_log_to_file, _, - {error, {cannot_create_parent_dirs, _, eacces}}}} -> ok + {badrpc, + {'EXIT', {rabbit,failure_during_boot, + {error, {cannot_log_to_file, _, + {error, + {cannot_create_parent_dirs, _, eacces}}}}}}} -> ok end, ok = set_permissions(TmpDir, 8#00700), ok = set_permissions(TmpLog, 8#00600), -- cgit v1.2.1 From fea7363c2873941d2135f6a480e38b2f1c12f3c4 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 8 Oct 2012 15:55:57 +0100 Subject: WIP --- src/rabbit_mirror_queue_coordinator.erl | 3 +- src/rabbit_mirror_queue_misc.erl | 68 ++++++++++++++++----------------- src/rabbit_mirror_queue_slave.erl | 7 +--- 3 files changed, 35 insertions(+), 43 deletions(-) diff --git a/src/rabbit_mirror_queue_coordinator.erl b/src/rabbit_mirror_queue_coordinator.erl index 40359da3..676cb519 100644 --- a/src/rabbit_mirror_queue_coordinator.erl +++ b/src/rabbit_mirror_queue_coordinator.erl @@ -344,10 +344,9 @@ handle_cast({gm_deaths, Deaths}, State = #state { q = #amqqueue { name = QueueName, pid = MPid } }) when node(MPid) =:= node() -> case rabbit_mirror_queue_misc:remove_from_queue(QueueName, Deaths) of - {ok, MPid, DeadPids, ExtraNodes} -> + {ok, MPid, DeadPids} -> rabbit_mirror_queue_misc:report_deaths(MPid, true, QueueName, DeadPids), - rabbit_mirror_queue_misc:add_mirrors(QueueName, ExtraNodes), noreply(State); {error, not_found} -> {stop, normal, State} diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index b0226bcb..dbc4a253 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -31,7 +31,7 @@ -spec(remove_from_queue/2 :: (rabbit_amqqueue:name(), [pid()]) - -> {'ok', pid(), [pid()], [node()]} | {'error', 'not_found'}). + -> {'ok', pid(), [pid()]} | {'error', 'not_found'}). -spec(on_node_up/0 :: () -> 'ok'). -spec(add_mirrors/2 :: (rabbit_amqqueue:name(), [node()]) -> 'ok'). -spec(add_mirror/2 :: @@ -59,7 +59,6 @@ remove_from_queue(QueueName, DeadGMPids) -> DeadNodes = [node(DeadGMPid) || DeadGMPid <- DeadGMPids], - ClusterNodes = rabbit_mnesia:cluster_nodes(running) -- DeadNodes, rabbit_misc:execute_mnesia_transaction( fun () -> %% Someone else could have deleted the queue before we @@ -73,42 +72,45 @@ remove_from_queue(QueueName, DeadGMPids) -> {QPid1, SPids1} = promote_slave(Alive), case {{QPid, SPids}, {QPid1, SPids1}} of {Same, Same} -> - {ok, QPid1, [], []}; + {ok, QPid1, []}; _ when QPid =:= QPid1 orelse node(QPid1) =:= node() -> %% Either master hasn't changed, so %% we're ok to update mnesia; or we have %% become the master. - Q1 = store_updated_slaves( - Q #amqqueue { pid = QPid1, - slave_pids = SPids1 }), - %% Sometimes a slave dying means we need - %% to start more on other nodes - - %% "exactly" mode can cause this to - %% happen. - {_, OldNodes} = actual_queue_nodes(Q1), - {_, NewNodes} = suggested_queue_nodes( - Q1, ClusterNodes), - {ok, QPid1, [QPid | SPids] -- Alive, - NewNodes -- OldNodes}; + store_updated_slaves( + Q #amqqueue { pid = QPid1, + slave_pids = SPids1 }), + {ok, QPid1, [QPid | SPids] -- Alive}; _ -> %% Master has changed, and we're not it, %% so leave alone to allow the promoted %% slave to find it and make its %% promotion atomic. - {ok, QPid1, [], []} + {ok, QPid1, []} end end end). on_node_up() -> - ClusterNodes = rabbit_mnesia:cluster_nodes(running), QNames = rabbit_misc:execute_mnesia_transaction( fun () -> mnesia:foldl( - fun (Q = #amqqueue{name = QName}, QNames0) -> + fun (Q = #amqqueue{name = QName, + pid = Pid, + slave_pids = SPids}, QNames0) -> + %% We don't want to pass in the whole + %% cluster - we don't want a situation + %% where starting one node causes us to + %% decide to start a mirror on another + PossibleNodes0 = [node(P) || P <- [Pid | SPids]], + PossibleNodes = + case lists:member(node(), PossibleNodes0) of + true -> PossibleNodes0; + false -> [node() | PossibleNodes0] + end, {_MNode, SNodes} = suggested_queue_nodes( - Q, ClusterNodes), + Q, PossibleNodes), case lists:member(node(), SNodes) of true -> [QName | QNames0]; false -> QNames0 @@ -234,14 +236,17 @@ 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, ClusterNodes) -> +suggested_queue_nodes(Q, PossibleNodes) -> {MNode0, SNodes} = actual_queue_nodes(Q), MNode = case MNode0 of none -> node(); _ -> MNode0 end, - suggested_queue_nodes(policy(<<"ha-mode">>, Q), policy(<<"ha-params">>, Q), - {MNode, SNodes}, ClusterNodes). + R = suggested_queue_nodes(policy(<<"ha-mode">>, Q), policy(<<"ha-params">>, Q), + {MNode, SNodes}, PossibleNodes), + io:format("SQN: ~p~n", [{policy(<<"ha-mode">>, Q), policy(<<"ha-params">>, Q), + {MNode, SNodes}, PossibleNodes, R}]), + R. policy(Policy, Q) -> case rabbit_policy:get(Policy, Q) of @@ -249,11 +254,11 @@ policy(Policy, Q) -> _ -> none end. -suggested_queue_nodes(<<"all">>, _Params, {MNode, _SNodes}, All) -> - {MNode, All -- [MNode]}; -suggested_queue_nodes(<<"nodes">>, Nodes0, {MNode, _SNodes}, All) -> +suggested_queue_nodes(<<"all">>, _Params, {MNode, _SNodes}, Possible) -> + {MNode, Possible -- [MNode]}; +suggested_queue_nodes(<<"nodes">>, Nodes0, {MNode, _SNodes}, Possible) -> Nodes = [list_to_atom(binary_to_list(Node)) || Node <- Nodes0], - Unavailable = Nodes -- All, + Unavailable = Nodes -- Possible, Available = Nodes -- Unavailable, case Available of [] -> %% We have never heard of anything? Not much we can do but @@ -264,10 +269,10 @@ suggested_queue_nodes(<<"nodes">>, Nodes0, {MNode, _SNodes}, All) -> false -> promote_slave(Available) end end; -suggested_queue_nodes(<<"exactly">>, Count, {MNode, SNodes}, All) -> +suggested_queue_nodes(<<"exactly">>, Count, {MNode, SNodes}, Possible) -> SCount = Count - 1, {MNode, case SCount > length(SNodes) of - true -> Cand = (All -- [MNode]) -- SNodes, + true -> Cand = (Possible -- [MNode]) -- SNodes, SNodes ++ lists:sublist(Cand, SCount - length(SNodes)); false -> lists:sublist(SNodes, SCount) end}; @@ -309,13 +314,6 @@ update_mirrors0(OldQ = #amqqueue{name = QName}, All = fun ({A,B}) -> [A|B] end, OldNodes = All(actual_queue_nodes(OldQ)), NewNodes = All(suggested_queue_nodes(NewQ)), - %% When a mirror dies, remove_from_queue/2 might have to add new - %% slaves (in "exactly" mode). It will check mnesia to see which - %% slaves there currently are. If drop_mirror/2 is invoked first - %% then when we end up in remove_from_queue/2 it will not see the - %% slaves that add_mirror/2 will add, and also want to add them - %% (even though we are not responding to the death of a - %% mirror). Breakage ensues. add_mirrors(QName, NewNodes -- OldNodes), drop_mirrors(QName, OldNodes -- NewNodes), ok. diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 307f2b4f..61423202 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -181,25 +181,20 @@ handle_call({gm_deaths, Deaths}, From, {error, not_found} -> gen_server2:reply(From, ok), {stop, normal, State}; - {ok, Pid, DeadPids, ExtraNodes} -> + {ok, Pid, DeadPids} -> rabbit_mirror_queue_misc:report_deaths(self(), false, QueueName, DeadPids), if node(Pid) =:= node(MPid) -> %% master hasn't changed gen_server2:reply(From, ok), - rabbit_mirror_queue_misc:add_mirrors(QueueName, ExtraNodes), noreply(State); node(Pid) =:= node() -> %% we've become master QueueState = promote_me(From, State), - rabbit_mirror_queue_misc:add_mirrors(QueueName, ExtraNodes), {become, rabbit_amqqueue_process, QueueState, hibernate}; true -> %% master has changed to not us. gen_server2:reply(From, ok), - %% assertion, we don't need to add_mirrors/2 in this - %% branch, see last clause in remove_from_queue/2 - [] = ExtraNodes, erlang:monitor(process, Pid), %% GM is lazy. So we know of the death of the %% slave since it is a neighbour of ours, but -- cgit v1.2.1 From 1fbba866b471d3260efca33761d788004eb67231 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 8 Oct 2012 18:00:49 +0100 Subject: Remove debugging code. --- src/rabbit_mirror_queue_misc.erl | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index dbc4a253..75d84ffc 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -242,11 +242,8 @@ suggested_queue_nodes(Q, PossibleNodes) -> none -> node(); _ -> MNode0 end, - R = suggested_queue_nodes(policy(<<"ha-mode">>, Q), policy(<<"ha-params">>, Q), - {MNode, SNodes}, PossibleNodes), - io:format("SQN: ~p~n", [{policy(<<"ha-mode">>, Q), policy(<<"ha-params">>, Q), - {MNode, SNodes}, PossibleNodes, R}]), - R. + suggested_queue_nodes(policy(<<"ha-mode">>, Q), policy(<<"ha-params">>, Q), + {MNode, SNodes}, PossibleNodes). policy(Policy, Q) -> case rabbit_policy:get(Policy, Q) of -- cgit v1.2.1 From 70766ad51b4e1cfd2b5ee359291400d86706404d Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 8 Oct 2012 18:02:16 +0100 Subject: Removing the ExtraNodes stuff unmasked another bug: we weren't cleaning up slave_pids when stopping mirroring. --- src/rabbit_mirror_queue_master.erl | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index ea98430c..d865d675 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -149,7 +149,17 @@ stop_all_slaves(Reason, #state{gm = GM}) -> MRefs = [erlang:monitor(process, S) || S <- Slaves], ok = gm:broadcast(GM, {delete_and_terminate, Reason}), [receive {'DOWN', MRef, process, _Pid, _Info} -> ok end || MRef <- MRefs], - ok = gm:forget_group(proplists:get_value(group_name, Info)). + %% Normally when we remove a slave another slave or master will + %% notice and update Mnesia. But we just removed them all, and + %% have stopped listening ourselves. So manually clean up. + QName = proplists:get_value(group_name, Info), + rabbit_misc:execute_mnesia_transaction( + fun () -> + [Q] = mnesia:read({rabbit_queue, QName}), + rabbit_mirror_queue_misc:store_updated_slaves( + Q #amqqueue { slave_pids = [] }) + end), + ok = gm:forget_group(QName). purge(State = #state { gm = GM, backing_queue = BQ, -- cgit v1.2.1 From 939f5a70e3f855bdec85730a579ebd8a745b27fd Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Mon, 8 Oct 2012 18:03:15 +0100 Subject: Policy validation updates --- src/rabbit_mirror_queue_misc.erl | 2 +- src/rabbit_policy.erl | 7 ++----- src/rabbit_runtime_parameters.erl | 21 +++++++++++++++------ src/rabbit_runtime_parameters_test.erl | 6 ++---- 4 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index f02308a1..f0c555c9 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -352,7 +352,7 @@ validate_policy(TagList) -> [length(X)]} end; [_, _|_] -> - {error, "ha-mode may appear once at most", []}; + {error, "ha-mode may appear at most once", []}; [Other] -> {error, "~p is not a valid ha-mode value", [Other]} end. diff --git a/src/rabbit_policy.erl b/src/rabbit_policy.erl index 5540de83..cc9e05c5 100644 --- a/src/rabbit_policy.erl +++ b/src/rabbit_policy.erl @@ -83,7 +83,7 @@ notify_clear(VHost, <<"policy">>, _Name) -> list(VHost) -> [[{<<"name">>, pget(key, P)} | pget(value, P)] - || P <- rabbit_runtime_parameters:list(VHost, <<"policy">>)]. + || P <- rabbit_runtime_parameters:list_policies(VHost)]. update_policies(VHost) -> Policies = list(VHost), @@ -144,10 +144,7 @@ validation(_Name, []) -> validation(_Name, Terms) when is_list(Terms) -> {Tags, Modules} = lists:unzip( rabbit_registry:lookup_all(policy_validator)), - case lists:usort(Tags -- lists:usort(Tags)) of - [] -> ok; - Dup -> rabbit_log:warning("Duplicate policy validators: ~p~n", [Dup]) - end, + [] = lists:usort(Tags -- lists:usort(Tags)), %% ASSERTION Validators = lists:zipwith(fun (M, T) -> {M, a2b(T)} end, Modules, Tags), case lists:foldl( fun (_, {Error, _} = Acc) when Error /= ok -> diff --git a/src/rabbit_runtime_parameters.erl b/src/rabbit_runtime_parameters.erl index 70428e96..51ccab89 100644 --- a/src/rabbit_runtime_parameters.erl +++ b/src/rabbit_runtime_parameters.erl @@ -20,8 +20,8 @@ -export([parse_set/4, parse_set_policy/3, set/4, set_policy/3, clear/3, clear_policy/2, list/0, list/1, list_strict/1, list/2, list_strict/2, - list_formatted/1, list_formatted_policies/1, lookup/3, value/3, - value/4, info_keys/0]). + list_policies/0, list_policies/1, list_formatted/1, + list_formatted_policies/1, lookup/3, value/3, value/4, info_keys/0]). %%---------------------------------------------------------------------------- @@ -160,13 +160,16 @@ mnesia_clear(VHost, Component, Key) -> end). list() -> - [p(P) || P <- rabbit_misc:dirty_read_all(?TABLE)]. + [p(P) || #runtime_parameters{ key = {_VHost, Comp, _Key}} = P <- + rabbit_misc:dirty_read_all(?TABLE), Comp /= <<"policy">>]. list(VHost) -> list(VHost, '_', []). list_strict(Component) -> list('_', Component, not_found). list(VHost, Component) -> list(VHost, Component, []). list_strict(VHost, Component) -> list(VHost, Component, not_found). +list(_VHost, <<"policy">>, _Default) -> + {error, "policies may not be listed using this method"}; list(VHost, Component, Default) -> case component_good(Component) of true -> Match = #runtime_parameters{key = {VHost, Component, '_'}, @@ -175,13 +178,19 @@ list(VHost, Component, Default) -> _ -> Default end. +list_policies() -> + list_policies('_'). + +list_policies(VHost) -> + Match = #runtime_parameters{key = {VHost, <<"policy">>, '_'}, _ = '_'}, + [p(P) || P <- mnesia:dirty_match_object(?TABLE, Match)]. + list_formatted(VHost) -> - [pset(value, format(pget(value, P)), P) - || P <- list(VHost), pget(component, P) /= <<"policy">>]. + [pset(value, format(pget(value, P)), P) || P <- list(VHost)]. list_formatted_policies(VHost) -> [proplists:delete(component, pset(value, format(pget(value, P)), P)) - || P <- list(VHost), pget(component, P) == <<"policy">>]. + || P <- list_policies(VHost)]. lookup(VHost, Component, Key) -> case lookup0(VHost, Component, Key, rabbit_misc:const(not_found)) of diff --git a/src/rabbit_runtime_parameters_test.erl b/src/rabbit_runtime_parameters_test.erl index b39a98bc..44ae4d0f 100644 --- a/src/rabbit_runtime_parameters_test.erl +++ b/src/rabbit_runtime_parameters_test.erl @@ -68,10 +68,8 @@ validate_policy([{Tag1, Arg1}, {Tag2, Arg2}]) when is_list(Arg1), is_list(Arg2) -> case [Tag1, Tag2] -- [<<"testpos">>, <<"testeven">>] of [] -> - case {lists:all(fun (N) -> - is_integer(N) andalso - N > 0 - end, Arg1 ++ Arg2), + case {lists:all(fun (N) -> is_integer(N) andalso N > 0 end, + Arg1 ++ Arg2), length(Arg1) rem 2, length(Arg2) rem 2} of {true, 0, 0} -> ok; _ -> {error, "meh", []} -- cgit v1.2.1 From 578e5b166e4f535fedb106a0b974419a9a3c5c96 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 9 Oct 2012 13:10:47 +0100 Subject: Remove rabbit_misc:interval_operation/3, it doesn't really work. --- src/rabbit_misc.erl | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 83ac1004..a0536a50 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -63,7 +63,6 @@ -export([version/0]). -export([sequence_error/1]). -export([json_encode/1, json_decode/1, json_to_term/1, term_to_json/1]). --export([interval_operation/3]). %% Horrible macro to use in guards -define(IS_BENIGN_EXIT(R), @@ -228,8 +227,6 @@ -spec(json_decode/1 :: (string()) -> {'ok', any()} | 'error'). -spec(json_to_term/1 :: (any()) -> any()). -spec(term_to_json/1 :: (any()) -> any()). --spec(interval_operation/3 :: - (thunk(A), float(), non_neg_integer()) -> {A, non_neg_integer()}). -endif. @@ -990,13 +987,3 @@ term_to_json(L) when is_list(L) -> term_to_json(V) when is_binary(V) orelse is_number(V) orelse V =:= null orelse V =:= true orelse V =:= false -> V. - -%% Ideally, you'd want Fun to run every IdealInterval. but you don't -%% want it to take more than MaxRatio of IdealInterval. So if it takes -%% more then you want to run it less often. So we time how long it -%% takes to run, and then suggest how long you should wait before -%% running it again. Times are in millis. -interval_operation(Fun, MaxRatio, IdealInterval) -> - {Micros, Res} = timer:tc(Fun), - Ratio = lists:max([1, Micros / (MaxRatio * IdealInterval) / 1000]), - {Res, round(IdealInterval * Ratio)}. -- cgit v1.2.1 From c0db9df803e28355acf8978923061473e0f302fe Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Tue, 9 Oct 2012 14:19:39 +0100 Subject: Stricter HA validation --- src/rabbit_mirror_queue_misc.erl | 35 ++++++++++++++++++++--------------- src/rabbit_policy.erl | 14 +++++++++++--- 2 files changed, 31 insertions(+), 18 deletions(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index f0c555c9..9a5f0475 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -340,27 +340,32 @@ validate_policy(TagList) -> [<<"all">>] -> ok; [<<"nodes">>] -> - validate_params(fun erlang:is_binary/1, lists:append(Params), - "~p has invalid node names when ha-mode=nodes"); + validate_params(lists:append(Params), + fun erlang:is_binary/1, + "~p has invalid node names when ha-mode=nodes", + fun (N) -> N > 0 end, + "at least one node expected when ha-mode=nodes"); [<<"exactly">>] -> - case Params of - [_] -> validate_params( - fun (N) -> is_integer(N) andalso N >= 0 end, - Params, "~p must be a positive integer"); - X -> {error, "ha-params must be supplied with one number " - "when ha-mode=exactly. found ~p arguments", - [length(X)]} - end; + validate_params(Params, + fun (N) -> is_integer(N) andalso N >= 0 end, + "~p must be a positive integer", + fun (N) -> N == 1 end, + "ha-params must be supplied with one number " + "when ha-mode=exactly"); [_, _|_] -> {error, "ha-mode may appear at most once", []}; [Other] -> {error, "~p is not a valid ha-mode value", [Other]} end. -validate_params(FilterFun, Params, Msg) when is_list(Params) -> - case lists:filter(fun (P) -> not FilterFun(P) end, Params) of - [] -> ok; - X -> {error, Msg, [X]} +validate_params(Params, FilterPred, FilterMsg, SizePred, SizeMsg) + when is_list(Params) -> + case SizePred(length(Params)) of + true -> case lists:filter(fun (P) -> not FilterPred(P) end, Params) of + [] -> ok; + X -> {error, FilterMsg, [X]} + end; + false -> {error, SizeMsg, []} end; -validate_params(_, Params, _) -> +validate_params(Params, _, _, _, _) -> {error, "~p was expected to be a list", [Params]}. diff --git a/src/rabbit_policy.erl b/src/rabbit_policy.erl index cc9e05c5..d9ac89ee 100644 --- a/src/rabbit_policy.erl +++ b/src/rabbit_policy.erl @@ -146,6 +146,16 @@ validation(_Name, Terms) when is_list(Terms) -> rabbit_registry:lookup_all(policy_validator)), [] = lists:usort(Tags -- lists:usort(Tags)), %% ASSERTION Validators = lists:zipwith(fun (M, T) -> {M, a2b(T)} end, Modules, Tags), + + {TermKeys, _} = lists:unzip(Terms), + case TermKeys -- lists:usort(TermKeys) of + [] -> validation0(Validators, Terms); + Dup -> {error, "~p duplicate keys not allowed", [Dup]} + end; +validation(_Name, Term) -> + {error, "parse error while reading policy: ~p", [Term]}. + +validation0(Validators, Terms) -> case lists:foldl( fun (_, {Error, _} = Acc) when Error /= ok -> Acc; @@ -163,8 +173,6 @@ validation(_Name, Terms) when is_list(Terms) -> {error, "~p are not recognised policy settings", Unvalidated}; {Error, _} -> Error - end; -validation(_Name, Term) -> - {error, "parse error while reading policy: ~p", [Term]}. + end. a2b(A) -> list_to_binary(atom_to_list(A)). -- cgit v1.2.1 From 3b6b74c13ebd0490ccfe85e7a1c25d22bd1dd598 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Tue, 9 Oct 2012 15:44:47 +0100 Subject: Simpler policy interface --- docs/rabbitmqctl.1.xml | 19 +++++++++++++++---- src/rabbit_control_main.erl | 9 ++++++--- src/rabbit_runtime_parameters.erl | 26 +++++++++++++++++++------- 3 files changed, 40 insertions(+), 14 deletions(-) diff --git a/docs/rabbitmqctl.1.xml b/docs/rabbitmqctl.1.xml index eea42484..564af9fd 100644 --- a/docs/rabbitmqctl.1.xml +++ b/docs/rabbitmqctl.1.xml @@ -905,7 +905,7 @@ - set_policy -p vhostpath key value + set_policy -p vhostpath key pattern definition priority Sets a policy. @@ -918,17 +918,28 @@ - value + pattern + + The regular expression, which when matches on a given resources causes the policy to apply. + + + + definition The definition of the policy, as a JSON string. In most shells you are very likely to need to quote this. + + priority + + The priority of the policy as an integer, defaulting to 0. Higher numbers indicate greater precedence. + + For example: - rabbitmqctl set_policy federate-me '{"pattern":"^amq.",\ - "policy":{"federation-upstream-set":"all"}}' + rabbitmqctl set_policy federate-me "^amq." '{"federation-upstream-set":"all"}' This command sets the policy federate-me in the default virtual host so that built-in exchanges are federated. diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index 15fa1fd5..473f4237 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -462,11 +462,14 @@ 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, Value], Opts, Inform) -> +action(set_policy, Node, [Key, Pattern, Defn], Opts, Inform) -> + action(set_policy, Node, [Key, Pattern, Defn, default], Opts, Inform); + +action(set_policy, Node, [Key, Pattern, Defn, Priority], Opts, Inform) -> VHostArg = list_to_binary(proplists:get_value(?VHOST_OPT, Opts)), - Inform("Setting policy ~p to ~p", [Key, Value]), + Inform("Setting policy ~p to ~p", [Key, Defn]), rpc_call(Node, rabbit_runtime_parameters, parse_set_policy, - [VHostArg, list_to_binary(Key), Value]); + [VHostArg, list_to_binary(Key), Pattern, Defn, Priority]); action(clear_policy, Node, [Key], Opts, Inform) -> VHostArg = list_to_binary(proplists:get_value(?VHOST_OPT, Opts)), diff --git a/src/rabbit_runtime_parameters.erl b/src/rabbit_runtime_parameters.erl index 51ccab89..f814e0f0 100644 --- a/src/rabbit_runtime_parameters.erl +++ b/src/rabbit_runtime_parameters.erl @@ -18,7 +18,7 @@ -include("rabbit.hrl"). --export([parse_set/4, parse_set_policy/3, set/4, set_policy/3, clear/3, +-export([parse_set/4, parse_set_policy/5, set/4, set_policy/3, clear/3, clear_policy/2, list/0, list/1, list_strict/1, list/2, list_strict/2, list_policies/0, list_policies/1, list_formatted/1, list_formatted_policies/1, lookup/3, value/3, value/4, info_keys/0]). @@ -31,8 +31,8 @@ -spec(parse_set/4 :: (rabbit_types:vhost(), binary(), binary(), string()) -> ok_or_error_string()). --spec(parse_set_policy/3 :: (rabbit_types:vhost(), binary(), string()) - -> ok_or_error_string()). +-spec(parse_set_policy/5 :: (rabbit_types:vhost(), binary(), string(), string(), + string()) -> ok_or_error_string()). -spec(set/4 :: (rabbit_types:vhost(), binary(), binary(), term()) -> ok_or_error_string()). -spec(set_policy/3 :: (rabbit_types:vhost(), binary(), term()) @@ -74,11 +74,23 @@ parse_set(VHost, Component, Key, String) -> error -> {error_string, "JSON decoding error"} end. -parse_set_policy(VHost, Key, String) -> - case rabbit_misc:json_decode(String) of +parse_set_policy(VHost, Key, Pat, Defn, default) -> + parse_set_policy0(VHost, Key, Pat, Defn, []); +parse_set_policy(VHost, Key, Pat, Defn, Priority) -> + try list_to_integer(Priority) of + Num -> parse_set_policy0(VHost, Key, Pat, Defn, [{<<"priority">>, Num}]) + catch + _:_ -> {error, "~p priority must be a number", [Priority]} + end. + +parse_set_policy0(VHost, Key, Pattern, Defn, Priority) -> + case rabbit_misc:json_decode(Defn) of {ok, JSON} -> - set_policy(VHost, Key, rabbit_misc:json_to_term(JSON)); - error -> + set_policy(VHost, Key, pset(<<"pattern">>, list_to_binary(Pattern), + pset(<<"policy">>, + rabbit_misc:json_to_term(JSON), + Priority)), + error -> {error_string, "JSON decoding error"} end. -- cgit v1.2.1 From 28062ca1238cd9b32a105494b457741c704fbf86 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 9 Oct 2012 16:05:35 +0100 Subject: Ignore process_death, explain why. --- src/rabbit_mirror_queue_slave.erl | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 307f2b4f..36f9766d 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -257,8 +257,8 @@ handle_info(timeout, State) -> noreply(backing_queue_timeout(State)); handle_info({'DOWN', _MonitorRef, process, MPid, _Reason}, - State = #state { gm = GM, master_pid = MPid }) -> - ok = gm:broadcast(GM, {process_death, MPid}), + State = #state { gm = GM, master_pid = MPid }) -> + ok = gm:broadcast(GM, process_death), noreply(State); handle_info({'DOWN', _MonitorRef, process, ChPid, _Reason}, State) -> @@ -353,8 +353,15 @@ handle_msg([_SPid], _From, request_depth) -> handle_msg([_SPid], _From, {ensure_monitoring, _Pid}) -> %% This is only of value to the master ok; -handle_msg([SPid], _From, {process_death, Pid}) -> - inform_deaths(SPid, [Pid]); +handle_msg([_SPid], _From, process_death) -> + %% Since GM is by nature lazy we need to make sure there is some + %% traffic when a master dies, to make sure we get informed of the + %% death. That's all process_death does, create some traffic. We + %% must not take any notice of the master death here since it + %% comes without ordering guarantees - there could still be + %% messages from the master we have yet to receive. When we get + %% members_changed, then there will be no more messages. + ok; handle_msg([CPid], _From, {delete_and_terminate, _Reason} = Msg) -> ok = gen_server2:cast(CPid, {gm, Msg}), {stop, {shutdown, ring_shutdown}}; -- cgit v1.2.1 From 6520300813d6394733c8c6a5ee9e7145eed908b4 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 9 Oct 2012 16:06:39 +0100 Subject: Remove this master_changed nonsense. --- src/rabbit_mirror_queue_coordinator.erl | 2 -- src/rabbit_mirror_queue_slave.erl | 11 ----------- 2 files changed, 13 deletions(-) diff --git a/src/rabbit_mirror_queue_coordinator.erl b/src/rabbit_mirror_queue_coordinator.erl index 40359da3..4d8b6ade 100644 --- a/src/rabbit_mirror_queue_coordinator.erl +++ b/src/rabbit_mirror_queue_coordinator.erl @@ -398,8 +398,6 @@ members_changed([_CPid], _Births, []) -> members_changed([CPid], _Births, Deaths) -> ok = gen_server2:cast(CPid, {gm_deaths, Deaths}). -handle_msg([_CPid], _From, master_changed) -> - ok; handle_msg([CPid], _From, request_depth = Msg) -> ok = gen_server2:cast(CPid, Msg); handle_msg([CPid], _From, {ensure_monitoring, _Pids} = Msg) -> diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 36f9766d..60d7ad0c 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -201,12 +201,6 @@ handle_call({gm_deaths, Deaths}, From, %% branch, see last clause in remove_from_queue/2 [] = ExtraNodes, erlang:monitor(process, Pid), - %% GM is lazy. So we know of the death of the - %% slave since it is a neighbour of ours, but - %% until a message is sent, not all members will - %% know. That might include the new master. So - %% broadcast a no-op message to wake everyone up. - ok = gm:broadcast(GM, master_changed), noreply(State #state { master_pid = Pid }) end end; @@ -345,8 +339,6 @@ joined([SPid], _Members) -> SPid ! {joined, self()}, ok. members_changed([_SPid], _Births, []) -> ok; members_changed([ SPid], _Births, Deaths) -> inform_deaths(SPid, Deaths). -handle_msg([_SPid], _From, master_changed) -> - ok; handle_msg([_SPid], _From, request_depth) -> %% This is only of value to the master ok; @@ -461,9 +453,6 @@ promote_me(From, #state { q = Q = #amqqueue { name = QName }, rabbit_mirror_queue_master:depth_fun()), true = unlink(GM), gen_server2:reply(From, {promote, CPid}), - %% TODO this has been in here since the beginning, but it's not - %% obvious if it is needed. Investigate... - ok = gm:confirmed_broadcast(GM, master_changed), %% Everything that we're monitoring, we need to ensure our new %% coordinator is monitoring. -- cgit v1.2.1 From 81cc69afc8a2b408ba9dbabbc1ecf3776fc12739 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 9 Oct 2012 16:46:52 +0100 Subject: Unused var. --- src/rabbit_mirror_queue_slave.erl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 60d7ad0c..53e61e2d 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -173,7 +173,6 @@ handle_call({deliver, Delivery, true}, From, State) -> handle_call({gm_deaths, Deaths}, From, State = #state { q = #amqqueue { name = QueueName }, - gm = GM, master_pid = MPid }) -> %% The GM has told us about deaths, which means we're not going to %% receive any more messages from GM -- cgit v1.2.1 From 8d9ac0f503c86a45fc5d5de7fac9b3aa7c94a3af Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Tue, 9 Oct 2012 17:52:51 +0100 Subject: Policy interface updates --- src/rabbit_control_main.erl | 4 ++-- src/rabbit_policy.erl | 2 +- src/rabbit_runtime_parameters.erl | 22 +++++++++++++++++----- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index 473f4237..4170295f 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -467,7 +467,7 @@ action(set_policy, Node, [Key, Pattern, Defn], Opts, Inform) -> action(set_policy, Node, [Key, Pattern, Defn, Priority], Opts, Inform) -> VHostArg = list_to_binary(proplists:get_value(?VHOST_OPT, Opts)), - Inform("Setting policy ~p to ~p", [Key, Defn]), + Inform("Setting policy ~p to ~p for pattern ~p", [Key, Defn, Pattern]), rpc_call(Node, rabbit_runtime_parameters, parse_set_policy, [VHostArg, list_to_binary(Key), Pattern, Defn, Priority]); @@ -482,7 +482,7 @@ action(list_policies, Node, [], Opts, Inform) -> Inform("Listing policies", []), display_info_list( rpc_call(Node, rabbit_runtime_parameters, list_formatted_policies, [VHostArg]), - rabbit_runtime_parameters:info_keys() -- [component]); + rabbit_runtime_parameters:info_keys_policies()); action(report, Node, _Args, _Opts, Inform) -> Inform("Reporting server status on ~p~n~n", [erlang:universaltime()]), diff --git a/src/rabbit_policy.erl b/src/rabbit_policy.erl index d9ac89ee..a38834e4 100644 --- a/src/rabbit_policy.erl +++ b/src/rabbit_policy.erl @@ -140,7 +140,7 @@ policy_validation() -> {<<"policy">>, fun validation/2, mandatory}]. validation(_Name, []) -> - ok; + {error, "no policy provided", []}; validation(_Name, Terms) when is_list(Terms) -> {Tags, Modules} = lists:unzip( rabbit_registry:lookup_all(policy_validator)), diff --git a/src/rabbit_runtime_parameters.erl b/src/rabbit_runtime_parameters.erl index f814e0f0..e857f804 100644 --- a/src/rabbit_runtime_parameters.erl +++ b/src/rabbit_runtime_parameters.erl @@ -21,7 +21,8 @@ -export([parse_set/4, parse_set_policy/5, set/4, set_policy/3, clear/3, clear_policy/2, list/0, list/1, list_strict/1, list/2, list_strict/2, list_policies/0, list_policies/1, list_formatted/1, - list_formatted_policies/1, lookup/3, value/3, value/4, info_keys/0]). + list_formatted_policies/1, lookup/3, value/3, value/4, info_keys/0, + info_keys_policies/0]). %%---------------------------------------------------------------------------- @@ -60,7 +61,7 @@ %%--------------------------------------------------------------------------- --import(rabbit_misc, [pget/2, pset/3]). +-import(rabbit_misc, [pget/2, pget/3, pset/3]). -define(TABLE, rabbit_runtime_parameters). @@ -89,7 +90,7 @@ parse_set_policy0(VHost, Key, Pattern, Defn, Priority) -> set_policy(VHost, Key, pset(<<"pattern">>, list_to_binary(Pattern), pset(<<"policy">>, rabbit_misc:json_to_term(JSON), - Priority)), + Priority))); error -> {error_string, "JSON decoding error"} end. @@ -129,6 +130,7 @@ set0(VHost, Component, Key, Term) -> end. mnesia_update(VHost, Component, Key, Term) -> +rabbit_log:info("setting parameter vh ~p~n comp ~p~n key ~p~n term ~p~n~n", [VHost, Component, Key, Term]), rabbit_misc:execute_mnesia_transaction( fun () -> Res = case mnesia:read(?TABLE, {VHost, Component, Key}, read) of @@ -201,8 +203,17 @@ list_formatted(VHost) -> [pset(value, format(pget(value, P)), P) || P <- list(VHost)]. list_formatted_policies(VHost) -> - [proplists:delete(component, pset(value, format(pget(value, P)), P)) - || P <- list_policies(VHost)]. + [begin + Key = pget(key, P), + Val = pget(value, P), + [{key, Key}, + {pattern, pget(<<"pattern">>, Val)}, + {definition, format(pget(<<"policy">>, Val))}] ++ + case pget(<<"priority">>, Val) of + undefined -> []; + Priority -> [{priority, Priority}] + end + end || P <- list_policies(VHost)]. lookup(VHost, Component, Key) -> case lookup0(VHost, Component, Key, rabbit_misc:const(not_found)) of @@ -251,6 +262,7 @@ p(#runtime_parameters{key = {VHost, Component, Key}, value = Value}) -> {value, Value}]. info_keys() -> [component, key, value]. +info_keys_policies() -> [key, pattern, definition, priority]. %%--------------------------------------------------------------------------- -- cgit v1.2.1 From 817484b140818b8b746090450b155cc9064d1cdc Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 10 Oct 2012 11:49:31 +0100 Subject: Cosmetic --- src/rabbit_mirror_queue_master.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index 593df59b..96c79b8f 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -105,7 +105,7 @@ init_with_existing_bq(Q, BQ, BQS) -> ok = rabbit_misc:execute_mnesia_transaction( fun () -> ok = rabbit_amqqueue:store_queue(Q1) - end), + end), #state { gm = GM, coordinator = CPid, backing_queue = BQ, -- cgit v1.2.1 From 81565f66a3dc2f52e2e83615e155c83cd562e2ba Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 10 Oct 2012 11:50:29 +0100 Subject: Nicer. --- src/rabbit_mirror_queue_slave.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 7f59445d..c2604e86 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -141,7 +141,7 @@ init(#amqqueue { name = QueueName } = Q) -> duplicate_live_master -> {stop, {duplicate_live_master, Node}}; existing -> - exit(GM, normal), + gm:leave(GM), ignore end. -- cgit v1.2.1 From 3f13a798ee99f7cebcbb14c5e45e48e2a0bfe81d Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 10 Oct 2012 12:13:27 +0100 Subject: Fix spec broken by bug 25212 --- src/rabbit_mirror_queue_misc.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 75d84ffc..6a97cd0d 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -35,7 +35,8 @@ -spec(on_node_up/0 :: () -> 'ok'). -spec(add_mirrors/2 :: (rabbit_amqqueue:name(), [node()]) -> 'ok'). -spec(add_mirror/2 :: - (rabbit_amqqueue:name(), node()) -> rabbit_types:ok_or_error(any())). + (rabbit_amqqueue:name(), node()) -> + {'ok', atom()} | rabbit_types:error(any())). -spec(store_updated_slaves/1 :: (rabbit_types:amqqueue()) -> rabbit_types:amqqueue()). -spec(suggested_queue_nodes/1 :: (rabbit_types:amqqueue()) -> -- cgit v1.2.1 From a4dd56925e36b8d9ba1b6b7f0a52aa0d1f8d9992 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 10 Oct 2012 12:47:32 +0100 Subject: Clean up after we stop mirroring. --- src/rabbit_mirror_queue_master.erl | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index 96c79b8f..d2490581 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -120,7 +120,6 @@ stop_mirroring(State = #state { coordinator = CPid, backing_queue = BQ, backing_queue_state = BQS }) -> unlink(CPid), - %% TODO remove GM from mnesia stop_all_slaves(shutdown, State), {BQ, BQS}. @@ -155,7 +154,16 @@ stop_all_slaves(Reason, #state{gm = GM}) -> MRefs = [erlang:monitor(process, S) || S <- Slaves], ok = gm:broadcast(GM, {delete_and_terminate, Reason}), [receive {'DOWN', MRef, process, _Pid, _Info} -> ok end || MRef <- MRefs], - ok = gm:forget_group(proplists:get_value(group_name, Info)). + QName = proplists:get_value(group_name, Info), + ok = gm:forget_group(QName), + ok = rabbit_misc:execute_mnesia_transaction( + fun () -> + [Q] = mnesia:read({rabbit_queue, QName}), + rabbit_mirror_queue_misc:store_updated_slaves( + Q#amqqueue{gm_pids = [], + slave_pids = []}), + ok. + end). purge(State = #state { gm = GM, backing_queue = BQ, -- cgit v1.2.1 From bc1cf285f1522d3872115a7382901b87b1002f33 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 10 Oct 2012 12:54:11 +0100 Subject: throttle index walking to the rate at which we can process the results Thus preventing excessive memory use on recovery --- src/gatherer.erl | 51 ++++++++++++++++++++++++++++++---------------- src/rabbit_queue_index.erl | 2 +- 2 files changed, 34 insertions(+), 19 deletions(-) diff --git a/src/gatherer.erl b/src/gatherer.erl index 98b36038..29d2d713 100644 --- a/src/gatherer.erl +++ b/src/gatherer.erl @@ -18,7 +18,7 @@ -behaviour(gen_server2). --export([start_link/0, stop/1, fork/1, finish/1, in/2, out/1]). +-export([start_link/0, stop/1, fork/1, finish/1, in/2, sync_in/2, out/1]). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). @@ -32,6 +32,7 @@ -spec(fork/1 :: (pid()) -> 'ok'). -spec(finish/1 :: (pid()) -> 'ok'). -spec(in/2 :: (pid(), any()) -> 'ok'). +-spec(sync_in/2 :: (pid(), any()) -> 'ok'). -spec(out/1 :: (pid()) -> {'value', any()} | 'empty'). -endif. @@ -62,6 +63,9 @@ finish(Pid) -> in(Pid, Value) -> gen_server2:cast(Pid, {in, Value}). +sync_in(Pid, Value) -> + gen_server2:call(Pid, {in, Value}, infinity). + out(Pid) -> gen_server2:call(Pid, out, infinity). @@ -78,19 +82,22 @@ handle_call(stop, _From, State) -> handle_call(fork, _From, State = #gstate { forks = Forks }) -> {reply, ok, State #gstate { forks = Forks + 1 }, hibernate}; +handle_call({in, Value}, From, State) -> + {noreply, in(Value, From, State), hibernate}; + handle_call(out, From, State = #gstate { forks = Forks, values = Values, blocked = Blocked }) -> case queue:out(Values) of + {empty, _} when Forks == 0 -> + {reply, empty, State, hibernate}; {empty, _} -> - case Forks of - 0 -> {reply, empty, State, hibernate}; - _ -> {noreply, - State #gstate { blocked = queue:in(From, Blocked) }, - hibernate} - end; - {{value, _Value} = V, NewValues} -> - {reply, V, State #gstate { values = NewValues }, hibernate} + {noreply, State #gstate { blocked = queue:in(From, Blocked) }, + hibernate}; + {{value, {PendingIn, Value}}, NewValues} -> + reply(PendingIn, ok), + {reply, {value, Value}, State #gstate { values = NewValues }, + hibernate} end; handle_call(Msg, _From, State) -> @@ -107,15 +114,8 @@ handle_cast(finish, State = #gstate { forks = Forks, blocked = Blocked }) -> {noreply, State #gstate { forks = NewForks, blocked = NewBlocked }, hibernate}; -handle_cast({in, Value}, State = #gstate { values = Values, - blocked = Blocked }) -> - {noreply, case queue:out(Blocked) of - {empty, _} -> - State #gstate { values = queue:in(Value, Values) }; - {{value, From}, NewBlocked} -> - gen_server2:reply(From, {value, Value}), - State #gstate { blocked = NewBlocked } - end, hibernate}; +handle_cast({in, Value}, State) -> + {noreply, in(Value, undefined, State), hibernate}; handle_cast(Msg, State) -> {stop, {unexpected_cast, Msg}, State}. @@ -128,3 +128,18 @@ code_change(_OldVsn, State, _Extra) -> terminate(_Reason, State) -> State. + +%%---------------------------------------------------------------------------- + +in(Value, From, State = #gstate { values = Values, blocked = Blocked }) -> + case queue:out(Blocked) of + {empty, _} -> + State #gstate { values = queue:in({From, Value}, Values) }; + {{value, PendingOut}, NewBlocked} -> + reply(From, ok), + gen_server2:reply(PendingOut, {value, Value}), + State #gstate { blocked = NewBlocked } + end. + +reply(undefined, _Reply) -> ok; +reply(From, Reply) -> gen_server2:reply(From, Reply). diff --git a/src/rabbit_queue_index.erl b/src/rabbit_queue_index.erl index 6d6c648a..21f58154 100644 --- a/src/rabbit_queue_index.erl +++ b/src/rabbit_queue_index.erl @@ -537,7 +537,7 @@ queue_index_walker_reader(QueueName, Gatherer) -> State = blank_state(QueueName), ok = scan_segments( fun (_SeqId, MsgId, _MsgProps, true, _IsDelivered, no_ack, ok) -> - gatherer:in(Gatherer, {MsgId, 1}); + gatherer:sync_in(Gatherer, {MsgId, 1}); (_SeqId, _MsgId, _MsgProps, _IsPersistent, _IsDelivered, _IsAcked, Acc) -> Acc -- cgit v1.2.1 From 3c8f9564867410fb0b4e1e106aecbc2522d1c25d Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 10 Oct 2012 13:57:49 +0100 Subject: Make sure we update Mnesia when only the gm_pids have changed (i.e. we are cleaning up a spare GM pid from when we found an existing slave at startup). --- src/rabbit_mirror_queue_misc.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 844f67ee..e7a608b6 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -80,7 +80,7 @@ remove_from_queue(QueueName, DeadGMPids) -> end, [QPid | SPids]), {QPid1, SPids1} = promote_slave(Alive), - case {{QPid, SPids}, {QPid1, SPids1}} of + case {{QPid, SPids, GMPids}, {QPid1, SPids1, GMPids1}} of {Same, Same} -> {ok, QPid1, []}; _ when QPid =:= QPid1 orelse node(QPid1) =:= node() -> -- cgit v1.2.1 From 60aa98ee44ad3de0a5bcd3ca21ca888d398e78ba Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Wed, 10 Oct 2012 17:54:49 +0100 Subject: Policy interface updates --- src/rabbit_control_main.erl | 7 +-- src/rabbit_mirror_queue_misc.erl | 2 +- src/rabbit_policy.erl | 2 +- src/rabbit_runtime_parameters.erl | 102 ++++++++++++++++++++++++++------------ 4 files changed, 73 insertions(+), 40 deletions(-) diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index 4170295f..04304111 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -462,14 +462,11 @@ 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], Opts, Inform) -> - action(set_policy, Node, [Key, Pattern, Defn, default], Opts, Inform); - -action(set_policy, Node, [Key, Pattern, Defn, Priority], Opts, Inform) -> +action(set_policy, Node, [Key, Pattern, Defn | Priority], Opts, Inform) -> VHostArg = list_to_binary(proplists:get_value(?VHOST_OPT, Opts)), Inform("Setting policy ~p to ~p for pattern ~p", [Key, Defn, Pattern]), rpc_call(Node, rabbit_runtime_parameters, parse_set_policy, - [VHostArg, list_to_binary(Key), Pattern, Defn, Priority]); + [VHostArg, list_to_binary(Key), Pattern, Defn] ++ Priority); action(clear_policy, Node, [Key], Opts, Inform) -> VHostArg = list_to_binary(proplists:get_value(?VHOST_OPT, Opts)), diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 9a5f0475..201e1690 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -347,7 +347,7 @@ validate_policy(TagList) -> "at least one node expected when ha-mode=nodes"); [<<"exactly">>] -> validate_params(Params, - fun (N) -> is_integer(N) andalso N >= 0 end, + fun (N) -> is_integer(N) andalso N > 0 end, "~p must be a positive integer", fun (N) -> N == 1 end, "ha-params must be supplied with one number " diff --git a/src/rabbit_policy.erl b/src/rabbit_policy.erl index a38834e4..e0bed10c 100644 --- a/src/rabbit_policy.erl +++ b/src/rabbit_policy.erl @@ -83,7 +83,7 @@ notify_clear(VHost, <<"policy">>, _Name) -> list(VHost) -> [[{<<"name">>, pget(key, P)} | pget(value, P)] - || P <- rabbit_runtime_parameters:list_policies(VHost)]. + || P <- rabbit_runtime_parameters:list_policies_raw(VHost)]. update_policies(VHost) -> Policies = list(VHost), diff --git a/src/rabbit_runtime_parameters.erl b/src/rabbit_runtime_parameters.erl index e857f804..ab3ab303 100644 --- a/src/rabbit_runtime_parameters.erl +++ b/src/rabbit_runtime_parameters.erl @@ -18,11 +18,13 @@ -include("rabbit.hrl"). --export([parse_set/4, parse_set_policy/5, set/4, set_policy/3, clear/3, - clear_policy/2, list/0, list/1, list_strict/1, list/2, list_strict/2, - list_policies/0, list_policies/1, list_formatted/1, - list_formatted_policies/1, lookup/3, value/3, value/4, info_keys/0, - info_keys_policies/0]). +-export([parse_set/4, parse_set_policy/4, parse_set_policy/5, set_policy/4, + set_policy/5, + clear/3, clear_policy/2, + list/0, list/1, list_strict/1, list/2, list_strict/2, list_policies/0, + list_policies/1, list_formatted/1, list_formatted_policies/1, + list_policies_raw/1, + lookup/3, value/3, value/4, info_keys/0, info_keys_policies/0]). %%---------------------------------------------------------------------------- @@ -32,11 +34,13 @@ -spec(parse_set/4 :: (rabbit_types:vhost(), binary(), binary(), string()) -> ok_or_error_string()). +-spec(parse_set_policy/4 :: (rabbit_types:vhost(), binary(), string(), + string()) -> ok_or_error_string()). -spec(parse_set_policy/5 :: (rabbit_types:vhost(), binary(), string(), string(), string()) -> ok_or_error_string()). --spec(set/4 :: (rabbit_types:vhost(), binary(), binary(), term()) - -> ok_or_error_string()). --spec(set_policy/3 :: (rabbit_types:vhost(), binary(), term()) +-spec(set_policy/4 :: (rabbit_types:vhost(), binary(), term(), term()) + -> ok_or_error_string()). +-spec(set_policy/5 :: (rabbit_types:vhost(), binary(), term(), term(), term()) -> ok_or_error_string()). -spec(clear/3 :: (rabbit_types:vhost(), binary(), binary()) -> ok_or_error_string()). @@ -67,6 +71,8 @@ %%--------------------------------------------------------------------------- +% used by rabbit_control_main + parse_set(_, <<"policy">>, _, _) -> {error_string, "policies may not be set using this method"}; parse_set(VHost, Component, Key, String) -> @@ -74,44 +80,50 @@ parse_set(VHost, Component, Key, String) -> {ok, JSON} -> set(VHost, Component, Key, rabbit_misc:json_to_term(JSON)); error -> {error_string, "JSON decoding error"} end. +set(VHost, Component, Key, Term) when Component /= <<"policy">> -> + case set0(VHost, Component, Key, Term) of + ok -> ok; + {errors, L} -> format_error(L) + end. -parse_set_policy(VHost, Key, Pat, Defn, default) -> - parse_set_policy0(VHost, Key, Pat, Defn, []); +parse_set_policy(VHost, Key, Pat, Defn) -> + parse_set_policy0(VHost, Key, Pat, Defn, []). parse_set_policy(VHost, Key, Pat, Defn, Priority) -> try list_to_integer(Priority) of Num -> parse_set_policy0(VHost, Key, Pat, Defn, [{<<"priority">>, Num}]) catch - _:_ -> {error, "~p priority must be a number", [Priority]} + _:_ -> {error, "~p priority must be a number", [Priority]} end. parse_set_policy0(VHost, Key, Pattern, Defn, Priority) -> case rabbit_misc:json_decode(Defn) of {ok, JSON} -> - set_policy(VHost, Key, pset(<<"pattern">>, list_to_binary(Pattern), - pset(<<"policy">>, - rabbit_misc:json_to_term(JSON), - Priority))); + set_policy0(VHost, Key, pset(<<"pattern">>, list_to_binary(Pattern), + pset(<<"policy">>, + rabbit_misc:json_to_term(JSON), + Priority))); error -> {error_string, "JSON decoding error"} end. -set(_, <<"policy">>, _, _) -> - {error_string, "policies may not be set using this method"}; -set(VHost, Component, Key, Term) -> - case set0(VHost, Component, Key, Term) of - ok -> ok; - {errors, L} -> format_error(L) - end. +% used by management plugin + +set_policy(VHost, Key, Pattern, Defn, Priority) -> + set_policy0(VHost, Key, pset(<<"pattern">>, Pattern, + pset(<<"policy">>, Defn, + [{<<"priority">>, Priority}]))). +set_policy(VHost, Key, Pattern, Defn) -> + set_policy0(VHost, Key, pset(<<"pattern">>, Pattern, + [{<<"policy">>, Defn}])). -set_policy(VHost, Key, Term) -> +% common interface used by both parameters and policies + +set_policy0(VHost, Key, Term) -> case set0(VHost, <<"policy">>, Key, Term) of ok -> ok; {errors, L} -> format_error(L) end. -format_error(L) -> - {error_string, rabbit_misc:format_many([{"Validation failed~n", []} | L])}. - set0(VHost, Component, Key, Term) -> case lookup_component(Component) of {ok, Mod} -> @@ -130,7 +142,6 @@ set0(VHost, Component, Key, Term) -> end. mnesia_update(VHost, Component, Key, Term) -> -rabbit_log:info("setting parameter vh ~p~n comp ~p~n key ~p~n term ~p~n~n", [VHost, Component, Key, Term]), rabbit_misc:execute_mnesia_transaction( fun () -> Res = case mnesia:read(?TABLE, {VHost, Component, Key}, read) of @@ -141,6 +152,8 @@ rabbit_log:info("setting parameter vh ~p~n comp ~p~n key ~p~n term ~p~n~n", [VHo Res end). +%%--------------------------------------------------------------------------- + clear(_, <<"policy">> , _) -> {error_string, "policies may not be cleared using this method"}; clear(VHost, Component, Key) -> @@ -173,6 +186,8 @@ mnesia_clear(VHost, Component, Key) -> ok = mnesia:delete(?TABLE, {VHost, Component, Key}, write) end). +%%--------------------------------------------------------------------------- + list() -> [p(P) || #runtime_parameters{ key = {_VHost, Comp, _Key}} = P <- rabbit_misc:dirty_read_all(?TABLE), Comp /= <<"policy">>]. @@ -196,24 +211,42 @@ list_policies() -> list_policies('_'). list_policies(VHost) -> + list_as_proplist(list_policies0(VHost)). + +list_policies0(VHost) -> Match = #runtime_parameters{key = {VHost, <<"policy">>, '_'}, _ = '_'}, [p(P) || P <- mnesia:dirty_match_object(?TABLE, Match)]. +% used by rabbit_control_main + list_formatted(VHost) -> [pset(value, format(pget(value, P)), P) || P <- list(VHost)]. list_formatted_policies(VHost) -> + [pset(definition, format(pget(definition, Props)), Props) || + Props <- list_as_proplist(list_policies0(VHost))]. + +list_as_proplist(Source) -> [begin Key = pget(key, P), Val = pget(value, P), - [{key, Key}, - {pattern, pget(<<"pattern">>, Val)}, - {definition, format(pget(<<"policy">>, Val))}] ++ + [{vhost, pget(vhost, P)}, + {key, Key}, + {pattern, pget(<<"pattern">>, Val)}, + {definition, pget(<<"policy">>, Val)}] ++ case pget(<<"priority">>, Val) of undefined -> []; Priority -> [{priority, Priority}] end - end || P <- list_policies(VHost)]. + end || P <- Source]. + +% used by rabbit_policy + +list_policies_raw(VHost) -> + Match = #runtime_parameters{key = {VHost, <<"policy">>, '_'}, _ = '_'}, + [p(P) || P <- mnesia:dirty_match_object(?TABLE, Match)]. + +%%--------------------------------------------------------------------------- lookup(VHost, Component, Key) -> case lookup0(VHost, Component, Key, rabbit_misc:const(not_found)) of @@ -261,8 +294,8 @@ p(#runtime_parameters{key = {VHost, Component, Key}, value = Value}) -> {key, Key}, {value, Value}]. -info_keys() -> [component, key, value]. -info_keys_policies() -> [key, pattern, definition, priority]. +info_keys() -> [component, key, value]. +info_keys_policies() -> [vhost, key, pattern, definition, priority]. %%--------------------------------------------------------------------------- @@ -280,6 +313,9 @@ lookup_component(Component) -> {ok, Module} -> {ok, Module} end. +format_error(L) -> + {error_string, rabbit_misc:format_many([{"Validation failed~n", []} | L])}. + format(Term) -> {ok, JSON} = rabbit_misc:json_encode(rabbit_misc:term_to_json(Term)), list_to_binary(JSON). -- cgit v1.2.1 From bbf532facbd213069b6fc88d448144f24b0cfd49 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Thu, 11 Oct 2012 10:20:17 +0100 Subject: Tidying --- docs/rabbitmqctl.1.xml | 5 ++--- src/rabbit_control_main.erl | 2 +- src/rabbit_tests.erl | 1 - 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/docs/rabbitmqctl.1.xml b/docs/rabbitmqctl.1.xml index 564af9fd..522a51ee 100644 --- a/docs/rabbitmqctl.1.xml +++ b/docs/rabbitmqctl.1.xml @@ -899,9 +899,8 @@ Policies are used to control and modify the behaviour of queues and exchanges on a cluster-wide basis. Policies apply within a - given vhost, and consist of a key and a value. The key must be a - string and the value must be a JSON string. Policies can be set, - cleared and listed. + given vhost, and consist of a key, pattern, definition and an + optional priority. Policies can be set, cleared and listed. diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index 04304111..2d99030b 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -464,7 +464,7 @@ action(list_parameters, Node, [], Opts, Inform) -> action(set_policy, Node, [Key, Pattern, Defn | Priority], Opts, Inform) -> VHostArg = list_to_binary(proplists:get_value(?VHOST_OPT, Opts)), - Inform("Setting policy ~p to ~p for pattern ~p", [Key, Defn, Pattern]), + Inform("Setting policy ~p for pattern ~p to ~p", [Key, Pattern, Defn]), rpc_call(Node, rabbit_runtime_parameters, parse_set_policy, [VHostArg, list_to_binary(Key), Pattern, Defn] ++ Priority); diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 9550a482..9dca1a5a 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -1494,7 +1494,6 @@ test_declare_on_dead_queue(SecondaryNode) -> %%--------------------------------------------------------------------- control_action(Command, Args) -> -rabbit_log:info("control args ~p~n", [Args]), control_action(Command, node(), Args, default_options()). control_action(Command, Args, NewOpts) -> -- cgit v1.2.1 From 4fecf72cd3f0f3a9d08f98e14ef55052722c8b94 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 11 Oct 2012 14:30:19 +0100 Subject: better types --- src/rabbit_backing_queue.erl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/rabbit_backing_queue.erl b/src/rabbit_backing_queue.erl index d69a6c3b..c6d17785 100644 --- a/src/rabbit_backing_queue.erl +++ b/src/rabbit_backing_queue.erl @@ -24,6 +24,7 @@ -type(ack() :: any()). -type(state() :: any()). +-type(msg_ids() :: [rabbit_types:msg_id()]). -type(fetch_result(Ack) :: ('empty' | %% Message, IsDelivered, AckTag, Remaining_Len @@ -117,7 +118,7 @@ %% first time the message id appears in the result of %% drain_confirmed. All subsequent appearances of that message id will %% be ignored. --callback drain_confirmed(state()) -> {[rabbit_guid:guid()], state()}. +-callback drain_confirmed(state()) -> {msg_ids(), state()}. %% Drop messages from the head of the queue while the supplied predicate returns %% true. Also accepts a boolean parameter that determines whether the messages @@ -136,7 +137,7 @@ %% Acktags supplied are for messages which can now be forgotten %% about. Must return 1 msg_id per Ack, in the same order as Acks. --callback ack([ack()], state()) -> {[rabbit_guid:guid()], state()}. +-callback ack([ack()], state()) -> {msg_ids(), state()}. %% Acktags supplied are for messages which should be processed. The %% provided callback function is called with each message. @@ -144,7 +145,7 @@ %% Reinsert messages into the queue which have already been delivered %% and were pending acknowledgement. --callback requeue([ack()], state()) -> {[rabbit_guid:guid()], state()}. +-callback requeue([ack()], state()) -> {msg_ids(), state()}. %% How long is my queue? -callback len(state()) -> non_neg_integer(). -- cgit v1.2.1 From 7fe89a881ed98badf5d1a1ce59322f8e658e28f1 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 11 Oct 2012 14:46:35 +0100 Subject: remove a remnant of 'immediate' --- src/rabbit_amqqueue_process.erl | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 30df2b5c..9706efbf 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -543,16 +543,10 @@ attempt_delivery(#delivery{sender = SenderPid, message = Message}, Props, {{Message, Props#message_properties.delivered, AckTag}, true, State1#q{backing_queue_state = BQS3}} end, false, State#q{backing_queue_state = BQS1}); - {Duplicate, BQS1} -> - %% if the message has previously been seen by the BQ then - %% it must have been seen under the same circumstances as - %% now: i.e. if it is now a deliver_immediately then it - %% must have been before. - {case Duplicate of - published -> true; - discarded -> false - end, - State#q{backing_queue_state = BQS1}} + {published, BQS1} -> + {true, State#q{backing_queue_state = BQS1}}; + {discarded, BQS1} -> + {false, State#q{backing_queue_state = BQS1}} end. deliver_or_enqueue(Delivery = #delivery{message = Message, -- cgit v1.2.1 From 1a2e46cbc786aea76d77a05dc52a33d163d9b426 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 11 Oct 2012 15:50:40 +0100 Subject: Bug 25110 broke ability to invoke "rabbitmqctl stop_app; rabbitmqctl status". Fix that. --- src/rabbit_vm.erl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/rabbit_vm.erl b/src/rabbit_vm.erl index c28584c4..53f3df18 100644 --- a/src/rabbit_vm.erl +++ b/src/rabbit_vm.erl @@ -100,8 +100,11 @@ child_memory(Pid, supervisor) when is_pid (Pid) -> sup_memory(Pid); child_memory(_, _) -> 0. mnesia_memory() -> - lists:sum([bytes(mnesia:table_info(Tab, memory)) || - Tab <- mnesia:system_info(tables)]). + case mnesia:system_info(is_running) of + yes -> lists:sum([bytes(mnesia:table_info(Tab, memory)) || + Tab <- mnesia:system_info(tables)]); + no -> 0 + end. ets_memory(Name) -> lists:sum([bytes(ets:info(T, memory)) || T <- ets:all(), -- cgit v1.2.1 From 4570c679cd6f33fc38e5be9af9a2a8ace3c365a2 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Thu, 11 Oct 2012 18:05:08 +0100 Subject: Parameter API renaming and policy tweaks --- src/rabbit_control_main.erl | 24 +++-- src/rabbit_mirror_queue_misc.erl | 2 +- src/rabbit_policy.erl | 2 +- src/rabbit_runtime_parameters.erl | 176 ++++++++++++++++++++------------- src/rabbit_runtime_parameters_test.erl | 22 +++-- src/rabbit_tests.erl | 6 +- src/rabbit_vhost.erl | 9 +- 7 files changed, 143 insertions(+), 98 deletions(-) diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index 2d99030b..d85418c5 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -445,26 +445,31 @@ action(set_parameter, Node, [Component, Key, Value], Opts, Inform) -> VHostArg = list_to_binary(proplists:get_value(?VHOST_OPT, Opts)), Inform("Setting runtime parameter ~p for component ~p to ~p", [Key, Component, Value]), - rpc_call(Node, rabbit_runtime_parameters, parse_set, + rpc_call(Node, rabbit_runtime_parameters, parse_set_param, [VHostArg, list_to_binary(Component), list_to_binary(Key), Value]); action(clear_parameter, Node, [Component, Key], Opts, Inform) -> VHostArg = list_to_binary(proplists:get_value(?VHOST_OPT, Opts)), Inform("Clearing runtime parameter ~p for component ~p", [Key, Component]), - rpc_call(Node, rabbit_runtime_parameters, clear, [VHostArg, - list_to_binary(Component), - list_to_binary(Key)]); + rpc_call(Node, rabbit_runtime_parameters, clear_param, + [VHostArg, list_to_binary(Component), list_to_binary(Key)]); action(list_parameters, Node, [], Opts, Inform) -> VHostArg = list_to_binary(proplists:get_value(?VHOST_OPT, Opts)), Inform("Listing runtime parameters", []), display_info_list( - rpc_call(Node, rabbit_runtime_parameters, list_formatted, [VHostArg]), + rpc_call(Node, rabbit_runtime_parameters, list_formatted_param, + [VHostArg]), rabbit_runtime_parameters:info_keys()); -action(set_policy, Node, [Key, Pattern, Defn | Priority], Opts, Inform) -> +action(set_policy, Node, [Key, Pattern, Defn | Priority], Opts, Inform) + when Priority == [] orelse length(Priority) == 1 -> + Msg = "Setting policy ~p for pattern ~p to ~p", + InformMsg = case Priority of [] -> Msg; + [_] -> Msg ++ " with priority ~p" + end, VHostArg = list_to_binary(proplists:get_value(?VHOST_OPT, Opts)), - Inform("Setting policy ~p for pattern ~p to ~p", [Key, Pattern, Defn]), + Inform(InformMsg, [Key, Pattern, Defn] ++ Priority), rpc_call(Node, rabbit_runtime_parameters, parse_set_policy, [VHostArg, list_to_binary(Key), Pattern, Defn] ++ Priority); @@ -478,8 +483,9 @@ action(list_policies, Node, [], Opts, Inform) -> VHostArg = list_to_binary(proplists:get_value(?VHOST_OPT, Opts)), Inform("Listing policies", []), display_info_list( - rpc_call(Node, rabbit_runtime_parameters, list_formatted_policies, [VHostArg]), - rabbit_runtime_parameters:info_keys_policies()); + rpc_call(Node, rabbit_runtime_parameters, list_formatted_policies, + [VHostArg]), + rabbit_runtime_parameters:info_keys_policy()); action(report, Node, _Args, _Opts, Inform) -> Inform("Reporting server status on ~p~n~n", [erlang:universaltime()]), diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 201e1690..8da0e209 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -27,7 +27,7 @@ -include("rabbit.hrl"). -rabbit_boot_step({?MODULE, - [{description, "HA policy validator hook"}, + [{description, "HA policy validation capability"}, {mfa, {rabbit_registry, register, [policy_validator, <<"ha-mode">>, ?MODULE]}}, {mfa, {rabbit_registry, register, diff --git a/src/rabbit_policy.erl b/src/rabbit_policy.erl index e0bed10c..266c05a6 100644 --- a/src/rabbit_policy.erl +++ b/src/rabbit_policy.erl @@ -170,7 +170,7 @@ validation0(Validators, Terms) -> {ok, []} -> ok; {ok, Unvalidated} -> - {error, "~p are not recognised policy settings", Unvalidated}; + {error, "~p are not recognised policy settings", [Unvalidated]}; {Error, _} -> Error end. diff --git a/src/rabbit_runtime_parameters.erl b/src/rabbit_runtime_parameters.erl index ab3ab303..c2031759 100644 --- a/src/rabbit_runtime_parameters.erl +++ b/src/rabbit_runtime_parameters.erl @@ -18,13 +18,16 @@ -include("rabbit.hrl"). --export([parse_set/4, parse_set_policy/4, parse_set_policy/5, set_policy/4, - set_policy/5, - clear/3, clear_policy/2, - list/0, list/1, list_strict/1, list/2, list_strict/2, list_policies/0, - list_policies/1, list_formatted/1, list_formatted_policies/1, - list_policies_raw/1, - lookup/3, value/3, value/4, info_keys/0, info_keys_policies/0]). + +-export([parse_set_param/4, set_param/4, + parse_set_policy/4, parse_set_policy/5, set_policy/4, set_policy/5, + clear_param/3, clear_policy/2, + list_param/0, list_param/1, list_param/2, + list_param_strict/1, list_param_strict/2, + list_policies/0, list_policies/1, + list_formatted_param/1, list_formatted_policies/1, list_policies_raw/1, + lookup_param/3, lookup_policy/2, + value/3, value/4, info_keys/0, info_keys_policy/0]). %%---------------------------------------------------------------------------- @@ -32,34 +35,42 @@ -type(ok_or_error_string() :: 'ok' | {'error_string', string()}). --spec(parse_set/4 :: (rabbit_types:vhost(), binary(), binary(), string()) - -> ok_or_error_string()). +-spec(parse_set_param/4 :: (rabbit_types:vhost(), binary(), binary(), string()) + -> ok_or_error_string()). +-spec(set_param/4 :: (rabbit_types:vhost(), binary(), binary(), term()) + -> ok_or_error_string()). -spec(parse_set_policy/4 :: (rabbit_types:vhost(), binary(), string(), - string()) -> ok_or_error_string()). + string()) -> ok_or_error_string()). -spec(parse_set_policy/5 :: (rabbit_types:vhost(), binary(), string(), string(), - string()) -> ok_or_error_string()). + string()) -> ok_or_error_string()). -spec(set_policy/4 :: (rabbit_types:vhost(), binary(), term(), term()) - -> ok_or_error_string()). + -> ok_or_error_string()). -spec(set_policy/5 :: (rabbit_types:vhost(), binary(), term(), term(), term()) - -> ok_or_error_string()). --spec(clear/3 :: (rabbit_types:vhost(), binary(), binary()) - -> ok_or_error_string()). --spec(clear_policy/2 :: (rabbit_types:vhost(), binary()) + -> ok_or_error_string()). +-spec(clear_param/3 :: (rabbit_types:vhost(), binary(), binary()) -> ok_or_error_string()). --spec(list/0 :: () -> [rabbit_types:infos()]). --spec(list/1 :: (rabbit_types:vhost()) -> [rabbit_types:infos()]). --spec(list_strict/1 :: (binary()) -> [rabbit_types:infos()] | 'not_found'). --spec(list/2 :: (rabbit_types:vhost(), binary()) -> [rabbit_types:infos()]). --spec(list_strict/2 :: (rabbit_types:vhost(), binary()) +-spec(clear_policy/2 :: (rabbit_types:vhost(), binary()) + -> ok_or_error_string()). +-spec(list_param/0 :: () -> [rabbit_types:infos()]). +-spec(list_param/1 :: (rabbit_types:vhost()) -> [rabbit_types:infos()]). +-spec(list_param_strict/1 :: (binary()) + -> [rabbit_types:infos()] | 'not_found'). +-spec(list_param/2 :: (rabbit_types:vhost(), binary()) + -> [rabbit_types:infos()]). +-spec(list_param_strict/2 :: (rabbit_types:vhost(), binary()) -> [rabbit_types:infos()] | 'not_found'). --spec(list_formatted/1 :: (rabbit_types:vhost()) -> [rabbit_types:infos()]). +-spec(list_formatted_param/1 :: (rabbit_types:vhost()) + -> [rabbit_types:infos()]). -spec(list_formatted_policies/1 :: (rabbit_types:vhost()) -> [rabbit_types:infos()]). --spec(lookup/3 :: (rabbit_types:vhost(), binary(), binary()) - -> rabbit_types:infos()). +-spec(lookup_param/3 :: (rabbit_types:vhost(), binary(), binary()) + -> rabbit_types:infos()). +-spec(lookup_policy/2 :: (rabbit_types:vhost(), binary()) + -> rabbit_types:infos()). -spec(value/3 :: (rabbit_types:vhost(), binary(), binary()) -> term()). -spec(value/4 :: (rabbit_types:vhost(), binary(), binary(), term()) -> term()). -spec(info_keys/0 :: () -> rabbit_types:info_keys()). +-spec(info_keys_policy/0 :: () -> rabbit_types:info_keys()). -endif. @@ -73,19 +84,28 @@ % used by rabbit_control_main -parse_set(_, <<"policy">>, _, _) -> +parse_set_param(_, <<"policy">>, _, _) -> {error_string, "policies may not be set using this method"}; -parse_set(VHost, Component, Key, String) -> +parse_set_param(VHost, Component, Key, String) -> case rabbit_misc:json_decode(String) of - {ok, JSON} -> set(VHost, Component, Key, rabbit_misc:json_to_term(JSON)); + {ok, JSON} -> set_param(VHost, Component, Key, + rabbit_misc:json_to_term(JSON)); error -> {error_string, "JSON decoding error"} end. -set(VHost, Component, Key, Term) when Component /= <<"policy">> -> + +% used by management plugin tests and rabbit_mgmt_wm_parameter + +set_param(_, <<"policy">>, _, _) -> + {error_string, "policies may not be set using this method"}; +set_param(VHost, Component, Key, Term) -> case set0(VHost, Component, Key, Term) of ok -> ok; {errors, L} -> format_error(L) end. + +% used by rabbit_control_main + parse_set_policy(VHost, Key, Pat, Defn) -> parse_set_policy0(VHost, Key, Pat, Defn, []). parse_set_policy(VHost, Key, Pat, Defn, Priority) -> @@ -108,15 +128,16 @@ parse_set_policy0(VHost, Key, Pattern, Defn, Priority) -> % used by management plugin -set_policy(VHost, Key, Pattern, Defn, Priority) -> - set_policy0(VHost, Key, pset(<<"pattern">>, Pattern, - pset(<<"policy">>, Defn, - [{<<"priority">>, Priority}]))). set_policy(VHost, Key, Pattern, Defn) -> - set_policy0(VHost, Key, pset(<<"pattern">>, Pattern, - [{<<"policy">>, Defn}])). + set_policy0(VHost, Key, policy_values(Pattern, Defn)). +set_policy(VHost, Key, Pattern, Defn, Priority) -> + set_policy0(VHost, Key, [{<<"priority">>, Priority} | + policy_values(Pattern, Defn)]). -% common interface used by both parameters and policies +policy_values(Pattern, Defn) -> + [{<<"pattern">>, Pattern}, {<<"policy">>, Defn}]. + +% common, used by both parameters and policies set_policy0(VHost, Key, Term) -> case set0(VHost, <<"policy">>, Key, Term) of @@ -154,9 +175,9 @@ mnesia_update(VHost, Component, Key, Term) -> %%--------------------------------------------------------------------------- -clear(_, <<"policy">> , _) -> +clear_param(_, <<"policy">> , _) -> {error_string, "policies may not be cleared using this method"}; -clear(VHost, Component, Key) -> +clear_param(VHost, Component, Key) -> case clear0(VHost, Component, Key) of ok -> ok; {errors, L} -> format_error(L) @@ -188,18 +209,20 @@ mnesia_clear(VHost, Component, Key) -> %%--------------------------------------------------------------------------- -list() -> +% used by broker internally (rabbit_vhost) + +list_param() -> [p(P) || #runtime_parameters{ key = {_VHost, Comp, _Key}} = P <- rabbit_misc:dirty_read_all(?TABLE), Comp /= <<"policy">>]. -list(VHost) -> list(VHost, '_', []). -list_strict(Component) -> list('_', Component, not_found). -list(VHost, Component) -> list(VHost, Component, []). -list_strict(VHost, Component) -> list(VHost, Component, not_found). +list_param(VHost) -> list_param(VHost, '_', []). +list_param_strict(Component) -> list_param('_', Component, not_found). +list_param(VHost, Component) -> list_param(VHost, Component, []). +list_param_strict(VHost, Component) -> list_param(VHost, Component, not_found). -list(_VHost, <<"policy">>, _Default) -> +list_param(_VHost, <<"policy">>, _Default) -> {error, "policies may not be listed using this method"}; -list(VHost, Component, Default) -> +list_param(VHost, Component, Default) -> case component_good(Component) of true -> Match = #runtime_parameters{key = {VHost, Component, '_'}, _ = '_'}, @@ -207,38 +230,29 @@ list(VHost, Component, Default) -> _ -> Default end. +% used by management plugin REST interface + list_policies() -> list_policies('_'). list_policies(VHost) -> - list_as_proplist(list_policies0(VHost)). - -list_policies0(VHost) -> - Match = #runtime_parameters{key = {VHost, <<"policy">>, '_'}, _ = '_'}, - [p(P) || P <- mnesia:dirty_match_object(?TABLE, Match)]. + list_policies0(VHost, fun ident/1). % used by rabbit_control_main -list_formatted(VHost) -> - [pset(value, format(pget(value, P)), P) || P <- list(VHost)]. +list_formatted_param(VHost) -> + [pset(value, format(pget(value, P)), P) || P <- list_param(VHost)]. list_formatted_policies(VHost) -> - [pset(definition, format(pget(definition, Props)), Props) || - Props <- list_as_proplist(list_policies0(VHost))]. - -list_as_proplist(Source) -> - [begin - Key = pget(key, P), - Val = pget(value, P), - [{vhost, pget(vhost, P)}, - {key, Key}, - {pattern, pget(<<"pattern">>, Val)}, - {definition, pget(<<"policy">>, Val)}] ++ - case pget(<<"priority">>, Val) of - undefined -> []; - Priority -> [{priority, Priority}] - end - end || P <- Source]. + order_policies(list_policies0(VHost, fun format/1)). + +list_policies0(VHost, DefnFun) -> + Match = #runtime_parameters{key = {VHost, <<"policy">>, '_'}, _ = '_'}, + [pol(P, DefnFun) || P <- mnesia:dirty_match_object(?TABLE, Match)]. + +order_policies(PropList) -> + lists:sort(fun (A, B) -> pget(priority, A, 0) < pget(priority, B, 0) end, + PropList). % used by rabbit_policy @@ -248,12 +262,20 @@ list_policies_raw(VHost) -> %%--------------------------------------------------------------------------- -lookup(VHost, Component, Key) -> +% used by management plugin (rabbit_mgmt_wm_policy and _parameter) + +lookup_param(VHost, Component, Key) -> case lookup0(VHost, Component, Key, rabbit_misc:const(not_found)) of not_found -> not_found; Params -> p(Params) end. +lookup_policy(VHost, Key) -> + case lookup0(VHost, <<"policy">>, Key, rabbit_misc:const(not_found)) of + not_found -> not_found; + Policy -> pol(Policy, fun ident/1) + end. + value(VHost, Component, Key) -> case lookup0(VHost, Component, Key, rabbit_misc:const(not_found)) of not_found -> not_found; @@ -294,8 +316,19 @@ p(#runtime_parameters{key = {VHost, Component, Key}, value = Value}) -> {key, Key}, {value, Value}]. -info_keys() -> [component, key, value]. -info_keys_policies() -> [vhost, key, pattern, definition, priority]. +pol(#runtime_parameters{key = {VHost, <<"policy">>, Key}, value = Value}, + DefnFun) -> + [{vhost, VHost}, + {key, Key}, + {pattern, pget(<<"pattern">>, Value)}, + {definition, DefnFun(pget(<<"policy">>, Value))}] ++ + case pget(<<"priority">>, Value) of + undefined -> []; + Priority -> [{priority, Priority}] + end. + +info_keys() -> [component, key, value]. +info_keys_policy() -> [vhost, key, pattern, definition, priority]. %%--------------------------------------------------------------------------- @@ -320,8 +353,11 @@ format(Term) -> {ok, JSON} = rabbit_misc:json_encode(rabbit_misc:term_to_json(Term)), list_to_binary(JSON). +ident(X) -> X. + flatten_errors(L) -> case [{F, A} || I <- lists:flatten([L]), {error, F, A} <- [I]] of [] -> ok; E -> {errors, E} end. + diff --git a/src/rabbit_runtime_parameters_test.erl b/src/rabbit_runtime_parameters_test.erl index 44ae4d0f..807655a3 100644 --- a/src/rabbit_runtime_parameters_test.erl +++ b/src/rabbit_runtime_parameters_test.erl @@ -53,13 +53,13 @@ unregister_policy_validator() -> rabbit_registry:unregister(policy_validator, <<"testpos">>). validate_policy([{<<"testeven">>, Terms}]) when is_list(Terms) -> - case length(Terms) rem 2 =:= 0 of + case check_even(Terms) of true -> ok; false -> {error, "meh", []} end; validate_policy([{<<"testpos">>, Terms}]) when is_list(Terms) -> - case lists:all(fun (N) -> is_integer(N) andalso N > 0 end, Terms) of + case check_pos(Terms) of true -> ok; false -> {error, "meh", []} end; @@ -67,15 +67,17 @@ validate_policy([{<<"testpos">>, Terms}]) when is_list(Terms) -> validate_policy([{Tag1, Arg1}, {Tag2, Arg2}]) when is_list(Arg1), is_list(Arg2) -> case [Tag1, Tag2] -- [<<"testpos">>, <<"testeven">>] of - [] -> - case {lists:all(fun (N) -> is_integer(N) andalso N > 0 end, - Arg1 ++ Arg2), - length(Arg1) rem 2, length(Arg2) rem 2} of - {true, 0, 0} -> ok; - _ -> {error, "meh", []} - end; - _ -> {error, "meh", []} + [] -> case check_pos (Arg1) andalso check_pos (Arg2) andalso + check_even(Arg1) andalso check_even(Arg2) of + true -> ok; + _ -> {error, "meh", []} + end; + _ -> {error, "meh", []} end; validate_policy(_) -> {error, "meh", []}. + +check_even(Terms) -> length(Terms) rem 2 =:= 0. +check_pos(Terms) -> lists:all(fun (N) -> is_integer(N) andalso + N > 0 end, Terms). diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 9dca1a5a..a4cc89a0 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -1040,8 +1040,7 @@ test_policy_validation() -> end, "", TagValList), control_action( set_policy, - ["name", rabbit_misc:format("{\"pattern\":\".*\", \"policy\":" - "{~s}}", [string:join(Frag, ",")])]) + ["name", ".*", "{" ++ string:join(Frag, ",") ++ "}"]) end, ok = SetPol([{"testeven", []}]), @@ -1056,7 +1055,8 @@ test_policy_validation() -> {error_string, _} = SetPol([{"testpos", [2, 16, 32]}, {"testeven", [12, 24]}]), {error_string, _} = SetPol([{"testpos", [2, 16]}, {"testeven", [12, -2]}]), {error_string, _} = SetPol([{"not_registered", []}]), - rabbit_runtime_parameters_test:unregister_policy_validator(). + rabbit_runtime_parameters_test:unregister_policy_validator(), + passed. test_server_status() -> %% create a few things so there is some useful information to list diff --git a/src/rabbit_vhost.erl b/src/rabbit_vhost.erl index 03dfbe24..f29f0104 100644 --- a/src/rabbit_vhost.erl +++ b/src/rabbit_vhost.erl @@ -93,10 +93,11 @@ internal_delete(VHostPath) -> [ok = rabbit_auth_backend_internal:clear_permissions( proplists:get_value(user, Info), VHostPath) || Info <- rabbit_auth_backend_internal:list_vhost_permissions(VHostPath)], - [ok = rabbit_runtime_parameters:clear(VHostPath, - proplists:get_value(component, Info), - proplists:get_value(key, Info)) - || Info <- rabbit_runtime_parameters:list(VHostPath)], + [ok = rabbit_runtime_parameters:clear_param( + VHostPath, + proplists:get_value(component, Info), + proplists:get_value(key, Info)) + || Info <- rabbit_runtime_parameters:list_param(VHostPath)], ok = mnesia:delete({rabbit_vhost, VHostPath}), ok. -- cgit v1.2.1 From 1b70aae6aadbc36641b3362d79f0cfcb8cc6a723 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Fri, 12 Oct 2012 00:11:40 +0100 Subject: Remove hints, spaces --- src/rabbit_mirror_queue_misc.erl | 2 +- src/rabbit_policy.erl | 1 - src/rabbit_runtime_parameters.erl | 23 ----------------------- src/rabbit_runtime_parameters_test.erl | 2 +- 4 files changed, 2 insertions(+), 26 deletions(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 8da0e209..95d54d71 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -334,7 +334,7 @@ update_mirrors0(OldQ = #amqqueue{name = QName}, %%---------------------------------------------------------------------------- validate_policy(TagList) -> - Mode = proplists:get_all_values(<<"ha-mode">>, TagList), + Mode = proplists:get_all_values(<<"ha-mode">>, TagList), Params = proplists:get_all_values(<<"ha-params">>, TagList), case Mode of [<<"all">>] -> diff --git a/src/rabbit_policy.erl b/src/rabbit_policy.erl index 266c05a6..5b997875 100644 --- a/src/rabbit_policy.erl +++ b/src/rabbit_policy.erl @@ -80,7 +80,6 @@ notify_clear(VHost, <<"policy">>, _Name) -> %%---------------------------------------------------------------------------- - list(VHost) -> [[{<<"name">>, pget(key, P)} | pget(value, P)] || P <- rabbit_runtime_parameters:list_policies_raw(VHost)]. diff --git a/src/rabbit_runtime_parameters.erl b/src/rabbit_runtime_parameters.erl index c2031759..564ffeea 100644 --- a/src/rabbit_runtime_parameters.erl +++ b/src/rabbit_runtime_parameters.erl @@ -18,7 +18,6 @@ -include("rabbit.hrl"). - -export([parse_set_param/4, set_param/4, parse_set_policy/4, parse_set_policy/5, set_policy/4, set_policy/5, clear_param/3, clear_policy/2, @@ -82,8 +81,6 @@ %%--------------------------------------------------------------------------- -% used by rabbit_control_main - parse_set_param(_, <<"policy">>, _, _) -> {error_string, "policies may not be set using this method"}; parse_set_param(VHost, Component, Key, String) -> @@ -93,8 +90,6 @@ parse_set_param(VHost, Component, Key, String) -> error -> {error_string, "JSON decoding error"} end. -% used by management plugin tests and rabbit_mgmt_wm_parameter - set_param(_, <<"policy">>, _, _) -> {error_string, "policies may not be set using this method"}; set_param(VHost, Component, Key, Term) -> @@ -103,9 +98,6 @@ set_param(VHost, Component, Key, Term) -> {errors, L} -> format_error(L) end. - -% used by rabbit_control_main - parse_set_policy(VHost, Key, Pat, Defn) -> parse_set_policy0(VHost, Key, Pat, Defn, []). parse_set_policy(VHost, Key, Pat, Defn, Priority) -> @@ -126,8 +118,6 @@ parse_set_policy0(VHost, Key, Pattern, Defn, Priority) -> {error_string, "JSON decoding error"} end. -% used by management plugin - set_policy(VHost, Key, Pattern, Defn) -> set_policy0(VHost, Key, policy_values(Pattern, Defn)). set_policy(VHost, Key, Pattern, Defn, Priority) -> @@ -137,8 +127,6 @@ set_policy(VHost, Key, Pattern, Defn, Priority) -> policy_values(Pattern, Defn) -> [{<<"pattern">>, Pattern}, {<<"policy">>, Defn}]. -% common, used by both parameters and policies - set_policy0(VHost, Key, Term) -> case set0(VHost, <<"policy">>, Key, Term) of ok -> ok; @@ -209,8 +197,6 @@ mnesia_clear(VHost, Component, Key) -> %%--------------------------------------------------------------------------- -% used by broker internally (rabbit_vhost) - list_param() -> [p(P) || #runtime_parameters{ key = {_VHost, Comp, _Key}} = P <- rabbit_misc:dirty_read_all(?TABLE), Comp /= <<"policy">>]. @@ -230,16 +216,12 @@ list_param(VHost, Component, Default) -> _ -> Default end. -% used by management plugin REST interface - list_policies() -> list_policies('_'). list_policies(VHost) -> list_policies0(VHost, fun ident/1). -% used by rabbit_control_main - list_formatted_param(VHost) -> [pset(value, format(pget(value, P)), P) || P <- list_param(VHost)]. @@ -254,16 +236,12 @@ order_policies(PropList) -> lists:sort(fun (A, B) -> pget(priority, A, 0) < pget(priority, B, 0) end, PropList). -% used by rabbit_policy - list_policies_raw(VHost) -> Match = #runtime_parameters{key = {VHost, <<"policy">>, '_'}, _ = '_'}, [p(P) || P <- mnesia:dirty_match_object(?TABLE, Match)]. %%--------------------------------------------------------------------------- -% used by management plugin (rabbit_mgmt_wm_policy and _parameter) - lookup_param(VHost, Component, Key) -> case lookup0(VHost, Component, Key, rabbit_misc:const(not_found)) of not_found -> not_found; @@ -360,4 +338,3 @@ flatten_errors(L) -> [] -> ok; E -> {errors, E} end. - diff --git a/src/rabbit_runtime_parameters_test.erl b/src/rabbit_runtime_parameters_test.erl index 807655a3..63e60490 100644 --- a/src/rabbit_runtime_parameters_test.erl +++ b/src/rabbit_runtime_parameters_test.erl @@ -46,7 +46,7 @@ notify_clear(_, _, _) -> ok. register_policy_validator() -> rabbit_registry:register(policy_validator, <<"testeven">>, ?MODULE), - rabbit_registry:register(policy_validator, <<"testpos">>, ?MODULE). + rabbit_registry:register(policy_validator, <<"testpos">>, ?MODULE). unregister_policy_validator() -> rabbit_registry:unregister(policy_validator, <<"testeven">>), -- cgit v1.2.1 From 368e5aa8a088d74ed584ee8c970d9fa029dccfb2 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 12 Oct 2012 08:57:15 +0100 Subject: monitor channel on discard just as we do for publish/publish_delivered w/o that there is a 20s delay in slaves cleaning up after channel closure if all the messages sent on the channel were discarded. --- src/rabbit_mirror_queue_master.erl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index d865d675..6dac2808 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -379,8 +379,10 @@ discard(Msg = #basic_message { id = MsgId }, ChPid, case dict:find(MsgId, SS) of error -> ok = gm:broadcast(GM, {discard, ChPid, Msg}), - State #state { backing_queue_state = BQ:discard(Msg, ChPid, BQS), - seen_status = dict:erase(MsgId, SS) }; + ensure_monitoring( + ChPid, State #state { + backing_queue_state = BQ:discard(Msg, ChPid, BQS), + seen_status = dict:erase(MsgId, SS) }); {ok, discarded} -> State end. -- cgit v1.2.1 From cb49b58057ad6d75573a7409b118bf141f9b6b43 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Fri, 12 Oct 2012 10:27:15 +0100 Subject: Duplicate duplicate detection --- src/rabbit_mirror_queue_misc.erl | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 95d54d71..f596e404 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -352,8 +352,6 @@ validate_policy(TagList) -> fun (N) -> N == 1 end, "ha-params must be supplied with one number " "when ha-mode=exactly"); - [_, _|_] -> - {error, "ha-mode may appear at most once", []}; [Other] -> {error, "~p is not a valid ha-mode value", [Other]} end. -- cgit v1.2.1 From 5f02b210bccdbc7a673c6770cfbe34bca6232f8a Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 12 Oct 2012 13:54:46 +0100 Subject: Unused variable. --- src/rabbit_tests.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index df89de89..2e26837d 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -927,7 +927,7 @@ test_dynamic_mirroring() -> dm_list_match([], [], 0) -> ok; dm_list_match(_, [], _Extra) -> error; dm_list_match([H|T1], [H |T2], Extra) -> dm_list_match(T1, T2, Extra); -dm_list_match(L1, [H2|T2], Extra) -> dm_list_match(L1, T2, Extra - 1). +dm_list_match(L1, [_H|T2], Extra) -> dm_list_match(L1, T2, Extra - 1). test_user_management() -> -- cgit v1.2.1 From 948ed9e6f471468f7487c67fcc54b3261673e414 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Fri, 12 Oct 2012 16:43:10 +0100 Subject: Delete policies with vhosts --- src/rabbit_runtime_parameters.erl | 6 +++--- src/rabbit_vhost.erl | 4 ++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/rabbit_runtime_parameters.erl b/src/rabbit_runtime_parameters.erl index 564ffeea..138b46ad 100644 --- a/src/rabbit_runtime_parameters.erl +++ b/src/rabbit_runtime_parameters.erl @@ -206,13 +206,13 @@ list_param_strict(Component) -> list_param('_', Component, not_found). list_param(VHost, Component) -> list_param(VHost, Component, []). list_param_strict(VHost, Component) -> list_param(VHost, Component, not_found). -list_param(_VHost, <<"policy">>, _Default) -> - {error, "policies may not be listed using this method"}; list_param(VHost, Component, Default) -> case component_good(Component) of true -> Match = #runtime_parameters{key = {VHost, Component, '_'}, _ = '_'}, - [p(P) || P <- mnesia:dirty_match_object(?TABLE, Match)]; + [p(P) || #runtime_parameters{ key = {_VHost, Comp, _Key}} = P <- + mnesia:dirty_match_object(?TABLE, Match), + Comp /= <<"policy">>]; _ -> Default end. diff --git a/src/rabbit_vhost.erl b/src/rabbit_vhost.erl index f29f0104..9beedc51 100644 --- a/src/rabbit_vhost.erl +++ b/src/rabbit_vhost.erl @@ -98,6 +98,10 @@ internal_delete(VHostPath) -> proplists:get_value(component, Info), proplists:get_value(key, Info)) || Info <- rabbit_runtime_parameters:list_param(VHostPath)], + [ok = rabbit_runtime_parameters:clear_policy( + VHostPath, + proplists:get_value(key, Info)) + || Info <- rabbit_runtime_parameters:list_policies(VHostPath)], ok = mnesia:delete({rabbit_vhost, VHostPath}), ok. -- cgit v1.2.1 From c1446f87de4add3541738b342b25dd4f427aa288 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Fri, 12 Oct 2012 16:58:12 +0100 Subject: Simplify setting of policies --- src/rabbit_runtime_parameters.erl | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/rabbit_runtime_parameters.erl b/src/rabbit_runtime_parameters.erl index 138b46ad..d7f08687 100644 --- a/src/rabbit_runtime_parameters.erl +++ b/src/rabbit_runtime_parameters.erl @@ -19,7 +19,7 @@ -include("rabbit.hrl"). -export([parse_set_param/4, set_param/4, - parse_set_policy/4, parse_set_policy/5, set_policy/4, set_policy/5, + parse_set_policy/4, parse_set_policy/5, set_policy/5, clear_param/3, clear_policy/2, list_param/0, list_param/1, list_param/2, list_param_strict/1, list_param_strict/2, @@ -42,8 +42,6 @@ string()) -> ok_or_error_string()). -spec(parse_set_policy/5 :: (rabbit_types:vhost(), binary(), string(), string(), string()) -> ok_or_error_string()). --spec(set_policy/4 :: (rabbit_types:vhost(), binary(), term(), term()) - -> ok_or_error_string()). -spec(set_policy/5 :: (rabbit_types:vhost(), binary(), term(), term(), term()) -> ok_or_error_string()). -spec(clear_param/3 :: (rabbit_types:vhost(), binary(), binary()) @@ -118,14 +116,12 @@ parse_set_policy0(VHost, Key, Pattern, Defn, Priority) -> {error_string, "JSON decoding error"} end. -set_policy(VHost, Key, Pattern, Defn) -> - set_policy0(VHost, Key, policy_values(Pattern, Defn)). set_policy(VHost, Key, Pattern, Defn, Priority) -> - set_policy0(VHost, Key, [{<<"priority">>, Priority} | - policy_values(Pattern, Defn)]). - -policy_values(Pattern, Defn) -> - [{<<"pattern">>, Pattern}, {<<"policy">>, Defn}]. + PolicyProps = [{<<"pattern">>, Pattern}, {<<"policy">>, Defn}], + set_policy0(VHost, Key, case Priority of + undefined -> []; + _ -> [{<<"priority">>, Priority}] + end ++ PolicyProps). set_policy0(VHost, Key, Term) -> case set0(VHost, <<"policy">>, Key, Term) of -- cgit v1.2.1 From 5c9503287fa53f03e81e187dcfb01dc98a83dd99 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Fri, 12 Oct 2012 17:09:50 +0100 Subject: Simplify policy validation tests --- src/rabbit_runtime_parameters_test.erl | 19 ++----------------- src/rabbit_tests.erl | 25 ++++++++----------------- 2 files changed, 10 insertions(+), 34 deletions(-) diff --git a/src/rabbit_runtime_parameters_test.erl b/src/rabbit_runtime_parameters_test.erl index 63e60490..d4d7271e 100644 --- a/src/rabbit_runtime_parameters_test.erl +++ b/src/rabbit_runtime_parameters_test.erl @@ -53,31 +53,16 @@ unregister_policy_validator() -> rabbit_registry:unregister(policy_validator, <<"testpos">>). validate_policy([{<<"testeven">>, Terms}]) when is_list(Terms) -> - case check_even(Terms) of + case length(Terms) rem 2 =:= 0 of true -> ok; false -> {error, "meh", []} end; validate_policy([{<<"testpos">>, Terms}]) when is_list(Terms) -> - case check_pos(Terms) of + case lists:all(fun (N) -> is_integer(N) andalso N > 0 end, Terms) of true -> ok; false -> {error, "meh", []} end; -validate_policy([{Tag1, Arg1}, {Tag2, Arg2}]) - when is_list(Arg1), is_list(Arg2) -> - case [Tag1, Tag2] -- [<<"testpos">>, <<"testeven">>] of - [] -> case check_pos (Arg1) andalso check_pos (Arg2) andalso - check_even(Arg1) andalso check_even(Arg2) of - true -> ok; - _ -> {error, "meh", []} - end; - _ -> {error, "meh", []} - end; - validate_policy(_) -> {error, "meh", []}. - -check_even(Terms) -> length(Terms) rem 2 =:= 0. -check_pos(Terms) -> lists:all(fun (N) -> is_integer(N) andalso - N > 0 end, Terms). diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index a4cc89a0..2c416c76 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -1032,29 +1032,20 @@ test_runtime_parameters() -> test_policy_validation() -> rabbit_runtime_parameters_test:register_policy_validator(), SetPol = - fun (TagValList) -> - Frag = lists:foldl( - fun ({Pol, Val}, Acc) -> - [rabbit_misc:format("\"~s\":~p", [Pol, Val]) | - Acc] - end, "", TagValList), + fun (Tag, Val) -> control_action( set_policy, - ["name", ".*", "{" ++ string:join(Frag, ",") ++ "}"]) + ["name", ".*", rabbit_misc:format("{\"~s\":~p}", [Tag, Val])]) end, - ok = SetPol([{"testeven", []}]), - ok = SetPol([{"testeven", [1, 2]}]), - ok = SetPol([{"testeven", [1, 2, 3, 4]}]), - ok = SetPol([{"testpos", [2, 3, 5, 562]}]), + 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_string, _} = SetPol("testpos", [-1, 0, 1]), + {error_string, _} = SetPol("testeven", [ 1, 2, 3]), - ok = SetPol([{"testpos", [2, 16]}, {"testeven", [12, 24]}]), - {error_string, _} = SetPol([{"testpos", [2, 16, 32]}, {"testeven", [12, 24]}]), - {error_string, _} = SetPol([{"testpos", [2, 16]}, {"testeven", [12, -2]}]), - {error_string, _} = SetPol([{"not_registered", []}]), rabbit_runtime_parameters_test:unregister_policy_validator(), passed. -- cgit v1.2.1 From 6e026d45e83cb9bbde36d1077684b741cbcb43d0 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 12 Oct 2012 19:16:49 +0100 Subject: optimise x-message-ttl=0 & confirms path on queue changes to slave still to come --- src/rabbit_amqqueue_process.erl | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 9706efbf..d4834835 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -554,19 +554,21 @@ deliver_or_enqueue(Delivery = #delivery{message = Message, State) -> Confirm = should_confirm_message(Delivery, State), Props = message_properties(Confirm, Delivered, State), - case attempt_delivery(Delivery, Props, State) of + case attempt_delivery(Delivery, Props, + maybe_record_confirm_message(Confirm, State)) of {true, State1} -> - maybe_record_confirm_message(Confirm, State1); + State1; %% the next one is an optimisations - %% TODO: optimise the Confirm =/= never case too - {false, State1 = #q{ttl = 0, dlx = undefined}} when Confirm == never -> - discard_delivery(Delivery, State1); - {false, State1} -> + {false, State1 = #q{ttl = 0, dlx = undefined}} -> + %% fake an 'eventual' confirm from BQ; noop if not needed State2 = #q{backing_queue = BQ, backing_queue_state = BQS} = - maybe_record_confirm_message(Confirm, State1), + confirm_messages([Message#basic_message.id], State1), + BQS1 = BQ:discard(Message, SenderPid, BQS), + State2#q{backing_queue_state = BQS1}; + {false, State1 = #q{backing_queue = BQ, backing_queue_state = BQS}} -> BQS1 = BQ:publish(Message, Props, SenderPid, BQS), ensure_ttl_timer(Props#message_properties.expiry, - State2#q{backing_queue_state = BQS1}) + State1#q{backing_queue_state = BQS1}) end. requeue_and_run(AckTags, State = #q{backing_queue = BQ, @@ -683,12 +685,6 @@ subtract_acks(ChPid, AckTags, State, Fun) -> Fun(State) end. -discard_delivery(#delivery{sender = SenderPid, - message = Message}, - State = #q{backing_queue = BQ, - backing_queue_state = BQS}) -> - State#q{backing_queue_state = BQ:discard(Message, SenderPid, BQS)}. - message_properties(Confirm, Delivered, #q{ttl = TTL}) -> #message_properties{expiry = calculate_msg_expiry(TTL), needs_confirming = needs_confirming(Confirm), -- cgit v1.2.1 From 23f2939a49ebf6c08f6083a5aeb6b0f1f3caebf8 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 12 Oct 2012 19:32:26 +0100 Subject: a spot of inlining ...and refactoring; exploiting the fact that maybe_record_confirm_message is a no-op when Confirm == never --- src/rabbit_amqqueue_process.erl | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 9706efbf..5ee7ca9a 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -554,19 +554,20 @@ deliver_or_enqueue(Delivery = #delivery{message = Message, State) -> Confirm = should_confirm_message(Delivery, State), Props = message_properties(Confirm, Delivered, State), - case attempt_delivery(Delivery, Props, State) of + case attempt_delivery(Delivery, Props, + maybe_record_confirm_message(Confirm, State)) of {true, State1} -> - maybe_record_confirm_message(Confirm, State1); + State1; %% the next one is an optimisations - %% TODO: optimise the Confirm =/= never case too - {false, State1 = #q{ttl = 0, dlx = undefined}} when Confirm == never -> - discard_delivery(Delivery, State1); - {false, State1} -> - State2 = #q{backing_queue = BQ, backing_queue_state = BQS} = - maybe_record_confirm_message(Confirm, State1), + {false, State1 = #q{ttl = 0, dlx = undefined, + backing_queue = BQ, backing_queue_state = BQS}} + when Confirm == never -> + BQS1 = BQ:discard(Message, SenderPid, BQS), + State1#q{backing_queue_state = BQS1}; + {false, State1 = #q{backing_queue = BQ, backing_queue_state = BQS}} -> BQS1 = BQ:publish(Message, Props, SenderPid, BQS), ensure_ttl_timer(Props#message_properties.expiry, - State2#q{backing_queue_state = BQS1}) + State1#q{backing_queue_state = BQS1}) end. requeue_and_run(AckTags, State = #q{backing_queue = BQ, @@ -683,12 +684,6 @@ subtract_acks(ChPid, AckTags, State, Fun) -> Fun(State) end. -discard_delivery(#delivery{sender = SenderPid, - message = Message}, - State = #q{backing_queue = BQ, - backing_queue_state = BQS}) -> - State#q{backing_queue_state = BQ:discard(Message, SenderPid, BQS)}. - message_properties(Confirm, Delivered, #q{ttl = TTL}) -> #message_properties{expiry = calculate_msg_expiry(TTL), needs_confirming = needs_confirming(Confirm), -- cgit v1.2.1 From 6b41bae9a520b12dfcd875a3bfca5d32ff8ffdab Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 12 Oct 2012 19:36:17 +0100 Subject: re-instate TODO --- src/rabbit_amqqueue_process.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 5ee7ca9a..dcf08d29 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -559,6 +559,7 @@ deliver_or_enqueue(Delivery = #delivery{message = Message, {true, State1} -> State1; %% the next one is an optimisations + %% TODO: optimise the Confirm =/= never case too {false, State1 = #q{ttl = 0, dlx = undefined, backing_queue = BQ, backing_queue_state = BQS}} when Confirm == never -> -- cgit v1.2.1 From 1586c9c0528d410848b38bc3c35453357d49f4c9 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 12 Oct 2012 20:17:17 +0100 Subject: cosmetic --- src/rabbit_amqqueue_process.erl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index dcf08d29..a744cde0 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -549,9 +549,8 @@ attempt_delivery(#delivery{sender = SenderPid, message = Message}, Props, {false, State#q{backing_queue_state = BQS1}} end. -deliver_or_enqueue(Delivery = #delivery{message = Message, - sender = SenderPid}, Delivered, - State) -> +deliver_or_enqueue(Delivery = #delivery{message = Message, sender = SenderPid}, + Delivered, State) -> Confirm = should_confirm_message(Delivery, State), Props = message_properties(Confirm, Delivered, State), case attempt_delivery(Delivery, Props, -- cgit v1.2.1 From 88a9592e06dd8721bb3bdf4ea5c47a0f1238669e Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 12 Oct 2012 20:56:41 +0100 Subject: refactor: simplify confirm handling in queue three functions into one --- src/rabbit_amqqueue_process.erl | 50 ++++++++++++++++------------------------- 1 file changed, 19 insertions(+), 31 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index a744cde0..f1821b5a 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -496,32 +496,21 @@ confirm_messages(MsgIds, State = #q{msg_id_to_channel = MTC}) -> rabbit_misc:gb_trees_foreach(fun rabbit_misc:confirm_to_sender/2, CMs), State#q{msg_id_to_channel = MTC1}. -should_confirm_message(#delivery{msg_seq_no = undefined}, _State) -> - never; -should_confirm_message(#delivery{sender = SenderPid, +send_or_record_confirm(#delivery{msg_seq_no = undefined}, State) -> + {never, State}; +send_or_record_confirm(#delivery{sender = SenderPid, msg_seq_no = MsgSeqNo, message = #basic_message { is_persistent = true, id = MsgId}}, - #q{q = #amqqueue{durable = true}}) -> - {eventually, SenderPid, MsgSeqNo, MsgId}; -should_confirm_message(#delivery{sender = SenderPid, - msg_seq_no = MsgSeqNo}, - _State) -> - {immediately, SenderPid, MsgSeqNo}. - -needs_confirming({eventually, _, _, _}) -> true; -needs_confirming(_) -> false. - -maybe_record_confirm_message({eventually, SenderPid, MsgSeqNo, MsgId}, - State = #q{msg_id_to_channel = MTC}) -> - State#q{msg_id_to_channel = - gb_trees:insert(MsgId, {SenderPid, MsgSeqNo}, MTC)}; -maybe_record_confirm_message({immediately, SenderPid, MsgSeqNo}, State) -> + State = #q{q = #amqqueue{durable = true}, + msg_id_to_channel = MTC}) -> + MTC1 = gb_trees:insert(MsgId, {SenderPid, MsgSeqNo}, MTC), + {eventually, State#q{msg_id_to_channel = MTC1}}; +send_or_record_confirm(#delivery{sender = SenderPid, + msg_seq_no = MsgSeqNo}, State) -> rabbit_misc:confirm_to_sender(SenderPid, [MsgSeqNo]), - State; -maybe_record_confirm_message(_Confirm, State) -> - State. + {immediately, State}. run_message_queue(State) -> State1 = #q{backing_queue = BQ, backing_queue_state = BQS} = @@ -551,23 +540,22 @@ attempt_delivery(#delivery{sender = SenderPid, message = Message}, Props, deliver_or_enqueue(Delivery = #delivery{message = Message, sender = SenderPid}, Delivered, State) -> - Confirm = should_confirm_message(Delivery, State), + {Confirm, State1} = send_or_record_confirm(Delivery, State), Props = message_properties(Confirm, Delivered, State), - case attempt_delivery(Delivery, Props, - maybe_record_confirm_message(Confirm, State)) of - {true, State1} -> - State1; + case attempt_delivery(Delivery, Props, State1) of + {true, State2} -> + State2; %% the next one is an optimisations %% TODO: optimise the Confirm =/= never case too - {false, State1 = #q{ttl = 0, dlx = undefined, + {false, State2 = #q{ttl = 0, dlx = undefined, backing_queue = BQ, backing_queue_state = BQS}} when Confirm == never -> BQS1 = BQ:discard(Message, SenderPid, BQS), - State1#q{backing_queue_state = BQS1}; - {false, State1 = #q{backing_queue = BQ, backing_queue_state = BQS}} -> + State2#q{backing_queue_state = BQS1}; + {false, State2 = #q{backing_queue = BQ, backing_queue_state = BQS}} -> BQS1 = BQ:publish(Message, Props, SenderPid, BQS), ensure_ttl_timer(Props#message_properties.expiry, - State1#q{backing_queue_state = BQS1}) + State2#q{backing_queue_state = BQS1}) end. requeue_and_run(AckTags, State = #q{backing_queue = BQ, @@ -686,7 +674,7 @@ subtract_acks(ChPid, AckTags, State, Fun) -> message_properties(Confirm, Delivered, #q{ttl = TTL}) -> #message_properties{expiry = calculate_msg_expiry(TTL), - needs_confirming = needs_confirming(Confirm), + needs_confirming = Confirm == eventually, delivered = Delivered}. calculate_msg_expiry(undefined) -> undefined; -- cgit v1.2.1 From 100a85a5a47f229624c9e82694d0aaa77649b391 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 12 Oct 2012 22:52:40 +0100 Subject: store less in the slave's msg_id_status There is no need to track the ChPid of 'published' and 'confirmed'. Also: neater conversion of msg_id_status on promotion. --- src/rabbit_mirror_queue_slave.erl | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 931a7f90..0530fa7f 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -406,16 +406,16 @@ confirm_messages(MsgIds, State = #state { msg_id_status = MS }) -> %% If it needed confirming, it'll have %% already been done. Acc; - {ok, {published, ChPid}} -> + {ok, published} -> %% Still not seen it from the channel, just %% record that it's been confirmed. - {CMsN, dict:store(MsgId, {confirmed, ChPid}, MSN)}; + {CMsN, dict:store(MsgId, confirmed, MSN)}; {ok, {published, ChPid, MsgSeqNo}} -> %% Seen from both GM and Channel. Can now %% confirm. {rabbit_misc:gb_trees_cons(ChPid, MsgSeqNo, CMsN), dict:erase(MsgId, MSN)}; - {ok, {confirmed, _ChPid}} -> + {ok, confirmed} -> %% It's already been confirmed. This is %% probably it's been both sync'd to disk %% and then delivered and ack'd before we've @@ -482,18 +482,18 @@ promote_me(From, #state { q = Q = #amqqueue { name = QName }, %% %% MS contains the following three entry types: %% - %% a) {published, ChPid}: + %% a) published: %% published via gm only; pending arrival of publication from %% channel, maybe pending confirm. %% %% b) {published, ChPid, MsgSeqNo}: %% published via gm and channel; pending confirm. %% - %% c) {confirmed, ChPid}: + %% c) confirmed: %% published via gm only, and confirmed; pending publication %% from channel. %% - %% d) discarded + %% d) discarded: %% seen via gm only as discarded. Pending publication from %% channel %% @@ -511,22 +511,18 @@ promote_me(From, #state { q = Q = #amqqueue { name = QName }, %% this does not affect MS, nor which bits go through to SS in %% Master, or MTC in queue_process. - MSList = dict:to_list(MS), - SS = dict:from_list( - [E || E = {_MsgId, discarded} <- MSList] ++ - [{MsgId, Status} - || {MsgId, {Status, _ChPid}} <- MSList, - Status =:= published orelse Status =:= confirmed]), + St = [published, confirmed, discarded], + SS = dict:filter(fun (_MsgId, Status) -> lists:member(Status, St) end, MS), AckTags = [AckTag || {_MsgId, AckTag} <- dict:to_list(MA)], MasterState = rabbit_mirror_queue_master:promote_backing_queue_state( CPid, BQ, BQS, GM, AckTags, SS, MPids), - MTC = lists:foldl(fun ({MsgId, {published, ChPid, MsgSeqNo}}, MTC0) -> - gb_trees:insert(MsgId, {ChPid, MsgSeqNo}, MTC0); - (_, MTC0) -> - MTC0 - end, gb_trees:empty(), MSList), + MTC = dict:fold(fun (MsgId, {published, ChPid, MsgSeqNo}, MTC0) -> + gb_trees:insert(MsgId, {ChPid, MsgSeqNo}, MTC0); + (_Msgid, _Status, MTC0) -> + MTC0 + end, gb_trees:empty(), MS), Deliveries = [Delivery || {_ChPid, {PubQ, _PendCh}} <- dict:to_list(SQ), Delivery <- queue:to_list(PubQ)], rabbit_amqqueue_process:init_with_backing_queue_state( @@ -637,14 +633,14 @@ maybe_enqueue_message( MQ1 = queue:in(Delivery, MQ), SQ1 = dict:store(ChPid, {MQ1, PendingCh}, SQ), State1 #state { sender_queues = SQ1 }; - {ok, {confirmed, ChPid}} -> + {ok, confirmed} -> %% BQ has confirmed it but we didn't know what the %% msg_seq_no was at the time. We do now! ok = rabbit_misc:confirm_to_sender(ChPid, [MsgSeqNo]), SQ1 = remove_from_pending_ch(MsgId, ChPid, SQ), State1 #state { msg_id_status = dict:erase(MsgId, MS), sender_queues = SQ1 }; - {ok, {published, ChPid}} -> + {ok, published} -> %% It was published to the BQ and we didn't know the %% msg_seq_no so couldn't confirm it at the time. {MS1, SQ1} = @@ -703,7 +699,7 @@ process_instruction( case queue:out(MQ) of {empty, _MQ2} -> {MQ, sets:add_element(MsgId, PendingCh), - dict:store(MsgId, {published, ChPid}, MS)}; + dict:store(MsgId, published, MS)}; {{value, Delivery = #delivery { msg_seq_no = MsgSeqNo, message = #basic_message { id = MsgId } }}, MQ2} -> -- cgit v1.2.1 From 7562d6dbacb54e30e4307eff63632e0ca3ec25fa Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 12 Oct 2012 23:31:15 +0100 Subject: correct essay & comments regarding the 'delayed confirm' rationale Matthew has confirmed that the "we don't know the msg_seq_no until we receive the msg from the channel" reason is bogus. The msg_seq_no is allocated by the channel prior to routing and thus is the same across the master and all slaves. Hence the 'publish' via gm contains all the information we need to issue a confirm. Nevertheless we cannot actually issue the confirm until we've received the message from the channel. The essay now explains the real reason. --- src/rabbit_mirror_queue_coordinator.erl | 30 ++++++++++++++++++------------ src/rabbit_mirror_queue_slave.erl | 15 ++++----------- 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/src/rabbit_mirror_queue_coordinator.erl b/src/rabbit_mirror_queue_coordinator.erl index 72dcfc95..6cd71fc3 100644 --- a/src/rabbit_mirror_queue_coordinator.erl +++ b/src/rabbit_mirror_queue_coordinator.erl @@ -101,19 +101,25 @@ %% channel during a publish, only some of the mirrors may receive that %% publish. As a result of this problem, the messages broadcast over %% the gm contain published content, and thus slaves can operate -%% successfully on messages that they only receive via the gm. The key -%% purpose of also sending messages directly from the channels to the -%% slaves is that without this, in the event of the death of the -%% master, messages could be lost until a suitable slave is promoted. +%% successfully on messages that they only receive via the gm. %% -%% However, that is not the only reason. For example, if confirms are -%% in use, then there is no guarantee that every slave will see the -%% delivery with the same msg_seq_no. As a result, the slaves have to -%% wait until they've seen both the publish via gm, and the publish -%% via the channel before they have enough information to be able to -%% perform the publish to their own bq, and subsequently issue the -%% confirm, if necessary. Either form of publish can arrive first, and -%% a slave can be upgraded to the master at any point during this +%% The key purpose of also sending messages directly from the channels +%% to the slaves is that without this, in the event of the death of +%% the master, messages could be lost until a suitable slave is +%% promoted. However, that is not the only reason. A slave cannot send +%% confirms for a message until it has seen it from the +%% channel. Otherwise, it might send a confirm to a channel for a +%% message that it might *never* receive from that channel. This can +%% happen because new slaves join the gm ring (and thus receive +%% messages from the master) before inserting themselves in the +%% queue's mnesia record (which is what channels look at for routing). +%% As it turns out, channels will simply ignore such bogus confirms, +%% but relying on that would introduce a dangerously tight coupling. +%% +%% Hence the slaves have to wait until they've seen both the publish +%% via gm, and the publish via the channel before they issue the +%% confirm. Either form of publish can arrive first, and a slave can +%% be upgraded to the master at any point during this %% process. Confirms continue to be issued correctly, however. %% %% Because the slave is a full process, it impersonates parts of the diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 0530fa7f..f4679184 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -634,15 +634,11 @@ maybe_enqueue_message( SQ1 = dict:store(ChPid, {MQ1, PendingCh}, SQ), State1 #state { sender_queues = SQ1 }; {ok, confirmed} -> - %% BQ has confirmed it but we didn't know what the - %% msg_seq_no was at the time. We do now! ok = rabbit_misc:confirm_to_sender(ChPid, [MsgSeqNo]), SQ1 = remove_from_pending_ch(MsgId, ChPid, SQ), State1 #state { msg_id_status = dict:erase(MsgId, MS), sender_queues = SQ1 }; {ok, published} -> - %% It was published to the BQ and we didn't know the - %% msg_seq_no so couldn't confirm it at the time. {MS1, SQ1} = case needs_confirming(Delivery, State1) of never -> {dict:erase(MsgId, MS), @@ -686,13 +682,10 @@ process_instruction( msg_id_status = MS }) -> %% We really are going to do the publish right now, even though we - %% may not have seen it directly from the channel. As a result, we - %% may know that it needs confirming without knowing its - %% msg_seq_no, which means that we can see the confirmation come - %% back from the backing queue without knowing the msg_seq_no, - %% which means that we're going to have to hang on to the fact - %% that we've seen the msg_id confirmed until we can associate it - %% with a msg_seq_no. + %% may not have seen it directly from the channel. But we cannot + %% issues 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), {MQ1, PendingCh1, MS1} = -- cgit v1.2.1 From 3338f612ecd75213351e9ae42833a83606ba3482 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 13 Oct 2012 08:44:47 +0100 Subject: plug leak always remove from PendingCh when we receive a msg via gm that we previously received from the channel. --- src/rabbit_mirror_queue_slave.erl | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index f4679184..9e290126 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -639,17 +639,15 @@ maybe_enqueue_message( State1 #state { msg_id_status = dict:erase(MsgId, MS), sender_queues = SQ1 }; {ok, published} -> - {MS1, SQ1} = - case needs_confirming(Delivery, State1) of - never -> {dict:erase(MsgId, MS), - remove_from_pending_ch(MsgId, ChPid, SQ)}; - eventually -> MMS = {published, ChPid, MsgSeqNo}, - {dict:store(MsgId, MMS, MS), SQ}; - immediately -> ok = rabbit_misc:confirm_to_sender( - ChPid, [MsgSeqNo]), - {dict:erase(MsgId, MS), - remove_from_pending_ch(MsgId, ChPid, SQ)} - end, + MS1 = case needs_confirming(Delivery, State1) of + never -> dict:erase(MsgId, MS); + eventually -> MMS = {published, ChPid, MsgSeqNo}, + dict:store(MsgId, MMS, MS); + immediately -> ok = rabbit_misc:confirm_to_sender( + ChPid, [MsgSeqNo]), + dict:erase(MsgId, MS) + end, + SQ1 = remove_from_pending_ch(MsgId, ChPid, SQ), State1 #state { msg_id_status = MS1, sender_queues = SQ1 }; {ok, discarded} -> -- cgit v1.2.1 From 93129443584f3e310613e1d752be2cea0a10a013 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 15 Oct 2012 15:07:28 +0100 Subject: deal with confirms in slave 'discard' --- src/rabbit_mirror_queue_slave.erl | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 9e290126..b1b27c7e 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -653,6 +653,10 @@ maybe_enqueue_message( {ok, discarded} -> %% We've already heard from GM that the msg is to be %% discarded. We won't see this again. + case needs_confirming(Delivery, State1) of + never -> ok; + _ -> ok = rabbit_misc:confirm_to_sender(ChPid, [MsgSeqNo]) + end, SQ1 = remove_from_pending_ch(MsgId, ChPid, SQ), State1 #state { msg_id_status = dict:erase(MsgId, MS), sender_queues = SQ1 } @@ -742,8 +746,16 @@ process_instruction({discard, ChPid, Msg = #basic_message { id = MsgId }}, {empty, _MQ} -> {MQ, sets:add_element(MsgId, PendingCh), dict:store(MsgId, discarded, MS)}; - {{value, #delivery { message = #basic_message { id = MsgId } }}, - MQ2} -> + {{value, Delivery = #delivery { + msg_seq_no = MsgSeqNo, + message = #basic_message { id = MsgId } }}, MQ2} -> + %% We received the msg from the channel first. Thus + %% we need to deal with confirms here. + case needs_confirming(Delivery, State1) of + never -> ok; + _ -> ok = rabbit_misc:confirm_to_sender( + ChPid, [MsgSeqNo]) + end, %% We've already seen it from the channel, we're not %% going to see this again, so don't add it to MS {MQ2, PendingCh, MS}; -- cgit v1.2.1 From 8404d0a107c9cf89e31b099b36bfa1866555900c Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 15 Oct 2012 15:37:32 +0100 Subject: Subscribe to system events, receive inconsistent_database. --- src/rabbit_mnesia.erl | 12 +++++++++++- src/rabbit_node_monitor.erl | 38 ++++++++++++++++++++++++++++++++------ 2 files changed, 43 insertions(+), 7 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 87069228..b07ac7d4 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -320,10 +320,20 @@ status() -> [{nodes, (IfNonEmpty(disc, cluster_nodes(disc)) ++ IfNonEmpty(ram, cluster_nodes(ram)))}] ++ case mnesia:system_info(is_running) of - yes -> [{running_nodes, cluster_nodes(running)}]; + yes -> RunningNodes = cluster_nodes(running), + [{running_nodes, cluster_nodes(running)}, + {partitions, mnesia_partitions(RunningNodes)}]; no -> [] end. +mnesia_partitions(Nodes) -> + {Replies, _BadNodes} = rpc:multicall( + Nodes, rabbit_node_monitor, partition, []), + case [Reply || Reply = {_, R} <- Replies, R =/= none] of + [] -> none; + List -> List + end. + is_clustered() -> AllNodes = cluster_nodes(all), AllNodes =/= [] andalso AllNodes =/= [node()]. diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index 026aa362..77aea364 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -24,6 +24,7 @@ write_cluster_status/1, read_cluster_status/0, update_cluster_status/0, reset_cluster_status/0]). -export([notify_node_up/0, notify_joined_cluster/0, notify_left_cluster/1]). +-export([partition/0]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, @@ -32,6 +33,8 @@ -define(SERVER, ?MODULE). -define(RABBIT_UP_RPC_TIMEOUT, 2000). +-record(state, {monitors, partition}). + %%---------------------------------------------------------------------------- -ifdef(use_specs). @@ -50,6 +53,8 @@ -spec(notify_joined_cluster/0 :: () -> 'ok'). -spec(notify_left_cluster/1 :: (node()) -> 'ok'). +-spec(partition/0 :: () -> consistent | {atom(), node()}). + -endif. %%---------------------------------------------------------------------------- @@ -167,11 +172,24 @@ notify_left_cluster(Node) -> gen_server:abcast(Nodes, ?SERVER, {left_cluster, Node}), ok. +%%---------------------------------------------------------------------------- +%% Server calls +%%---------------------------------------------------------------------------- + +partition() -> + gen_server:call(?SERVER, partition, infinity). + %%---------------------------------------------------------------------------- %% gen_server callbacks %%---------------------------------------------------------------------------- -init([]) -> {ok, pmon:new()}. +init([]) -> + {ok, _} = mnesia:subscribe(system), + {ok, #state{monitors = pmon:new(), + partition = none}}. + +handle_call(partition, _From, State = #state{partition = Partition}) -> + {reply, {node(), Partition}, State}; handle_call(_Request, _From, State) -> {noreply, State}. @@ -179,9 +197,10 @@ handle_call(_Request, _From, State) -> %% Note: when updating the status file, we can't simply write the %% mnesia information since the message can (and will) overtake the %% mnesia propagation. -handle_cast({node_up, Node, NodeType}, Monitors) -> +handle_cast({node_up, Node, NodeType}, + State = #state{monitors = Monitors}) -> case pmon:is_monitored({rabbit, Node}, Monitors) of - true -> {noreply, Monitors}; + true -> {noreply, State}; false -> rabbit_log:info("rabbit on node ~p up~n", [Node]), {AllNodes, DiscNodes, RunningNodes} = read_cluster_status(), write_cluster_status({add_node(Node, AllNodes), @@ -191,7 +210,8 @@ handle_cast({node_up, Node, NodeType}, Monitors) -> end, add_node(Node, RunningNodes)}), ok = handle_live_rabbit(Node), - {noreply, pmon:monitor({rabbit, Node}, Monitors)} + {noreply, State#state{ + monitors = pmon:monitor({rabbit, Node}, Monitors)}} end; handle_cast({joined_cluster, Node, NodeType}, State) -> {AllNodes, DiscNodes, RunningNodes} = read_cluster_status(), @@ -210,12 +230,18 @@ handle_cast({left_cluster, Node}, State) -> handle_cast(_Msg, State) -> {noreply, State}. -handle_info({'DOWN', _MRef, process, {rabbit, Node}, _Reason}, Monitors) -> +handle_info({'DOWN', _MRef, process, {rabbit, Node}, _Reason}, + State = #state{monitors = Monitors}) -> rabbit_log:info("rabbit on node ~p down~n", [Node]), {AllNodes, DiscNodes, RunningNodes} = read_cluster_status(), write_cluster_status({AllNodes, DiscNodes, del_node(Node, RunningNodes)}), ok = handle_dead_rabbit(Node), - {noreply, pmon:erase({rabbit, Node}, Monitors)}; + {noreply, State#state{monitors = pmon:erase({rabbit, Node}, Monitors)}}; + +handle_info({mnesia_system_event, {inconsistent_database, Context, Node}}, + State) -> + {noreply, State#state{partition = {Context, Node}}}; + handle_info(_Info, State) -> {noreply, State}. -- cgit v1.2.1 From 58c1346b1ddede2ea88c0b0e1623c145faa0f319 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 15 Oct 2012 16:13:50 +0100 Subject: refactor: extract commonality between 'publish' and 'discard' --- src/rabbit_mirror_queue_slave.erl | 103 +++++++++++--------------------------- 1 file changed, 30 insertions(+), 73 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index b1b27c7e..aea3b54e 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -387,13 +387,13 @@ run_backing_queue(Mod, Fun, State = #state { backing_queue = BQ, backing_queue_state = BQS }) -> State #state { backing_queue_state = BQ:invoke(Mod, Fun, BQS) }. -needs_confirming(#delivery{ msg_seq_no = undefined }, _State) -> +needs_confirming(_, #delivery{ msg_seq_no = undefined }, _State) -> never; -needs_confirming(#delivery { message = #basic_message { - is_persistent = true } }, +needs_confirming(published, #delivery { message = #basic_message { + is_persistent = true } }, #state { q = #amqqueue { durable = true } }) -> eventually; -needs_confirming(_Delivery, _State) -> +needs_confirming(_Status, _Delivery, _State) -> immediately. confirm_messages(MsgIds, State = #state { msg_id_status = MS }) -> @@ -638,10 +638,10 @@ maybe_enqueue_message( SQ1 = remove_from_pending_ch(MsgId, ChPid, SQ), State1 #state { msg_id_status = dict:erase(MsgId, MS), sender_queues = SQ1 }; - {ok, published} -> - MS1 = case needs_confirming(Delivery, State1) of + {ok, Status} when Status =:= published orelse Status =:= discarded -> + MS1 = case needs_confirming(Status, Delivery, State1) of never -> dict:erase(MsgId, MS); - eventually -> MMS = {published, ChPid, MsgSeqNo}, + eventually -> MMS = {Status, ChPid, MsgSeqNo}, dict:store(MsgId, MMS, MS); immediately -> ok = rabbit_misc:confirm_to_sender( ChPid, [MsgSeqNo]), @@ -649,16 +649,6 @@ maybe_enqueue_message( end, SQ1 = remove_from_pending_ch(MsgId, ChPid, SQ), State1 #state { msg_id_status = MS1, - sender_queues = SQ1 }; - {ok, discarded} -> - %% We've already heard from GM that the msg is to be - %% discarded. We won't see this again. - case needs_confirming(Delivery, State1) of - never -> ok; - _ -> ok = rabbit_misc:confirm_to_sender(ChPid, [MsgSeqNo]) - end, - SQ1 = remove_from_pending_ch(MsgId, ChPid, SQ), - State1 #state { msg_id_status = dict:erase(MsgId, MS), sender_queues = SQ1 } end. @@ -676,34 +666,30 @@ remove_from_pending_ch(MsgId, ChPid, SQ) -> dict:store(ChPid, {MQ, sets:del_element(MsgId, PendingCh)}, SQ) end. -process_instruction( - {publish, Deliver, ChPid, MsgProps, Msg = #basic_message { id = MsgId }}, - State = #state { sender_queues = SQ, - backing_queue = BQ, - backing_queue_state = BQS, - msg_id_status = MS }) -> - - %% We really are going to do the publish 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 need to - %% keep track of the MsgId and its confirmation status in the - %% meantime. +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 + %% 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), {MQ1, PendingCh1, MS1} = case queue:out(MQ) of {empty, _MQ2} -> {MQ, sets:add_element(MsgId, PendingCh), - dict:store(MsgId, published, MS)}; + dict:store(MsgId, Status, MS)}; {{value, Delivery = #delivery { msg_seq_no = MsgSeqNo, message = #basic_message { id = MsgId } }}, MQ2} -> {MQ2, PendingCh, %% We received the msg from the channel first. Thus %% we need to deal with confirms here. - case needs_confirming(Delivery, State1) of + case needs_confirming(Status, Delivery, State1) of never -> MS; - eventually -> MMS = {published, ChPid, MsgSeqNo}, + eventually -> MMS = {Status, ChPid, MsgSeqNo}, dict:store(MsgId, MMS , MS); immediately -> ok = rabbit_misc:confirm_to_sender( ChPid, [MsgSeqNo]), @@ -717,60 +703,31 @@ process_instruction( %% expecting any confirms from us. {MQ, PendingCh, MS} end, - SQ1 = dict:store(ChPid, {MQ1, PendingCh1}, SQ), - State2 = State1 #state { sender_queues = SQ1, msg_id_status = MS1 }, + State1 #state { sender_queues = SQ1, msg_id_status = MS1 }. + +process_instruction({publish, Deliver, ChPid, MsgProps, + Msg = #basic_message { id = MsgId }}, State) -> + State1 = #state { backing_queue = BQ, backing_queue_state = BQS } = + publish_or_discard(published, ChPid, MsgId, State), {ok, case Deliver of false -> BQS1 = BQ:publish(Msg, MsgProps, ChPid, BQS), - State2 #state { backing_queue_state = BQS1 }; + State1 #state { backing_queue_state = BQS1 }; {true, AckRequired} -> {AckTag, BQS1} = BQ:publish_delivered(AckRequired, Msg, MsgProps, ChPid, BQS), maybe_store_ack(AckRequired, MsgId, AckTag, - State2 #state { backing_queue_state = BQS1 }) + State1 #state { backing_queue_state = BQS1 }) end}; process_instruction({discard, ChPid, Msg = #basic_message { id = MsgId }}, - State = #state { sender_queues = SQ, - backing_queue = BQ, - backing_queue_state = BQS, - msg_id_status = MS }) -> - %% Many of the comments around the publish head above apply here - %% too. - State1 = ensure_monitoring(ChPid, State), - {MQ, PendingCh} = get_sender_queue(ChPid, SQ), - {MQ1, PendingCh1, MS1} = - case queue:out(MQ) of - {empty, _MQ} -> - {MQ, sets:add_element(MsgId, PendingCh), - dict:store(MsgId, discarded, MS)}; - {{value, Delivery = #delivery { - msg_seq_no = MsgSeqNo, - message = #basic_message { id = MsgId } }}, MQ2} -> - %% We received the msg from the channel first. Thus - %% we need to deal with confirms here. - case needs_confirming(Delivery, State1) of - never -> ok; - _ -> ok = rabbit_misc:confirm_to_sender( - ChPid, [MsgSeqNo]) - end, - %% We've already seen it from the channel, we're not - %% going to see this again, so don't add it to MS - {MQ2, PendingCh, MS}; - {{value, #delivery {}}, _MQ2} -> - %% The instruction was sent to us before we were - %% within the slave_pids within the #amqqueue{} - %% record. We'll never receive the message directly - %% from the channel. - {MQ, PendingCh, MS} - end, - SQ1 = dict:store(ChPid, {MQ1, PendingCh1}, SQ), + State) -> + State1 = #state { backing_queue = BQ, backing_queue_state = BQS } = + publish_or_discard(discarded, ChPid, MsgId, State), BQS1 = BQ:discard(Msg, ChPid, BQS), - {ok, State1 #state { sender_queues = SQ1, - msg_id_status = MS1, - backing_queue_state = BQS1 }}; + {ok, State1 #state { backing_queue_state = BQS1 }}; process_instruction({drop, Length, Dropped, AckRequired}, State = #state { backing_queue = BQ, backing_queue_state = BQS }) -> -- cgit v1.2.1 From da773f7bb2f0956b2ebcc480ca0fd84e7c1f6ee4 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 15 Oct 2012 16:25:34 +0100 Subject: refactor: deal with 'publish' and 'publish_delivered' in separate clauses --- src/rabbit_mirror_queue_slave.erl | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index aea3b54e..3bd787d3 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -707,21 +707,20 @@ publish_or_discard(Status, ChPid, MsgId, State1 #state { sender_queues = SQ1, msg_id_status = MS1 }. -process_instruction({publish, Deliver, ChPid, MsgProps, +process_instruction({publish, false, ChPid, MsgProps, Msg = #basic_message { id = MsgId }}, State) -> State1 = #state { backing_queue = BQ, backing_queue_state = BQS } = publish_or_discard(published, ChPid, MsgId, State), - {ok, - case Deliver of - false -> - BQS1 = BQ:publish(Msg, MsgProps, ChPid, BQS), - State1 #state { backing_queue_state = BQS1 }; - {true, AckRequired} -> - {AckTag, BQS1} = BQ:publish_delivered(AckRequired, Msg, MsgProps, - ChPid, BQS), - maybe_store_ack(AckRequired, MsgId, AckTag, - State1 #state { backing_queue_state = BQS1 }) - end}; + BQS1 = BQ:publish(Msg, MsgProps, ChPid, BQS), + {ok, State1 #state { backing_queue_state = BQS1 }}; +process_instruction({publish, {true, AckRequired}, ChPid, MsgProps, + Msg = #basic_message { id = MsgId }}, State) -> + State1 = #state { backing_queue = BQ, backing_queue_state = BQS } = + publish_or_discard(published, ChPid, MsgId, State), + {AckTag, BQS1} = BQ:publish_delivered(AckRequired, Msg, MsgProps, + ChPid, BQS), + {ok, maybe_store_ack(AckRequired, MsgId, AckTag, + State1 #state { backing_queue_state = BQS1 })}; process_instruction({discard, ChPid, Msg = #basic_message { id = MsgId }}, State) -> State1 = #state { backing_queue = BQ, backing_queue_state = BQS } = -- cgit v1.2.1 From 8db1447ace87f3c0a3d63bb05f1bd8566c149009 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 15 Oct 2012 16:29:48 +0100 Subject: refactor: handle all msg statuses uniformly --- src/rabbit_mirror_queue_slave.erl | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 3bd787d3..6d7bc304 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -633,12 +633,7 @@ maybe_enqueue_message( MQ1 = queue:in(Delivery, MQ), SQ1 = dict:store(ChPid, {MQ1, PendingCh}, SQ), State1 #state { sender_queues = SQ1 }; - {ok, confirmed} -> - ok = rabbit_misc:confirm_to_sender(ChPid, [MsgSeqNo]), - SQ1 = remove_from_pending_ch(MsgId, ChPid, SQ), - State1 #state { msg_id_status = dict:erase(MsgId, MS), - sender_queues = SQ1 }; - {ok, Status} when Status =:= published orelse Status =:= discarded -> + {ok, Status} -> MS1 = case needs_confirming(Status, Delivery, State1) of never -> dict:erase(MsgId, MS); eventually -> MMS = {Status, ChPid, MsgSeqNo}, -- cgit v1.2.1 From 56917912572d387cbc1522069ac594abd6c7f9a4 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 15 Oct 2012 17:34:43 +0100 Subject: merge slave 'confirm' decision making and action similar to what we did in rabbit_amqqueue_process --- src/rabbit_mirror_queue_slave.erl | 52 ++++++++++++++++----------------------- 1 file changed, 21 insertions(+), 31 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 6d7bc304..2314ffea 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -387,14 +387,20 @@ run_backing_queue(Mod, Fun, State = #state { backing_queue = BQ, backing_queue_state = BQS }) -> State #state { backing_queue_state = BQ:invoke(Mod, Fun, BQS) }. -needs_confirming(_, #delivery{ msg_seq_no = undefined }, _State) -> - never; -needs_confirming(published, #delivery { message = #basic_message { - is_persistent = true } }, - #state { q = #amqqueue { durable = true } }) -> - eventually; -needs_confirming(_Status, _Delivery, _State) -> - immediately. +send_or_record_confirm(_, #delivery{ msg_seq_no = undefined }, MS, _State) -> + MS; +send_or_record_confirm(published, #delivery { sender = ChPid, + msg_seq_no = MsgSeqNo, + message = #basic_message { + id = MsgId, + is_persistent = true } }, + MS, #state { q = #amqqueue { durable = true } }) -> + dict:store(MsgId, {published, ChPid, MsgSeqNo} , MS); +send_or_record_confirm(_Status, #delivery { sender = ChPid, + msg_seq_no = MsgSeqNo }, + MS, _State) -> + ok = rabbit_misc:confirm_to_sender(ChPid, [MsgSeqNo]), + MS. confirm_messages(MsgIds, State = #state { msg_id_status = MS }) -> {CMs, MS1} = @@ -621,9 +627,8 @@ confirm_sender_death(Pid) -> ok. maybe_enqueue_message( - Delivery = #delivery { message = #basic_message { id = MsgId }, - msg_seq_no = MsgSeqNo, - sender = ChPid }, + Delivery = #delivery { message = #basic_message { id = MsgId }, + sender = ChPid }, State = #state { sender_queues = SQ, msg_id_status = MS }) -> State1 = ensure_monitoring(ChPid, State), %% We will never see {published, ChPid, MsgSeqNo} here. @@ -634,14 +639,8 @@ maybe_enqueue_message( SQ1 = dict:store(ChPid, {MQ1, PendingCh}, SQ), State1 #state { sender_queues = SQ1 }; {ok, Status} -> - MS1 = case needs_confirming(Status, Delivery, State1) of - never -> dict:erase(MsgId, MS); - eventually -> MMS = {Status, ChPid, MsgSeqNo}, - dict:store(MsgId, MMS, MS); - immediately -> ok = rabbit_misc:confirm_to_sender( - ChPid, [MsgSeqNo]), - dict:erase(MsgId, MS) - end, + MS1 = send_or_record_confirm( + Status, Delivery, dict:erase(MsgId, MS), State1), SQ1 = remove_from_pending_ch(MsgId, ChPid, SQ), State1 #state { msg_id_status = MS1, sender_queues = SQ1 } @@ -662,8 +661,7 @@ remove_from_pending_ch(MsgId, ChPid, SQ) -> end. publish_or_discard(Status, ChPid, MsgId, - State = #state { sender_queues = SQ, - msg_id_status = MS }) -> + 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 @@ -677,19 +675,11 @@ publish_or_discard(Status, ChPid, MsgId, {MQ, sets:add_element(MsgId, PendingCh), dict:store(MsgId, Status, MS)}; {{value, Delivery = #delivery { - msg_seq_no = MsgSeqNo, - message = #basic_message { id = MsgId } }}, MQ2} -> + message = #basic_message { id = MsgId } }}, MQ2} -> {MQ2, PendingCh, %% We received the msg from the channel first. Thus %% we need to deal with confirms here. - case needs_confirming(Status, Delivery, State1) of - never -> MS; - eventually -> MMS = {Status, ChPid, MsgSeqNo}, - dict:store(MsgId, MMS , MS); - immediately -> ok = rabbit_misc:confirm_to_sender( - ChPid, [MsgSeqNo]), - MS - end}; + send_or_record_confirm(Status, Delivery, MS, State1)}; {{value, #delivery {}}, _MQ2} -> %% The instruction was sent to us before we were %% within the slave_pids within the #amqqueue{} -- cgit v1.2.1 From 3c0d32a5a3fc9c53301cbc21d2e8a40c5ca2a9a3 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Mon, 15 Oct 2012 17:46:25 +0100 Subject: Rename and refactor policies --- src/rabbit_control_main.erl | 35 ++++--- src/rabbit_policy.erl | 91 ++++++++++++++++-- src/rabbit_runtime_parameters.erl | 197 ++++++++++---------------------------- src/rabbit_vhost.erl | 13 ++- 4 files changed, 155 insertions(+), 181 deletions(-) diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index d85418c5..3f3a0b8b 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -445,47 +445,44 @@ action(set_parameter, Node, [Component, Key, Value], Opts, Inform) -> VHostArg = list_to_binary(proplists:get_value(?VHOST_OPT, Opts)), Inform("Setting runtime parameter ~p for component ~p to ~p", [Key, Component, Value]), - rpc_call(Node, rabbit_runtime_parameters, parse_set_param, + rpc_call(Node, rabbit_runtime_parameters, parse_set, [VHostArg, list_to_binary(Component), list_to_binary(Key), Value]); action(clear_parameter, Node, [Component, Key], Opts, Inform) -> VHostArg = list_to_binary(proplists:get_value(?VHOST_OPT, Opts)), Inform("Clearing runtime parameter ~p for component ~p", [Key, Component]), - rpc_call(Node, rabbit_runtime_parameters, clear_param, - [VHostArg, list_to_binary(Component), list_to_binary(Key)]); + rpc_call(Node, rabbit_runtime_parameters, clear, [VHostArg, + list_to_binary(Component), + list_to_binary(Key)]); action(list_parameters, Node, [], Opts, Inform) -> VHostArg = list_to_binary(proplists:get_value(?VHOST_OPT, Opts)), Inform("Listing runtime parameters", []), display_info_list( - rpc_call(Node, rabbit_runtime_parameters, list_formatted_param, - [VHostArg]), + rpc_call(Node, rabbit_runtime_parameters, list_formatted, [VHostArg]), rabbit_runtime_parameters:info_keys()); -action(set_policy, Node, [Key, Pattern, Defn | Priority], Opts, Inform) - when Priority == [] orelse length(Priority) == 1 -> +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 = case Priority of [] -> Msg; - [_] -> Msg ++ " with priority ~p" - end, + {InformMsg, Prio1} = case Prio of [] -> {Msg, undefined}; + [P] -> {Msg ++ " with priority ~s", P} + end, VHostArg = list_to_binary(proplists:get_value(?VHOST_OPT, Opts)), - Inform(InformMsg, [Key, Pattern, Defn] ++ Priority), - rpc_call(Node, rabbit_runtime_parameters, parse_set_policy, - [VHostArg, list_to_binary(Key), Pattern, Defn] ++ Priority); + Inform(InformMsg, [Key, Pattern, Defn] ++ Prio), + rpc_call(Node, rabbit_policy, parse_add, + [VHostArg, list_to_binary(Key), Pattern, Defn, Prio1]); action(clear_policy, Node, [Key], Opts, Inform) -> VHostArg = list_to_binary(proplists:get_value(?VHOST_OPT, Opts)), Inform("Clearing policy ~p", [Key]), - rpc_call(Node, rabbit_runtime_parameters, clear_policy, - [VHostArg, list_to_binary(Key)]); + rpc_call(Node, rabbit_policy, delete, [VHostArg, list_to_binary(Key)]); action(list_policies, Node, [], Opts, Inform) -> VHostArg = list_to_binary(proplists:get_value(?VHOST_OPT, Opts)), Inform("Listing policies", []), - display_info_list( - rpc_call(Node, rabbit_runtime_parameters, list_formatted_policies, - [VHostArg]), - rabbit_runtime_parameters:info_keys_policy()); + display_info_list(rpc_call(Node, rabbit_policy, list_formatted, [VHostArg]), + rabbit_policy:info_keys()); action(report, Node, _Args, _Opts, Inform) -> Inform("Reporting server status on ~p~n~n", [erlang:universaltime()]), diff --git a/src/rabbit_policy.erl b/src/rabbit_policy.erl index 5b997875..3e7472cc 100644 --- a/src/rabbit_policy.erl +++ b/src/rabbit_policy.erl @@ -27,6 +27,10 @@ -export([register/0]). -export([name/1, get/2, set/1]). -export([validate/4, validate_clear/3, notify/4, notify_clear/3]). +-export([parse_add/5, add/5, delete/2, lookup/2, list/0, list/1, + list_formatted/1, info_keys/0]). + +-define(TABLE, rabbit_runtime_parameters). -rabbit_boot_step({?MODULE, [{description, "policy parameters"}, @@ -55,7 +59,7 @@ get(Name, EntityName = #resource{virtual_host = VHost}) -> get0(Name, match(EntityName, list(VHost))). get0(_Name, undefined) -> {error, not_found}; -get0(Name, List) -> case pget(<<"policy">>, List) of +get0(Name, List) -> case pget(definition, List) of undefined -> {error, not_found}; Policy -> case pget(Name, Policy) of undefined -> {error, not_found}; @@ -65,6 +69,83 @@ get0(Name, List) -> case pget(<<"policy">>, List) of %%---------------------------------------------------------------------------- +parse_add(VHost, Key, Pattern, Definition, undefined) -> + parse_add_policy0(VHost, Key, Pattern, Definition, []); +parse_add(VHost, Key, Pattern, Definition, Priority) -> + try list_to_integer(Priority) of + Num -> parse_add_policy0(VHost, Key, Pattern, Definition, + [{<<"priority">>, Num}]) + catch + error:badarg -> {error, "~p priority must be a number", [Priority]} + end. + +parse_add_policy0(VHost, Key, Pattern, Defn, Priority) -> + case rabbit_misc:json_decode(Defn) of + {ok, JSON} -> + add0(VHost, Key, [{<<"pattern">>, list_to_binary(Pattern)}, + {<<"policy">>, rabbit_misc:json_to_term(JSON)}] ++ + Priority); + error -> + {error_string, "JSON decoding error"} + end. + +add(VHost, Key, Pattern, Definition, Priority) -> + PolicyProps = [{<<"pattern">>, Pattern}, {<<"policy">>, Definition}], + add0(VHost, Key, case Priority of + undefined -> []; + _ -> [{<<"priority">>, Priority}] + end ++ PolicyProps). + +add0(VHost, Key, Term) -> + rabbit_runtime_parameters:set_any(VHost, <<"policy">>, Key, Term). + +delete(VHost, Key) -> + rabbit_runtime_parameters:clear_any(VHost, <<"policy">>, Key). + +lookup(VHost, Key) -> + case mnesia:dirty_read(?TABLE, {VHost, <<"policy">>, Key}) of + [] -> not_found; + [P] -> p(P, fun ident/1) + end. + +list() -> + list('_'). + +list(VHost) -> + list0(VHost, fun ident/1). + +list_formatted(VHost) -> + order_policies(list0(VHost, fun format/1)). + +list0(VHost, DefnFun) -> + Match = #runtime_parameters{key = {VHost, <<"policy">>, '_'}, _ = '_'}, + [p(P, DefnFun) || P <- mnesia:dirty_match_object(?TABLE, Match)]. + +order_policies(PropList) -> + lists:sort(fun (A, B) -> pget(priority, A, 0) < pget(priority, B, 0) end, + PropList). + +p(#runtime_parameters{key = {VHost, <<"policy">>, Key}, value = Value}, + DefnFun) -> + [{vhost, VHost}, + {key, Key}, + {pattern, pget(<<"pattern">>, Value)}, + {definition, DefnFun(pget(<<"policy">>, Value))}] ++ + case pget(<<"priority">>, Value) of + undefined -> []; + Priority -> [{priority, Priority}] + end. + +format(Term) -> + {ok, JSON} = rabbit_misc:json_encode(rabbit_misc:term_to_json(Term)), + list_to_binary(JSON). + +ident(X) -> X. + +info_keys() -> [vhost, key, pattern, definition, priority]. + +%%---------------------------------------------------------------------------- + validate(_VHost, <<"policy">>, Name, Term) -> rabbit_parameter_validation:proplist( Name, policy_validation(), Term). @@ -80,10 +161,6 @@ notify_clear(VHost, <<"policy">>, _Name) -> %%---------------------------------------------------------------------------- -list(VHost) -> - [[{<<"name">>, pget(key, P)} | pget(value, P)] - || P <- rabbit_runtime_parameters:list_policies_raw(VHost)]. - update_policies(VHost) -> Policies = list(VHost), {Xs, Qs} = rabbit_misc:execute_mnesia_transaction( @@ -127,9 +204,9 @@ match(Name, Policies) -> end. matches(#resource{name = Name}, Policy) -> - match =:= re:run(Name, pget(<<"pattern">>, Policy), [{capture, none}]). + match =:= re:run(Name, pget(pattern, Policy), [{capture, none}]). -sort_pred(A, B) -> pget(<<"priority">>, A, 0) >= pget(<<"priority">>, B, 0). +sort_pred(A, B) -> pget(priority, A, 0) >= pget(priority, B, 0). %%---------------------------------------------------------------------------- diff --git a/src/rabbit_runtime_parameters.erl b/src/rabbit_runtime_parameters.erl index d7f08687..e66cb749 100644 --- a/src/rabbit_runtime_parameters.erl +++ b/src/rabbit_runtime_parameters.erl @@ -18,15 +18,9 @@ -include("rabbit.hrl"). --export([parse_set_param/4, set_param/4, - parse_set_policy/4, parse_set_policy/5, set_policy/5, - clear_param/3, clear_policy/2, - list_param/0, list_param/1, list_param/2, - list_param_strict/1, list_param_strict/2, - list_policies/0, list_policies/1, - list_formatted_param/1, list_formatted_policies/1, list_policies_raw/1, - lookup_param/3, lookup_policy/2, - value/3, value/4, info_keys/0, info_keys_policy/0]). +-export([parse_set/4, set/4, set_any/4, clear/3, clear_any/3, list/0, list/1, + list_strict/1, list/2, list_strict/2, list_formatted/1, lookup/3, + value/3, value/4, info_keys/0]). %%---------------------------------------------------------------------------- @@ -34,102 +28,62 @@ -type(ok_or_error_string() :: 'ok' | {'error_string', string()}). --spec(parse_set_param/4 :: (rabbit_types:vhost(), binary(), binary(), string()) - -> ok_or_error_string()). --spec(set_param/4 :: (rabbit_types:vhost(), binary(), binary(), term()) - -> ok_or_error_string()). --spec(parse_set_policy/4 :: (rabbit_types:vhost(), binary(), string(), - string()) -> ok_or_error_string()). --spec(parse_set_policy/5 :: (rabbit_types:vhost(), binary(), string(), string(), - string()) -> ok_or_error_string()). --spec(set_policy/5 :: (rabbit_types:vhost(), binary(), term(), term(), term()) - -> ok_or_error_string()). --spec(clear_param/3 :: (rabbit_types:vhost(), binary(), binary()) - -> ok_or_error_string()). --spec(clear_policy/2 :: (rabbit_types:vhost(), binary()) - -> ok_or_error_string()). --spec(list_param/0 :: () -> [rabbit_types:infos()]). --spec(list_param/1 :: (rabbit_types:vhost()) -> [rabbit_types:infos()]). --spec(list_param_strict/1 :: (binary()) - -> [rabbit_types:infos()] | 'not_found'). --spec(list_param/2 :: (rabbit_types:vhost(), binary()) - -> [rabbit_types:infos()]). --spec(list_param_strict/2 :: (rabbit_types:vhost(), binary()) +-spec(parse_set/4 :: (rabbit_types:vhost(), binary(), binary(), string()) + -> ok_or_error_string()). +-spec(set/4 :: (rabbit_types:vhost(), binary(), binary(), term()) + -> ok_or_error_string()). +-spec(set_any/4 :: (rabbit_types:vhost(), binary(), binary(), term()) + -> ok_or_error_string()). +-spec(clear/3 :: (rabbit_types:vhost(), binary(), binary()) + -> ok_or_error_string()). +-spec(clear_any/3 :: (rabbit_types:vhost(), binary(), binary()) + -> ok_or_error_string()). +-spec(list/0 :: () -> [rabbit_types:infos()]). +-spec(list/1 :: (rabbit_types:vhost()) -> [rabbit_types:infos()]). +-spec(list_strict/1 :: (binary()) -> [rabbit_types:infos()] | 'not_found'). +-spec(list/2 :: (rabbit_types:vhost(), binary()) -> [rabbit_types:infos()]). +-spec(list_strict/2 :: (rabbit_types:vhost(), binary()) -> [rabbit_types:infos()] | 'not_found'). --spec(list_formatted_param/1 :: (rabbit_types:vhost()) - -> [rabbit_types:infos()]). --spec(list_formatted_policies/1 :: (rabbit_types:vhost()) -> - [rabbit_types:infos()]). --spec(lookup_param/3 :: (rabbit_types:vhost(), binary(), binary()) - -> rabbit_types:infos()). --spec(lookup_policy/2 :: (rabbit_types:vhost(), binary()) - -> rabbit_types:infos()). +-spec(list_formatted/1 :: (rabbit_types:vhost()) -> [rabbit_types:infos()]). +-spec(lookup/3 :: (rabbit_types:vhost(), binary(), binary()) + -> rabbit_types:infos()). -spec(value/3 :: (rabbit_types:vhost(), binary(), binary()) -> term()). -spec(value/4 :: (rabbit_types:vhost(), binary(), binary(), term()) -> term()). -spec(info_keys/0 :: () -> rabbit_types:info_keys()). --spec(info_keys_policy/0 :: () -> rabbit_types:info_keys()). -endif. %%--------------------------------------------------------------------------- --import(rabbit_misc, [pget/2, pget/3, pset/3]). +-import(rabbit_misc, [pget/2, pset/3]). -define(TABLE, rabbit_runtime_parameters). %%--------------------------------------------------------------------------- -parse_set_param(_, <<"policy">>, _, _) -> +parse_set(_, <<"policy">>, _, _) -> {error_string, "policies may not be set using this method"}; -parse_set_param(VHost, Component, Key, String) -> +parse_set(VHost, Component, Key, String) -> case rabbit_misc:json_decode(String) of - {ok, JSON} -> set_param(VHost, Component, Key, - rabbit_misc:json_to_term(JSON)); + {ok, JSON} -> set(VHost, Component, Key, rabbit_misc:json_to_term(JSON)); error -> {error_string, "JSON decoding error"} end. -set_param(_, <<"policy">>, _, _) -> +set(_, <<"policy">>, _, _) -> {error_string, "policies may not be set using this method"}; -set_param(VHost, Component, Key, Term) -> - case set0(VHost, Component, Key, Term) of - ok -> ok; - {errors, L} -> format_error(L) - end. - -parse_set_policy(VHost, Key, Pat, Defn) -> - parse_set_policy0(VHost, Key, Pat, Defn, []). -parse_set_policy(VHost, Key, Pat, Defn, Priority) -> - try list_to_integer(Priority) of - Num -> parse_set_policy0(VHost, Key, Pat, Defn, [{<<"priority">>, Num}]) - catch - _:_ -> {error, "~p priority must be a number", [Priority]} - end. - -parse_set_policy0(VHost, Key, Pattern, Defn, Priority) -> - case rabbit_misc:json_decode(Defn) of - {ok, JSON} -> - set_policy0(VHost, Key, pset(<<"pattern">>, list_to_binary(Pattern), - pset(<<"policy">>, - rabbit_misc:json_to_term(JSON), - Priority))); - error -> - {error_string, "JSON decoding error"} - end. +set(VHost, Component, Key, Term) -> + set_any(VHost, Component, Key, Term). -set_policy(VHost, Key, Pattern, Defn, Priority) -> - PolicyProps = [{<<"pattern">>, Pattern}, {<<"policy">>, Defn}], - set_policy0(VHost, Key, case Priority of - undefined -> []; - _ -> [{<<"priority">>, Priority}] - end ++ PolicyProps). +format_error(L) -> + {error_string, rabbit_misc:format_many([{"Validation failed~n", []} | L])}. -set_policy0(VHost, Key, Term) -> - case set0(VHost, <<"policy">>, Key, Term) of +set_any(VHost, Component, Key, Term) -> + case set_any0(VHost, Component, Key, Term) of ok -> ok; {errors, L} -> format_error(L) end. -set0(VHost, Component, Key, Term) -> +set_any0(VHost, Component, Key, Term) -> case lookup_component(Component) of {ok, Mod} -> case flatten_errors(Mod:validate(VHost, Component, Key, Term)) of @@ -157,23 +111,18 @@ mnesia_update(VHost, Component, Key, Term) -> Res end). -%%--------------------------------------------------------------------------- - -clear_param(_, <<"policy">> , _) -> +clear(_, <<"policy">> , _) -> {error_string, "policies may not be cleared using this method"}; -clear_param(VHost, Component, Key) -> - case clear0(VHost, Component, Key) of - ok -> ok; - {errors, L} -> format_error(L) - end. +clear(VHost, Component, Key) -> + clear_any(VHost, Component, Key). -clear_policy(VHost, Key) -> - case clear0(VHost, <<"policy">>, Key) of +clear_any(VHost, Component, Key) -> + case clear_any0(VHost, Component, Key) of ok -> ok; {errors, L} -> format_error(L) end. -clear0(VHost, Component, Key) -> +clear_any0(VHost, Component, Key) -> case lookup_component(Component) of {ok, Mod} -> case flatten_errors( Mod:validate_clear(VHost, Component, Key)) of @@ -191,18 +140,16 @@ mnesia_clear(VHost, Component, Key) -> ok = mnesia:delete(?TABLE, {VHost, Component, Key}, write) end). -%%--------------------------------------------------------------------------- - -list_param() -> +list() -> [p(P) || #runtime_parameters{ key = {_VHost, Comp, _Key}} = P <- rabbit_misc:dirty_read_all(?TABLE), Comp /= <<"policy">>]. -list_param(VHost) -> list_param(VHost, '_', []). -list_param_strict(Component) -> list_param('_', Component, not_found). -list_param(VHost, Component) -> list_param(VHost, Component, []). -list_param_strict(VHost, Component) -> list_param(VHost, Component, not_found). +list(VHost) -> list(VHost, '_', []). +list_strict(Component) -> list('_', Component, not_found). +list(VHost, Component) -> list(VHost, Component, []). +list_strict(VHost, Component) -> list(VHost, Component, not_found). -list_param(VHost, Component, Default) -> +list(VHost, Component, Default) -> case component_good(Component) of true -> Match = #runtime_parameters{key = {VHost, Component, '_'}, _ = '_'}, @@ -212,44 +159,15 @@ list_param(VHost, Component, Default) -> _ -> Default end. -list_policies() -> - list_policies('_'). +list_formatted(VHost) -> + [pset(value, format(pget(value, P)), P) || P <- list(VHost)]. -list_policies(VHost) -> - list_policies0(VHost, fun ident/1). - -list_formatted_param(VHost) -> - [pset(value, format(pget(value, P)), P) || P <- list_param(VHost)]. - -list_formatted_policies(VHost) -> - order_policies(list_policies0(VHost, fun format/1)). - -list_policies0(VHost, DefnFun) -> - Match = #runtime_parameters{key = {VHost, <<"policy">>, '_'}, _ = '_'}, - [pol(P, DefnFun) || P <- mnesia:dirty_match_object(?TABLE, Match)]. - -order_policies(PropList) -> - lists:sort(fun (A, B) -> pget(priority, A, 0) < pget(priority, B, 0) end, - PropList). - -list_policies_raw(VHost) -> - Match = #runtime_parameters{key = {VHost, <<"policy">>, '_'}, _ = '_'}, - [p(P) || P <- mnesia:dirty_match_object(?TABLE, Match)]. - -%%--------------------------------------------------------------------------- - -lookup_param(VHost, Component, Key) -> +lookup(VHost, Component, Key) -> case lookup0(VHost, Component, Key, rabbit_misc:const(not_found)) of not_found -> not_found; Params -> p(Params) end. -lookup_policy(VHost, Key) -> - case lookup0(VHost, <<"policy">>, Key, rabbit_misc:const(not_found)) of - not_found -> not_found; - Policy -> pol(Policy, fun ident/1) - end. - value(VHost, Component, Key) -> case lookup0(VHost, Component, Key, rabbit_misc:const(not_found)) of not_found -> not_found; @@ -290,19 +208,7 @@ p(#runtime_parameters{key = {VHost, Component, Key}, value = Value}) -> {key, Key}, {value, Value}]. -pol(#runtime_parameters{key = {VHost, <<"policy">>, Key}, value = Value}, - DefnFun) -> - [{vhost, VHost}, - {key, Key}, - {pattern, pget(<<"pattern">>, Value)}, - {definition, DefnFun(pget(<<"policy">>, Value))}] ++ - case pget(<<"priority">>, Value) of - undefined -> []; - Priority -> [{priority, Priority}] - end. - -info_keys() -> [component, key, value]. -info_keys_policy() -> [vhost, key, pattern, definition, priority]. +info_keys() -> [component, key, value]. %%--------------------------------------------------------------------------- @@ -320,15 +226,10 @@ lookup_component(Component) -> {ok, Module} -> {ok, Module} end. -format_error(L) -> - {error_string, rabbit_misc:format_many([{"Validation failed~n", []} | L])}. - format(Term) -> {ok, JSON} = rabbit_misc:json_encode(rabbit_misc:term_to_json(Term)), list_to_binary(JSON). -ident(X) -> X. - flatten_errors(L) -> case [{F, A} || I <- lists:flatten([L]), {error, F, A} <- [I]] of [] -> ok; diff --git a/src/rabbit_vhost.erl b/src/rabbit_vhost.erl index 9beedc51..68b04cb1 100644 --- a/src/rabbit_vhost.erl +++ b/src/rabbit_vhost.erl @@ -93,15 +93,14 @@ internal_delete(VHostPath) -> [ok = rabbit_auth_backend_internal:clear_permissions( proplists:get_value(user, Info), VHostPath) || Info <- rabbit_auth_backend_internal:list_vhost_permissions(VHostPath)], - [ok = rabbit_runtime_parameters:clear_param( + [ok = rabbit_runtime_parameters:clear(VHostPath, + proplists:get_value(component, Info), + proplists:get_value(key, Info)) + || Info <- rabbit_runtime_parameters:list(VHostPath)], + [ok = rabbit_policy:delete( VHostPath, - proplists:get_value(component, Info), proplists:get_value(key, Info)) - || Info <- rabbit_runtime_parameters:list_param(VHostPath)], - [ok = rabbit_runtime_parameters:clear_policy( - VHostPath, - proplists:get_value(key, Info)) - || Info <- rabbit_runtime_parameters:list_policies(VHostPath)], + || Info <- rabbit_policy:list(VHostPath)], ok = mnesia:delete({rabbit_vhost, VHostPath}), ok. -- cgit v1.2.1 From 572ec0a712306d4e4055884a8b80b063c3bf324f Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 15 Oct 2012 18:11:38 +0100 Subject: We can be partitioned from more than one node. And ignore the starting_partitioned_network, it seems to get copied across the network without marking which node it is from! --- src/rabbit_mnesia.erl | 7 ++----- src/rabbit_node_monitor.erl | 27 +++++++++++++++------------ 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index b07ac7d4..04ac0904 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -328,11 +328,8 @@ status() -> mnesia_partitions(Nodes) -> {Replies, _BadNodes} = rpc:multicall( - Nodes, rabbit_node_monitor, partition, []), - case [Reply || Reply = {_, R} <- Replies, R =/= none] of - [] -> none; - List -> List - end. + Nodes, rabbit_node_monitor, partitions, []), + [Reply || Reply = {_, R} <- Replies, R =/= []]. is_clustered() -> AllNodes = cluster_nodes(all), AllNodes =/= [] andalso AllNodes =/= [node()]. diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index 77aea364..b11c9d04 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -24,7 +24,7 @@ write_cluster_status/1, read_cluster_status/0, update_cluster_status/0, reset_cluster_status/0]). -export([notify_node_up/0, notify_joined_cluster/0, notify_left_cluster/1]). --export([partition/0]). +-export([partitions/0]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, @@ -33,7 +33,7 @@ -define(SERVER, ?MODULE). -define(RABBIT_UP_RPC_TIMEOUT, 2000). --record(state, {monitors, partition}). +-record(state, {monitors, partitions}). %%---------------------------------------------------------------------------- @@ -53,7 +53,7 @@ -spec(notify_joined_cluster/0 :: () -> 'ok'). -spec(notify_left_cluster/1 :: (node()) -> 'ok'). --spec(partition/0 :: () -> consistent | {atom(), node()}). +-spec(partitions/0 :: () -> {node(), [{atom(), node()}]}). -endif. @@ -176,8 +176,8 @@ notify_left_cluster(Node) -> %% Server calls %%---------------------------------------------------------------------------- -partition() -> - gen_server:call(?SERVER, partition, infinity). +partitions() -> + gen_server:call(?SERVER, partitions, infinity). %%---------------------------------------------------------------------------- %% gen_server callbacks @@ -185,11 +185,11 @@ partition() -> init([]) -> {ok, _} = mnesia:subscribe(system), - {ok, #state{monitors = pmon:new(), - partition = none}}. + {ok, #state{monitors = pmon:new(), + partitions = []}}. -handle_call(partition, _From, State = #state{partition = Partition}) -> - {reply, {node(), Partition}, State}; +handle_call(partitions, _From, State = #state{partitions = Partitions}) -> + {reply, {node(), Partitions}, State}; handle_call(_Request, _From, State) -> {noreply, State}. @@ -238,9 +238,12 @@ handle_info({'DOWN', _MRef, process, {rabbit, Node}, _Reason}, ok = handle_dead_rabbit(Node), {noreply, State#state{monitors = pmon:erase({rabbit, Node}, Monitors)}}; -handle_info({mnesia_system_event, {inconsistent_database, Context, Node}}, - State) -> - {noreply, State#state{partition = {Context, Node}}}; +handle_info({mnesia_system_event, + {inconsistent_database, running_partitioned_network, Node}}, + State = #state{partitions = Partitions}) -> + Partitions1 = ordsets:to_list( + ordsets:add_element(Node, ordsets:from_list(Partitions))), + {noreply, State#state{partitions = Partitions1}}; handle_info(_Info, State) -> {noreply, State}. -- cgit v1.2.1 From e5f8663bb6b20f7ff515ee6bf021e9905f2629ef Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 16 Oct 2012 12:07:34 +0100 Subject: refactor: correct confusing variable naming --- src/rabbit_mirror_queue_slave.erl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 9e290126..7d05bcc0 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -145,10 +145,10 @@ init(#amqqueue { name = QueueName } = Q) -> end. init_it(Self, Node, QueueName) -> - [Q1 = #amqqueue { pid = QPid, slave_pids = MPids }] = + [Q1 = #amqqueue { pid = QPid, slave_pids = SPids }] = mnesia:read({rabbit_queue, QueueName}), - case [Pid || Pid <- [QPid | MPids], node(Pid) =:= Node] of - [] -> add_slave(Q1, Self, MPids), + case [Pid || Pid <- [QPid | SPids], node(Pid) =:= Node] of + [] -> add_slave(Q1, Self, SPids), {new, QPid}; [QPid] -> case rabbit_misc:is_process_alive(QPid) of true -> duplicate_live_master; @@ -156,15 +156,15 @@ init_it(Self, Node, QueueName) -> end; [SPid] -> case rabbit_misc:is_process_alive(SPid) of true -> existing; - false -> add_slave(Q1, Self, MPids -- [SPid]), + false -> add_slave(Q1, Self, SPids -- [SPid]), {new, QPid} end end. %% Add to the end, so they are in descending order of age, see %% rabbit_mirror_queue_misc:promote_slave/1 -add_slave(Q, New, MPids) -> rabbit_mirror_queue_misc:store_updated_slaves( - Q#amqqueue{slave_pids = MPids ++ [New]}). +add_slave(Q, New, SPids) -> rabbit_mirror_queue_misc:store_updated_slaves( + Q#amqqueue{slave_pids = SPids ++ [New]}). handle_call({deliver, Delivery, true}, From, State) -> %% Synchronous, "mandatory" deliver mode. -- cgit v1.2.1 From e352e4c34c92ff3be1cb6beb3692b9a31e194ede Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 16 Oct 2012 12:12:31 +0100 Subject: refactor: slightly more sensible signature for add_slave --- src/rabbit_mirror_queue_slave.erl | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 7d05bcc0..6caf135b 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -145,10 +145,10 @@ init(#amqqueue { name = QueueName } = Q) -> end. init_it(Self, Node, QueueName) -> - [Q1 = #amqqueue { pid = QPid, slave_pids = SPids }] = - mnesia:read({rabbit_queue, QueueName}), + [Q = #amqqueue { pid = QPid, slave_pids = SPids }] = + mnesia:read({rabbit_queue, QueueName}), case [Pid || Pid <- [QPid | SPids], node(Pid) =:= Node] of - [] -> add_slave(Q1, Self, SPids), + [] -> add_slave(Q, Self), {new, QPid}; [QPid] -> case rabbit_misc:is_process_alive(QPid) of true -> duplicate_live_master; @@ -156,15 +156,17 @@ init_it(Self, Node, QueueName) -> end; [SPid] -> case rabbit_misc:is_process_alive(SPid) of true -> existing; - false -> add_slave(Q1, Self, SPids -- [SPid]), + false -> Q1 = Q#amqqueue { slave_pids = SPids -- [SPid] }, + add_slave(Q1, Self), {new, QPid} end end. %% Add to the end, so they are in descending order of age, see %% rabbit_mirror_queue_misc:promote_slave/1 -add_slave(Q, New, SPids) -> rabbit_mirror_queue_misc:store_updated_slaves( - Q#amqqueue{slave_pids = SPids ++ [New]}). +add_slave(Q = #amqqueue { slave_pids = SPids }, New) -> + rabbit_mirror_queue_misc:store_updated_slaves( + Q#amqqueue{slave_pids = SPids ++ [New]}). handle_call({deliver, Delivery, true}, From, State) -> %% Synchronous, "mandatory" deliver mode. -- cgit v1.2.1 From e13235921c44b37bba3f6f855f97d1e68f4558f8 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 16 Oct 2012 12:31:30 +0100 Subject: simplify --- src/rabbit_mirror_queue_misc.erl | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index ae1004c3..d1de7861 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -74,10 +74,7 @@ remove_from_queue(QueueName, DeadGMPids) -> lists:member(GM, DeadGMPids) end, GMPids), DeadPids = [Pid || {_GM, Pid} <- Dead, Pid =/= existing], - {_, Alive} = lists:partition( - fun (Pid) -> - lists:member(Pid, DeadPids) - end, [QPid | SPids]), + Alive = [QPid | SPids] -- DeadPids, {QPid1, SPids1} = promote_slave(Alive), case {{QPid, SPids, GMPids}, {QPid1, SPids1, GMPids1}} of -- cgit v1.2.1 From c55a4aacc8de48bb3824a7b82507f7104a0a79b4 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 16 Oct 2012 13:09:21 +0100 Subject: We don't need the 'existing' records, and without them we don't need to compare GMPids for sameness. --- src/rabbit_mirror_queue_misc.erl | 6 +++--- src/rabbit_mirror_queue_slave.erl | 5 +---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index d1de7861..48cbf645 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -73,12 +73,12 @@ remove_from_queue(QueueName, DeadGMPids) -> fun ({GM, _}) -> lists:member(GM, DeadGMPids) end, GMPids), - DeadPids = [Pid || {_GM, Pid} <- Dead, Pid =/= existing], + DeadPids = [Pid || {_GM, Pid} <- Dead], Alive = [QPid | SPids] -- DeadPids, {QPid1, SPids1} = promote_slave(Alive), - - case {{QPid, SPids, GMPids}, {QPid1, SPids1, GMPids1}} of + case {{QPid, SPids}, {QPid1, SPids1}} of {Same, Same} -> + GMPids = GMPids1, %% ASSERTION {ok, QPid1, []}; _ when QPid =:= QPid1 orelse node(QPid1) =:= node() -> %% Either master hasn't changed, so diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index ff0ac9cd..7aa9c31f 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -156,10 +156,7 @@ init_it(Self, GM, Node, QueueName) -> false -> {stale, QPid} end; [SPid] -> case rabbit_misc:is_process_alive(SPid) of - true -> Q1 = Q#amqqueue { gm_pids = [{GM, existing} | - GMPids] }, - ok = rabbit_amqqueue:store_queue(Q1), - existing; + true -> existing; false -> Q1 = Q#amqqueue { slave_pids = SPids -- [SPid], gm_pids = [T || T = {_, S} <- GMPids, -- cgit v1.2.1 From 830e94ba3243c2e9fd7c20dcc69c730c804bcfa7 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 16 Oct 2012 13:36:28 +0100 Subject: Make sure we don't stomp on GMPids if a slave manages to get to Mnesia before us. --- src/rabbit_mirror_queue_master.erl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index e6638b01..ce0d8d31 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -97,14 +97,17 @@ init(Q = #amqqueue{name = QName}, Recover, AsyncCallback) -> ok = gm:broadcast(GM, {depth, BQ:depth(BQS)}), State. -init_with_existing_bq(Q, BQ, BQS) -> +init_with_existing_bq(Q = #amqqueue{name = QName}, BQ, BQS) -> {ok, CPid} = rabbit_mirror_queue_coordinator:start_link( Q, undefined, sender_death_fun(), depth_fun()), GM = rabbit_mirror_queue_coordinator:get_gm(CPid), - Q1 = Q#amqqueue{gm_pids = [{GM, self()}]}, + Self = self(), ok = rabbit_misc:execute_mnesia_transaction( fun () -> - ok = rabbit_amqqueue:store_queue(Q1) + [Q1 = #amqqueue{gm_pids = GMPids}] + = mnesia:read({rabbit_queue, QName}), + Q2 = Q1#amqqueue{gm_pids = [{GM, Self} | GMPids]}, + ok = rabbit_amqqueue:store_queue(Q2) end), #state { gm = GM, coordinator = CPid, -- cgit v1.2.1 From d73f63373a0d7fcf2645376a594d052d737eb3ff Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 16 Oct 2012 13:59:16 +0100 Subject: Typo --- src/rabbit_amqqueue_process.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index b8aad11a..7cdd7c8b 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -546,7 +546,7 @@ deliver_or_enqueue(Delivery = #delivery{message = Message, sender = SenderPid}, case attempt_delivery(Delivery, Props, State1) of {true, State2} -> State2; - %% the next one is an optimisations + %% The next one is an optimisation %% TODO: optimise the Confirm =/= never case too {false, State2 = #q{ttl = 0, dlx = undefined, backing_queue = BQ, backing_queue_state = BQS}} -- cgit v1.2.1 From b7aba78211abfd80c7fda5d4967219188978dc08 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 16 Oct 2012 14:02:41 +0100 Subject: refactor: don't track slave's master_pid separately --- src/rabbit_mirror_queue_slave.erl | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 6caf135b..7b6e4dd1 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -64,7 +64,6 @@ -record(state, { q, gm, - master_pid, backing_queue, backing_queue_state, sync_timer_ref, @@ -87,7 +86,7 @@ set_maximum_since_use(QPid, Age) -> info(QPid) -> gen_server2:call(QPid, info, infinity). -init(#amqqueue { name = QueueName } = Q) -> +init(Q = #amqqueue { name = QName }) -> %% We join the GM group before we add ourselves to the amqqueue %% record. As a result: %% 1. We can receive msgs from GM that correspond to messages we will @@ -100,23 +99,23 @@ init(#amqqueue { name = QueueName } = Q) -> %% above. %% process_flag(trap_exit, true), %% amqqueue_process traps exits too. - {ok, GM} = gm:start_link(QueueName, ?MODULE, [self()]), + {ok, GM} = gm:start_link(QName, ?MODULE, [self()]), receive {joined, GM} -> ok end, Self = self(), Node = node(), case rabbit_misc:execute_mnesia_transaction( - fun() -> init_it(Self, Node, QueueName) end) of - {new, MPid} -> - erlang:monitor(process, MPid), + fun() -> init_it(Self, Node, QName) end) of + {new, QPid} -> + erlang:monitor(process, QPid), ok = file_handle_cache:register_callback( rabbit_amqqueue, set_maximum_since_use, [Self]), ok = rabbit_memory_monitor:register( Self, {rabbit_amqqueue, set_ram_duration_target, [Self]}), {ok, BQ} = application:get_env(backing_queue_module), - BQS = bq_init(BQ, Q, false), - State = #state { q = Q, + Q1 = Q #amqqueue { pid = QPid }, + BQS = bq_init(BQ, Q1, false), + State = #state { q = Q1, gm = GM, - master_pid = MPid, backing_queue = BQ, backing_queue_state = BQS, rate_timer_ref = undefined, @@ -144,9 +143,9 @@ init(#amqqueue { name = QueueName } = Q) -> ignore end. -init_it(Self, Node, QueueName) -> +init_it(Self, Node, QName) -> [Q = #amqqueue { pid = QPid, slave_pids = SPids }] = - mnesia:read({rabbit_queue, QueueName}), + mnesia:read({rabbit_queue, QName}), case [Pid || Pid <- [QPid | SPids], node(Pid) =:= Node] of [] -> add_slave(Q, Self), {new, QPid}; @@ -174,16 +173,15 @@ handle_call({deliver, Delivery, true}, From, State) -> noreply(maybe_enqueue_message(Delivery, State)); handle_call({gm_deaths, Deaths}, From, - State = #state { q = #amqqueue { name = QueueName }, - master_pid = MPid }) -> + State = #state { q = Q = #amqqueue { name = QName, pid = MPid }}) -> %% The GM has told us about deaths, which means we're not going to %% receive any more messages from GM - case rabbit_mirror_queue_misc:remove_from_queue(QueueName, Deaths) of + case rabbit_mirror_queue_misc:remove_from_queue(QName, Deaths) of {error, not_found} -> gen_server2:reply(From, ok), {stop, normal, State}; {ok, Pid, DeadPids} -> - rabbit_mirror_queue_misc:report_deaths(self(), false, QueueName, + rabbit_mirror_queue_misc:report_deaths(self(), false, QName, DeadPids), if node(Pid) =:= node(MPid) -> %% master hasn't changed @@ -197,7 +195,7 @@ handle_call({gm_deaths, Deaths}, From, %% master has changed to not us. gen_server2:reply(From, ok), erlang:monitor(process, Pid), - noreply(State #state { master_pid = Pid }) + noreply(State #state { q = Q #amqqueue { pid = Pid } }) end end; @@ -247,7 +245,7 @@ handle_info(timeout, State) -> noreply(backing_queue_timeout(State)); handle_info({'DOWN', _MonitorRef, process, MPid, _Reason}, - State = #state { gm = GM, master_pid = MPid }) -> + State = #state { gm = GM, q = #amqqueue { pid = MPid } }) -> ok = gm:broadcast(GM, process_death), noreply(State); @@ -370,7 +368,7 @@ infos(Items, State) -> [{Item, i(Item, State)} || Item <- Items]. i(pid, _State) -> self(); i(name, #state { q = #amqqueue { name = Name } }) -> Name; -i(master_pid, #state { master_pid = MPid }) -> MPid; +i(master_pid, #state { q = #amqqueue { pid = MPid } }) -> MPid; i(is_synchronised, #state { depth_delta = DD }) -> DD =:= 0; i(Item, _State) -> throw({bad_argument, Item}). -- cgit v1.2.1 From 40aa88793d71e803310490c88c8f161a76fa8764 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 16 Oct 2012 14:07:57 +0100 Subject: remove comment that doesn't make any sense and is irrelevant --- src/rabbit_mirror_queue_slave.erl | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 7b6e4dd1..a26708af 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -174,8 +174,6 @@ handle_call({deliver, Delivery, true}, From, State) -> handle_call({gm_deaths, Deaths}, From, State = #state { q = Q = #amqqueue { name = QName, pid = MPid }}) -> - %% The GM has told us about deaths, which means we're not going to - %% receive any more messages from GM case rabbit_mirror_queue_misc:remove_from_queue(QName, Deaths) of {error, not_found} -> gen_server2:reply(From, ok), -- cgit v1.2.1 From 31e6d4997fb9784023e74f627ad0710259979995 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 16 Oct 2012 15:14:07 +0100 Subject: compare pids instead of nodes in slave's handle_call/gm_deaths --- src/rabbit_mirror_queue_slave.erl | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index a26708af..807e6310 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -179,18 +179,20 @@ handle_call({gm_deaths, Deaths}, From, gen_server2:reply(From, ok), {stop, normal, State}; {ok, Pid, DeadPids} -> - rabbit_mirror_queue_misc:report_deaths(self(), false, QName, + Self = self(), + rabbit_mirror_queue_misc:report_deaths(Self, false, QName, DeadPids), - if node(Pid) =:= node(MPid) -> + case Pid of + MPid -> %% master hasn't changed gen_server2:reply(From, ok), noreply(State); - node(Pid) =:= node() -> + Self -> %% we've become master QueueState = promote_me(From, State), {become, rabbit_amqqueue_process, QueueState, hibernate}; - true -> - %% master has changed to not us. + _ -> + %% master has changed to not us gen_server2:reply(From, ok), erlang:monitor(process, Pid), noreply(State #state { q = Q #amqqueue { pid = Pid } }) -- cgit v1.2.1 From 01dbbdb6c072d65714bbc3ee6722741fec1384bc Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 16 Oct 2012 16:04:58 +0100 Subject: cosmetic --- src/rabbit_mirror_queue_master.erl | 7 +++---- src/rabbit_mirror_queue_misc.erl | 1 - src/rabbit_mirror_queue_slave.erl | 3 +-- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index ce0d8d31..377d5186 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -106,8 +106,8 @@ init_with_existing_bq(Q = #amqqueue{name = QName}, BQ, BQS) -> fun () -> [Q1 = #amqqueue{gm_pids = GMPids}] = mnesia:read({rabbit_queue, QName}), - Q2 = Q1#amqqueue{gm_pids = [{GM, Self} | GMPids]}, - ok = rabbit_amqqueue:store_queue(Q2) + ok = rabbit_amqqueue:store_queue( + Q1#amqqueue{gm_pids = [{GM, Self} | GMPids]}) end), #state { gm = GM, coordinator = CPid, @@ -165,8 +165,7 @@ stop_all_slaves(Reason, #state{gm = GM}) -> fun () -> [Q] = mnesia:read({rabbit_queue, QName}), rabbit_mirror_queue_misc:store_updated_slaves( - Q #amqqueue { gm_pids = [], - slave_pids = [] }) + Q #amqqueue { gm_pids = [], slave_pids = [] }) end), ok = gm:forget_group(QName). diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 48cbf645..901f33b1 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -68,7 +68,6 @@ remove_from_queue(QueueName, DeadGMPids) -> [Q = #amqqueue { pid = QPid, slave_pids = SPids, gm_pids = GMPids }] -> - {Dead, GMPids1} = lists:partition( fun ({GM, _}) -> lists:member(GM, DeadGMPids) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 303569e0..afb85738 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -169,8 +169,7 @@ init_it(Self, GM, Node, QName) -> %% rabbit_mirror_queue_misc:promote_slave/1 add_slave(Q = #amqqueue { slave_pids = SPids, gm_pids = GMPids }, New, GM) -> rabbit_mirror_queue_misc:store_updated_slaves( - Q#amqqueue{slave_pids = SPids ++ [New], - gm_pids = [{GM, New} | GMPids]}). + Q#amqqueue{slave_pids = SPids ++ [New], gm_pids = [{GM, New} | GMPids]}). handle_call({deliver, Delivery, true}, From, State) -> %% Synchronous, "mandatory" deliver mode. -- cgit v1.2.1 From ad47942380b78d676568106e5b3bc5a462951c01 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 16 Oct 2012 16:08:52 +0100 Subject: Refactor a little bit. And they're Keys, not Tags. --- src/rabbit_mirror_queue_misc.erl | 2 +- src/rabbit_policy.erl | 27 ++++++++++++++------------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index f596e404..6976dbbc 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -27,7 +27,7 @@ -include("rabbit.hrl"). -rabbit_boot_step({?MODULE, - [{description, "HA policy validation capability"}, + [{description, "HA policy validation"}, {mfa, {rabbit_registry, register, [policy_validator, <<"ha-mode">>, ?MODULE]}}, {mfa, {rabbit_registry, register, diff --git a/src/rabbit_policy.erl b/src/rabbit_policy.erl index 3e7472cc..c938d1be 100644 --- a/src/rabbit_policy.erl +++ b/src/rabbit_policy.erl @@ -218,13 +218,12 @@ policy_validation() -> validation(_Name, []) -> {error, "no policy provided", []}; validation(_Name, Terms) when is_list(Terms) -> - {Tags, Modules} = lists:unzip( - rabbit_registry:lookup_all(policy_validator)), - [] = lists:usort(Tags -- lists:usort(Tags)), %% ASSERTION - Validators = lists:zipwith(fun (M, T) -> {M, a2b(T)} end, Modules, Tags), - + {Keys, Modules} = lists:unzip( + rabbit_registry:lookup_all(policy_validator)), + [] = dups(Keys), %% ASSERTION + Validators = lists:zipwith(fun (M, K) -> {M, a2b(K)} end, Modules, Keys), {TermKeys, _} = lists:unzip(Terms), - case TermKeys -- lists:usort(TermKeys) of + case dups(TermKeys) of [] -> validation0(Validators, Terms); Dup -> {error, "~p duplicate keys not allowed", [Dup]} end; @@ -233,15 +232,15 @@ validation(_Name, Term) -> validation0(Validators, Terms) -> case lists:foldl( - fun (_, {Error, _} = Acc) when Error /= ok -> - Acc; - (Mod, {ok, TermsLeft}) -> - ModTags = proplists:get_all_values(Mod, Validators), - case [T || {Tag, _} = T <- TermsLeft, - lists:member(Tag, ModTags)] of + fun (Mod, {ok, TermsLeft}) -> + ModKeys = proplists:get_all_values(Mod, Validators), + case [T || {Key, _} = T <- TermsLeft, + lists:member(Key, ModKeys)] of [] -> {ok, TermsLeft}; Scope -> {Mod:validate_policy(Scope), TermsLeft -- Scope} - end + end; + (_, Acc) -> + Acc end, {ok, Terms}, proplists:get_keys(Validators)) of {ok, []} -> ok; @@ -252,3 +251,5 @@ validation0(Validators, Terms) -> end. a2b(A) -> list_to_binary(atom_to_list(A)). + +dups(L) -> L -- lists:usort(L). -- cgit v1.2.1 From 5eb37c01f3d3cc02d174e36b75a91d754029bd62 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 16 Oct 2012 16:13:26 +0100 Subject: Refactor --- src/rabbit_mirror_queue_misc.erl | 44 ++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 6976dbbc..8950051e 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -333,28 +333,28 @@ update_mirrors0(OldQ = #amqqueue{name = QName}, %%---------------------------------------------------------------------------- -validate_policy(TagList) -> - Mode = proplists:get_all_values(<<"ha-mode">>, TagList), - Params = proplists:get_all_values(<<"ha-params">>, TagList), - case Mode of - [<<"all">>] -> - ok; - [<<"nodes">>] -> - validate_params(lists:append(Params), - fun erlang:is_binary/1, - "~p has invalid node names when ha-mode=nodes", - fun (N) -> N > 0 end, - "at least one node expected when ha-mode=nodes"); - [<<"exactly">>] -> - validate_params(Params, - fun (N) -> is_integer(N) andalso N > 0 end, - "~p must be a positive integer", - fun (N) -> N == 1 end, - "ha-params must be supplied with one number " - "when ha-mode=exactly"); - [Other] -> - {error, "~p is not a valid ha-mode value", [Other]} - end. +validate_policy(KeyList) -> + validate_policy( + proplists:get_value(<<"ha-mode">>, KeyList), + proplists:get_value(<<"ha-params">>, KeyList)). + +validate_policy(<<"all">>, _Params) -> + ok; +validate_policy(<<"nodes">>, Params) -> + validate_params(lists:append(Params), + fun erlang:is_binary/1, + "~p has invalid node names when ha-mode=nodes", + fun (N) -> N > 0 end, + "at least one node expected when ha-mode=nodes"); +validate_policy(<<"exactly">>, Params) -> + validate_params(Params, + fun (N) -> is_integer(N) andalso N > 0 end, + "~p must be a positive integer", + fun (N) -> N == 1 end, + "ha-params must be supplied with one number " + "when ha-mode=exactly"); +validate_policy(Mode, _Params) -> + {error, "~p is not a valid ha-mode value", [Mode]}. validate_params(Params, FilterPred, FilterMsg, SizePred, SizeMsg) when is_list(Params) -> -- cgit v1.2.1 From 9402f51a6585e0544d77a0511d176aae69f3f5b4 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 16 Oct 2012 16:16:04 +0100 Subject: We call it "set" not "add" everywhere else. --- src/rabbit_control_main.erl | 2 +- src/rabbit_policy.erl | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index 3f3a0b8b..c5ded019 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -470,7 +470,7 @@ action(set_policy, Node, [Key, Pattern, Defn | Prio], Opts, Inform) end, VHostArg = list_to_binary(proplists:get_value(?VHOST_OPT, Opts)), Inform(InformMsg, [Key, Pattern, Defn] ++ Prio), - rpc_call(Node, rabbit_policy, parse_add, + rpc_call(Node, rabbit_policy, parse_set, [VHostArg, list_to_binary(Key), Pattern, Defn, Prio1]); action(clear_policy, Node, [Key], Opts, Inform) -> diff --git a/src/rabbit_policy.erl b/src/rabbit_policy.erl index c938d1be..4039607a 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, validate_clear/3, notify/4, notify_clear/3]). --export([parse_add/5, add/5, delete/2, lookup/2, list/0, list/1, +-export([parse_set/5, set/5, delete/2, lookup/2, list/0, list/1, list_formatted/1, info_keys/0]). -define(TABLE, rabbit_runtime_parameters). @@ -69,34 +69,34 @@ get0(Name, List) -> case pget(definition, List) of %%---------------------------------------------------------------------------- -parse_add(VHost, Key, Pattern, Definition, undefined) -> - parse_add_policy0(VHost, Key, Pattern, Definition, []); -parse_add(VHost, Key, Pattern, Definition, Priority) -> +parse_set(VHost, Key, Pattern, Definition, undefined) -> + parse_set0(VHost, Key, Pattern, Definition, []); +parse_set(VHost, Key, Pattern, Definition, Priority) -> try list_to_integer(Priority) of - Num -> parse_add_policy0(VHost, Key, Pattern, Definition, + Num -> parse_set0(VHost, Key, Pattern, Definition, [{<<"priority">>, Num}]) catch error:badarg -> {error, "~p priority must be a number", [Priority]} end. -parse_add_policy0(VHost, Key, Pattern, Defn, Priority) -> +parse_set0(VHost, Key, Pattern, Defn, Priority) -> case rabbit_misc:json_decode(Defn) of {ok, JSON} -> - add0(VHost, Key, [{<<"pattern">>, list_to_binary(Pattern)}, + set0(VHost, Key, [{<<"pattern">>, list_to_binary(Pattern)}, {<<"policy">>, rabbit_misc:json_to_term(JSON)}] ++ Priority); error -> {error_string, "JSON decoding error"} end. -add(VHost, Key, Pattern, Definition, Priority) -> +set(VHost, Key, Pattern, Definition, Priority) -> PolicyProps = [{<<"pattern">>, Pattern}, {<<"policy">>, Definition}], - add0(VHost, Key, case Priority of + set0(VHost, Key, case Priority of undefined -> []; _ -> [{<<"priority">>, Priority}] end ++ PolicyProps). -add0(VHost, Key, Term) -> +set0(VHost, Key, Term) -> rabbit_runtime_parameters:set_any(VHost, <<"policy">>, Key, Term). delete(VHost, Key) -> -- cgit v1.2.1 From fb57129b1030702c2a281e6bed154955b0411156 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 16 Oct 2012 16:18:39 +0100 Subject: Another lurking "Tags" --- src/rabbit_tests.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 2c416c76..22742da1 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -1032,10 +1032,10 @@ test_runtime_parameters() -> test_policy_validation() -> rabbit_runtime_parameters_test:register_policy_validator(), SetPol = - fun (Tag, Val) -> + fun (Key, Val) -> control_action( set_policy, - ["name", ".*", rabbit_misc:format("{\"~s\":~p}", [Tag, Val])]) + ["name", ".*", rabbit_misc:format("{\"~s\":~p}", [Key, Val])]) end, ok = SetPol("testeven", []), -- cgit v1.2.1 From 5e5e7c66d2a2bace209461dc60c3d2a95cba8083 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 16 Oct 2012 16:24:14 +0100 Subject: Cosmetic --- src/rabbit_policy.erl | 2 +- src/rabbit_vhost.erl | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/rabbit_policy.erl b/src/rabbit_policy.erl index 4039607a..e8c1548a 100644 --- a/src/rabbit_policy.erl +++ b/src/rabbit_policy.erl @@ -84,7 +84,7 @@ parse_set0(VHost, Key, Pattern, Defn, Priority) -> {ok, JSON} -> set0(VHost, Key, [{<<"pattern">>, list_to_binary(Pattern)}, {<<"policy">>, rabbit_misc:json_to_term(JSON)}] ++ - Priority); + Priority); error -> {error_string, "JSON decoding error"} end. diff --git a/src/rabbit_vhost.erl b/src/rabbit_vhost.erl index 68b04cb1..297fa56f 100644 --- a/src/rabbit_vhost.erl +++ b/src/rabbit_vhost.erl @@ -97,9 +97,7 @@ internal_delete(VHostPath) -> proplists:get_value(component, Info), proplists:get_value(key, Info)) || Info <- rabbit_runtime_parameters:list(VHostPath)], - [ok = rabbit_policy:delete( - VHostPath, - proplists:get_value(key, Info)) + [ok = rabbit_policy:delete(VHostPath, proplists:get_value(key, Info)) || Info <- rabbit_policy:list(VHostPath)], ok = mnesia:delete({rabbit_vhost, VHostPath}), ok. -- cgit v1.2.1 From 067f9c2a9caeb52109286393385b4d7a20f31e86 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 16 Oct 2012 16:34:04 +0100 Subject: one less place to equate mirrors with nodes --- src/rabbit_mirror_queue_coordinator.erl | 2 +- src/rabbit_mirror_queue_misc.erl | 11 +++++------ src/rabbit_mirror_queue_slave.erl | 4 ++-- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/rabbit_mirror_queue_coordinator.erl b/src/rabbit_mirror_queue_coordinator.erl index 6cd71fc3..16690693 100644 --- a/src/rabbit_mirror_queue_coordinator.erl +++ b/src/rabbit_mirror_queue_coordinator.erl @@ -349,7 +349,7 @@ handle_call(get_gm, _From, State = #state { gm = GM }) -> handle_cast({gm_deaths, Deaths}, State = #state { q = #amqqueue { name = QueueName, pid = MPid } }) when node(MPid) =:= node() -> - case rabbit_mirror_queue_misc:remove_from_queue(QueueName, Deaths) of + case rabbit_mirror_queue_misc:remove_from_queue(QueueName, MPid, Deaths) of {ok, MPid, DeadPids} -> rabbit_mirror_queue_misc:report_deaths(MPid, true, QueueName, DeadPids), diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 901f33b1..8a216494 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -16,7 +16,7 @@ -module(rabbit_mirror_queue_misc). --export([remove_from_queue/2, on_node_up/0, add_mirrors/2, add_mirror/2, +-export([remove_from_queue/3, on_node_up/0, add_mirrors/2, add_mirror/2, report_deaths/4, store_updated_slaves/1, suggested_queue_nodes/1, is_mirrored/1, update_mirrors/2]). @@ -29,8 +29,8 @@ -ifdef(use_specs). --spec(remove_from_queue/2 :: - (rabbit_amqqueue:name(), [pid()]) +-spec(remove_from_queue/3 :: + (rabbit_amqqueue:name(), pid(), [pid()]) -> {'ok', pid(), [pid()]} | {'error', 'not_found'}). -spec(on_node_up/0 :: () -> 'ok'). -spec(add_mirrors/2 :: (rabbit_amqqueue:name(), [node()]) -> 'ok'). @@ -57,8 +57,7 @@ %% slave (now master) receives messages it's not ready for (for %% example, new consumers). %% Returns {ok, NewMPid, DeadPids} - -remove_from_queue(QueueName, DeadGMPids) -> +remove_from_queue(QueueName, Self, DeadGMPids) -> rabbit_misc:execute_mnesia_transaction( fun () -> %% Someone else could have deleted the queue before we @@ -79,7 +78,7 @@ remove_from_queue(QueueName, DeadGMPids) -> {Same, Same} -> GMPids = GMPids1, %% ASSERTION {ok, QPid1, []}; - _ when QPid =:= QPid1 orelse node(QPid1) =:= node() -> + _ when QPid =:= QPid1 orelse QPid1 =:= Self -> %% Either master hasn't changed, so %% we're ok to update mnesia; or we have %% become the master. diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index afb85738..3d8bd8b4 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -178,12 +178,12 @@ handle_call({deliver, Delivery, true}, From, State) -> handle_call({gm_deaths, Deaths}, From, State = #state { q = Q = #amqqueue { name = QName, pid = MPid }}) -> - case rabbit_mirror_queue_misc:remove_from_queue(QName, Deaths) of + Self = self(), + case rabbit_mirror_queue_misc:remove_from_queue(QName, Self, Deaths) of {error, not_found} -> gen_server2:reply(From, ok), {stop, normal, State}; {ok, Pid, DeadPids} -> - Self = self(), rabbit_mirror_queue_misc:report_deaths(Self, false, QName, DeadPids), case Pid of -- cgit v1.2.1 From 1c53d4743ffacad14f2fb553f9f2344c0ae6579b Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 16 Oct 2012 16:48:32 +0100 Subject: cosmetic --- src/rabbit_mirror_queue_misc.erl | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 8a216494..4c8406d9 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -161,10 +161,8 @@ add_mirror(QName, MirrorNode) -> start_child(Name, MirrorNode, Q); [SPid] -> case rabbit_misc:is_process_alive(SPid) of - true -> - {ok, already_mirrored}; - false -> - start_child(Name, MirrorNode, Q) + true -> {ok, already_mirrored}; + false -> start_child(Name, MirrorNode, Q) end end end). -- cgit v1.2.1 From 4a11eab72b23b0a1ffdd8834db7bd98b2a44de83 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 16 Oct 2012 17:00:12 +0100 Subject: Define rabbit_policy entirely in terms of parameters, don't go mucking about in mnesia. --- src/rabbit_policy.erl | 34 ++++++++++++++++------------------ src/rabbit_runtime_parameters.erl | 3 ++- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/src/rabbit_policy.erl b/src/rabbit_policy.erl index e8c1548a..5e9f10a8 100644 --- a/src/rabbit_policy.erl +++ b/src/rabbit_policy.erl @@ -30,8 +30,6 @@ -export([parse_set/5, set/5, delete/2, lookup/2, list/0, list/1, list_formatted/1, info_keys/0]). --define(TABLE, rabbit_runtime_parameters). - -rabbit_boot_step({?MODULE, [{description, "policy parameters"}, {mfa, {rabbit_policy, register, []}}, @@ -82,15 +80,16 @@ parse_set(VHost, Key, Pattern, Definition, Priority) -> parse_set0(VHost, Key, Pattern, Defn, Priority) -> case rabbit_misc:json_decode(Defn) of {ok, JSON} -> - set0(VHost, Key, [{<<"pattern">>, list_to_binary(Pattern)}, - {<<"policy">>, rabbit_misc:json_to_term(JSON)}] ++ + set0(VHost, Key, + [{<<"pattern">>, list_to_binary(Pattern)}, + {<<"definition">>, rabbit_misc:json_to_term(JSON)}] ++ Priority); error -> {error_string, "JSON decoding error"} end. set(VHost, Key, Pattern, Definition, Priority) -> - PolicyProps = [{<<"pattern">>, Pattern}, {<<"policy">>, Definition}], + PolicyProps = [{<<"pattern">>, Pattern}, {<<"definition">>, Definition}], set0(VHost, Key, case Priority of undefined -> []; _ -> [{<<"priority">>, Priority}] @@ -103,9 +102,9 @@ delete(VHost, Key) -> rabbit_runtime_parameters:clear_any(VHost, <<"policy">>, Key). lookup(VHost, Key) -> - case mnesia:dirty_read(?TABLE, {VHost, <<"policy">>, Key}) of - [] -> not_found; - [P] -> p(P, fun ident/1) + case rabbit_runtime_parameters:lookup(VHost, <<"policy">>, Key) of + not_found -> not_found; + P -> p(P, fun ident/1) end. list() -> @@ -118,19 +117,18 @@ list_formatted(VHost) -> order_policies(list0(VHost, fun format/1)). list0(VHost, DefnFun) -> - Match = #runtime_parameters{key = {VHost, <<"policy">>, '_'}, _ = '_'}, - [p(P, DefnFun) || P <- mnesia:dirty_match_object(?TABLE, Match)]. + [p(P, DefnFun) || P <- rabbit_runtime_parameters:list(VHost, <<"policy">>)]. order_policies(PropList) -> lists:sort(fun (A, B) -> pget(priority, A, 0) < pget(priority, B, 0) end, PropList). -p(#runtime_parameters{key = {VHost, <<"policy">>, Key}, value = Value}, - DefnFun) -> - [{vhost, VHost}, - {key, Key}, +p(Parameter, DefnFun) -> + Value = pget(value, Parameter), + [{vhost, pget(vhost, Parameter)}, + {key, pget(key, Parameter)}, {pattern, pget(<<"pattern">>, Value)}, - {definition, DefnFun(pget(<<"policy">>, Value))}] ++ + {definition, DefnFun(pget(<<"definition">>, Value))}] ++ case pget(<<"priority">>, Value) of undefined -> []; Priority -> [{priority, Priority}] @@ -211,9 +209,9 @@ sort_pred(A, B) -> pget(priority, A, 0) >= pget(priority, B, 0). %%---------------------------------------------------------------------------- policy_validation() -> - [{<<"priority">>, fun rabbit_parameter_validation:number/2, optional}, - {<<"pattern">>, fun rabbit_parameter_validation:regex/2, mandatory}, - {<<"policy">>, fun validation/2, mandatory}]. + [{<<"priority">>, fun rabbit_parameter_validation:number/2, optional}, + {<<"pattern">>, fun rabbit_parameter_validation:regex/2, mandatory}, + {<<"definition">>, fun validation/2, mandatory}]. validation(_Name, []) -> {error, "no policy provided", []}; diff --git a/src/rabbit_runtime_parameters.erl b/src/rabbit_runtime_parameters.erl index e66cb749..3ee93fa1 100644 --- a/src/rabbit_runtime_parameters.erl +++ b/src/rabbit_runtime_parameters.erl @@ -155,7 +155,8 @@ list(VHost, Component, Default) -> _ = '_'}, [p(P) || #runtime_parameters{ key = {_VHost, Comp, _Key}} = P <- mnesia:dirty_match_object(?TABLE, Match), - Comp /= <<"policy">>]; + Comp =/= <<"policy">> orelse + Component =:= <<"policy">>]; _ -> Default end. -- cgit v1.2.1 From 7a1127b12f62296ec1f40ecbe329041c24b5a9ea Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 16 Oct 2012 17:10:21 +0100 Subject: I think it's clearer if priority is always set. --- src/rabbit_policy.erl | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/src/rabbit_policy.erl b/src/rabbit_policy.erl index 5e9f10a8..4dcce4e0 100644 --- a/src/rabbit_policy.erl +++ b/src/rabbit_policy.erl @@ -22,7 +22,7 @@ -include("rabbit.hrl"). --import(rabbit_misc, [pget/2, pget/3]). +-import(rabbit_misc, [pget/2]). -export([register/0]). -export([name/1, get/2, set/1]). @@ -68,11 +68,10 @@ get0(Name, List) -> case pget(definition, List) of %%---------------------------------------------------------------------------- parse_set(VHost, Key, Pattern, Definition, undefined) -> - parse_set0(VHost, Key, Pattern, Definition, []); + parse_set0(VHost, Key, Pattern, Definition, 0); parse_set(VHost, Key, Pattern, Definition, Priority) -> try list_to_integer(Priority) of - Num -> parse_set0(VHost, Key, Pattern, Definition, - [{<<"priority">>, Num}]) + Num -> parse_set0(VHost, Key, Pattern, Definition, Num) catch error:badarg -> {error, "~p priority must be a number", [Priority]} end. @@ -81,19 +80,21 @@ parse_set0(VHost, Key, Pattern, Defn, Priority) -> case rabbit_misc:json_decode(Defn) of {ok, JSON} -> set0(VHost, Key, - [{<<"pattern">>, list_to_binary(Pattern)}, - {<<"definition">>, rabbit_misc:json_to_term(JSON)}] ++ - Priority); + [{<<"pattern">>, list_to_binary(Pattern)}, + {<<"definition">>, rabbit_misc:json_to_term(JSON)}, + {<<"priority">>, Priority}]); error -> {error_string, "JSON decoding error"} end. set(VHost, Key, Pattern, Definition, Priority) -> - PolicyProps = [{<<"pattern">>, Pattern}, {<<"definition">>, Definition}], - set0(VHost, Key, case Priority of - undefined -> []; - _ -> [{<<"priority">>, Priority}] - end ++ PolicyProps). + PolicyProps = [{<<"pattern">>, Pattern}, + {<<"definition">>, Definition}, + {<<"priority">>, case Priority of + undefined -> 0; + _ -> Priority + end}], + set0(VHost, Key, PolicyProps). set0(VHost, Key, Term) -> rabbit_runtime_parameters:set_any(VHost, <<"policy">>, Key, Term). @@ -120,7 +121,7 @@ list0(VHost, DefnFun) -> [p(P, DefnFun) || P <- rabbit_runtime_parameters:list(VHost, <<"policy">>)]. order_policies(PropList) -> - lists:sort(fun (A, B) -> pget(priority, A, 0) < pget(priority, B, 0) end, + lists:sort(fun (A, B) -> pget(priority, A) < pget(priority, B) end, PropList). p(Parameter, DefnFun) -> @@ -128,11 +129,8 @@ p(Parameter, DefnFun) -> [{vhost, pget(vhost, Parameter)}, {key, pget(key, Parameter)}, {pattern, pget(<<"pattern">>, Value)}, - {definition, DefnFun(pget(<<"definition">>, Value))}] ++ - case pget(<<"priority">>, Value) of - undefined -> []; - Priority -> [{priority, Priority}] - end. + {definition, DefnFun(pget(<<"definition">>, Value))}, + {priority, pget(<<"priority">>, Value)}]. format(Term) -> {ok, JSON} = rabbit_misc:json_encode(rabbit_misc:term_to_json(Term)), @@ -204,12 +202,12 @@ match(Name, Policies) -> matches(#resource{name = Name}, Policy) -> match =:= re:run(Name, pget(pattern, Policy), [{capture, none}]). -sort_pred(A, B) -> pget(priority, A, 0) >= pget(priority, B, 0). +sort_pred(A, B) -> pget(priority, A) >= pget(priority, B). %%---------------------------------------------------------------------------- policy_validation() -> - [{<<"priority">>, fun rabbit_parameter_validation:number/2, optional}, + [{<<"priority">>, fun rabbit_parameter_validation:number/2, mandatory}, {<<"pattern">>, fun rabbit_parameter_validation:regex/2, mandatory}, {<<"definition">>, fun validation/2, mandatory}]. -- cgit v1.2.1 From 5da4862faf958f1bfacc4b0a5f9326dac998afe0 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 16 Oct 2012 17:22:32 +0100 Subject: Fix formatted error handling as broken by Matthias in 891e659f0e77. --- src/rabbit_control_main.erl | 10 ++++++---- src/rabbit_tests.erl | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index e75e1f6f..a3cbf6e5 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -170,8 +170,8 @@ start() -> {error, Reason} -> print_error("~p", [Reason]), rabbit_misc:quit(2); - {parse_error, {_Line, Mod, Err}} -> - print_error("~s", [lists:flatten(Mod:format_error(Err))]), + {error_string, Reason} -> + print_error("~s", [Reason]), rabbit_misc:quit(2); {badrpc, {'EXIT', Reason}} -> print_error("~p", [Reason]), @@ -477,12 +477,14 @@ action(eval, Node, [Expr], _Opts, _Inform) -> Node, erl_eval, exprs, [Parsed, []]), io:format("~p~n", [Value]), ok; - {error, E} -> {parse_error, E} + {error, E} -> {error_string, format_parse_error(E)} end; {error, E, _} -> - {parse_error, E} + {error_string, format_parse_error(E)} end. +format_parse_error({_Line, Mod, Err}) -> lists:flatten(Mod:format_error(Err)). + %%---------------------------------------------------------------------------- wait_for_application(Node, PidFile, Application, Inform) -> diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 2e26837d..aa48f228 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -1100,8 +1100,8 @@ test_server_status() -> ok = control_action(set_vm_memory_high_watermark, [float_to_list(HWM)]), %% eval - {parse_error, _} = control_action(eval, ["\""]), - {parse_error, _} = control_action(eval, ["a("]), + {error_string, _} = control_action(eval, ["\""]), + {error_string, _} = control_action(eval, ["a("]), ok = control_action(eval, ["a."]), %% cleanup -- cgit v1.2.1 From dffb2c08c87cc5bc977379eb519e32530f18e4d0 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 16 Oct 2012 17:33:04 +0100 Subject: As far as I could tell validation for "exactly" was completely broken. Remove attempt at abstraction. Also validate that mode=all does *not* have any params. --- src/rabbit_mirror_queue_misc.erl | 39 ++++++++++++++++----------------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 1b487534..187388a5 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -346,32 +346,25 @@ validate_policy(KeyList) -> proplists:get_value(<<"ha-mode">>, KeyList), proplists:get_value(<<"ha-params">>, KeyList)). -validate_policy(<<"all">>, _Params) -> +validate_policy(<<"all">>, undefined) -> ok; +validate_policy(<<"all">>, _Params) -> + {error, "ha-mode=\"all\" does not take parameters", []}; + +validate_policy(<<"nodes">>, []) -> + {error, "ha-mode=\"nodes\" list must be non-empty", []}; +validate_policy(<<"nodes">>, Nodes) when is_list(Nodes) -> + case [I || I <- Nodes, not is_binary(I)] of + [] -> ok; + _ -> {error, "ha-mode=\"nodes\" takes a list of strings", []} + end; validate_policy(<<"nodes">>, Params) -> - validate_params(lists:append(Params), - fun erlang:is_binary/1, - "~p has invalid node names when ha-mode=nodes", - fun (N) -> N > 0 end, - "at least one node expected when ha-mode=nodes"); + {error, "ha-mode=\"nodes\" takes a list, ~p given", [Params]}; + +validate_policy(<<"exactly">>, N) when is_integer(N) andalso N > 0 -> + ok; validate_policy(<<"exactly">>, Params) -> - validate_params(Params, - fun (N) -> is_integer(N) andalso N > 0 end, - "~p must be a positive integer", - fun (N) -> N == 1 end, - "ha-params must be supplied with one number " - "when ha-mode=exactly"); + {error, "ha-mode=\"exactly\" takes an integer, ~p given", [Params]}; validate_policy(Mode, _Params) -> {error, "~p is not a valid ha-mode value", [Mode]}. -validate_params(Params, FilterPred, FilterMsg, SizePred, SizeMsg) - when is_list(Params) -> - case SizePred(length(Params)) of - true -> case lists:filter(fun (P) -> not FilterPred(P) end, Params) of - [] -> ok; - X -> {error, FilterMsg, [X]} - end; - false -> {error, SizeMsg, []} - end; -validate_params(Params, _, _, _, _) -> - {error, "~p was expected to be a list", [Params]}. -- cgit v1.2.1 From 36b6702cf233c5649add07a7f297af4cb8456a26 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 16 Oct 2012 17:39:53 +0100 Subject: publish_delivered(false, ...) -> discard The two really are the same. Since 'discard' doesn't need the message or props, there is less stuff to pass around over GM. --- src/rabbit_amqqueue_process.erl | 29 ++++++++++++------- src/rabbit_backing_queue.erl | 26 +++++++---------- src/rabbit_mirror_queue_master.erl | 59 ++++++++++++++++++-------------------- src/rabbit_mirror_queue_slave.erl | 14 ++++----- src/rabbit_variable_queue.erl | 21 ++++---------- 5 files changed, 69 insertions(+), 80 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 7ce69582..9def64ff 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -513,6 +513,14 @@ send_or_record_confirm(#delivery{sender = SenderPid, rabbit_misc:confirm_to_sender(SenderPid, [MsgSeqNo]), {immediately, State}. +discard(#delivery{sender = SenderPid, message = #basic_message{id = MsgId}}, + State) -> + %% fake an 'eventual' confirm from BQ; noop if not needed + State1 = #q{backing_queue = BQ, backing_queue_state = BQS} = + confirm_messages([MsgId], State), + BQS1 = BQ:discard(MsgId, SenderPid, BQS), + State1#q{backing_queue_state = BQS1}. + run_message_queue(State) -> State1 = #q{backing_queue = BQ, backing_queue_state = BQS} = drop_expired_messages(State), @@ -521,17 +529,20 @@ run_message_queue(State) -> BQ:is_empty(BQS), State1), State2. -attempt_delivery(#delivery{sender = SenderPid, message = Message}, Props, +attempt_delivery(Delivery = #delivery{sender = SenderPid, message = Message}, + Props = #message_properties{delivered = Delivered}, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> case BQ:is_duplicate(Message, BQS) of {false, BQS1} -> deliver_msgs_to_consumers( - fun (AckRequired, State1 = #q{backing_queue_state = BQS2}) -> + fun (true, State1 = #q{backing_queue_state = BQS2}) -> {AckTag, BQS3} = BQ:publish_delivered( - AckRequired, Message, Props, - SenderPid, BQS2), - {{Message, Props#message_properties.delivered, AckTag}, - true, State1#q{backing_queue_state = BQS3}} + Message, Props, SenderPid, BQS2), + {{Message, Delivered, AckTag}, + true, State1#q{backing_queue_state = BQS3}}; + (false, State1) -> + {{Message, Delivered, undefined}, + true, discard(Delivery, State1)} end, false, State#q{backing_queue_state = BQS1}); {published, BQS1} -> {true, State#q{backing_queue_state = BQS1}}; @@ -548,11 +559,7 @@ deliver_or_enqueue(Delivery = #delivery{message = Message, sender = SenderPid}, State2; %% The next one is an optimisation {false, State2 = #q{ttl = 0, dlx = undefined}} -> - %% fake an 'eventual' confirm from BQ; noop if not needed - State3 = #q{backing_queue = BQ, backing_queue_state = BQS} = - confirm_messages([Message#basic_message.id], State2), - BQS1 = BQ:discard(Message, SenderPid, BQS), - State3#q{backing_queue_state = BQS1}; + discard(Delivery, State2); {false, State2 = #q{backing_queue = BQ, backing_queue_state = BQS}} -> BQS1 = BQ:publish(Message, Props, SenderPid, BQS), ensure_ttl_timer(Props#message_properties.expiry, diff --git a/src/rabbit_backing_queue.erl b/src/rabbit_backing_queue.erl index c6d17785..f257c977 100644 --- a/src/rabbit_backing_queue.erl +++ b/src/rabbit_backing_queue.erl @@ -84,12 +84,16 @@ %% Called for messages which have already been passed straight %% out to a client. The queue will be empty for these calls %% (i.e. saves the round trip through the backing queue). --callback publish_delivered(true, rabbit_types:basic_message(), +-callback publish_delivered(rabbit_types:basic_message(), rabbit_types:message_properties(), pid(), state()) - -> {ack(), state()}; - (false, rabbit_types:basic_message(), - rabbit_types:message_properties(), pid(), state()) - -> {undefined, state()}. + -> {ack(), state()}. + +%% Called to inform the BQ about messages which have reached the +%% queue, but are not going to be further passed to BQ for some +%% reason. Note that this is may be invoked for messages for which +%% BQ:is_duplicate/2 has already returned {'published' | 'discarded', +%% BQS}. +-callback discard(rabbit_types:msg_id(), pid(), state()) -> state(). %% Return ids of messages which have been confirmed since the last %% invocation of this function (or initialisation). @@ -200,13 +204,6 @@ -callback is_duplicate(rabbit_types:basic_message(), state()) -> {'false'|'published'|'discarded', state()}. -%% Called to inform the BQ about messages which have reached the -%% queue, but are not going to be further passed to BQ for some -%% reason. Note that this is may be invoked for messages for which -%% BQ:is_duplicate/2 has already returned {'published' | 'discarded', -%% BQS}. --callback discard(rabbit_types:basic_message(), pid(), state()) -> state(). - -else. -export([behaviour_info/1]). @@ -214,12 +211,11 @@ behaviour_info(callbacks) -> [{start, 1}, {stop, 0}, {init, 3}, {terminate, 2}, {delete_and_terminate, 2}, {purge, 1}, {publish, 4}, - {publish_delivered, 5}, {drain_confirmed, 1}, {dropwhile, 3}, + {publish_delivered, 4}, {discard, 3}, {drain_confirmed, 1}, {dropwhile, 3}, {fetch, 2}, {ack, 2}, {fold, 3}, {requeue, 2}, {len, 1}, {is_empty, 1}, {depth, 1}, {set_ram_duration_target, 2}, {ram_duration, 1}, {needs_timeout, 1}, {timeout, 1}, - {handle_pre_hibernate, 1}, {status, 1}, {invoke, 3}, {is_duplicate, 2}, - {discard, 3}]; + {handle_pre_hibernate, 1}, {status, 1}, {invoke, 3}, {is_duplicate, 2}] ; behaviour_info(_Other) -> undefined. diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index 377d5186..cce19c90 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -17,11 +17,11 @@ -module(rabbit_mirror_queue_master). -export([init/3, terminate/2, delete_and_terminate/2, - purge/1, publish/4, publish_delivered/5, fetch/2, ack/2, + purge/1, publish/4, publish_delivered/4, discard/3, fetch/2, ack/2, requeue/2, len/1, is_empty/1, depth/1, drain_confirmed/1, dropwhile/3, set_ram_duration_target/2, ram_duration/1, needs_timeout/1, timeout/1, handle_pre_hibernate/1, - status/1, invoke/3, is_duplicate/2, discard/3, fold/3]). + status/1, invoke/3, is_duplicate/2, fold/3]). -export([start/1, stop/0]). @@ -183,25 +183,42 @@ publish(Msg = #basic_message { id = MsgId }, MsgProps, ChPid, backing_queue = BQ, backing_queue_state = BQS }) -> false = dict:is_key(MsgId, SS), %% ASSERTION - ok = gm:broadcast(GM, {publish, false, ChPid, MsgProps, Msg}), + ok = gm:broadcast(GM, {publish, ChPid, MsgProps, Msg}), BQS1 = BQ:publish(Msg, MsgProps, ChPid, BQS), ensure_monitoring(ChPid, State #state { backing_queue_state = BQS1 }). -publish_delivered(AckRequired, Msg = #basic_message { id = MsgId }, MsgProps, +publish_delivered(Msg = #basic_message { id = MsgId }, MsgProps, ChPid, State = #state { gm = GM, seen_status = SS, backing_queue = BQ, backing_queue_state = BQS, ack_msg_id = AM }) -> false = dict:is_key(MsgId, SS), %% ASSERTION - ok = gm:broadcast( - GM, {publish, {true, AckRequired}, ChPid, MsgProps, Msg}), - {AckTag, BQS1} = - BQ:publish_delivered(AckRequired, Msg, MsgProps, ChPid, BQS), + ok = gm:broadcast(GM, {publish_delivered, ChPid, MsgProps, Msg}), + {AckTag, BQS1} = BQ:publish_delivered(Msg, MsgProps, ChPid, BQS), AM1 = maybe_store_acktag(AckTag, MsgId, AM), - {AckTag, - ensure_monitoring(ChPid, State #state { backing_queue_state = BQS1, - ack_msg_id = AM1 })}. + State1 = State #state { backing_queue_state = BQS1, ack_msg_id = AM1 }, + {AckTag, ensure_monitoring(ChPid, State1)}. + +discard(MsgId, ChPid, State = #state { gm = GM, + backing_queue = BQ, + backing_queue_state = BQS, + seen_status = SS }) -> + %% It's a massive error if we get told to discard something that's + %% already been published or published-and-confirmed. To do that + %% would require non FIFO access. Hence we should not find + %% 'published' or 'confirmed' in this dict:find. + case dict:find(MsgId, SS) of + error -> + ok = gm:broadcast(GM, {discard, ChPid, MsgId}), + BQS1 = BQ:discard(MsgId, ChPid, BQS), + ensure_monitoring( + ChPid, State #state { + backing_queue_state = BQS1, + seen_status = dict:erase(MsgId, SS) }); + {ok, discarded} -> + State + end. dropwhile(Pred, AckRequired, State = #state{gm = GM, @@ -375,26 +392,6 @@ is_duplicate(Message = #basic_message { id = MsgId }, {discarded, State} end. -discard(Msg = #basic_message { id = MsgId }, ChPid, - State = #state { gm = GM, - backing_queue = BQ, - backing_queue_state = BQS, - seen_status = SS }) -> - %% It's a massive error if we get told to discard something that's - %% already been published or published-and-confirmed. To do that - %% would require non FIFO access. Hence we should not find - %% 'published' or 'confirmed' in this dict:find. - case dict:find(MsgId, SS) of - error -> - ok = gm:broadcast(GM, {discard, ChPid, Msg}), - ensure_monitoring( - ChPid, State #state { - backing_queue_state = BQ:discard(Msg, ChPid, BQS), - seen_status = dict:erase(MsgId, SS) }); - {ok, discarded} -> - State - end. - %% --------------------------------------------------------------------------- %% Other exported functions %% --------------------------------------------------------------------------- diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 3d8bd8b4..e763bd9b 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -696,25 +696,23 @@ publish_or_discard(Status, ChPid, MsgId, State1 #state { sender_queues = SQ1, msg_id_status = MS1 }. -process_instruction({publish, false, ChPid, MsgProps, +process_instruction({publish, ChPid, MsgProps, Msg = #basic_message { id = MsgId }}, State) -> State1 = #state { backing_queue = BQ, backing_queue_state = BQS } = publish_or_discard(published, ChPid, MsgId, State), BQS1 = BQ:publish(Msg, MsgProps, ChPid, BQS), {ok, State1 #state { backing_queue_state = BQS1 }}; -process_instruction({publish, {true, AckRequired}, ChPid, MsgProps, +process_instruction({publish_delivered, ChPid, MsgProps, Msg = #basic_message { id = MsgId }}, State) -> State1 = #state { backing_queue = BQ, backing_queue_state = BQS } = publish_or_discard(published, ChPid, MsgId, State), - {AckTag, BQS1} = BQ:publish_delivered(AckRequired, Msg, MsgProps, - ChPid, BQS), - {ok, maybe_store_ack(AckRequired, MsgId, AckTag, + {AckTag, BQS1} = BQ:publish_delivered(Msg, MsgProps, ChPid, BQS), + {ok, maybe_store_ack(true, MsgId, AckTag, State1 #state { backing_queue_state = BQS1 })}; -process_instruction({discard, ChPid, Msg = #basic_message { id = MsgId }}, - State) -> +process_instruction({discard, ChPid, MsgId}, State) -> State1 = #state { backing_queue = BQ, backing_queue_state = BQS } = publish_or_discard(discarded, ChPid, MsgId, State), - BQS1 = BQ:discard(Msg, ChPid, BQS), + BQS1 = BQ:discard(MsgId, ChPid, BQS), {ok, State1 #state { backing_queue_state = BQS1 }}; process_instruction({drop, Length, Dropped, AckRequired}, State = #state { backing_queue = BQ, diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index ddb136a7..8ff44a29 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -17,11 +17,11 @@ -module(rabbit_variable_queue). -export([init/3, terminate/2, delete_and_terminate/2, purge/1, - publish/4, publish_delivered/5, drain_confirmed/1, + publish/4, publish_delivered/4, discard/3, drain_confirmed/1, dropwhile/3, fetch/2, ack/2, requeue/2, len/1, is_empty/1, depth/1, set_ram_duration_target/2, ram_duration/1, needs_timeout/1, timeout/1, handle_pre_hibernate/1, status/1, invoke/3, - is_duplicate/2, discard/3, multiple_routing_keys/0, fold/3]). + is_duplicate/2, multiple_routing_keys/0, fold/3]). -export([start/1, stop/0]). @@ -545,17 +545,8 @@ publish(Msg = #basic_message { is_persistent = IsPersistent, id = MsgId }, ram_msg_count = RamMsgCount + 1, unconfirmed = UC1 })). -publish_delivered(false, #basic_message { id = MsgId }, - #message_properties { needs_confirming = NeedsConfirming }, - _ChPid, State = #vqstate { async_callback = Callback, - len = 0 }) -> - case NeedsConfirming of - true -> blind_confirm(Callback, gb_sets:singleton(MsgId)); - false -> ok - end, - {undefined, a(State)}; -publish_delivered(true, Msg = #basic_message { is_persistent = IsPersistent, - id = MsgId }, +publish_delivered(Msg = #basic_message { is_persistent = IsPersistent, + id = MsgId }, MsgProps = #message_properties { needs_confirming = NeedsConfirming }, _ChPid, State = #vqstate { len = 0, @@ -579,6 +570,8 @@ publish_delivered(true, Msg = #basic_message { is_persistent = IsPersistent, persistent_count = PCount1, unconfirmed = UC1 }))}. +discard(_MsgId, _ChPid, State) -> State. + drain_confirmed(State = #vqstate { confirmed = C }) -> case gb_sets:is_empty(C) of true -> {[], State}; %% common case @@ -821,8 +814,6 @@ invoke(?MODULE, Fun, State) -> Fun(?MODULE, State). is_duplicate(_Msg, State) -> {false, State}. -discard(_Msg, _ChPid, State) -> State. - %%---------------------------------------------------------------------------- %% Minor helpers %%---------------------------------------------------------------------------- -- cgit v1.2.1 From 35f83166676f7e6e7e11cd4829b7de18c3339fc6 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 16 Oct 2012 17:41:24 +0100 Subject: no more blind confirms --- src/rabbit_variable_queue.erl | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 8ff44a29..8a3fd9d9 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -1316,12 +1316,9 @@ must_sync_index(#vqstate { msg_indices_on_disk = MIOD, %% subtraction. not (gb_sets:is_empty(UC) orelse gb_sets:is_subset(UC, MIOD)). -blind_confirm(Callback, MsgIdSet) -> - Callback(?MODULE, - fun (?MODULE, State) -> record_confirms(MsgIdSet, State) end). - msgs_written_to_disk(Callback, MsgIdSet, ignored) -> - blind_confirm(Callback, MsgIdSet); + Callback(?MODULE, + fun (?MODULE, State) -> record_confirms(MsgIdSet, State) end); msgs_written_to_disk(Callback, MsgIdSet, written) -> Callback(?MODULE, fun (?MODULE, State = #vqstate { msgs_on_disk = MOD, -- cgit v1.2.1 From e47b703be707e44e951c18ef4956681e1479c29c Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 17 Oct 2012 12:53:52 +0100 Subject: Add policies and parameters to "rabbitmqctl report" --- src/rabbit_control_main.erl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index 424b9808..25f7d758 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -102,7 +102,9 @@ {"Bindings", rabbit_binding, info_all, info_keys}, {"Consumers", rabbit_amqqueue, consumers_all, consumer_info_keys}, {"Permissions", rabbit_auth_backend_internal, list_vhost_permissions, - vhost_perms_info_keys}]). + vhost_perms_info_keys}, + {"Policies", rabbit_policy, list_formatted, info_keys}, + {"Parameters", rabbit_runtime_parameters, list_formatted, info_keys}]). %%---------------------------------------------------------------------------- -- cgit v1.2.1 From a6210401aa5711d8c3d8adef4aa1ed943ae05a7d Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Wed, 17 Oct 2012 13:56:54 +0100 Subject: revert to a79f54b909f --- src/rabbit.erl | 51 ++++++++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 4876d679..ddaaeb25 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -301,8 +301,10 @@ start() -> %% mnesia after just restarting the app ok = ensure_application_loaded(), ok = ensure_working_log_handlers(), - rabbit_node_monitor:prepare_cluster_status_files(), - rabbit_mnesia:check_cluster_consistency(), + apply_post_boot_step( + fun rabbit_node_monitor:prepare_cluster_status_files/0), + apply_post_boot_step( + fun rabbit_mnesia:check_cluster_consistency/0), ok = app_utils:start_applications( app_startup_order(), fun handle_app_error/2), ok = print_plugin_info(rabbit_plugins:active()) @@ -313,12 +315,14 @@ boot() -> ok = ensure_application_loaded(), maybe_hipe_compile(), ok = ensure_working_log_handlers(), - rabbit_node_monitor:prepare_cluster_status_files(), + apply_post_boot_step( + fun rabbit_node_monitor:prepare_cluster_status_files/0), ok = rabbit_upgrade:maybe_upgrade_mnesia(), %% It's important that the consistency check happens after %% the upgrade, since if we are a secondary node the %% primary node will have forgotten us - rabbit_mnesia:check_cluster_consistency(), + apply_post_boot_step( + fun rabbit_mnesia:check_cluster_consistency/0), Plugins = rabbit_plugins:setup(), ToBeLoaded = Plugins ++ ?APPS, ok = app_utils:load_applications(ToBeLoaded), @@ -329,20 +333,22 @@ boot() -> ok = print_plugin_info(Plugins) end). +apply_post_boot_step(Step) -> + try + ok = Step() + catch + _:Reason -> boot_error(Reason, erlang:get_stacktrace()) + end. + handle_app_error(App, {bad_return, {_MFA, {'EXIT', {Reason, _}}}}) -> - throw({could_not_start, App, Reason}); + boot_error({could_not_start, App, Reason}, not_available); handle_app_error(App, Reason) -> - throw({could_not_start, App, Reason}). + boot_error({could_not_start, App, Reason}, not_available). start_it(StartFun) -> try StartFun() - catch - throw:{could_not_start, _App, _Reason}=Err -> - boot_error(Err, not_available); - _:Reason -> - boot_error(Reason, erlang:get_stacktrace()) after %% give the error loggers some time to catch up timer:sleep(100) @@ -503,16 +509,13 @@ sort_boot_steps(UnsortedSteps) -> not erlang:function_exported(M, F, length(A))] of [] -> SortedSteps; MissingFunctions -> basic_boot_error( - {missing_functions, MissingFunctions}, "Boot step functions not exported: ~p~n", [MissingFunctions]) end; {error, {vertex, duplicate, StepName}} -> - basic_boot_error({duplicate_boot_step, StepName}, - "Duplicate boot step name: ~w~n", [StepName]); + basic_boot_error("Duplicate boot step name: ~w~n", [StepName]); {error, {edge, Reason, From, To}} -> basic_boot_error( - {invalid_boot_step_dependency, From, To}, "Could not add boot step dependency of ~w on ~w:~n~s", [To, From, case Reason of @@ -526,7 +529,7 @@ sort_boot_steps(UnsortedSteps) -> end]) end. -boot_error(Term={error, {timeout_waiting_for_tables, _}}, _Stacktrace) -> +boot_error({error, {timeout_waiting_for_tables, _}}, _Stacktrace) -> AllNodes = rabbit_mnesia:cluster_nodes(all), {Err, Nodes} = case AllNodes -- [node()] of @@ -537,27 +540,25 @@ boot_error(Term={error, {timeout_waiting_for_tables, _}}, _Stacktrace) -> "Timeout contacting cluster nodes: ~p.~n", [Ns]), Ns} end, - basic_boot_error(Term, - Err ++ rabbit_nodes:diagnostics(Nodes) ++ "~n~n", []); + basic_boot_error(Err ++ rabbit_nodes:diagnostics(Nodes) ++ "~n~n", []); boot_error(Reason, Stacktrace) -> Fmt = "Error description:~n ~p~n~n" ++ "Log files (may contain more information):~n ~s~n ~s~n~n", Args = [Reason, log_location(kernel), log_location(sasl)], - boot_error(Reason, Fmt, Args, Stacktrace). + boot_error(Fmt, Args, Stacktrace). -boot_error(Reason, Fmt, Args, Stacktrace) -> +boot_error(Fmt, Args, Stacktrace) -> case Stacktrace of - not_available -> basic_boot_error(Reason, Fmt, Args); - _ -> basic_boot_error(Reason, Fmt ++ - "Stack trace:~n ~p~n~n", + not_available -> basic_boot_error(Fmt, Args); + _ -> basic_boot_error(Fmt ++ "Stack trace:~n ~p~n~n", Args ++ [Stacktrace]) end. -basic_boot_error(Reason, Format, Args) -> +basic_boot_error(Format, Args) -> io:format("~n~nBOOT FAILED~n===========~n~n" ++ Format, Args), error_logger:error_msg(Format, Args), timer:sleep(1000), - exit({?MODULE, failure_during_boot, Reason}). + exit({?MODULE, failure_during_boot}). %%--------------------------------------------------------------------------- %% boot step functions -- cgit v1.2.1 From 80d879c17eb4e46cf94d158cc8c86787211a4cb0 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Wed, 17 Oct 2012 15:36:19 +0100 Subject: use the worker pool for gm mnesia txns --- src/gm.erl | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/gm.erl b/src/gm.erl index 90433e84..285bb927 100644 --- a/src/gm.erl +++ b/src/gm.erl @@ -523,10 +523,10 @@ info(Server) -> gen_server2:call(Server, info, infinity). forget_group(GroupName) -> - {atomic, ok} = mnesia:sync_transaction( - fun () -> - mnesia:delete({?GROUP_TABLE, GroupName}) - end), + ok = rabbit_misc:execute_mnesia_transaction( + fun () -> + mnesia:delete({?GROUP_TABLE, GroupName}) + end), ok. init([GroupName, Module, Args]) -> @@ -1033,8 +1033,8 @@ read_group(GroupName) -> end. prune_or_create_group(Self, GroupName) -> - {atomic, Group} = - mnesia:sync_transaction( + Group = + rabbit_misc:execute_mnesia_transaction( fun () -> GroupNew = #gm_group { name = GroupName, members = [Self], version = ?VERSION_START }, @@ -1053,8 +1053,8 @@ prune_or_create_group(Self, GroupName) -> Group. record_dead_member_in_group(Member, GroupName) -> - {atomic, Group} = - mnesia:sync_transaction( + Group = + rabbit_misc:execute_mnesia_transaction( fun () -> [Group1 = #gm_group { members = Members, version = Ver }] = mnesia:read({?GROUP_TABLE, GroupName}), case lists:splitwith( @@ -1072,8 +1072,8 @@ record_dead_member_in_group(Member, GroupName) -> Group. record_new_member_in_group(GroupName, Left, NewMember, Fun) -> - {atomic, {Result, Group}} = - mnesia:sync_transaction( + {Result, Group} = + rabbit_misc:execute_mnesia_transaction( fun () -> [#gm_group { members = Members, version = Ver } = Group1] = mnesia:read({?GROUP_TABLE, GroupName}), @@ -1090,8 +1090,8 @@ record_new_member_in_group(GroupName, Left, NewMember, Fun) -> erase_members_in_group(Members, GroupName) -> DeadMembers = [{dead, Id} || Id <- Members], - {atomic, Group} = - mnesia:sync_transaction( + Group = + rabbit_misc:execute_mnesia_transaction( fun () -> [Group1 = #gm_group { members = [_|_] = Members1, version = Ver }] = -- cgit v1.2.1 From 0a744726491bec6d2c0d0c75434bcc2c3c264bcd Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Wed, 17 Oct 2012 16:25:40 +0100 Subject: do not couple gm with rabbit_misc --- src/gm.erl | 115 +++++++++++++++++--------------- src/gm_soak_test.erl | 4 +- src/gm_speed_test.erl | 3 +- src/gm_tests.erl | 10 ++- src/rabbit_mirror_queue_coordinator.erl | 5 +- src/rabbit_mirror_queue_slave.erl | 3 +- 6 files changed, 81 insertions(+), 59 deletions(-) diff --git a/src/gm.erl b/src/gm.erl index 285bb927..43cca495 100644 --- a/src/gm.erl +++ b/src/gm.erl @@ -376,7 +376,7 @@ -behaviour(gen_server2). --export([create_tables/0, start_link/3, leave/1, broadcast/2, +-export([create_tables/0, start_link/4, leave/1, broadcast/2, confirmed_broadcast/2, info/1, forget_group/1]). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, @@ -408,7 +408,8 @@ callback_args, confirms, broadcast_buffer, - broadcast_timer + broadcast_timer, + txn_executor }). -record(gm_group, { name, version, members }). @@ -428,9 +429,10 @@ -export_type([group_name/0]). -type(group_name() :: any()). +-type(txn_fun() :: fun((fun(() -> any())) -> any())). -spec(create_tables/0 :: () -> 'ok' | {'aborted', any()}). --spec(start_link/3 :: (group_name(), atom(), any()) -> +-spec(start_link/4 :: (group_name(), atom(), any(), txn_fun()) -> rabbit_types:ok_pid_or_error()). -spec(leave/1 :: (pid()) -> 'ok'). -spec(broadcast/2 :: (pid(), any()) -> 'ok'). @@ -507,8 +509,8 @@ table_definitions() -> {Name, Attributes} = ?TABLE, [{Name, [?TABLE_MATCH | Attributes]}]. -start_link(GroupName, Module, Args) -> - gen_server2:start_link(?MODULE, [GroupName, Module, Args], []). +start_link(GroupName, Module, TxnFun, Args) -> + gen_server2:start_link(?MODULE, [GroupName, Module, TxnFun, Args], []). leave(Server) -> gen_server2:cast(Server, leave). @@ -523,13 +525,13 @@ info(Server) -> gen_server2:call(Server, info, infinity). forget_group(GroupName) -> - ok = rabbit_misc:execute_mnesia_transaction( - fun () -> - mnesia:delete({?GROUP_TABLE, GroupName}) - end), + {atomic, ok} = mnesia:sync_transaction( + fun () -> + mnesia:delete({?GROUP_TABLE, GroupName}) + end), ok. -init([GroupName, Module, Args]) -> +init([GroupName, Module, TxnFun, Args]) -> {MegaSecs, Secs, MicroSecs} = now(), random:seed(MegaSecs, Secs, MicroSecs), Self = make_member(GroupName), @@ -545,7 +547,8 @@ init([GroupName, Module, Args]) -> callback_args = Args, confirms = queue:new(), broadcast_buffer = [], - broadcast_timer = undefined }, hibernate, + broadcast_timer = undefined, + txn_executor = TxnFun }, hibernate, {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}. @@ -585,7 +588,8 @@ handle_call({add_on_right, NewMember}, _From, view = View, members_state = MembersState, module = Module, - callback_args = Args }) -> + callback_args = Args, + txn_executor = TxnFun }) -> {MembersState1, Group} = record_new_member_in_group( GroupName, Self, NewMember, @@ -596,7 +600,7 @@ handle_call({add_on_right, NewMember}, _From, {catchup, Self, prepare_members_state(MembersState1)}), MembersState1 - end), + end, TxnFun), View2 = group_to_view(Group), State1 = check_neighbours(State #state { view = View2, members_state = MembersState1 }), @@ -642,8 +646,9 @@ handle_cast(join, State = #state { self = Self, group_name = GroupName, members_state = undefined, module = Module, - callback_args = Args }) -> - View = join_group(Self, GroupName), + callback_args = Args, + txn_executor = TxnFun }) -> + View = join_group(Self, GroupName, TxnFun), MembersState = case alive_view_members(View) of [Self] -> blank_member_state(); @@ -670,7 +675,8 @@ handle_info({'DOWN', MRef, process, _Pid, Reason}, view = View, module = Module, callback_args = Args, - confirms = Confirms }) -> + confirms = Confirms, + txn_executor = TxnFun }) -> Member = case {Left, Right} of {{Member1, MRef}, _} -> Member1; {_, {Member1, MRef}} -> Member1; @@ -683,7 +689,8 @@ handle_info({'DOWN', MRef, process, _Pid, Reason}, noreply(State); _ -> View1 = - group_to_view(record_dead_member_in_group(Member, GroupName)), + group_to_view(record_dead_member_in_group(Member, + GroupName, TxnFun)), {Result, State2} = case alive_view_members(View1) of [Self] -> @@ -985,14 +992,15 @@ ensure_alive_suffix1(MembersQ) -> %% View modification %% --------------------------------------------------------------------------- -join_group(Self, GroupName) -> - join_group(Self, GroupName, read_group(GroupName)). +join_group(Self, GroupName, TxnFun) -> + join_group(Self, GroupName, read_group(GroupName), TxnFun). -join_group(Self, GroupName, {error, not_found}) -> - join_group(Self, GroupName, prune_or_create_group(Self, GroupName)); -join_group(Self, _GroupName, #gm_group { members = [Self] } = Group) -> +join_group(Self, GroupName, {error, not_found}, TxnFun) -> + join_group(Self, GroupName, + prune_or_create_group(Self, GroupName, TxnFun), TxnFun); +join_group(Self, _GroupName, #gm_group { members = [Self] } = Group, _TxnFun) -> group_to_view(Group); -join_group(Self, GroupName, #gm_group { members = Members } = Group) -> +join_group(Self, GroupName, #gm_group { members = Members } = Group, TxnFun) -> case lists:member(Self, Members) of true -> group_to_view(Group); @@ -1000,20 +1008,23 @@ join_group(Self, GroupName, #gm_group { members = Members } = Group) -> case lists:filter(fun is_member_alive/1, Members) of [] -> join_group(Self, GroupName, - prune_or_create_group(Self, GroupName)); + prune_or_create_group(Self, GroupName, TxnFun)); Alive -> Left = lists:nth(random:uniform(length(Alive)), Alive), Handler = fun () -> join_group( Self, GroupName, - record_dead_member_in_group(Left, GroupName)) + record_dead_member_in_group(Left, + GroupName, + TxnFun), + TxnFun) end, try case gen_server2:call( get_pid(Left), {add_on_right, Self}, infinity) of {ok, Group1} -> group_to_view(Group1); - not_ready -> join_group(Self, GroupName) + not_ready -> join_group(Self, GroupName, TxnFun) end catch exit:{R, _} @@ -1032,29 +1043,28 @@ read_group(GroupName) -> [Group] -> Group end. -prune_or_create_group(Self, GroupName) -> - Group = - rabbit_misc:execute_mnesia_transaction( - fun () -> GroupNew = #gm_group { name = GroupName, - members = [Self], - version = ?VERSION_START }, - case mnesia:read({?GROUP_TABLE, GroupName}) of - [] -> - mnesia:write(GroupNew), - GroupNew; - [Group1 = #gm_group { members = Members }] -> - case lists:any(fun is_member_alive/1, Members) of - true -> Group1; - false -> mnesia:write(GroupNew), - GroupNew - end - end - end), +prune_or_create_group(Self, GroupName, TxnFun) -> + Group = TxnFun( + fun () -> GroupNew = #gm_group { name = GroupName, + members = [Self], + version = ?VERSION_START }, + case mnesia:read({?GROUP_TABLE, GroupName}) of + [] -> + mnesia:write(GroupNew), + GroupNew; + [Group1 = #gm_group { members = Members }] -> + case lists:any(fun is_member_alive/1, Members) of + true -> Group1; + false -> mnesia:write(GroupNew), + GroupNew + end + end + end), Group. -record_dead_member_in_group(Member, GroupName) -> +record_dead_member_in_group(Member, GroupName, TxnFun) -> Group = - rabbit_misc:execute_mnesia_transaction( + TxnFun( fun () -> [Group1 = #gm_group { members = Members, version = Ver }] = mnesia:read({?GROUP_TABLE, GroupName}), case lists:splitwith( @@ -1071,9 +1081,9 @@ record_dead_member_in_group(Member, GroupName) -> end), Group. -record_new_member_in_group(GroupName, Left, NewMember, Fun) -> +record_new_member_in_group(GroupName, Left, NewMember, Fun, TxnFun) -> {Result, Group} = - rabbit_misc:execute_mnesia_transaction( + TxnFun( fun () -> [#gm_group { members = Members, version = Ver } = Group1] = mnesia:read({?GROUP_TABLE, GroupName}), @@ -1088,10 +1098,10 @@ record_new_member_in_group(GroupName, Left, NewMember, Fun) -> end), {Result, Group}. -erase_members_in_group(Members, GroupName) -> +erase_members_in_group(Members, GroupName, TxnFun) -> DeadMembers = [{dead, Id} || Id <- Members], Group = - rabbit_misc:execute_mnesia_transaction( + TxnFun( fun () -> [Group1 = #gm_group { members = [_|_] = Members1, version = Ver }] = @@ -1112,7 +1122,8 @@ maybe_erase_aliases(State = #state { self = Self, view = View0, members_state = MembersState, module = Module, - callback_args = Args }, View) -> + callback_args = Args, + txn_executor = TxnFun }, View) -> #view_member { aliases = Aliases } = fetch_view_member(Self, View), {Erasable, MembersState1} = ?SETS:fold( @@ -1129,7 +1140,7 @@ maybe_erase_aliases(State = #state { self = Self, case Erasable of [] -> {ok, State1 #state { view = View }}; _ -> View1 = group_to_view( - erase_members_in_group(Erasable, GroupName)), + erase_members_in_group(Erasable, GroupName, TxnFun)), {callback_view_changed(Args, Module, View0, View1), check_neighbours(State1 #state { view = View1 })} end. diff --git a/src/gm_soak_test.erl b/src/gm_soak_test.erl index 57217541..5fbfc223 100644 --- a/src/gm_soak_test.erl +++ b/src/gm_soak_test.erl @@ -105,7 +105,9 @@ spawn_member() -> random:seed(MegaSecs, Secs, MicroSecs), %% start up delay of no more than 10 seconds timer:sleep(random:uniform(10000)), - {ok, Pid} = gm:start_link(?MODULE, ?MODULE, []), + {ok, Pid} = gm:start_link( + ?MODULE, ?MODULE, [], + fun rabbit_misc:execute_mnesia_transaction/1), Start = random:uniform(10000), send_loop(Pid, Start, Start + random:uniform(10000)), gm:leave(Pid), diff --git a/src/gm_speed_test.erl b/src/gm_speed_test.erl index dad75bd4..84d4ab2f 100644 --- a/src/gm_speed_test.erl +++ b/src/gm_speed_test.erl @@ -44,7 +44,8 @@ terminate(Owner, _Reason) -> %% other wile_e_coyote(Time, WriteUnit) -> - {ok, Pid} = gm:start_link(?MODULE, ?MODULE, self()), + {ok, Pid} = gm:start_link(?MODULE, ?MODULE, self(), + fun rabbit_misc:execute_mnesia_transaction/1), receive joined -> ok end, timer:sleep(1000), %% wait for all to join timer:send_after(Time, stop), diff --git a/src/gm_tests.erl b/src/gm_tests.erl index 0a2d4204..a9c0ba90 100644 --- a/src/gm_tests.erl +++ b/src/gm_tests.erl @@ -76,7 +76,9 @@ test_confirmed_broadcast() -> test_member_death() -> with_two_members( fun (Pid, Pid2) -> - {ok, Pid3} = gm:start_link(?MODULE, ?MODULE, self()), + {ok, Pid3} = gm:start_link( + ?MODULE, ?MODULE, self(), + fun rabbit_misc:execute_mnesia_transaction/1), passed = receive_joined(Pid3, [Pid, Pid2, Pid3], timeout_joining_gm_group_3), passed = receive_birth(Pid, Pid3, timeout_waiting_for_birth_3_1), @@ -128,10 +130,12 @@ test_broadcast_fun(Fun) -> with_two_members(Fun) -> ok = gm:create_tables(), - {ok, Pid} = gm:start_link(?MODULE, ?MODULE, self()), + {ok, Pid} = gm:start_link(?MODULE, ?MODULE, self(), + fun rabbit_misc:execute_mnesia_transaction/1), passed = receive_joined(Pid, [Pid], timeout_joining_gm_group_1), - {ok, Pid2} = gm:start_link(?MODULE, ?MODULE, self()), + {ok, Pid2} = gm:start_link(?MODULE, ?MODULE, self(), + fun rabbit_misc:execute_mnesia_transaction/1), passed = receive_joined(Pid2, [Pid, Pid2], timeout_joining_gm_group_2), passed = receive_birth(Pid, Pid2, timeout_waiting_for_birth_2), diff --git a/src/rabbit_mirror_queue_coordinator.erl b/src/rabbit_mirror_queue_coordinator.erl index 16690693..bcb6192a 100644 --- a/src/rabbit_mirror_queue_coordinator.erl +++ b/src/rabbit_mirror_queue_coordinator.erl @@ -326,7 +326,10 @@ ensure_monitoring(CPid, Pids) -> init([#amqqueue { name = QueueName } = Q, GM, DeathFun, DepthFun]) -> GM1 = case GM of undefined -> - {ok, GM2} = gm:start_link(QueueName, ?MODULE, [self()]), + {ok, GM2} = gm:start_link( + QueueName, ?MODULE, + [self()], + fun rabbit_misc:execute_mnesia_transaction/1), receive {joined, GM2, _Members} -> ok end, diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 3d8bd8b4..d0efe37a 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -99,7 +99,8 @@ init(Q = #amqqueue { name = QName }) -> %% above. %% process_flag(trap_exit, true), %% amqqueue_process traps exits too. - {ok, GM} = gm:start_link(QName, ?MODULE, [self()]), + {ok, GM} = gm:start_link(QName, ?MODULE, [self()], + fun rabbit_misc:execute_mnesia_transaction/1), receive {joined, GM} -> ok end, Self = self(), Node = node(), -- cgit v1.2.1 From 23a1ed1918acf89412669b825981b7df61992e8d Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Wed, 17 Oct 2012 16:48:40 +0100 Subject: oops --- src/gm_soak_test.erl | 5 +++-- src/gm_speed_test.erl | 5 +++-- src/gm_tests.erl | 15 +++++++++------ src/rabbit_mirror_queue_coordinator.erl | 4 ++-- src/rabbit_mirror_queue_slave.erl | 5 +++-- 5 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/gm_soak_test.erl b/src/gm_soak_test.erl index 5fbfc223..f49ed94b 100644 --- a/src/gm_soak_test.erl +++ b/src/gm_soak_test.erl @@ -106,8 +106,9 @@ spawn_member() -> %% start up delay of no more than 10 seconds timer:sleep(random:uniform(10000)), {ok, Pid} = gm:start_link( - ?MODULE, ?MODULE, [], - fun rabbit_misc:execute_mnesia_transaction/1), + ?MODULE, ?MODULE, + fun rabbit_misc:execute_mnesia_transaction/1, + []), Start = random:uniform(10000), send_loop(Pid, Start, Start + random:uniform(10000)), gm:leave(Pid), diff --git a/src/gm_speed_test.erl b/src/gm_speed_test.erl index 84d4ab2f..fd9bd79e 100644 --- a/src/gm_speed_test.erl +++ b/src/gm_speed_test.erl @@ -44,8 +44,9 @@ terminate(Owner, _Reason) -> %% other wile_e_coyote(Time, WriteUnit) -> - {ok, Pid} = gm:start_link(?MODULE, ?MODULE, self(), - fun rabbit_misc:execute_mnesia_transaction/1), + {ok, Pid} = gm:start_link(?MODULE, ?MODULE, + fun rabbit_misc:execute_mnesia_transaction/1, + self()), receive joined -> ok end, timer:sleep(1000), %% wait for all to join timer:send_after(Time, stop), diff --git a/src/gm_tests.erl b/src/gm_tests.erl index a9c0ba90..7a4a11a3 100644 --- a/src/gm_tests.erl +++ b/src/gm_tests.erl @@ -77,8 +77,9 @@ test_member_death() -> with_two_members( fun (Pid, Pid2) -> {ok, Pid3} = gm:start_link( - ?MODULE, ?MODULE, self(), - fun rabbit_misc:execute_mnesia_transaction/1), + ?MODULE, ?MODULE, + fun rabbit_misc:execute_mnesia_transaction/1, + self()), passed = receive_joined(Pid3, [Pid, Pid2, Pid3], timeout_joining_gm_group_3), passed = receive_birth(Pid, Pid3, timeout_waiting_for_birth_3_1), @@ -130,12 +131,14 @@ test_broadcast_fun(Fun) -> with_two_members(Fun) -> ok = gm:create_tables(), - {ok, Pid} = gm:start_link(?MODULE, ?MODULE, self(), - fun rabbit_misc:execute_mnesia_transaction/1), + {ok, Pid} = gm:start_link(?MODULE, ?MODULE, + fun rabbit_misc:execute_mnesia_transaction/1, + self()), passed = receive_joined(Pid, [Pid], timeout_joining_gm_group_1), - {ok, Pid2} = gm:start_link(?MODULE, ?MODULE, self(), - fun rabbit_misc:execute_mnesia_transaction/1), + {ok, Pid2} = gm:start_link(?MODULE, ?MODULE, + fun rabbit_misc:execute_mnesia_transaction/1, + self()), passed = receive_joined(Pid2, [Pid, Pid2], timeout_joining_gm_group_2), passed = receive_birth(Pid, Pid2, timeout_waiting_for_birth_2), diff --git a/src/rabbit_mirror_queue_coordinator.erl b/src/rabbit_mirror_queue_coordinator.erl index bcb6192a..daa247df 100644 --- a/src/rabbit_mirror_queue_coordinator.erl +++ b/src/rabbit_mirror_queue_coordinator.erl @@ -328,8 +328,8 @@ init([#amqqueue { name = QueueName } = Q, GM, DeathFun, DepthFun]) -> undefined -> {ok, GM2} = gm:start_link( QueueName, ?MODULE, - [self()], - fun rabbit_misc:execute_mnesia_transaction/1), + fun rabbit_misc:execute_mnesia_transaction/1, + [self()]), receive {joined, GM2, _Members} -> ok end, diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index d0efe37a..627630af 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -99,8 +99,9 @@ init(Q = #amqqueue { name = QName }) -> %% above. %% process_flag(trap_exit, true), %% amqqueue_process traps exits too. - {ok, GM} = gm:start_link(QName, ?MODULE, [self()], - fun rabbit_misc:execute_mnesia_transaction/1), + {ok, GM} = gm:start_link(QName, ?MODULE, + fun rabbit_misc:execute_mnesia_transaction/1, + [self()]), receive {joined, GM} -> ok end, Self = self(), Node = node(), -- cgit v1.2.1 From 3afed654e36d398054057fc0b830048d40cc3a83 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Thu, 18 Oct 2012 11:31:17 +0100 Subject: wrap the whole start after all, and use local_info_msg/2 to avoid duplication --- src/rabbit.erl | 55 +++++++++++++++++++++++++++---------------------------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 5dcf27ed..2e866ca2 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -301,10 +301,8 @@ start() -> %% mnesia after just restarting the app ok = ensure_application_loaded(), ok = ensure_working_log_handlers(), - apply_post_boot_step( - fun rabbit_node_monitor:prepare_cluster_status_files/0), - apply_post_boot_step( - fun rabbit_mnesia:check_cluster_consistency/0), + rabbit_node_monitor:prepare_cluster_status_files(), + rabbit_mnesia:check_cluster_consistency(), ok = app_utils:start_applications( app_startup_order(), fun handle_app_error/2), ok = print_plugin_info(rabbit_plugins:active()) @@ -315,14 +313,12 @@ boot() -> ok = ensure_application_loaded(), maybe_hipe_compile(), ok = ensure_working_log_handlers(), - apply_post_boot_step( - fun rabbit_node_monitor:prepare_cluster_status_files/0), + rabbit_node_monitor:prepare_cluster_status_files(), ok = rabbit_upgrade:maybe_upgrade_mnesia(), %% It's important that the consistency check happens after %% the upgrade, since if we are a secondary node the %% primary node will have forgotten us - apply_post_boot_step( - fun rabbit_mnesia:check_cluster_consistency/0), + rabbit_mnesia:check_cluster_consistency(), Plugins = rabbit_plugins:setup(), ToBeLoaded = Plugins ++ ?APPS, ok = app_utils:load_applications(ToBeLoaded), @@ -333,22 +329,20 @@ boot() -> ok = print_plugin_info(Plugins) end). -apply_post_boot_step(Step) -> - try - ok = Step() - catch - _:Reason -> boot_error(Reason, erlang:get_stacktrace()) - end. - handle_app_error(App, {bad_return, {_MFA, {'EXIT', {Reason, _}}}}) -> - boot_error({could_not_start, App, Reason}, not_available); + throw({could_not_start, App, Reason}); handle_app_error(App, Reason) -> - boot_error({could_not_start, App, Reason}, not_available). + throw({could_not_start, App, Reason}). start_it(StartFun) -> try StartFun() + catch + throw:{could_not_start, _App, _Reason}=Err -> + boot_error(Err, not_available); + _:Reason -> + boot_error(Reason, erlang:get_stacktrace()) after %% give the error loggers some time to catch up timer:sleep(100) @@ -375,7 +369,7 @@ status() -> {running_applications, application:which_applications(infinity)}, {os, os:type()}, {erlang_version, erlang:system_info(system_version)}, - {memory, rabbit_vm:memory()}], + {memory, erlang:memory()}], S2 = rabbit_misc:filter_exit_map( fun ({Key, {M, F, A}}) -> {Key, erlang:apply(M, F, A)} end, [{vm_memory_high_watermark, {vm_memory_monitor, @@ -509,13 +503,16 @@ sort_boot_steps(UnsortedSteps) -> not erlang:function_exported(M, F, length(A))] of [] -> SortedSteps; MissingFunctions -> basic_boot_error( + {missing_functions, MissingFunctions}, "Boot step functions not exported: ~p~n", [MissingFunctions]) end; {error, {vertex, duplicate, StepName}} -> - basic_boot_error("Duplicate boot step name: ~w~n", [StepName]); + basic_boot_error({duplicate_boot_step, StepName}, + "Duplicate boot step name: ~w~n", [StepName]); {error, {edge, Reason, From, To}} -> basic_boot_error( + {invalid_boot_step_dependency, From, To}, "Could not add boot step dependency of ~w on ~w:~n~s", [To, From, case Reason of @@ -529,7 +526,7 @@ sort_boot_steps(UnsortedSteps) -> end]) end. -boot_error({error, {timeout_waiting_for_tables, _}}, _Stacktrace) -> +boot_error(Term={error, {timeout_waiting_for_tables, _}}, _Stacktrace) -> AllNodes = rabbit_mnesia:cluster_nodes(all), {Err, Nodes} = case AllNodes -- [node()] of @@ -540,25 +537,27 @@ boot_error({error, {timeout_waiting_for_tables, _}}, _Stacktrace) -> "Timeout contacting cluster nodes: ~p.~n", [Ns]), Ns} end, - basic_boot_error(Err ++ rabbit_nodes:diagnostics(Nodes) ++ "~n~n", []); + basic_boot_error(Term, + Err ++ rabbit_nodes:diagnostics(Nodes) ++ "~n~n", []); boot_error(Reason, Stacktrace) -> Fmt = "Error description:~n ~p~n~n" ++ "Log files (may contain more information):~n ~s~n ~s~n~n", Args = [Reason, log_location(kernel), log_location(sasl)], - boot_error(Fmt, Args, Stacktrace). + boot_error(Reason, Fmt, Args, Stacktrace). -boot_error(Fmt, Args, Stacktrace) -> +boot_error(Reason, Fmt, Args, Stacktrace) -> case Stacktrace of - not_available -> basic_boot_error(Fmt, Args); - _ -> basic_boot_error(Fmt ++ "Stack trace:~n ~p~n~n", + not_available -> basic_boot_error(Reason, Fmt, Args); + _ -> basic_boot_error(Reason, Fmt ++ + "Stack trace:~n ~p~n~n", Args ++ [Stacktrace]) end. -basic_boot_error(Format, Args) -> +basic_boot_error(Reason, Format, Args) -> io:format("~n~nBOOT FAILED~n===========~n~n" ++ Format, Args), - error_logger:error_msg(Format, Args), + rabbit_misc:local_info_msg(Format, Args), timer:sleep(1000), - exit({?MODULE, failure_during_boot}). + exit({?MODULE, failure_during_boot, Reason}). %%--------------------------------------------------------------------------- %% boot step functions -- cgit v1.2.1 From 890b1f48570f12c6e2353262e0e245881c68cfda Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 18 Oct 2012 11:45:55 +0100 Subject: Log Rabbit / Erlang versions on startup, and fix the stop message. --- src/rabbit.erl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 93808f84..87abb9dc 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -344,7 +344,7 @@ start_it(StartFun) -> end. stop() -> - rabbit_log:info("Stopping Rabbit~n"), + rabbit_log:info("Stopping RabbitMQ~n"), ok = app_utils:stop_applications(app_shutdown_order()). stop_and_halt() -> @@ -412,6 +412,9 @@ rotate_logs(BinarySuffix) -> start(normal, []) -> case erts_version_check() of ok -> + {ok, Vsn} = application:get_key(rabbit, vsn), + error_logger:info_msg("Starting RabbitMQ ~s on Erlang ~s~n", + [Vsn, erlang:system_info(otp_release)]), {ok, SupPid} = rabbit_sup:start_link(), true = register(rabbit, self()), print_banner(), -- cgit v1.2.1 From 919f1011640f8a95a52376ddad85d42da5d0f580 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Thu, 18 Oct 2012 17:50:43 +0100 Subject: reinstate rabbit_vm:memory/0, accidentally stripped out whilst reverting changes --- src/rabbit.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 2e866ca2..c3938312 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -369,7 +369,7 @@ status() -> {running_applications, application:which_applications(infinity)}, {os, os:type()}, {erlang_version, erlang:system_info(system_version)}, - {memory, erlang:memory()}], + {memory, rabbit_vm:memory()}], S2 = rabbit_misc:filter_exit_map( fun ({Key, {M, F, A}}) -> {Key, erlang:apply(M, F, A)} end, [{vm_memory_high_watermark, {vm_memory_monitor, -- cgit v1.2.1 From 804c4bb4f3396f4ef5d60d12fb5beb77634ffce9 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 19 Oct 2012 14:05:51 +0100 Subject: complain when running 'proper' and there are no tests Twice already I wasted way too much time tracking this down. --- src/rabbit_backing_queue_qc.erl | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/rabbit_backing_queue_qc.erl b/src/rabbit_backing_queue_qc.erl index e40d9b29..d658f0f0 100644 --- a/src/rabbit_backing_queue_qc.erl +++ b/src/rabbit_backing_queue_qc.erl @@ -391,4 +391,13 @@ drop_messages(Messages) -> end end. +-else. + +-export([prop_disabled/0]). + +prop_disabled() -> + exit({compiled_without_proper, + "PropEr was not present during compilation of the test module. " + "Hence all tests are disabled."}). + -endif. -- cgit v1.2.1 From 1f188b8adf2d33f82e5d316a8f6ee428fc1bdade Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 19 Oct 2012 14:12:37 +0100 Subject: fix qc tests --- src/rabbit_backing_queue_qc.erl | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/rabbit_backing_queue_qc.erl b/src/rabbit_backing_queue_qc.erl index d658f0f0..b37fbb29 100644 --- a/src/rabbit_backing_queue_qc.erl +++ b/src/rabbit_backing_queue_qc.erl @@ -119,7 +119,7 @@ qc_publish_multiple(#state{}) -> qc_publish_delivered(#state{bqstate = BQ}) -> {call, ?BQMOD, publish_delivered, - [boolean(), qc_message(), #message_properties{}, self(), BQ]}. + [qc_message(), #message_properties{}, self(), BQ]}. qc_fetch(#state{bqstate = BQ}) -> {call, ?BQMOD, fetch, [boolean(), BQ]}. @@ -199,7 +199,7 @@ next_state(S, _BQ, {call, ?MODULE, publish_multiple, [PublishCount]}) -> next_state(S, Res, {call, ?BQMOD, publish_delivered, - [AckReq, Msg, MsgProps, _Pid, _BQ]}) -> + [Msg, MsgProps, _Pid, _BQ]}) -> #state{confirms = Confirms, acks = Acks, next_seq_id = NextSeq} = S, AckTag = {call, erlang, element, [1, Res]}, BQ1 = {call, erlang, element, [2, Res]}, @@ -213,10 +213,7 @@ next_state(S, Res, true -> gb_sets:add(MsgId, Confirms); _ -> Confirms end, - acks = case AckReq of - true -> [{AckTag, {NextSeq, {MsgProps, Msg}}}|Acks]; - false -> Acks - end + acks = [{AckTag, {NextSeq, {MsgProps, Msg}}}|Acks] }; next_state(S, Res, {call, ?BQMOD, fetch, [AckReq, _BQ]}) -> -- cgit v1.2.1 From 5a7a2b531dd233d414973d08988138580fda6fc9 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 19 Oct 2012 14:19:48 +0100 Subject: Cosmetic --- src/gm.erl | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/gm.erl b/src/gm.erl index 43cca495..b18ae8ce 100644 --- a/src/gm.erl +++ b/src/gm.erl @@ -1015,9 +1015,8 @@ join_group(Self, GroupName, #gm_group { members = Members } = Group, TxnFun) -> fun () -> join_group( Self, GroupName, - record_dead_member_in_group(Left, - GroupName, - TxnFun), + record_dead_member_in_group( + Left, GroupName, TxnFun), TxnFun) end, try @@ -1045,20 +1044,21 @@ read_group(GroupName) -> prune_or_create_group(Self, GroupName, TxnFun) -> Group = TxnFun( - fun () -> GroupNew = #gm_group { name = GroupName, - members = [Self], - version = ?VERSION_START }, - case mnesia:read({?GROUP_TABLE, GroupName}) of - [] -> - mnesia:write(GroupNew), - GroupNew; - [Group1 = #gm_group { members = Members }] -> - case lists:any(fun is_member_alive/1, Members) of - true -> Group1; - false -> mnesia:write(GroupNew), - GroupNew - end - end + fun () -> + GroupNew = #gm_group { name = GroupName, + members = [Self], + version = ?VERSION_START }, + case mnesia:read({?GROUP_TABLE, GroupName}) of + [] -> + mnesia:write(GroupNew), + GroupNew; + [Group1 = #gm_group { members = Members }] -> + case lists:any(fun is_member_alive/1, Members) of + true -> Group1; + false -> mnesia:write(GroupNew), + GroupNew + end + end end), Group. -- cgit v1.2.1 From 57a8e604bbde9ae11b58af94a1e95a525c3c3a84 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 19 Oct 2012 14:30:31 +0100 Subject: Rearrange order of args to gm:start_link/4 since it seems to make invocations more compact. Curiously I did not need to rearrange the spec as it was already that way round :-) --- src/gm.erl | 6 +++--- src/gm_soak_test.erl | 5 ++--- src/gm_speed_test.erl | 5 ++--- src/gm_tests.erl | 15 ++++++--------- src/rabbit_mirror_queue_coordinator.erl | 5 ++--- src/rabbit_mirror_queue_slave.erl | 5 ++--- 6 files changed, 17 insertions(+), 24 deletions(-) diff --git a/src/gm.erl b/src/gm.erl index b18ae8ce..4a95de0d 100644 --- a/src/gm.erl +++ b/src/gm.erl @@ -509,8 +509,8 @@ table_definitions() -> {Name, Attributes} = ?TABLE, [{Name, [?TABLE_MATCH | Attributes]}]. -start_link(GroupName, Module, TxnFun, Args) -> - gen_server2:start_link(?MODULE, [GroupName, Module, TxnFun, Args], []). +start_link(GroupName, Module, Args, TxnFun) -> + gen_server2:start_link(?MODULE, [GroupName, Module, Args, TxnFun], []). leave(Server) -> gen_server2:cast(Server, leave). @@ -531,7 +531,7 @@ forget_group(GroupName) -> end), ok. -init([GroupName, Module, TxnFun, Args]) -> +init([GroupName, Module, Args, TxnFun]) -> {MegaSecs, Secs, MicroSecs} = now(), random:seed(MegaSecs, Secs, MicroSecs), Self = make_member(GroupName), diff --git a/src/gm_soak_test.erl b/src/gm_soak_test.erl index f49ed94b..5fbfc223 100644 --- a/src/gm_soak_test.erl +++ b/src/gm_soak_test.erl @@ -106,9 +106,8 @@ spawn_member() -> %% start up delay of no more than 10 seconds timer:sleep(random:uniform(10000)), {ok, Pid} = gm:start_link( - ?MODULE, ?MODULE, - fun rabbit_misc:execute_mnesia_transaction/1, - []), + ?MODULE, ?MODULE, [], + fun rabbit_misc:execute_mnesia_transaction/1), Start = random:uniform(10000), send_loop(Pid, Start, Start + random:uniform(10000)), gm:leave(Pid), diff --git a/src/gm_speed_test.erl b/src/gm_speed_test.erl index fd9bd79e..84d4ab2f 100644 --- a/src/gm_speed_test.erl +++ b/src/gm_speed_test.erl @@ -44,9 +44,8 @@ terminate(Owner, _Reason) -> %% other wile_e_coyote(Time, WriteUnit) -> - {ok, Pid} = gm:start_link(?MODULE, ?MODULE, - fun rabbit_misc:execute_mnesia_transaction/1, - self()), + {ok, Pid} = gm:start_link(?MODULE, ?MODULE, self(), + fun rabbit_misc:execute_mnesia_transaction/1), receive joined -> ok end, timer:sleep(1000), %% wait for all to join timer:send_after(Time, stop), diff --git a/src/gm_tests.erl b/src/gm_tests.erl index 7a4a11a3..a9c0ba90 100644 --- a/src/gm_tests.erl +++ b/src/gm_tests.erl @@ -77,9 +77,8 @@ test_member_death() -> with_two_members( fun (Pid, Pid2) -> {ok, Pid3} = gm:start_link( - ?MODULE, ?MODULE, - fun rabbit_misc:execute_mnesia_transaction/1, - self()), + ?MODULE, ?MODULE, self(), + fun rabbit_misc:execute_mnesia_transaction/1), passed = receive_joined(Pid3, [Pid, Pid2, Pid3], timeout_joining_gm_group_3), passed = receive_birth(Pid, Pid3, timeout_waiting_for_birth_3_1), @@ -131,14 +130,12 @@ test_broadcast_fun(Fun) -> with_two_members(Fun) -> ok = gm:create_tables(), - {ok, Pid} = gm:start_link(?MODULE, ?MODULE, - fun rabbit_misc:execute_mnesia_transaction/1, - self()), + {ok, Pid} = gm:start_link(?MODULE, ?MODULE, self(), + fun rabbit_misc:execute_mnesia_transaction/1), passed = receive_joined(Pid, [Pid], timeout_joining_gm_group_1), - {ok, Pid2} = gm:start_link(?MODULE, ?MODULE, - fun rabbit_misc:execute_mnesia_transaction/1, - self()), + {ok, Pid2} = gm:start_link(?MODULE, ?MODULE, self(), + fun rabbit_misc:execute_mnesia_transaction/1), passed = receive_joined(Pid2, [Pid, Pid2], timeout_joining_gm_group_2), passed = receive_birth(Pid, Pid2, timeout_waiting_for_birth_2), diff --git a/src/rabbit_mirror_queue_coordinator.erl b/src/rabbit_mirror_queue_coordinator.erl index daa247df..e1a21cf7 100644 --- a/src/rabbit_mirror_queue_coordinator.erl +++ b/src/rabbit_mirror_queue_coordinator.erl @@ -327,9 +327,8 @@ init([#amqqueue { name = QueueName } = Q, GM, DeathFun, DepthFun]) -> GM1 = case GM of undefined -> {ok, GM2} = gm:start_link( - QueueName, ?MODULE, - fun rabbit_misc:execute_mnesia_transaction/1, - [self()]), + QueueName, ?MODULE, [self()], + fun rabbit_misc:execute_mnesia_transaction/1), receive {joined, GM2, _Members} -> ok end, diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 627630af..d0efe37a 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -99,9 +99,8 @@ init(Q = #amqqueue { name = QName }) -> %% above. %% process_flag(trap_exit, true), %% amqqueue_process traps exits too. - {ok, GM} = gm:start_link(QName, ?MODULE, - fun rabbit_misc:execute_mnesia_transaction/1, - [self()]), + {ok, GM} = gm:start_link(QName, ?MODULE, [self()], + fun rabbit_misc:execute_mnesia_transaction/1), receive {joined, GM} -> ok end, Self = self(), Node = node(), -- cgit v1.2.1 From ad8f1a01c955ff4a4a07878d4cd97512c7f0c115 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 19 Oct 2012 14:58:47 +0100 Subject: Symmetry. Link to the spawned process. Remove i/2 grossness. --- docs/rabbitmqctl.1.xml | 8 ++++++++ src/rabbit_reader.erl | 30 +++++++++++++++++++++--------- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/docs/rabbitmqctl.1.xml b/docs/rabbitmqctl.1.xml index 73347cea..df55a17f 100644 --- a/docs/rabbitmqctl.1.xml +++ b/docs/rabbitmqctl.1.xml @@ -1202,6 +1202,10 @@ port Server port. + + host + Server DNS host. + peer_address Peer address. @@ -1210,6 +1214,10 @@ peer_port Peer port. + + peer_host + Peer DNS host. + ssl Boolean indicating whether the diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index d3e8d4f6..3cffd1ae 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -39,11 +39,11 @@ connection_state, queue_collector, heartbeater, stats_timer, channel_sup_sup_pid, start_heartbeat_fun, buf, buf_len, auth_mechanism, auth_state, conserve_resources, - last_blocked_by, last_blocked_at, peer_host}). + last_blocked_by, last_blocked_at, host, peer_host}). -define(STATISTICS_KEYS, [pid, recv_oct, recv_cnt, send_oct, send_cnt, send_pend, state, last_blocked_by, last_blocked_age, - channels, peer_host]). + channels, host, peer_host]). -define(CREATION_EVENT_KEYS, [pid, name, address, port, peer_address, peer_port, ssl, peer_cert_subject, peer_cert_issuer, @@ -227,10 +227,7 @@ start_connection(Parent, ChannelSupSupPid, Collector, StartHeartbeatFun, Deb, last_blocked_at = never, peer_host = unknown}, Self = self(), - spawn(fun() -> - Res = rabbit_networking:tcp_host(i(peer_address, State)), - Self ! {rdns, Res} - end), + spawn_link(fun() -> do_reverse_dns(ClientSock, Self) end), try ok = inet_op(fun () -> rabbit_net:tune_buffer_size(ClientSock) end), recvloop(Deb, switch_callback(rabbit_event:init_stats_timer( @@ -256,6 +253,18 @@ start_connection(Parent, ChannelSupSupPid, Collector, StartHeartbeatFun, Deb, end, done. +do_reverse_dns(Sock, Reader) -> + Host = do_reverse_dns0(Sock, fun rabbit_net:sockname/1), + PeerHost = do_reverse_dns0(Sock, fun rabbit_net:peername/1), + unlink(Reader), + Reader ! {rdns, Host, PeerHost}. + +do_reverse_dns0(Sock, Fun) -> + case Fun(Sock) of + {ok, {IP, _Port}} -> rabbit_networking:tcp_host(IP); + _ -> undefined + end. + recvloop(Deb, State = #v1{pending_recv = true}) -> mainloop(Deb, State); recvloop(Deb, State = #v1{connection_state = blocked}) -> @@ -354,8 +363,9 @@ handle_other({system, From, Request}, Deb, State = #v1{parent = Parent}) -> handle_other({bump_credit, Msg}, Deb, State) -> credit_flow:handle_bump_msg(Msg), recvloop(Deb, control_throttle(State)); -handle_other({rdns, Host}, Deb, State) -> - mainloop(Deb, State#v1{peer_host = list_to_binary(Host)}); +handle_other({rdns, Host, PeerHost}, Deb, State) -> + mainloop(Deb, State#v1{host = list_to_binary(Host), + peer_host = list_to_binary(PeerHost)}); handle_other(Other, _Deb, _State) -> %% internal error -> something worth dying for exit({unexpected_message, Other}). @@ -883,8 +893,10 @@ i(pid, #v1{}) -> self(); i(name, #v1{sock = Sock}) -> list_to_binary(name(Sock)); -i(peer_host, #v1{peer_host = Host}) -> +i(host, #v1{host = Host}) -> Host; +i(peer_host, #v1{peer_host = PeerHost}) -> + PeerHost; i(address, #v1{sock = Sock}) -> socket_info(fun rabbit_net:sockname/1, fun ({A, _}) -> A end, Sock); i(port, #v1{sock = Sock}) -> -- cgit v1.2.1 From 9d28b43781d514f0fb4ca347ab54804dd4228792 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 19 Oct 2012 16:01:51 +0100 Subject: OK, I prefer this. Patch the dns lookups into connection_string, this makes names come out correctly for channels, stomp, mqtt and means we can remove some special casing from mgmt. Remove the extra process. Add a mechanism to disable the whole thing in case people have broken configs. --- ebin/rabbit_app.in | 1 + src/rabbit_net.erl | 40 ++++++++++++++++++++++++++++++++-------- src/rabbit_reader.erl | 21 ++++----------------- 3 files changed, 37 insertions(+), 25 deletions(-) diff --git a/ebin/rabbit_app.in b/ebin/rabbit_app.in index 9b1ff8bd..0431ee8a 100644 --- a/ebin/rabbit_app.in +++ b/ebin/rabbit_app.in @@ -43,6 +43,7 @@ {trace_vhosts, []}, {log_levels, [{connection, info}]}, {ssl_cert_login_from, distinguished_name}, + {reverse_dns_lookups, true}, {tcp_listen_options, [binary, {packet, raw}, {reuseaddr, true}, diff --git a/src/rabbit_net.erl b/src/rabbit_net.erl index 038154c3..23738d3e 100644 --- a/src/rabbit_net.erl +++ b/src/rabbit_net.erl @@ -20,7 +20,7 @@ -export([is_ssl/1, ssl_info/1, controlling_process/2, getstat/2, recv/1, async_recv/3, port_command/2, getopts/2, setopts/2, send/2, close/1, fast_close/1, sockname/1, peername/1, peercert/1, - tune_buffer_size/1, connection_string/2]). + tune_buffer_size/1, connection_string/2, rdns/2]). %%--------------------------------------------------------------------------- @@ -72,6 +72,9 @@ -spec(tune_buffer_size/1 :: (socket()) -> ok_or_any_error()). -spec(connection_string/2 :: (socket(), 'inbound' | 'outbound') -> ok_val_or_error(string())). +-spec(rdns/2 :: + (socket(), 'inbound' | 'outbound') -> {string() | 'unknown', + string() | 'unknown'}). -endif. @@ -193,17 +196,38 @@ tune_buffer_size(Sock) -> end. connection_string(Sock, Direction) -> - {From, To} = case Direction of - inbound -> {fun peername/1, fun sockname/1}; - outbound -> {fun sockname/1, fun peername/1} - end, + {From, To} = sock_funs(Direction), case {From(Sock), To(Sock)} of {{ok, {FromAddress, FromPort}}, {ok, {ToAddress, ToPort}}} -> - {ok, rabbit_misc:format("~s:~p -> ~s:~p", - [rabbit_misc:ntoab(FromAddress), FromPort, - rabbit_misc:ntoab(ToAddress), ToPort])}; + {ok, rabbit_misc:format( + "~s:~p -> ~s:~p", + [maybe_rdns(FromAddress, Sock, From), FromPort, + maybe_rdns(ToAddress, Sock, To), ToPort])}; {{error, _Reason} = Error, _} -> Error; {_, {error, _Reason} = Error} -> Error end. + +rdns(Sock, Direction) -> + {From, To} = sock_funs(Direction), + {rdns_lookup(Sock, From), rdns_lookup(Sock, To)}. + +maybe_rdns(Addr, Sock, Fun) -> + case rdns_lookup(Sock, Fun) of + unknown -> rabbit_misc:ntoab(Addr); + Host -> Host + end. + +rdns_lookup(Sock, Fun) -> + {ok, Lookup} = application:get_env(rabbit, reverse_dns_lookups), + case Lookup of + true -> case Fun(Sock) of + {ok, {IP, _Port}} -> rabbit_networking:tcp_host(IP); + _ -> unknown + end; + _ -> unknown + end. + +sock_funs(inbound) -> {fun peername/1, fun sockname/1}; +sock_funs(outbound) -> {fun sockname/1, fun peername/1}. diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 3cffd1ae..c3bd77ab 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -198,8 +198,8 @@ start_connection(Parent, ChannelSupSupPid, Collector, StartHeartbeatFun, Deb, ConnStr = name(Sock), log(info, "accepting AMQP connection ~p (~s)~n", [self(), ConnStr]), ClientSock = socket_op(Sock, SockTransform), - erlang:send_after(?HANDSHAKE_TIMEOUT * 1000, self(), - handshake_timeout), + erlang:send_after(?HANDSHAKE_TIMEOUT * 1000, self(), handshake_timeout), + {Host, PeerHost} = rabbit_net:rdns(Sock, inbound), State = #v1{parent = Parent, sock = ClientSock, connection = #connection{ @@ -225,9 +225,8 @@ start_connection(Parent, ChannelSupSupPid, Collector, StartHeartbeatFun, Deb, conserve_resources = false, last_blocked_by = none, last_blocked_at = never, - peer_host = unknown}, - Self = self(), - spawn_link(fun() -> do_reverse_dns(ClientSock, Self) end), + host = Host, + peer_host = PeerHost}, try ok = inet_op(fun () -> rabbit_net:tune_buffer_size(ClientSock) end), recvloop(Deb, switch_callback(rabbit_event:init_stats_timer( @@ -253,18 +252,6 @@ start_connection(Parent, ChannelSupSupPid, Collector, StartHeartbeatFun, Deb, end, done. -do_reverse_dns(Sock, Reader) -> - Host = do_reverse_dns0(Sock, fun rabbit_net:sockname/1), - PeerHost = do_reverse_dns0(Sock, fun rabbit_net:peername/1), - unlink(Reader), - Reader ! {rdns, Host, PeerHost}. - -do_reverse_dns0(Sock, Fun) -> - case Fun(Sock) of - {ok, {IP, _Port}} -> rabbit_networking:tcp_host(IP); - _ -> undefined - end. - recvloop(Deb, State = #v1{pending_recv = true}) -> mainloop(Deb, State); recvloop(Deb, State = #v1{connection_state = blocked}) -> -- cgit v1.2.1 From 6d7045769479c2cd9c8a1074c7de1ce1d7f63814 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 19 Oct 2012 16:14:45 +0100 Subject: Oops --- src/rabbit_reader.erl | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index c3bd77ab..9877eba3 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -350,9 +350,6 @@ handle_other({system, From, Request}, Deb, State = #v1{parent = Parent}) -> handle_other({bump_credit, Msg}, Deb, State) -> credit_flow:handle_bump_msg(Msg), recvloop(Deb, control_throttle(State)); -handle_other({rdns, Host, PeerHost}, Deb, State) -> - mainloop(Deb, State#v1{host = list_to_binary(Host), - peer_host = list_to_binary(PeerHost)}); handle_other(Other, _Deb, _State) -> %% internal error -> something worth dying for exit({unexpected_message, Other}). -- cgit v1.2.1 From 7e3a86f690c62e193569baa3a76d02503af87597 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 19 Oct 2012 16:15:15 +0100 Subject: cosmetic --- src/rabbit_net.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_net.erl b/src/rabbit_net.erl index 23738d3e..24843aa0 100644 --- a/src/rabbit_net.erl +++ b/src/rabbit_net.erl @@ -202,7 +202,7 @@ connection_string(Sock, Direction) -> {ok, rabbit_misc:format( "~s:~p -> ~s:~p", [maybe_rdns(FromAddress, Sock, From), FromPort, - maybe_rdns(ToAddress, Sock, To), ToPort])}; + maybe_rdns(ToAddress, Sock, To), ToPort])}; {{error, _Reason} = Error, _} -> Error; {_, {error, _Reason} = Error} -> -- cgit v1.2.1 From 1a226438ddaf82c7c429284feeb935341cb55ca0 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 19 Oct 2012 16:31:28 +0100 Subject: Move host and peer_host to creattion since they will not change now. --- src/rabbit_reader.erl | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 9877eba3..fab5af61 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -43,15 +43,14 @@ -define(STATISTICS_KEYS, [pid, recv_oct, recv_cnt, send_oct, send_cnt, send_pend, state, last_blocked_by, last_blocked_age, - channels, host, peer_host]). - --define(CREATION_EVENT_KEYS, [pid, name, address, port, peer_address, peer_port, - ssl, peer_cert_subject, peer_cert_issuer, - peer_cert_validity, auth_mechanism, - ssl_protocol, ssl_key_exchange, - ssl_cipher, ssl_hash, - protocol, user, vhost, timeout, frame_max, - client_properties]). + channels]). + +-define(CREATION_EVENT_KEYS, + [pid, name, address, port, peer_address, peer_port, host, + peer_host, ssl, peer_cert_subject, peer_cert_issuer, + peer_cert_validity, auth_mechanism, ssl_protocol, + ssl_key_exchange, ssl_cipher, ssl_hash, protocol, user, vhost, + timeout, frame_max, client_properties]). -define(INFO_KEYS, ?CREATION_EVENT_KEYS ++ ?STATISTICS_KEYS -- [pid]). -- cgit v1.2.1 From f684c976a797d634481de17d52bddeee8aeb7c59 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 19 Oct 2012 16:34:56 +0100 Subject: I feel a great sadness. --- ebin/rabbit_app.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ebin/rabbit_app.in b/ebin/rabbit_app.in index 0431ee8a..16dfd196 100644 --- a/ebin/rabbit_app.in +++ b/ebin/rabbit_app.in @@ -43,7 +43,7 @@ {trace_vhosts, []}, {log_levels, [{connection, info}]}, {ssl_cert_login_from, distinguished_name}, - {reverse_dns_lookups, true}, + {reverse_dns_lookups, false}, {tcp_listen_options, [binary, {packet, raw}, {reuseaddr, true}, -- cgit v1.2.1 From aedd8277d5b5a997aa0053e7e3f0275410deafb8 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 22 Oct 2012 10:09:19 +0100 Subject: cosmetic --- src/rabbit_amqqueue_process.erl | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 7ce69582..580c00fd 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -688,16 +688,15 @@ drop_expired_messages(State = #q{backing_queue_state = BQS, Now = now_micros(), DLXFun = dead_letter_fun(expired, State), ExpirePred = fun (#message_properties{expiry = Exp}) -> Now >= Exp end, - {Props, BQS1} = - case DLXFun of - undefined -> - {Next, undefined, BQS2} = BQ:dropwhile(ExpirePred, false, BQS), - {Next, BQS2}; - _ -> - {Next, Msgs, BQS2} = BQ:dropwhile(ExpirePred, true, BQS), - DLXFun(Msgs), - {Next, BQS2} - end, + {Props, BQS1} = case DLXFun of + undefined -> {Next, undefined, BQS2} = + BQ:dropwhile(ExpirePred, false, BQS), + {Next, BQS2}; + _ -> {Next, Msgs, BQS2} = + BQ:dropwhile(ExpirePred, true, BQS), + DLXFun(Msgs), + {Next, BQS2} + end, ensure_ttl_timer(case Props of undefined -> undefined; #message_properties{expiry = Exp} -> Exp -- cgit v1.2.1 From d8ba5e7f71c95eb1bef2a369cf7c994028a46106 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 22 Oct 2012 11:56:18 +0100 Subject: Wow, I think that's the stupidest thing I ever did. Added to test bug 25205, then accidentally committed. --- src/rabbit.erl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index ae73ebc8..69f77824 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -303,7 +303,6 @@ start() -> ok = ensure_working_log_handlers(), rabbit_node_monitor:prepare_cluster_status_files(), rabbit_mnesia:check_cluster_consistency(), - exit(bang), ok = app_utils:start_applications( app_startup_order(), fun handle_app_error/2), ok = print_plugin_info(rabbit_plugins:active()) -- cgit v1.2.1 From 9dd7217a71a66b1cb12a7259f15be204038df04c Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 22 Oct 2012 12:01:55 +0100 Subject: Abstract base64url/1. --- src/rabbit_guid.erl | 6 +----- src/rabbit_misc.erl | 9 +++++++++ 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/rabbit_guid.erl b/src/rabbit_guid.erl index ba0cb04f..cedbbdb3 100644 --- a/src/rabbit_guid.erl +++ b/src/rabbit_guid.erl @@ -144,11 +144,7 @@ gen_secure() -> %% employs base64url encoding, which is safer in more contexts than %% plain base64. string(G, Prefix) -> - Prefix ++ "-" ++ lists:foldl(fun ($\+, Acc) -> [$\- | Acc]; - ($\/, Acc) -> [$\_ | Acc]; - ($\=, Acc) -> Acc; - (Chr, Acc) -> [Chr | Acc] - end, [], base64:encode_to_string(G)). + Prefix ++ "-" ++ rabbit_misc:base64url(G). binary(G, Prefix) -> list_to_binary(string(G, Prefix)). diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index a0536a50..ab9a9ceb 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -63,6 +63,7 @@ -export([version/0]). -export([sequence_error/1]). -export([json_encode/1, json_decode/1, json_to_term/1, term_to_json/1]). +-export([base64url/1]). %% Horrible macro to use in guards -define(IS_BENIGN_EXIT(R), @@ -227,6 +228,7 @@ -spec(json_decode/1 :: (string()) -> {'ok', any()} | 'error'). -spec(json_to_term/1 :: (any()) -> any()). -spec(term_to_json/1 :: (any()) -> any()). +-spec(base64url/1 :: (binary()) -> string()). -endif. @@ -987,3 +989,10 @@ term_to_json(L) when is_list(L) -> term_to_json(V) when is_binary(V) orelse is_number(V) orelse V =:= null orelse V =:= true orelse V =:= false -> V. + +base64url(In) -> + lists:reverse(lists:foldl(fun ($\+, Acc) -> [$\- | Acc]; + ($\/, Acc) -> [$\_ | Acc]; + ($\=, Acc) -> Acc; + (Chr, Acc) -> [Chr | Acc] + end, [], base64:encode_to_string(In))). -- cgit v1.2.1 From 3cd89a4583d5e06d8a6cd08f50512736aa66e96b Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 22 Oct 2012 12:14:49 +0100 Subject: Cosmetic + show non-string nodes params + display "none" when params missing. --- src/rabbit_mirror_queue_misc.erl | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 187388a5..4a00846e 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -344,9 +344,9 @@ update_mirrors0(OldQ = #amqqueue{name = QName}, validate_policy(KeyList) -> validate_policy( proplists:get_value(<<"ha-mode">>, KeyList), - proplists:get_value(<<"ha-params">>, KeyList)). + proplists:get_value(<<"ha-params">>, KeyList, none)). -validate_policy(<<"all">>, undefined) -> +validate_policy(<<"all">>, none) -> ok; validate_policy(<<"all">>, _Params) -> {error, "ha-mode=\"all\" does not take parameters", []}; @@ -355,8 +355,9 @@ validate_policy(<<"nodes">>, []) -> {error, "ha-mode=\"nodes\" list must be non-empty", []}; validate_policy(<<"nodes">>, Nodes) when is_list(Nodes) -> case [I || I <- Nodes, not is_binary(I)] of - [] -> ok; - _ -> {error, "ha-mode=\"nodes\" takes a list of strings", []} + [] -> ok; + Invalid -> {error, "ha-mode=\"nodes\" takes a list of strings, " + "~p was not a string", [Invalid]} end; validate_policy(<<"nodes">>, Params) -> {error, "ha-mode=\"nodes\" takes a list, ~p given", [Params]}; @@ -365,6 +366,7 @@ validate_policy(<<"exactly">>, N) when is_integer(N) andalso N > 0 -> ok; validate_policy(<<"exactly">>, Params) -> {error, "ha-mode=\"exactly\" takes an integer, ~p given", [Params]}; + validate_policy(Mode, _Params) -> {error, "~p is not a valid ha-mode value", [Mode]}. -- cgit v1.2.1 From 1400865c9a9b996b9137953ed882fc41d7c0dc64 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 22 Oct 2012 15:05:49 +0100 Subject: s/key/name/, and a couple of other minor tidyups that I noticed at the same time. --- docs/rabbitmqctl.1.xml | 34 ++++++++-------- src/rabbit_policy.erl | 32 +++++++-------- src/rabbit_runtime_parameters.erl | 85 ++++++++++++++++++++------------------- 3 files changed, 76 insertions(+), 75 deletions(-) diff --git a/docs/rabbitmqctl.1.xml b/docs/rabbitmqctl.1.xml index 522a51ee..53411d8b 100644 --- a/docs/rabbitmqctl.1.xml +++ b/docs/rabbitmqctl.1.xml @@ -806,8 +806,8 @@ Certain features of RabbitMQ (such as the federation plugin) are controlled by dynamic, cluster-wide parameters. Each parameter - consists of a component name, a key and a value, and is - associated with a virtual host. The component name and key are + consists of a component name, a name and a value, and is + associated with a virtual host. The component name and name are strings, and the value is an Erlang term. Parameters can be set, cleared and listed. In general you should refer to the documentation for the feature in question to see how to set @@ -815,7 +815,7 @@ - set_parameter -p vhostpath component_name key value + set_parameter -p vhostpath component_name name value Sets a parameter. @@ -829,24 +829,24 @@ - key + name - The key for which the parameter is being set. + The name of the parameter being set. value - The value for the parameter, as an - Erlang term. In most shells you are very likely to + The value for the parameter, as a + JSON term. In most shells you are very likely to need to quote this. For example: - rabbitmqctl set_parameter federation local_username '<<"guest">>' + rabbitmqctl set_parameter federation local_username '"guest"' - This command sets the parameter local_username for the federation component in the default virtual host to the Erlang term <<"guest">>. + This command sets the parameter local_username for the federation component in the default virtual host to the JSON term "guest". @@ -865,9 +865,9 @@ - key + name - The key for which the parameter is being cleared. + The name of the parameter being cleared. @@ -899,19 +899,19 @@ Policies are used to control and modify the behaviour of queues and exchanges on a cluster-wide basis. Policies apply within a - given vhost, and consist of a key, pattern, definition and an + given vhost, and consist of a name, pattern, definition and an optional priority. Policies can be set, cleared and listed. - set_policy -p vhostpath key pattern definition priority + set_policy -p vhostpath name pattern definition priority Sets a policy. - key + name The name of the policy. @@ -926,7 +926,7 @@ definition The definition of the policy, as a - JSON string. In most shells you are very likely to + JSON term. In most shells you are very likely to need to quote this. @@ -945,14 +945,14 @@ - clear_policy -p vhostpath key + clear_policy -p vhostpath name Clears a policy. - key + name The name of the policy being cleared. diff --git a/src/rabbit_policy.erl b/src/rabbit_policy.erl index 4dcce4e0..9af8fa18 100644 --- a/src/rabbit_policy.erl +++ b/src/rabbit_policy.erl @@ -67,19 +67,19 @@ get0(Name, List) -> case pget(definition, List) of %%---------------------------------------------------------------------------- -parse_set(VHost, Key, Pattern, Definition, undefined) -> - parse_set0(VHost, Key, Pattern, Definition, 0); -parse_set(VHost, Key, Pattern, Definition, Priority) -> +parse_set(VHost, Name, Pattern, Definition, undefined) -> + parse_set0(VHost, Name, Pattern, Definition, 0); +parse_set(VHost, Name, Pattern, Definition, Priority) -> try list_to_integer(Priority) of - Num -> parse_set0(VHost, Key, Pattern, Definition, Num) + Num -> parse_set0(VHost, Name, Pattern, Definition, Num) catch error:badarg -> {error, "~p priority must be a number", [Priority]} end. -parse_set0(VHost, Key, Pattern, Defn, Priority) -> +parse_set0(VHost, Name, Pattern, Defn, Priority) -> case rabbit_misc:json_decode(Defn) of {ok, JSON} -> - set0(VHost, Key, + set0(VHost, Name, [{<<"pattern">>, list_to_binary(Pattern)}, {<<"definition">>, rabbit_misc:json_to_term(JSON)}, {<<"priority">>, Priority}]); @@ -87,23 +87,23 @@ parse_set0(VHost, Key, Pattern, Defn, Priority) -> {error_string, "JSON decoding error"} end. -set(VHost, Key, Pattern, Definition, Priority) -> +set(VHost, Name, Pattern, Definition, Priority) -> PolicyProps = [{<<"pattern">>, Pattern}, {<<"definition">>, Definition}, {<<"priority">>, case Priority of undefined -> 0; _ -> Priority end}], - set0(VHost, Key, PolicyProps). + set0(VHost, Name, PolicyProps). -set0(VHost, Key, Term) -> - rabbit_runtime_parameters:set_any(VHost, <<"policy">>, Key, Term). +set0(VHost, Name, Term) -> + rabbit_runtime_parameters:set_any(VHost, <<"policy">>, Name, Term). -delete(VHost, Key) -> - rabbit_runtime_parameters:clear_any(VHost, <<"policy">>, Key). +delete(VHost, Name) -> + rabbit_runtime_parameters:clear_any(VHost, <<"policy">>, Name). -lookup(VHost, Key) -> - case rabbit_runtime_parameters:lookup(VHost, <<"policy">>, Key) of +lookup(VHost, Name) -> + case rabbit_runtime_parameters:lookup(VHost, <<"policy">>, Name) of not_found -> not_found; P -> p(P, fun ident/1) end. @@ -127,7 +127,7 @@ order_policies(PropList) -> p(Parameter, DefnFun) -> Value = pget(value, Parameter), [{vhost, pget(vhost, Parameter)}, - {key, pget(key, Parameter)}, + {name, pget(name, Parameter)}, {pattern, pget(<<"pattern">>, Value)}, {definition, DefnFun(pget(<<"definition">>, Value))}, {priority, pget(<<"priority">>, Value)}]. @@ -138,7 +138,7 @@ format(Term) -> ident(X) -> X. -info_keys() -> [vhost, key, pattern, definition, priority]. +info_keys() -> [vhost, name, pattern, definition, priority]. %%---------------------------------------------------------------------------- diff --git a/src/rabbit_runtime_parameters.erl b/src/rabbit_runtime_parameters.erl index 3ee93fa1..4a83e61f 100644 --- a/src/rabbit_runtime_parameters.erl +++ b/src/rabbit_runtime_parameters.erl @@ -63,34 +63,35 @@ parse_set(_, <<"policy">>, _, _) -> {error_string, "policies may not be set using this method"}; -parse_set(VHost, Component, Key, String) -> +parse_set(VHost, Component, Name, String) -> case rabbit_misc:json_decode(String) of - {ok, JSON} -> set(VHost, Component, Key, rabbit_misc:json_to_term(JSON)); + {ok, JSON} -> set(VHost, Component, Name, + rabbit_misc:json_to_term(JSON)); error -> {error_string, "JSON decoding error"} end. set(_, <<"policy">>, _, _) -> {error_string, "policies may not be set using this method"}; -set(VHost, Component, Key, Term) -> - set_any(VHost, Component, Key, Term). +set(VHost, Component, Name, Term) -> + set_any(VHost, Component, Name, Term). format_error(L) -> {error_string, rabbit_misc:format_many([{"Validation failed~n", []} | L])}. -set_any(VHost, Component, Key, Term) -> - case set_any0(VHost, Component, Key, Term) of +set_any(VHost, Component, Name, Term) -> + case set_any0(VHost, Component, Name, Term) of ok -> ok; {errors, L} -> format_error(L) end. -set_any0(VHost, Component, Key, Term) -> +set_any0(VHost, Component, Name, Term) -> case lookup_component(Component) of {ok, Mod} -> - case flatten_errors(Mod:validate(VHost, Component, Key, Term)) of + case flatten_errors(Mod:validate(VHost, Component, Name, Term)) of ok -> - case mnesia_update(VHost, Component, Key, Term) of + case mnesia_update(VHost, Component, Name, Term) of {old, Term} -> ok; - _ -> Mod:notify(VHost, Component, Key, Term) + _ -> Mod:notify(VHost, Component, Name, Term) end, ok; E -> @@ -100,48 +101,48 @@ set_any0(VHost, Component, Key, Term) -> E end. -mnesia_update(VHost, Component, Key, Term) -> +mnesia_update(VHost, Component, Name, Term) -> rabbit_misc:execute_mnesia_transaction( fun () -> - Res = case mnesia:read(?TABLE, {VHost, Component, Key}, read) of + Res = case mnesia:read(?TABLE, {VHost, Component, Name}, read) of [] -> new; [Params] -> {old, Params#runtime_parameters.value} end, - ok = mnesia:write(?TABLE, c(VHost, Component, Key, Term), write), + ok = mnesia:write(?TABLE, c(VHost, Component, Name, Term), write), Res end). clear(_, <<"policy">> , _) -> {error_string, "policies may not be cleared using this method"}; -clear(VHost, Component, Key) -> - clear_any(VHost, Component, Key). +clear(VHost, Component, Name) -> + clear_any(VHost, Component, Name). -clear_any(VHost, Component, Key) -> - case clear_any0(VHost, Component, Key) of +clear_any(VHost, Component, Name) -> + case clear_any0(VHost, Component, Name) of ok -> ok; {errors, L} -> format_error(L) end. -clear_any0(VHost, Component, Key) -> +clear_any0(VHost, Component, Name) -> case lookup_component(Component) of {ok, Mod} -> case flatten_errors( - Mod:validate_clear(VHost, Component, Key)) of - ok -> mnesia_clear(VHost, Component, Key), - Mod:notify_clear(VHost, Component, Key), + Mod:validate_clear(VHost, Component, Name)) of + ok -> mnesia_clear(VHost, Component, Name), + Mod:notify_clear(VHost, Component, Name), ok; E -> E end; E -> E end. -mnesia_clear(VHost, Component, Key) -> +mnesia_clear(VHost, Component, Name) -> ok = rabbit_misc:execute_mnesia_transaction( fun () -> - ok = mnesia:delete(?TABLE, {VHost, Component, Key}, write) + ok = mnesia:delete(?TABLE, {VHost, Component, Name}, write) end). list() -> - [p(P) || #runtime_parameters{ key = {_VHost, Comp, _Key}} = P <- + [p(P) || #runtime_parameters{ key = {_VHost, Comp, _Name}} = P <- rabbit_misc:dirty_read_all(?TABLE), Comp /= <<"policy">>]. list(VHost) -> list(VHost, '_', []). @@ -153,7 +154,7 @@ list(VHost, Component, Default) -> case component_good(Component) of true -> Match = #runtime_parameters{key = {VHost, Component, '_'}, _ = '_'}, - [p(P) || #runtime_parameters{ key = {_VHost, Comp, _Key}} = P <- + [p(P) || #runtime_parameters{ key = {_VHost, Comp, _Name}} = P <- mnesia:dirty_match_object(?TABLE, Match), Comp =/= <<"policy">> orelse Component =:= <<"policy">>]; @@ -163,53 +164,53 @@ list(VHost, Component, Default) -> list_formatted(VHost) -> [pset(value, format(pget(value, P)), P) || P <- list(VHost)]. -lookup(VHost, Component, Key) -> - case lookup0(VHost, Component, Key, rabbit_misc:const(not_found)) of +lookup(VHost, Component, Name) -> + case lookup0(VHost, Component, Name, rabbit_misc:const(not_found)) of not_found -> not_found; Params -> p(Params) end. -value(VHost, Component, Key) -> - case lookup0(VHost, Component, Key, rabbit_misc:const(not_found)) of +value(VHost, Component, Name) -> + case lookup0(VHost, Component, Name, rabbit_misc:const(not_found)) of not_found -> not_found; Params -> Params#runtime_parameters.value end. -value(VHost, Component, Key, Default) -> - Params = lookup0(VHost, Component, Key, +value(VHost, Component, Name, Default) -> + Params = lookup0(VHost, Component, Name, fun () -> - lookup_missing(VHost, Component, Key, Default) + lookup_missing(VHost, Component, Name, Default) end), Params#runtime_parameters.value. -lookup0(VHost, Component, Key, DefaultFun) -> - case mnesia:dirty_read(?TABLE, {VHost, Component, Key}) of +lookup0(VHost, Component, Name, DefaultFun) -> + case mnesia:dirty_read(?TABLE, {VHost, Component, Name}) of [] -> DefaultFun(); [R] -> R end. -lookup_missing(VHost, Component, Key, Default) -> +lookup_missing(VHost, Component, Name, Default) -> rabbit_misc:execute_mnesia_transaction( fun () -> - case mnesia:read(?TABLE, {VHost, Component, Key}, read) of - [] -> Record = c(VHost, Component, Key, Default), + case mnesia:read(?TABLE, {VHost, Component, Name}, read) of + [] -> Record = c(VHost, Component, Name, Default), mnesia:write(?TABLE, Record, write), Record; [R] -> R end end). -c(VHost, Component, Key, Default) -> - #runtime_parameters{key = {VHost, Component, Key}, +c(VHost, Component, Name, Default) -> + #runtime_parameters{key = {VHost, Component, Name}, value = Default}. -p(#runtime_parameters{key = {VHost, Component, Key}, value = Value}) -> +p(#runtime_parameters{key = {VHost, Component, Name}, value = Value}) -> [{vhost, VHost}, {component, Component}, - {key, Key}, + {name, Name}, {value, Value}]. -info_keys() -> [component, key, value]. +info_keys() -> [component, name, value]. %%--------------------------------------------------------------------------- -- cgit v1.2.1 From 27d34196460179deba4798c04f28579bc7e8f4c1 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 23 Oct 2012 11:14:02 +0100 Subject: Everywhere else we say "disc" when clustering. Note that change_cluster_node_type will actually accept either spelling. --- docs/rabbitmqctl.1.xml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/rabbitmqctl.1.xml b/docs/rabbitmqctl.1.xml index 73347cea..23c392b7 100644 --- a/docs/rabbitmqctl.1.xml +++ b/docs/rabbitmqctl.1.xml @@ -313,8 +313,8 @@ linkend="stop_app">stop_app. - Cluster nodes can be of two types: disk or RAM. Disk nodes - replicate data in RAM and on disk, thus providing redundancy in + Cluster nodes can be of two types: disc or RAM. Disc nodes + replicate data in RAM and on disc, thus providing redundancy in the event of node failure and recovery from global events such as power failure across all nodes. RAM nodes replicate data in RAM only (with the exception of queue contents, which can reside @@ -322,10 +322,10 @@ and are mainly used for scalability. RAM nodes are more performant only when managing resources (e.g. adding/removing queues, exchanges, or bindings). A cluster must always have at - least one disk node, and usually should have more than one. + least one disc node, and usually should have more than one. - The node will be a disk node by default. If you wish to + The node will be a disc node by default. If you wish to create a RAM node, provide the --ram flag. @@ -367,18 +367,18 @@ - change_cluster_node_type disk | ram + change_cluster_node_type disc | ram Changes the type of the cluster node. The node must be stopped for this operation to succeed, and when turning a node into a RAM node - the node must not be the only disk node in the cluster. + the node must not be the only disc node in the cluster. For example: - rabbitmqctl change_cluster_node_type disk + rabbitmqctl change_cluster_node_type disc - This command will turn a RAM node into a disk node. + This command will turn a RAM node into a disc node. -- cgit v1.2.1 From 1fc6954cb5602f1cbef604e7ba4bfb410f80daba Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 23 Oct 2012 11:49:46 +0100 Subject: Typo --- src/rabbit_backing_queue.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_backing_queue.erl b/src/rabbit_backing_queue.erl index f257c977..af660c60 100644 --- a/src/rabbit_backing_queue.erl +++ b/src/rabbit_backing_queue.erl @@ -90,7 +90,7 @@ %% Called to inform the BQ about messages which have reached the %% queue, but are not going to be further passed to BQ for some -%% reason. Note that this is may be invoked for messages for which +%% reason. Note that this may be invoked for messages for which %% BQ:is_duplicate/2 has already returned {'published' | 'discarded', %% BQS}. -callback discard(rabbit_types:msg_id(), pid(), state()) -> state(). -- cgit v1.2.1 From 7a5c2bba14e02c1801ed30e16a5cce889b24d9c7 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 24 Oct 2012 00:07:12 +0100 Subject: better error messages for 'absent' queues on declaration - change amqqueue:internal_declare to return {absent, Q} instead of 'not_found' - let this ripple through amqqueue_process:declare, stopping with {absent, Q} instead - that in in turn will end up in in amqqueue:declare/5. Instead of inovking misc:not_found(QName), which was EXITing, we make {absent, Q} part of the API. - at the call site in the channel we handle the new {absent, Q} case and produce a nice error message. --- src/rabbit_amqqueue.erl | 22 ++++++++++------------ src/rabbit_amqqueue_process.erl | 10 +++++----- src/rabbit_channel.erl | 13 ++++++++++++- 3 files changed, 27 insertions(+), 18 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 6ad85b24..8ab9bc87 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -61,18 +61,19 @@ -type(ok_or_errors() :: 'ok' | {'error', [{'error' | 'exit' | 'throw', any()}]}). -type(routing_result() :: 'routed' | 'unroutable'). --type(queue_or_not_found() :: rabbit_types:amqqueue() | 'not_found'). +-type(queue_or_absent() :: rabbit_types:amqqueue() | + {'absent', rabbit_types:amqqueue()}). -spec(start/0 :: () -> [name()]). -spec(stop/0 :: () -> 'ok'). -spec(declare/5 :: (name(), boolean(), boolean(), rabbit_framing:amqp_table(), rabbit_types:maybe(pid())) - -> {'new' | 'existing', rabbit_types:amqqueue()} | + -> {'new' | 'existing' | 'absent', rabbit_types:amqqueue()} | rabbit_types:channel_exit()). -spec(internal_declare/2 :: (rabbit_types:amqqueue(), boolean()) - -> queue_or_not_found() | rabbit_misc:thunk(queue_or_not_found())). + -> queue_or_absent() | rabbit_misc:thunk(queue_or_absent())). -spec(update/2 :: (name(), fun((rabbit_types:amqqueue()) -> rabbit_types:amqqueue())) -> 'ok'). @@ -223,10 +224,7 @@ declare(QueueName, Durable, AutoDelete, Args, Owner) -> gm_pids = []}), {Node, _MNodes} = rabbit_mirror_queue_misc:suggested_queue_nodes(Q0), Q1 = start_queue_process(Node, Q0), - case gen_server2:call(Q1#amqqueue.pid, {init, false}, infinity) of - not_found -> rabbit_misc:not_found(QueueName); - Q2 -> Q2 - end. + gen_server2:call(Q1#amqqueue.pid, {init, false}, infinity). internal_declare(Q, true) -> rabbit_misc:execute_mnesia_tx_with_tail( @@ -237,12 +235,12 @@ internal_declare(Q = #amqqueue{name = QueueName}, false) -> case mnesia:wread({rabbit_queue, QueueName}) of [] -> case mnesia:read({rabbit_durable_queue, QueueName}) of - [] -> Q1 = rabbit_policy:set(Q), - ok = store_queue(Q1), - B = add_default_binding(Q1), - fun () -> B(), Q1 end; + [] -> Q1 = rabbit_policy:set(Q), + ok = store_queue(Q1), + B = add_default_binding(Q1), + fun () -> B(), Q1 end; %% Q exists on stopped node - [_] -> rabbit_misc:const(not_found) + [Q1] -> rabbit_misc:const({absent, Q1}) end; [ExistingQ = #amqqueue{pid = QPid}] -> case rabbit_misc:is_process_alive(QPid) of diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 68f95778..40240cb1 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -196,9 +196,7 @@ declare(Recover, From, State = #q{q = Q, backing_queue = BQ, backing_queue_state = undefined}) -> case rabbit_amqqueue:internal_declare(Q, Recover) of - not_found -> - {stop, normal, not_found, State}; - Q1 -> + #amqqueue{} = Q1 -> case matches(Recover, Q, Q1) of true -> gen_server2:reply(From, {new, Q}), @@ -216,8 +214,10 @@ declare(Recover, From, State = #q{q = Q, noreply(State1); false -> {stop, normal, {existing, Q1}, State} - end - end. + end; + Err -> + {stop, normal, Err, State} + end. matches(true, Q, Q) -> true; matches(true, _Q, _Q1) -> false; diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 0d13312b..276ad38f 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -423,6 +423,15 @@ precondition_failed(Format) -> precondition_failed(Format, []). precondition_failed(Format, Params) -> rabbit_misc:protocol_error(precondition_failed, Format, Params). +absent(#amqqueue{name = QueueName, pid = QPid, durable = true}) -> + %% The assertion of durability is mainly there because we mention + %% durability in the error message. That way we will hopefully + %% notice if at some future point our logic changes s.t. we get + %% here with non-durable queues. + rabbit_misc:protocol_error( + not_found, "home node '~s' of durable ~s is down or inaccessible", + [node(QPid), rabbit_misc:rs(QueueName)]). + return_queue_declare_ok(#resource{name = ActualName}, NoWait, MessageCount, ConsumerCount, State) -> return_ok(State#ch{most_recently_declared_queue = ActualName}, NoWait, @@ -960,7 +969,9 @@ handle_method(#'queue.declare'{queue = QueueNameBin, {existing, _Q} -> %% must have been created between the stat and the %% declare. Loop around again. - handle_method(Declare, none, State) + handle_method(Declare, none, State); + {absent, Q} -> + absent(Q) end end; -- cgit v1.2.1 From 0fa431b83108b046869f4c534ed42b4911e85af8 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 24 Oct 2012 09:37:04 +0100 Subject: better error messages for 'absent' queues in various commands - move channel:absent/1 to misc, since we need it in multiple modules - change amqqueue:with/3 s.t. the error fun is invoked with not_found|{'absent', Q}. - let this change ripple through to with/2, making it return either error. This in turn has just two call sites - in 'queue.declare' in the channel, which we expand to handle the 'absent' case (producing a descriptive not_found error, as per above) - in mq_misc:if_mirrored_queue, whose callers currently do not handle errors at all - see bug 25236 - let the change also ripple through to with_or_die, handling the new 'absent' case as above. This takes care of passive queue.declare. - with_or_die is called by with_exclusive_access_or_die. This takes care of basic.get, basic.consume, queue.delete and queue.purge --- src/rabbit_amqqueue.erl | 39 ++++++++++++++++++++++++++------------- src/rabbit_channel.erl | 15 ++++----------- src/rabbit_misc.erl | 12 +++++++++++- 3 files changed, 41 insertions(+), 25 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 8ab9bc87..0a544713 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -81,7 +81,9 @@ (name()) -> rabbit_types:ok(rabbit_types:amqqueue()) | rabbit_types:error('not_found'); ([name()]) -> [rabbit_types:amqqueue()]). --spec(with/2 :: (name(), qfun(A)) -> A | rabbit_types:error('not_found')). +-spec(with/2 :: (name(), qfun(A)) -> + A | rabbit_types:error( + 'not_found' | {'absent', rabbit_types:amqqueue()})). -spec(with_or_die/2 :: (name(), qfun(A)) -> A | rabbit_types:channel_exit()). -spec(assert_equivalence/5 :: @@ -300,22 +302,33 @@ with(Name, F, E) -> %% We check is_process_alive(QPid) in case we receive a %% nodedown (for example) in F() that has nothing to do %% with the QPid. - E1 = fun () -> - case rabbit_misc:is_process_alive(QPid) of - true -> E(); - false -> timer:sleep(25), - with(Name, F, E) - end - end, - rabbit_misc:with_exit_handler(E1, fun () -> F(Q) end); + rabbit_misc:with_exit_handler( + fun () -> + case rabbit_misc:is_process_alive(QPid) of + true -> E(not_found_or_absent(Name)); + false -> timer:sleep(25), + with(Name, F, E) + end + end, fun () -> F(Q) end); {error, not_found} -> - E() + E(not_found_or_absent(Name)) end. -with(Name, F) -> - with(Name, F, fun () -> {error, not_found} end). +not_found_or_absent(Name) -> + %% We should read from both tables inside a tx, to get a + %% consistent view. But the chances of an inconsistency are small, + %% and only affect the error kind. + case rabbit_misc:dirty_read({rabbit_durable_queue, Name}) of + {error, not_found} -> not_found; + {ok, Q} -> {absent, Q} + end. + +with(Name, F) -> with(Name, F, fun (E) -> {error, E} end). + with_or_die(Name, F) -> - with(Name, F, fun () -> rabbit_misc:not_found(Name) end). + with(Name, F, fun (not_found) -> rabbit_misc:not_found(Name); + ({absent, Q}) -> rabbit_misc:absent(Q) + end). assert_equivalence(#amqqueue{durable = Durable, auto_delete = AutoDelete} = Q, diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 276ad38f..91abcf1e 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -423,15 +423,6 @@ precondition_failed(Format) -> precondition_failed(Format, []). precondition_failed(Format, Params) -> rabbit_misc:protocol_error(precondition_failed, Format, Params). -absent(#amqqueue{name = QueueName, pid = QPid, durable = true}) -> - %% The assertion of durability is mainly there because we mention - %% durability in the error message. That way we will hopefully - %% notice if at some future point our logic changes s.t. we get - %% here with non-durable queues. - rabbit_misc:protocol_error( - not_found, "home node '~s' of durable ~s is down or inaccessible", - [node(QPid), rabbit_misc:rs(QueueName)]). - return_queue_declare_ok(#resource{name = ActualName}, NoWait, MessageCount, ConsumerCount, State) -> return_ok(State#ch{most_recently_declared_queue = ActualName}, NoWait, @@ -971,8 +962,10 @@ handle_method(#'queue.declare'{queue = QueueNameBin, %% declare. Loop around again. handle_method(Declare, none, State); {absent, Q} -> - absent(Q) - end + rabbit_misc:absent(Q) + end; + {error, {absent, Q}} -> + rabbit_misc:absent(Q) end; handle_method(#'queue.declare'{queue = QueueNameBin, diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index ab9a9ceb..78f25175 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -21,7 +21,7 @@ -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, assert_args_equivalence/4]). +-export([not_found/1, absent/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]). @@ -111,6 +111,7 @@ -spec(protocol_error/1 :: (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(assert_args_equivalence/4 :: (rabbit_framing:amqp_table(), rabbit_framing:amqp_table(), rabbit_types:r(any()), [binary()]) -> @@ -266,6 +267,15 @@ protocol_error(#amqp_error{} = Error) -> not_found(R) -> protocol_error(not_found, "no ~s", [rs(R)]). +absent(#amqqueue{name = QueueName, pid = QPid, durable = true}) -> + %% The assertion of durability is mainly there because we mention + %% durability in the error message. That way we will hopefully + %% notice if at some future point our logic changes s.t. we get + %% here with non-durable queues. + protocol_error(not_found, + "home node '~s' of durable ~s is down or inaccessible", + [node(QPid), rs(QueueName)]). + type_class(byte) -> int; type_class(short) -> int; type_class(signedint) -> int; -- cgit v1.2.1 From 521b6e453497de492257bb73c22bc80b733a21b0 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 24 Oct 2012 10:25:22 +0100 Subject: Slightly surprised that compiled on R15B02 - but it doesn't on R12B-5. Fix CI + nightlies. --- src/rabbit_policy_validator.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_policy_validator.erl b/src/rabbit_policy_validator.erl index 624c3d54..b59dec2b 100644 --- a/src/rabbit_policy_validator.erl +++ b/src/rabbit_policy_validator.erl @@ -29,7 +29,7 @@ behaviour_info(callbacks) -> [ - {validate_policy, 1}, + {validate_policy, 1} ]; behaviour_info(_Other) -> undefined. -- cgit v1.2.1 From 46b2632199b75cf16a17fd47fd530f25352ac3e3 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 24 Oct 2012 11:16:28 +0100 Subject: Fix policy info items --- src/rabbit_policy.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_policy.erl b/src/rabbit_policy.erl index 9af8fa18..2717cc92 100644 --- a/src/rabbit_policy.erl +++ b/src/rabbit_policy.erl @@ -43,7 +43,7 @@ name(#amqqueue{policy = Policy}) -> name0(Policy); name(#exchange{policy = Policy}) -> name0(Policy). name0(undefined) -> none; -name0(Policy) -> pget(<<"name">>, Policy). +name0(Policy) -> pget(name, Policy). set(Q = #amqqueue{name = Name}) -> Q#amqqueue{policy = set0(Name)}; set(X = #exchange{name = Name}) -> X#exchange{policy = set0(Name)}. -- cgit v1.2.1 From 68c86a6ba4888d5a79b779dc8ed273dda229b46a Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 24 Oct 2012 11:24:43 +0100 Subject: More accurate specs. --- src/rabbit_runtime_parameters.erl | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/rabbit_runtime_parameters.erl b/src/rabbit_runtime_parameters.erl index 4a83e61f..49060409 100644 --- a/src/rabbit_runtime_parameters.erl +++ b/src/rabbit_runtime_parameters.erl @@ -39,14 +39,16 @@ -spec(clear_any/3 :: (rabbit_types:vhost(), binary(), binary()) -> ok_or_error_string()). -spec(list/0 :: () -> [rabbit_types:infos()]). --spec(list/1 :: (rabbit_types:vhost()) -> [rabbit_types:infos()]). --spec(list_strict/1 :: (binary()) -> [rabbit_types:infos()] | 'not_found'). --spec(list/2 :: (rabbit_types:vhost(), binary()) -> [rabbit_types:infos()]). --spec(list_strict/2 :: (rabbit_types:vhost(), binary()) +-spec(list/1 :: (rabbit_types:vhost() | '_') -> [rabbit_types:infos()]). +-spec(list_strict/1 :: (binary() | '_') + -> [rabbit_types:infos()] | 'not_found'). +-spec(list/2 :: (rabbit_types:vhost() | '_', binary() | '_') + -> [rabbit_types:infos()]). +-spec(list_strict/2 :: (rabbit_types:vhost() | '_', binary() | '_') -> [rabbit_types:infos()] | 'not_found'). -spec(list_formatted/1 :: (rabbit_types:vhost()) -> [rabbit_types:infos()]). -spec(lookup/3 :: (rabbit_types:vhost(), binary(), binary()) - -> rabbit_types:infos()). + -> rabbit_types:infos() | 'not_found'). -spec(value/3 :: (rabbit_types:vhost(), binary(), binary()) -> term()). -spec(value/4 :: (rabbit_types:vhost(), binary(), binary(), term()) -> term()). -spec(info_keys/0 :: () -> rabbit_types:info_keys()). -- cgit v1.2.1 From b137801cbf7fbd4d31814f115dd11d1995e13fa0 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Wed, 24 Oct 2012 13:09:18 +0100 Subject: comment --- src/rabbit_mnesia.erl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 131f74ac..a82bd282 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -381,6 +381,8 @@ cluster_status_from_mnesia() -> disc -> nodes_incl_me(DiscCopies); ram -> DiscCopies end, + %% `mnesia:system_info(running_db_nodes)' is safe since + %% we know that mnesia is running RunningNodes = mnesia:system_info(running_db_nodes), {ok, {AllNodes, DiscNodes, RunningNodes}}; false -> {error, tables_not_present} -- cgit v1.2.1 From 839c886755d361caf8b909f958eedd74a42083ee Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 24 Oct 2012 13:25:03 +0100 Subject: If the current master is currently not in the nodes specified, act like it is for the purposes of suggested_queue_nodes - otherwise we will not return it in the results. --- src/rabbit_mirror_queue_misc.erl | 6 +++++- src/rabbit_tests.erl | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 4a00846e..8a363f76 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -268,7 +268,11 @@ policy(Policy, Q) -> suggested_queue_nodes(<<"all">>, _Params, {MNode, _SNodes}, Possible) -> {MNode, Possible -- [MNode]}; suggested_queue_nodes(<<"nodes">>, Nodes0, {MNode, _SNodes}, Possible) -> - Nodes = [list_to_atom(binary_to_list(Node)) || Node <- Nodes0], + Nodes1 = [list_to_atom(binary_to_list(Node)) || Node <- Nodes0], + %% If the current master is currently not in the nodes specified, + %% act like it is for the purposes below - otherwise we will not + %% return it in the results... + Nodes = lists:usort([MNode | Nodes1]), Unavailable = Nodes -- Possible, Available = Nodes -- Unavailable, case Available of diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 962bb648..afc10b9f 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -914,12 +914,12 @@ test_dynamic_mirroring() -> Test({b,[a,c],0},<<"nodes">>,[<<"a">>,<<"b">>,<<"c">>],{b,[a]},[a,b,c,d]), %% Add two nodes and drop one Test({a,[b,c],0},<<"nodes">>,[<<"a">>,<<"b">>,<<"c">>],{a,[d]},[a,b,c,d]), - %% Promote slave to master by policy - Test({a,[b,c],0},<<"nodes">>,[<<"a">>,<<"b">>,<<"c">>],{d,[a]},[a,b,c,d]), %% Don't try to include nodes that are not running Test({a,[b], 0},<<"nodes">>,[<<"a">>,<<"b">>,<<"f">>],{a,[b]},[a,b,c,d]), %% If we can't find any of the nodes listed then just keep the master Test({a,[], 0},<<"nodes">>,[<<"f">>,<<"g">>,<<"h">>],{a,[b]},[a,b,c,d]), + %% And once that's happened, still keep the master even when not listed + Test({a,[b,c],0},<<"nodes">>,[<<"b">>,<<"c">>], {a,[]}, [a,b,c,d]), Test({a,[], 1},<<"exactly">>,2,{a,[]}, [a,b,c,d]), Test({a,[], 2},<<"exactly">>,3,{a,[]}, [a,b,c,d]), -- cgit v1.2.1 From 6238c6e5d046f27898b4a1a16ea8ad54bec20e29 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 24 Oct 2012 13:29:15 +0100 Subject: better error messages for 'absent' queues in binding commands - change binding:call_with_source_and_destination/3 to replace {error, source_not_found | destination_not_found | source_and_destination_not_found} with {error, {resources_missing, [{not_found, #resource{}} | {absent, Q}]}}. - the change ripples through rabbit_channel:binding_action, which can now produce nice not_founds. - We now only report one of the errors when there is something wrong with both source and destination. I reckon this is acceptable; we could do better but it would involve a fair bit of extra code. --- src/rabbit_amqqueue.erl | 28 ++++++++++++++++++++-------- src/rabbit_binding.erl | 29 +++++++++++++++++++++-------- src/rabbit_channel.erl | 12 ++++-------- 3 files changed, 45 insertions(+), 24 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 0a544713..db1f5654 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -18,7 +18,7 @@ -export([start/0, stop/0, declare/5, delete_immediately/1, delete/3, purge/1]). -export([pseudo_queue/2]). --export([lookup/1, with/2, with_or_die/2, assert_equivalence/5, +-export([lookup/1, lookup_absent/1, with/2, with_or_die/2, assert_equivalence/5, 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]). @@ -81,6 +81,9 @@ (name()) -> rabbit_types:ok(rabbit_types:amqqueue()) | rabbit_types:error('not_found'); ([name()]) -> [rabbit_types:amqqueue()]). +-spec(lookup_absent/1 :: + (name()) -> rabbit_types:ok(rabbit_types:amqqueue()) | + rabbit_types:error('not_found')). -spec(with/2 :: (name(), qfun(A)) -> A | rabbit_types:error( 'not_found' | {'absent', rabbit_types:amqqueue()})). @@ -236,13 +239,14 @@ internal_declare(Q = #amqqueue{name = QueueName}, false) -> fun () -> case mnesia:wread({rabbit_queue, QueueName}) of [] -> - case mnesia:read({rabbit_durable_queue, QueueName}) of - [] -> Q1 = rabbit_policy:set(Q), - ok = store_queue(Q1), - B = add_default_binding(Q1), - fun () -> B(), Q1 end; - %% Q exists on stopped node - [Q1] -> rabbit_misc:const({absent, Q1}) + case lookup_absent(QueueName) of + {error, not_found} -> + Q1 = rabbit_policy:set(Q), + ok = store_queue(Q1), + B = add_default_binding(Q1), + fun () -> B(), Q1 end; + {ok, Q1} -> + rabbit_misc:const({absent, Q1}) end; [ExistingQ = #amqqueue{pid = QPid}] -> case rabbit_misc:is_process_alive(QPid) of @@ -296,6 +300,14 @@ lookup(Names) when is_list(Names) -> lookup(Name) -> rabbit_misc:dirty_read({rabbit_queue, Name}). +lookup_absent(Name) -> + %% NB: we assume that the caller has already performed a lookup on + %% rabbit_queue and not found anything + case mnesia:read({rabbit_durable_queue, Name}) of + [] -> {error, not_found}; + [Q] -> {ok, Q} %% Q exists on stopped node + end. + with(Name, F, E) -> case lookup(Name) of {ok, Q = #amqqueue{pid = QPid}} -> diff --git a/src/rabbit_binding.erl b/src/rabbit_binding.erl index 0d23f716..1375facb 100644 --- a/src/rabbit_binding.erl +++ b/src/rabbit_binding.erl @@ -35,9 +35,11 @@ -type(key() :: binary()). --type(bind_errors() :: rabbit_types:error('source_not_found' | - 'destination_not_found' | - 'source_and_destination_not_found')). +-type(bind_errors() :: rabbit_types:error( + {'resources_missing', + [{'not_found', (rabbit_types:binding_source() | + rabbit_types:binding_destination())} | + {'absent', rabbit_types:amqqueue()}]})). -type(bind_ok_or_error() :: 'ok' | bind_errors() | rabbit_types:error('binding_not_found')). -type(bind_res() :: bind_ok_or_error() | rabbit_misc:thunk(bind_ok_or_error())). @@ -330,21 +332,32 @@ sync_transient_route(Route, Fun) -> call_with_source_and_destination(SrcName, DstName, Fun) -> SrcTable = table_for_resource(SrcName), DstTable = table_for_resource(DstName), - ErrFun = fun (Err) -> rabbit_misc:const({error, Err}) end, + 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}), mnesia:read({DstTable, DstName})} of {[Src], [Dst]} -> Fun(Src, Dst); - {[], [_] } -> ErrFun(source_not_found); - {[_], [] } -> ErrFun(destination_not_found); - {[], [] } -> ErrFun(source_and_destination_not_found) - end + {[], [_] } -> ErrFun([SrcName]); + {[_], [] } -> ErrFun([DstName]); + {[], [] } -> ErrFun([SrcName, DstName]) + end end). table_for_resource(#resource{kind = exchange}) -> rabbit_exchange; table_for_resource(#resource{kind = queue}) -> rabbit_queue. +not_found_or_absent(#resource{kind = exchange} = Name) -> + {not_found, Name}; +not_found_or_absent(#resource{kind = queue} = Name) -> + case rabbit_amqqueue:lookup_absent(Name) of + {error, not_found} -> {not_found, Name}; + {ok, Q} -> {absent, Q} + end. + contains(Table, MatchHead) -> continue(mnesia:select(Table, [{MatchHead, [], ['$_']}], 1, read)). diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 91abcf1e..153193bf 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -1178,14 +1178,10 @@ binding_action(Fun, ExchangeNameBin, DestinationType, DestinationNameBin, (_X, #exchange{}) -> ok end) of - {error, source_not_found} -> - rabbit_misc:not_found(ExchangeName); - {error, destination_not_found} -> - rabbit_misc:not_found(DestinationName); - {error, source_and_destination_not_found} -> - rabbit_misc:protocol_error( - not_found, "no ~s and no ~s", [rabbit_misc:rs(ExchangeName), - rabbit_misc:rs(DestinationName)]); + {error, {resources_missing, [{not_found, Name} | _]}} -> + rabbit_misc:not_found(Name); + {error, {resources_missing, [{absent, Q} | _]}} -> + rabbit_misc:absent(Q); {error, binding_not_found} -> rabbit_misc:protocol_error( not_found, "no binding ~s between ~s and ~s", -- cgit v1.2.1 From 55e9e89ad9c16e50d682fe13b7d7559232cad965 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Wed, 24 Oct 2012 14:35:26 +0100 Subject: oops --- src/rabbit_mnesia.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index a82bd282..6df0367f 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -355,6 +355,7 @@ cluster_status(WhichNodes) -> status -> Nodes; all -> AllNodes; disc -> DiscNodes; + ram -> AllNodes -- DiscNodes; running -> RunningNodes end. -- cgit v1.2.1 From 4e1b30e6080810bd375b30a1d55f6647bb446166 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Wed, 24 Oct 2012 14:44:18 +0100 Subject: don't crash when appending table headers if the previous header wasn't an array --- src/rabbit_basic.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_basic.erl b/src/rabbit_basic.erl index db2b7e95..0e63ce12 100644 --- a/src/rabbit_basic.erl +++ b/src/rabbit_basic.erl @@ -181,8 +181,8 @@ append_table_header(Name, Info, undefined) -> append_table_header(Name, Info, []); append_table_header(Name, Info, Headers) -> Prior = case rabbit_misc:table_lookup(Headers, Name) of - undefined -> []; - {array, Existing} -> Existing + {array, Existing} -> Existing; + _ -> [] end, rabbit_misc:set_table_value(Headers, Name, array, [{table, Info} | Prior]). -- cgit v1.2.1 From deb3b4606d4d8e163b5a48ccf74cf399b5d70ffb Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Wed, 24 Oct 2012 15:18:48 +0100 Subject: post merge cleanup --- src/rabbit_mnesia.erl | 43 +++++++++++++++++++++---------------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 6df0367f..db4c58ca 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -338,27 +338,6 @@ is_clustered() -> AllNodes = cluster_nodes(all), cluster_nodes(WhichNodes) -> cluster_status(WhichNodes). -cluster_status(WhichNodes) -> - {AllNodes, DiscNodes, RunningNodes} = Nodes = - case cluster_status_from_mnesia() of - {ok, Nodes0} -> - Nodes0; - {error, _Reason} -> - {AllNodes0, DiscNodes0, RunningNodes0} = - rabbit_node_monitor:read_cluster_status(), - %% The cluster status file records the status when the node is - %% online, but we know for sure that the node is offline now, so - %% we can remove it from the list of running nodes. - {AllNodes0, DiscNodes0, nodes_excl_me(RunningNodes0)} - end, - case WhichNodes of - status -> Nodes; - all -> AllNodes; - disc -> DiscNodes; - ram -> AllNodes -- DiscNodes; - running -> RunningNodes - end. - %% This function is the actual source of information, since it gets %% the data from mnesia. Obviously it'll work only when mnesia is %% running. @@ -390,6 +369,27 @@ cluster_status_from_mnesia() -> end end. +cluster_status(WhichNodes) -> + {AllNodes, DiscNodes, RunningNodes} = Nodes = + case cluster_status_from_mnesia() of + {ok, Nodes0} -> + Nodes0; + {error, _Reason} -> + {AllNodes0, DiscNodes0, RunningNodes0} = + rabbit_node_monitor:read_cluster_status(), + %% The cluster status file records the status when the node is + %% online, but we know for sure that the node is offline now, so + %% we can remove it from the list of running nodes. + {AllNodes0, DiscNodes0, nodes_excl_me(RunningNodes0)} + end, + case WhichNodes of + status -> Nodes; + all -> AllNodes; + disc -> DiscNodes; + ram -> AllNodes -- DiscNodes; + running -> RunningNodes + end. + node_info() -> {erlang:system_info(otp_release), rabbit_misc:version(), cluster_status_from_mnesia()}. @@ -720,7 +720,6 @@ change_extra_db_nodes(ClusterNodes0, CheckOtherNodes) -> Nodes end. -%% when mnesia is stopped is_running_remote() -> {mnesia:system_info(is_running) =:= yes, node()}. check_consistency(OTP, Rabbit) -> -- cgit v1.2.1 From 89db71043a29dcdb8ef310403a14d8f4b493b407 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Wed, 24 Oct 2012 16:44:15 +0100 Subject: remove the `leave_cluster' refactor --- src/rabbit_mnesia.erl | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index db4c58ca..d6c6f360 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -676,10 +676,12 @@ remove_node_if_mnesia_running(Node) -> end. leave_cluster() -> - AllNodes = cluster_nodes(all) -- [node()], - case not is_clustered() orelse lists:any(fun leave_cluster/1, AllNodes) of - true -> ok; - false -> e(no_running_cluster_nodes) + case nodes_excl_me(cluster_nodes(all)) of + [] -> ok; + AllNodes -> case lists:any(fun leave_cluster/1, AllNodes) of + true -> ok; + false -> e(no_running_cluster_nodes) + end end. leave_cluster(Node) -> -- cgit v1.2.1 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(-) 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 0ea5176dbbe182cac2c1b6fd6fe751a79c72af87 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Thu, 25 Oct 2012 12:59:11 +0100 Subject: move invalid headers into a special 'invalid headers table' header --- include/rabbit.hrl | 1 + src/rabbit_basic.erl | 23 +++++++++++++++++++---- src/rabbit_basic_tests.erl | 43 +++++++++++++++++++++++++++++++++++++++++++ src/rabbit_tests.erl | 1 + 4 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 src/rabbit_basic_tests.erl diff --git a/include/rabbit.hrl b/include/rabbit.hrl index 3db2b68a..dc99bf78 100644 --- a/include/rabbit.hrl +++ b/include/rabbit.hrl @@ -101,5 +101,6 @@ -define(DESIRED_HIBERNATE, 10000). -define(CREDIT_DISC_BOUND, {2000, 500}). +-define(INVALID_HEADERS_KEY, <<"x-invalid-headers">>). -define(ROUTING_HEADERS, [<<"CC">>, <<"BCC">>]). -define(DELETED_HEADER, <<"BCC">>). diff --git a/src/rabbit_basic.erl b/src/rabbit_basic.erl index 0e63ce12..000a7ad8 100644 --- a/src/rabbit_basic.erl +++ b/src/rabbit_basic.erl @@ -180,12 +180,27 @@ properties(P) when is_list(P) -> append_table_header(Name, Info, undefined) -> append_table_header(Name, Info, []); append_table_header(Name, Info, Headers) -> - Prior = case rabbit_misc:table_lookup(Headers, Name) of - {array, Existing} -> Existing; - _ -> [] - end, + case rabbit_misc:table_lookup(Headers, Name) of + {array, Existing} -> + prepend_table(Headers, Name, Info, Existing); + undefined -> + prepend_table(Headers, Name, Info, []); + Other -> + Headers2 = prepend_table(Headers, Name, Info, []), + set_invalid_header(Name, Other, Headers2) + end. + +prepend_table(Headers, Name, Info, Prior) -> rabbit_misc:set_table_value(Headers, Name, array, [{table, Info} | Prior]). +set_invalid_header(Name, {_, _}=Value, Headers) when is_list(Headers) -> + case rabbit_misc:table_lookup(Headers, ?INVALID_HEADERS_KEY) of + undefined -> + Invalid = [{Name, array, [Value]}], + rabbit_misc:set_table_value(Headers, ?INVALID_HEADERS_KEY, + table, Invalid) + end. + extract_headers(Content) -> #content{properties = #'P_basic'{headers = Headers}} = rabbit_binary_parser:ensure_content_decoded(Content), diff --git a/src/rabbit_basic_tests.erl b/src/rabbit_basic_tests.erl new file mode 100644 index 00000000..147e081e --- /dev/null +++ b/src/rabbit_basic_tests.erl @@ -0,0 +1,43 @@ +%% 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 VMware, Inc. +%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% +-module(rabbit_basic_tests). + +-include("rabbit.hrl"). +-include_lib("eunit/include/eunit.hrl"). + +-compile(export_all). + +-define(XDEATH_TABLE, + [{<<"reason">>, longstr, <<"blah">>}, + {<<"queue">>, longstr, <<"foo.bar.baz">>}, + {<<"exchange">>, longstr, <<"my-exchange">>}, + {<<"routing-keys">>, array, []}]). + +write_table_with_invalid_existing_type_test() -> + assertInvalid(<<"x-death">>, {longstr, <<"this should be a table!!!">>}, + ?XDEATH_TABLE). + +assertInvalid(HeaderKey, {TBin, VBin}=InvalidEntry, HeaderTable) -> + Headers = rabbit_basic:append_table_header(HeaderKey, HeaderTable, + [{HeaderKey, TBin, VBin}]), + InvalidHeaders = rabbit_misc:table_lookup(Headers, ?INVALID_HEADERS_KEY), + ?assertMatch({table, _}, InvalidHeaders), + {_, Invalid} = InvalidHeaders, + InvalidArrayForKey = rabbit_misc:table_lookup(Invalid, HeaderKey), + ?assertMatch({array, _}, InvalidArrayForKey), + {_, Array} = InvalidArrayForKey, + ?assertMatch([InvalidEntry], Array). + diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index aa48f228..6fd2dd87 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -34,6 +34,7 @@ all_tests() -> ok = setup_cluster(), ok = supervisor2_tests:test_all(), + ok = rabbit_basic_tests:test(), passed = gm_tests:all_tests(), passed = mirrored_supervisor_tests:all_tests(), application:set_env(rabbit, file_handles_high_watermark, 10, infinity), -- cgit v1.2.1 From 80783857100b8353a42a38a5504da112475c9b8c Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Thu, 25 Oct 2012 13:21:54 +0100 Subject: test for correct prepending of table headers --- src/rabbit_basic_tests.erl | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/src/rabbit_basic_tests.erl b/src/rabbit_basic_tests.erl index 147e081e..2beb4c18 100644 --- a/src/rabbit_basic_tests.erl +++ b/src/rabbit_basic_tests.erl @@ -26,11 +26,30 @@ {<<"exchange">>, longstr, <<"my-exchange">>}, {<<"routing-keys">>, array, []}]). -write_table_with_invalid_existing_type_test() -> - assertInvalid(<<"x-death">>, {longstr, <<"this should be a table!!!">>}, - ?XDEATH_TABLE). +-define(ROUTE_TABLE, [{<<"redelivered">>, bool, <<"true">>}]). -assertInvalid(HeaderKey, {TBin, VBin}=InvalidEntry, HeaderTable) -> +write_table_with_invalid_existing_type_test_() -> + [{"existing entries with invalid types are moved to a table " + "stored as <<\"x-invalid-headers header\">>", + assert_invalid(<<"x-death">>, + {longstr, <<"this should be a table!!!">>}, + ?XDEATH_TABLE)}, + {"if invalid existing headers are moved, newly added " + "ones are still stored correctly", + begin + BadHeaders = [{<<"x-received-from">>, + longstr, <<"this should be a table!!!">>}], + Headers = rabbit_basic:append_table_header( + <<"x-received-from">>, ?ROUTE_TABLE, BadHeaders), + ?_assertEqual({array, [{table, ?ROUTE_TABLE}]}, + rabbit_misc:table_lookup(Headers, <<"x-received-from">>)) + end} + ]. + +assert_invalid(HeaderKey, Entry, Table) -> + fun() -> check_invalid(HeaderKey, Entry, Table) end. + +check_invalid(HeaderKey, {TBin, VBin}=InvalidEntry, HeaderTable) -> Headers = rabbit_basic:append_table_header(HeaderKey, HeaderTable, [{HeaderKey, TBin, VBin}]), InvalidHeaders = rabbit_misc:table_lookup(Headers, ?INVALID_HEADERS_KEY), @@ -39,5 +58,6 @@ assertInvalid(HeaderKey, {TBin, VBin}=InvalidEntry, HeaderTable) -> InvalidArrayForKey = rabbit_misc:table_lookup(Invalid, HeaderKey), ?assertMatch({array, _}, InvalidArrayForKey), {_, Array} = InvalidArrayForKey, - ?assertMatch([InvalidEntry], Array). + ?assertMatch([InvalidEntry], Array), + Headers. -- cgit v1.2.1 From 3666b54bdf1fb3a27d0bb36b7da3c40b850b944a Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Thu, 25 Oct 2012 13:24:37 +0100 Subject: we do not 'append' to the list --- src/rabbit_amqqueue_process.erl | 4 ++-- src/rabbit_basic.erl | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 7ce69582..21109499 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -847,8 +847,8 @@ make_dead_letter_msg(Reason, {<<"time">>, timestamp, TimeSec}, {<<"exchange">>, longstr, Exchange#resource.name}, {<<"routing-keys">>, array, RKs1}], - HeadersFun1(rabbit_basic:append_table_header(<<"x-death">>, - Info, Headers)) + HeadersFun1(rabbit_basic:prepend_table_header(<<"x-death">>, + Info, Headers)) end, Content1 = rabbit_basic:map_headers(HeadersFun2, Content), Msg#basic_message{exchange_name = DLX, id = rabbit_guid:gen(), diff --git a/src/rabbit_basic.erl b/src/rabbit_basic.erl index 000a7ad8..95aaf1cd 100644 --- a/src/rabbit_basic.erl +++ b/src/rabbit_basic.erl @@ -19,7 +19,7 @@ -include("rabbit_framing.hrl"). -export([publish/4, publish/5, publish/1, - message/3, message/4, properties/1, append_table_header/3, + message/3, message/4, properties/1, prepend_table_header/3, extract_headers/1, map_headers/2, delivery/3, header_routes/1]). -export([build_content/2, from_content/1]). @@ -177,9 +177,9 @@ properties(P) when is_list(P) -> end end, #'P_basic'{}, P). -append_table_header(Name, Info, undefined) -> - append_table_header(Name, Info, []); -append_table_header(Name, Info, Headers) -> +prepend_table_header(Name, Info, undefined) -> + prepend_table_header(Name, Info, []); +prepend_table_header(Name, Info, Headers) -> case rabbit_misc:table_lookup(Headers, Name) of {array, Existing} -> prepend_table(Headers, Name, Info, Existing); -- cgit v1.2.1 From 198acfe2c297187bf87b34e3a334dc2a07c63791 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Thu, 25 Oct 2012 13:37:10 +0100 Subject: oops --- src/rabbit_mnesia.erl | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 2444c0ae..7e61d214 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -108,8 +108,6 @@ init() -> ok. init_from_config() -> - {ok, {TryNodes, NodeType}} = - application:get_env(rabbit, cluster_nodes), {TryNodes, NodeType} = case application:get_env(rabbit, cluster_nodes) of {ok, {TryNodes, disc} = C} when is_list(TryNodes) -> -- cgit v1.2.1 From 7bf76115d28519ba4b42e23ceec5bf48367452f8 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Thu, 25 Oct 2012 13:48:25 +0100 Subject: handle legacy `cluster_nodes' --- src/rabbit_mnesia.erl | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 7e61d214..5658c9fe 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -110,10 +110,22 @@ init() -> init_from_config() -> {TryNodes, NodeType} = case application:get_env(rabbit, cluster_nodes) of - {ok, {TryNodes, disc} = C} when is_list(TryNodes) -> + {ok, {Nodes, disc} = C} when is_list(Nodes) -> C; - {ok, {TryNodes, ram } = C} when is_list(TryNodes) -> + {ok, {Nodes, ram } = C} when is_list(Nodes) -> C; + {ok, Nodes} when is_list(Nodes) -> + rabbit_log:info("blahblah ~p~n", [Nodes]), + %% Legacy config + rabbit_log:warning( + "Legacy 'cluster_nodes' configuration, use " + "{Nodes, NodeType}, where Nodes contains the nodes that the " + "node will try to cluster with, and NodeType is either " + "'disc' or 'ram'."), + {Nodes -- [node()], case lists:member(node(), Nodes) of + true -> disc; + false -> ram + end}; _ -> e(invalid_cluster_config) end, -- cgit v1.2.1 From 593f22dbe770d8da45d04c563bb8a2fcdebb324a Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Thu, 25 Oct 2012 13:49:40 +0100 Subject: `cluster_nodes(all)' instead of `nodes()' in `force_reset()' --- src/rabbit_mnesia.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 5658c9fe..a9eb9bec 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -202,7 +202,7 @@ reset(Force) -> ensure_mnesia_not_running(), Nodes = case Force of true -> - nodes(); + cluster_nodes(All); false -> AllNodes = cluster_nodes(all), %% Reconnecting so that we will get an up to date -- cgit v1.2.1 From 822c46a9b8803fdf4e62389a862883eda928b274 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Thu, 25 Oct 2012 14:04:56 +0100 Subject: oops pt 2 --- src/rabbit_mnesia.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index a9eb9bec..53c0da4a 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -202,7 +202,7 @@ reset(Force) -> ensure_mnesia_not_running(), Nodes = case Force of true -> - cluster_nodes(All); + cluster_nodes(all); false -> AllNodes = cluster_nodes(all), %% Reconnecting so that we will get an up to date -- cgit v1.2.1 From 0198214df35d5a358b2c9ab5561e92083821d86e Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Thu, 25 Oct 2012 14:20:11 +0100 Subject: better error message for `join_cluster' When `force_reset'ing a node, if we want to re-cluster it with the old cluster `update_cluster_nodes' can be used, but we can't simply use `join_cluster' again, because the rest of the cluster still thinks the `force_reset' node is in the cluster. --- src/rabbit_mnesia.erl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 53c0da4a..a47bdd4c 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -829,7 +829,9 @@ error_description(resetting_only_disc_node) -> "You cannot reset a node when it is the only disc node in a cluster. " "Please convert another node of the cluster to a disc node first."; error_description(already_clustered) -> - "You are already clustered with the nodes you have selected."; + "You are already clustered with the nodes you have selected. If the " + "node you're trying to cluster with is not present in the current node " + "status, use 'upgrade_cluster_nodes'."; error_description(not_clustered) -> "Non-clustered nodes can only be disc nodes."; error_description(cannot_connect_to_cluster) -> -- cgit v1.2.1 From a52b72fade58f00f622b69121776d55a9a899d32 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Thu, 25 Oct 2012 14:44:35 +0100 Subject: typo --- src/rabbit_mnesia.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index a47bdd4c..6fed391a 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -831,7 +831,7 @@ error_description(resetting_only_disc_node) -> error_description(already_clustered) -> "You are already clustered with the nodes you have selected. If the " "node you're trying to cluster with is not present in the current node " - "status, use 'upgrade_cluster_nodes'."; + "status, use 'update_cluster_nodes'."; error_description(not_clustered) -> "Non-clustered nodes can only be disc nodes."; error_description(cannot_connect_to_cluster) -> -- cgit v1.2.1 From 5a52b3ac5accfe0e4b78556ae2e2b827da8cc0a8 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Thu, 25 Oct 2012 15:19:05 +0100 Subject: accumulate invalid headers carefully, considering even misconfigured x-invalid-headers --- src/rabbit_basic.erl | 22 +++++++++++++++- src/rabbit_basic_tests.erl | 63 ++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 76 insertions(+), 9 deletions(-) diff --git a/src/rabbit_basic.erl b/src/rabbit_basic.erl index 95aaf1cd..c3250fb1 100644 --- a/src/rabbit_basic.erl +++ b/src/rabbit_basic.erl @@ -198,7 +198,27 @@ set_invalid_header(Name, {_, _}=Value, Headers) when is_list(Headers) -> undefined -> Invalid = [{Name, array, [Value]}], rabbit_misc:set_table_value(Headers, ?INVALID_HEADERS_KEY, - table, Invalid) + table, Invalid); + {table, InvalidEntries} -> + case rabbit_misc:table_lookup(InvalidEntries, Name) of + undefined -> + rabbit_misc:set_table_value( + Headers, ?INVALID_HEADERS_KEY, table, + rabbit_misc:set_table_value(InvalidEntries, + Name, array, [Value])); + {array, Prior} -> + rabbit_misc:set_table_value( + Headers, ?INVALID_HEADERS_KEY, table, + rabbit_misc:set_table_value(InvalidEntries, + Name, array, [Value | Prior])) + end; + Other -> + %% somehow the x-invalid-headers header is corrupt + set_invalid_header( + Name, Value, + rabbit_misc:set_table_value( + Headers, ?INVALID_HEADERS_KEY, + table, [{?INVALID_HEADERS_KEY, array, [Other]}])) end. extract_headers(Content) -> diff --git a/src/rabbit_basic_tests.erl b/src/rabbit_basic_tests.erl index 2beb4c18..19c901c2 100644 --- a/src/rabbit_basic_tests.erl +++ b/src/rabbit_basic_tests.erl @@ -31,26 +31,74 @@ write_table_with_invalid_existing_type_test_() -> [{"existing entries with invalid types are moved to a table " "stored as <<\"x-invalid-headers header\">>", - assert_invalid(<<"x-death">>, - {longstr, <<"this should be a table!!!">>}, - ?XDEATH_TABLE)}, + fun() -> + check_invalid(<<"x-death">>, + {longstr, <<"this should be a table!!!">>}, + ?XDEATH_TABLE) + end}, {"if invalid existing headers are moved, newly added " "ones are still stored correctly", begin BadHeaders = [{<<"x-received-from">>, longstr, <<"this should be a table!!!">>}], - Headers = rabbit_basic:append_table_header( + Headers = rabbit_basic:prepend_table_header( <<"x-received-from">>, ?ROUTE_TABLE, BadHeaders), ?_assertEqual({array, [{table, ?ROUTE_TABLE}]}, rabbit_misc:table_lookup(Headers, <<"x-received-from">>)) - end} - ]. + end}, + {"disparate invalid header entries are accumulated separately", + begin + BadHeaders = [{<<"x-received-from">>, + longstr, <<"this should be a table!!!">>}], + Headers = rabbit_basic:prepend_table_header( + <<"x-received-from">>, ?ROUTE_TABLE, BadHeaders), + BadHeaders2 = rabbit_basic:prepend_table_header( + <<"x-death">>, ?XDEATH_TABLE, + [{<<"x-death">>, + longstr, <<"and so should this!!!">>}|Headers]), + ?_assertMatch( + {table, + [{<<"x-death">>, array, [{longstr, <<"and so should this!!!">>}]}, + {<<"x-received-from">>, + array, [{longstr, <<"this should be a table!!!">>}]}]}, + rabbit_misc:table_lookup(BadHeaders2, ?INVALID_HEADERS_KEY)) + end}, + {"corrupt or invalid x-invalid-headers entries are overwritten!", + begin + Headers0 = [{<<"x-death">>, longstr, <<"this should be a table">>}, + {?INVALID_HEADERS_KEY, longstr, <<"what the!?">>}], + Headers1 = rabbit_basic:prepend_table_header( + <<"x-death">>, ?XDEATH_TABLE, Headers0), + ?_assertMatch( + {table, + [{<<"x-death">>, array, + [{longstr, <<"this should be a table">>}]}, + {?INVALID_HEADERS_KEY, array, + [{longstr, <<"what the!?">>}]}]}, + rabbit_misc:table_lookup(Headers1, ?INVALID_HEADERS_KEY)) + end}]. + +invalid_same_header_entry_accumulation_test() -> + Key = <<"x-received-from">>, + BadHeader1 = {longstr, <<"this should be a table!!!">>}, + Headers = check_invalid(Key, BadHeader1, ?ROUTE_TABLE), + Headers2 = rabbit_basic:prepend_table_header( + Key, + ?ROUTE_TABLE, + [{Key, longstr, + <<"this should also be a table!!!">>}|Headers]), + Invalid = rabbit_misc:table_lookup(Headers2, ?INVALID_HEADERS_KEY), + ?assertMatch({table, _}, Invalid), + {table, InvalidHeaders} = Invalid, + ?assertMatch({array, + [{longstr, <<"this should also be a table!!!">>},BadHeader1]}, + rabbit_misc:table_lookup(InvalidHeaders, Key)). assert_invalid(HeaderKey, Entry, Table) -> fun() -> check_invalid(HeaderKey, Entry, Table) end. check_invalid(HeaderKey, {TBin, VBin}=InvalidEntry, HeaderTable) -> - Headers = rabbit_basic:append_table_header(HeaderKey, HeaderTable, + Headers = rabbit_basic:prepend_table_header(HeaderKey, HeaderTable, [{HeaderKey, TBin, VBin}]), InvalidHeaders = rabbit_misc:table_lookup(Headers, ?INVALID_HEADERS_KEY), ?assertMatch({table, _}, InvalidHeaders), @@ -60,4 +108,3 @@ check_invalid(HeaderKey, {TBin, VBin}=InvalidEntry, HeaderTable) -> {_, Array} = InvalidArrayForKey, ?assertMatch([InvalidEntry], Array), Headers. - -- cgit v1.2.1 From 94da2cb2a045d56865a4d5f3e6a6dcc8d1c7a5c5 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Thu, 25 Oct 2012 15:31:01 +0100 Subject: fix spec --- src/rabbit_basic.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_basic.erl b/src/rabbit_basic.erl index c3250fb1..7cf3515e 100644 --- a/src/rabbit_basic.erl +++ b/src/rabbit_basic.erl @@ -58,7 +58,7 @@ -spec(properties/1 :: (properties_input()) -> rabbit_framing:amqp_property_record()). --spec(append_table_header/3 :: +-spec(prepend_table_header/3 :: (binary(), rabbit_framing:amqp_table(), headers()) -> headers()). -spec(extract_headers/1 :: (rabbit_types:content()) -> headers()). -- cgit v1.2.1 From 674eddc34171d47da71b596ca8303058b182fef8 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 25 Oct 2012 16:35:23 +0100 Subject: betterness - remove debug logging - actually log something - rabbit_log isn't operation at this point during startup so we need to use the plain error logger - be less clever about config format checking - if it vaguely looks like a legacy config then assume that, otherwise assume it's a new style config. - be more helpful in the warning message --- src/rabbit_mnesia.erl | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 6fed391a..756fbe3b 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -110,25 +110,22 @@ init() -> init_from_config() -> {TryNodes, NodeType} = case application:get_env(rabbit, cluster_nodes) of - {ok, {Nodes, disc} = C} when is_list(Nodes) -> - C; - {ok, {Nodes, ram } = C} when is_list(Nodes) -> - C; {ok, Nodes} when is_list(Nodes) -> - rabbit_log:info("blahblah ~p~n", [Nodes]), - %% Legacy config - rabbit_log:warning( - "Legacy 'cluster_nodes' configuration, use " + Config = {Nodes -- [node()], case lists:member(node(), Nodes) of + true -> disc; + false -> ram + end}, + error_logger:warning_msg( + "Converting legacy 'cluster_nodes' configuration~n ~w~n" + "to~n ~w.~n~n" + "Please update the configuration to the new format " "{Nodes, NodeType}, where Nodes contains the nodes that the " "node will try to cluster with, and NodeType is either " - "'disc' or 'ram'."), - {Nodes -- [node()], case lists:member(node(), Nodes) of - true -> disc; - false -> ram - end}; - _ -> - e(invalid_cluster_config) - end, + "'disc' or 'ram'~n", [Nodes, Config]), + Config; + {ok, Config} -> + Config + end, case find_good_node(nodes_excl_me(TryNodes)) of {ok, Node} -> rabbit_log:info("Node '~p' selected for clustering from " @@ -862,7 +859,4 @@ error_description(removing_node_from_offline_node) -> "To remove a node remotely from an offline node, the node you're removing " "from must be a disc node and all the other nodes must be offline."; error_description(no_running_cluster_nodes) -> - "You cannot leave a cluster if no online nodes are present."; -error_description(invalid_cluster_config) -> - "Invalid or missing cluster configuration. Check the 'cluster_nodes' field " - "in your config file.". + "You cannot leave a cluster if no online nodes are present.". -- cgit v1.2.1 From 9bafe35b2f4e46c19922bb0232defc1ee26bcf6f Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 25 Oct 2012 17:06:14 +0100 Subject: refactor 'reset' --- src/rabbit_mnesia.erl | 48 ++++++++++++++++++++++-------------------------- 1 file changed, 22 insertions(+), 26 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 756fbe3b..7a9c5d90 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -174,7 +174,7 @@ join_cluster(DiscoveryNode, NodeType) -> %% this case - we're joining a new cluster with new nodes which %% are not in synch with the current node. I also lifts the burden %% of reseting the node from the user. - reset(false), + reset_gracefully(), %% Join the cluster rabbit_misc:local_info_msg("Clustering with ~p as ~p node~n", @@ -188,39 +188,35 @@ join_cluster(DiscoveryNode, NodeType) -> %% cluster, has no cluster configuration, no local database, and no %% persisted messages reset() -> + ensure_mnesia_not_running(), rabbit_misc:local_info_msg("Resetting Rabbit~n", []), - reset(false). + reset_gracefully(). force_reset() -> + ensure_mnesia_not_running(), rabbit_misc:local_info_msg("Resetting Rabbit forcefully~n", []), - reset(true). + wipe(). + +reset_gracefully() -> + AllNodes = cluster_nodes(all), + %% Reconnecting so that we will get an up to date nodes. We don't + %% need to check for consistency because we are resetting. + %% Force=true here so that reset still works when clustered with a + %% node which is down. + init_db_with_mnesia(AllNodes, node_type(), false, false), + case is_only_clustered_disc_node() of + true -> e(resetting_only_disc_node); + false -> ok + end, + leave_cluster(), + rabbit_misc:ensure_ok(mnesia:delete_schema([node()]), cannot_delete_schema), + wipe(). -reset(Force) -> - ensure_mnesia_not_running(), - Nodes = case Force of - true -> - cluster_nodes(all); - false -> - AllNodes = cluster_nodes(all), - %% Reconnecting so that we will get an up to date - %% nodes. We don't need to check for consistency - %% because we are resetting. Force=true here so - %% that reset still works when clustered with a - %% node which is down. - init_db_with_mnesia(AllNodes, node_type(), false, false), - case is_only_clustered_disc_node() of - true -> e(resetting_only_disc_node); - false -> ok - end, - leave_cluster(), - rabbit_misc:ensure_ok(mnesia:delete_schema([node()]), - cannot_delete_schema), - cluster_nodes(all) - end, +wipe() -> %% We need to make sure that we don't end up in a distributed %% Erlang system with nodes while not being in an Mnesia cluster %% with them. We don't handle that well. - [erlang:disconnect_node(N) || N <- Nodes], + [erlang:disconnect_node(N) || N <- cluster_nodes(all)], %% remove persisted messages and any other garbage we find ok = rabbit_file:recursive_delete(filelib:wildcard(dir() ++ "/*")), ok = rabbit_node_monitor:reset_cluster_status(), -- cgit v1.2.1 From 4dc5a3bd43ce61e30445b083de572b8ffc9fcdba Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 25 Oct 2012 17:11:05 +0100 Subject: consistency --- src/rabbit_mnesia.erl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 7a9c5d90..8df8e653 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -817,20 +817,20 @@ e(Tag) -> throw({error, {Tag, error_description(Tag)}}). error_description(clustering_only_disc_node) -> "You cannot cluster a node if it is the only disc node in its existing " " cluster. If new nodes joined while this node was offline, use " - "\"update_cluster_nodes\" to add them manually."; + "'update_cluster_nodes' to add them manually."; error_description(resetting_only_disc_node) -> "You cannot reset a node when it is the only disc node in a cluster. " "Please convert another node of the cluster to a disc node first."; error_description(already_clustered) -> "You are already clustered with the nodes you have selected. If the " - "node you're trying to cluster with is not present in the current node " - "status, use 'update_cluster_nodes'."; + "node you are trying to cluster with is not present in the current " + "node status, use 'update_cluster_nodes'."; error_description(not_clustered) -> "Non-clustered nodes can only be disc nodes."; error_description(cannot_connect_to_cluster) -> "Could not connect to the cluster nodes present in this node's " "status file. If the cluster has changed, you can use the " - "\"update_cluster_nodes\" command to point to the new cluster nodes."; + "'update_cluster_nodes' command to point to the new cluster nodes."; error_description(no_online_cluster_nodes) -> "Could not find any online cluster nodes. If the cluster has changed, " "you can use the 'recluster' command."; @@ -848,11 +848,11 @@ error_description(offline_node_no_offline_flag) -> "but can be done with the --offline flag. Please consult the manual " "for rabbitmqctl for more information."; error_description(not_last_node_to_go_down) -> - "The node you're trying to remove from was not the last to go down " + "The node you are trying to remove from was not the last to go down " "(excluding the node you are removing). Please use the the last node " "to go down to remove nodes when the cluster is offline."; error_description(removing_node_from_offline_node) -> - "To remove a node remotely from an offline node, the node you're removing " + "To remove a node remotely from an offline node, the node you are removing " "from must be a disc node and all the other nodes must be offline."; error_description(no_running_cluster_nodes) -> "You cannot leave a cluster if no online nodes are present.". -- cgit v1.2.1 From b9c8164436a44c771b4b456ffbedebb9c042dd2a Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 26 Oct 2012 10:26:44 +0100 Subject: fix typo --- src/rabbit_mnesia.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 8df8e653..942048f9 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -833,7 +833,7 @@ error_description(cannot_connect_to_cluster) -> "'update_cluster_nodes' command to point to the new cluster nodes."; error_description(no_online_cluster_nodes) -> "Could not find any online cluster nodes. If the cluster has changed, " - "you can use the 'recluster' command."; + "you can use the 'update_cluster_nodes' command."; error_description(cannot_connect_to_node) -> "Could not connect to the cluster node provided."; error_description(inconsistent_cluster) -> -- 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(-) 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(-) 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 3b059d1d73a489fe80c05a2aeeb7384a738badd7 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 26 Oct 2012 14:00:37 +0100 Subject: simplify --- src/rabbit_plugins.erl | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/src/rabbit_plugins.erl b/src/rabbit_plugins.erl index 5fa9dd99..abe9b089 100644 --- a/src/rabbit_plugins.erl +++ b/src/rabbit_plugins.erl @@ -97,22 +97,16 @@ read_enabled(PluginsFile) -> %% When Reverse =:= true the bottom/leaf level applications are returned in %% the resulting list, otherwise they're skipped. dependencies(Reverse, Sources, AllPlugins) -> - %% A dict here is used en lieu of sets to dedup each Name, while - %% still maintaining the Deps list for each whose deps are - %% known. Missing plugins' dependencies cannot be known. - DepMap = lists:foldl( - fun({Name, Deps}, Acc) -> - dict:append_list(Name, Deps, Acc) - end, - dict:new(), - [{Name, Deps} || #plugin{name = Name, - dependencies = Deps} <- AllPlugins] ++ - [{Dep, []} || #plugin{dependencies = Deps} <- AllPlugins, - Dep <- Deps]), {ok, G} = rabbit_misc:build_acyclic_graph( fun (App, _Deps) -> [{App, App}] end, fun (App, Deps) -> [{App, Dep} || Dep <- Deps] end, - dict:to_list(DepMap)), + lists:ukeysort( + 1, [{Name, Deps} || + #plugin{name = Name, + dependencies = Deps} <- AllPlugins] ++ + [{Dep, []} || + #plugin{dependencies = Deps} <- AllPlugins, + Dep <- Deps])), Dests = case Reverse of false -> digraph_utils:reachable(Sources, G); true -> digraph_utils:reaching(Sources, G) -- cgit v1.2.1 From da82e950ea979347b67cf73ac9abe497c9068b38 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 26 Oct 2012 14:15:50 +0100 Subject: we can have both missing plugins and missing dependencies --- src/rabbit_plugins_main.erl | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/rabbit_plugins_main.erl b/src/rabbit_plugins_main.erl index b5429620..50c49229 100644 --- a/src/rabbit_plugins_main.erl +++ b/src/rabbit_plugins_main.erl @@ -111,15 +111,14 @@ action(enable, ToEnable0, _Opts, PluginsFile, PluginsDir) -> NewEnabled = lists:usort(Enabled ++ ToEnable), NewImplicitlyEnabled = rabbit_plugins:dependencies(false, NewEnabled, AllPlugins), - MissingDeps = NewImplicitlyEnabled -- plugin_names(AllPlugins), + MissingDeps = (NewImplicitlyEnabled -- plugin_names(AllPlugins)) -- Missing, case {Missing, MissingDeps} of {[], []} -> ok; - {Miss, []} -> throw({error_string, - fmt_list("The following plugins " - "could not be found:", Miss)}); - {[], Miss} -> throw({error_string, - fmt_list("The following plugin dependencies " - "could not be found:", Miss)}) + {Miss, []} -> throw({error_string, fmt_missing("plugins", Miss)}); + {[], Miss} -> throw({error_string, fmt_missing("dependencies", Miss)}); + {_, _} -> throw({error_string, + fmt_missing("plugins", Missing) ++ + fmt_missing("dependencies", MissingDeps)}) end, write_enabled_plugins(PluginsFile, NewEnabled), maybe_warn_mochiweb(NewImplicitlyEnabled), @@ -240,6 +239,9 @@ fmt_list(Header, Plugins) -> lists:flatten( [Header, $\n, [io_lib:format(" ~s~n", [P]) || P <- Plugins]]). +fmt_missing(Desc, Missing) -> + fmt_list("The following " ++ Desc ++ " could not be found:", Missing). + usort_plugins(Plugins) -> lists:usort(fun plugins_cmp/2, Plugins). -- 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(-) 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 e857f9ed2f1208638bf4fd504a37e62d990b2919 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 26 Oct 2012 14:30:20 +0100 Subject: remove cruft from display of missing plugins --- src/rabbit_plugins_main.erl | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/rabbit_plugins_main.erl b/src/rabbit_plugins_main.erl index 50c49229..33092f39 100644 --- a/src/rabbit_plugins_main.erl +++ b/src/rabbit_plugins_main.erl @@ -186,7 +186,7 @@ format_plugins(Pattern, Opts, PluginsFile, PluginsDir) -> EnabledImplicitly = rabbit_plugins:dependencies(false, EnabledExplicitly, AvailablePlugins) -- EnabledExplicitly, - Missing = [#plugin{name = Name} || + Missing = [#plugin{name = Name, dependencies = []} || Name <- ((EnabledExplicitly ++ EnabledImplicitly) -- plugin_names(AvailablePlugins))], {ok, RE} = re:compile(Pattern), @@ -220,15 +220,26 @@ format_plugin(#plugin{name = Name, version = Version, end, case Format of minimal -> io:format("~s~n", [Name]); - normal -> io:format("~s ~-" ++ integer_to_list(MaxWidth) ++ - "w ~s~n", [Glyph, Name, Version]); + normal -> io:format("~s ~-" ++ integer_to_list(MaxWidth) ++ "w ~s~n", + [Glyph, Name, case Version of + undefined -> ""; + _ -> Version + end]); verbose -> io:format("~s ~w~n", [Glyph, Name]), - io:format(" Version: \t~s~n", [Version]), + case Version of + undefined -> ok; + _ -> io:format(" Version: \t~s~n", + [Version]) + end, case Deps of [] -> ok; _ -> io:format(" Dependencies:\t~p~n", [Deps]) end, - io:format(" Description:\t~s~n", [Description]), + case Description of + undefined -> ok; + _ -> io:format(" Description:\t~s~n", + [Description]) + end, io:format("~n") end. -- cgit v1.2.1 From c6b860224dbed68853e7cb0ef4879e4c42f85bb5 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 26 Oct 2012 15:11:05 +0100 Subject: correct docs --- docs/rabbitmq-plugins.1.xml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/rabbitmq-plugins.1.xml b/docs/rabbitmq-plugins.1.xml index 5d74c6e1..8ecb4fc8 100644 --- a/docs/rabbitmq-plugins.1.xml +++ b/docs/rabbitmq-plugins.1.xml @@ -96,11 +96,13 @@ - Lists available plugins, their versions, dependencies and + Lists all plugins, their versions, dependencies and descriptions. Each plugin is prefixed with a status indicator - [ ] to indicate that the plugin is not enabled, [E] to indicate that it is explicitly enabled, - and [e] to indicate that it is implicitly enabled. + [e] to indicate that it is implicitly enabled, and [!] to + indicate that it is enabled but missing and thus not + operational. If the optional pattern is given, only plugins whose @@ -109,16 +111,15 @@ For example: rabbitmq-plugins list - This command lists all the plugins available, on one line each. + This command lists all plugins, on one line each. rabbitmq-plugins list -v - This command lists all the plugins available. + This command lists all plugins. rabbitmq-plugins list -v management - This command lists all the plugins available, but does not - display plugins whose name does not contain "management". + This command lists all plugins whose name contains "management". rabbitmq-plugins list -e rabbit -- cgit v1.2.1 From 2ae42dd8a6abcdecbcfe53ef946774f2b4691821 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 26 Oct 2012 15:31:50 +0100 Subject: Yet another way in which add_mirror / drop_mirror "failing" can be OK really. --- src/rabbit_amqqueue.erl | 3 ++- src/rabbit_mirror_queue_misc.erl | 7 ++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 6ad85b24..87e44aa3 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -18,7 +18,7 @@ -export([start/0, stop/0, declare/5, delete_immediately/1, delete/3, purge/1]). -export([pseudo_queue/2]). --export([lookup/1, with/2, with_or_die/2, assert_equivalence/5, +-export([lookup/1, with/2, with/3, with_or_die/2, assert_equivalence/5, 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]). @@ -81,6 +81,7 @@ rabbit_types:error('not_found'); ([name()]) -> [rabbit_types:amqqueue()]). -spec(with/2 :: (name(), qfun(A)) -> A | rabbit_types:error('not_found')). +-spec(with/3 :: (name(), qfun(A), fun(() -> B)) -> A | B). -spec(with_or_die/2 :: (name(), qfun(A)) -> A | rabbit_types:channel_exit()). -spec(assert_equivalence/5 :: diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 8a363f76..80e49008 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -137,7 +137,7 @@ on_node_up() -> ok. drop_mirrors(QName, Nodes) -> - [ok = drop_mirror(QName, Node) || Node <- Nodes], + [{ok, _} = drop_mirror(QName, Node) || Node <- Nodes], ok. drop_mirror(QName, MirrorNode) -> @@ -154,7 +154,7 @@ drop_mirror(QName, MirrorNode) -> "Dropping queue mirror on node ~p for ~s~n", [MirrorNode, rabbit_misc:rs(Name)]), exit(Pid, {shutdown, dropped}), - ok + {ok, dropped} end end). @@ -212,7 +212,8 @@ if_mirrored_queue(QName, Fun) -> false -> ok; true -> Fun(Q) end - end). + end, + rabbit_misc:const({ok, not_found})). report_deaths(_MirrorPid, _IsMaster, _QueueName, []) -> ok; -- cgit v1.2.1 From 732f05567ff46f6152920e47d6921e15248e662b Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 26 Oct 2012 19:08:33 +0100 Subject: remove incorrect/out-of-date comments - the failure type of {un}bind was corrected to a channel-level error in 0-9-1 - bindings to internal exchanges are ok; publishing isn't, and we check for that - bindings to the default exchange *aren't* ok, and we enforce that --- src/rabbit_channel.erl | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 0d13312b..54427206 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -1153,10 +1153,6 @@ binding_action(Fun, ExchangeNameBin, DestinationType, DestinationNameBin, RoutingKey, Arguments, ReturnMethod, NoWait, State = #ch{virtual_host = VHostPath, conn_pid = ConnPid }) -> - %% FIXME: connection exception (!) on failure?? - %% (see rule named "failure" in spec-XML) - %% FIXME: don't allow binding to internal exchanges - - %% including the one named "" ! {DestinationName, ActualRoutingKey} = expand_binding(DestinationType, DestinationNameBin, RoutingKey, State), check_write_permitted(DestinationName, State), -- cgit v1.2.1 From 6cbcd690d6002557a34e679a6602bb1166d85bcd Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 29 Oct 2012 12:29:16 +0000 Subject: Since bug 25243 we can never have the situation described in that comment - so remove the extra call to update_mirrors0/2 and get start_mirroring to start the mirrors itself. I think this is clearer. --- src/rabbit_mirror_queue_master.erl | 6 +++--- src/rabbit_mirror_queue_misc.erl | 9 +-------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index cce19c90..6a7a28f2 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -88,12 +88,10 @@ stop() -> %% Same as start/1. exit({not_valid_for_generic_backing_queue, ?MODULE}). -init(Q = #amqqueue{name = QName}, Recover, AsyncCallback) -> +init(Q, Recover, AsyncCallback) -> {ok, BQ} = application:get_env(backing_queue_module), BQS = BQ:init(Q, Recover, AsyncCallback), State = #state{gm = GM} = init_with_existing_bq(Q, BQ, BQS), - {_MNode, SNodes} = rabbit_mirror_queue_misc:suggested_queue_nodes(Q), - rabbit_mirror_queue_misc:add_mirrors(QName, SNodes), ok = gm:broadcast(GM, {depth, BQ:depth(BQS)}), State. @@ -109,6 +107,8 @@ init_with_existing_bq(Q = #amqqueue{name = QName}, BQ, BQS) -> ok = rabbit_amqqueue:store_queue( Q1#amqqueue{gm_pids = [{GM, Self} | GMPids]}) end), + {_MNode, SNodes} = rabbit_mirror_queue_misc:suggested_queue_nodes(Q), + rabbit_mirror_queue_misc:add_mirrors(QName, SNodes), #state { gm = GM, coordinator = CPid, backing_queue = BQ, diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 8a363f76..92ccc79d 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -318,19 +318,12 @@ is_mirrored(Q) -> _ -> false end. - -%% [1] - rabbit_amqqueue:start_mirroring/1 will turn unmirrored to -%% master and start any needed slaves. However, if node(QPid) is not -%% in the nodes for the policy, it won't switch it. So this is for the -%% case where we kill the existing queue and restart elsewhere. TODO: -%% is this TRTTD? All alternatives seem ugly. update_mirrors(OldQ = #amqqueue{pid = QPid}, NewQ = #amqqueue{pid = QPid}) -> case {is_mirrored(OldQ), is_mirrored(NewQ)} of {false, false} -> ok; {true, false} -> rabbit_amqqueue:stop_mirroring(QPid); - {false, true} -> rabbit_amqqueue:start_mirroring(QPid), - update_mirrors0(OldQ, NewQ); %% [1] + {false, true} -> rabbit_amqqueue:start_mirroring(QPid); {true, true} -> update_mirrors0(OldQ, NewQ) end. -- cgit v1.2.1 From 9bc892f59c5e0f77478f6e5c698dc75c85689789 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 29 Oct 2012 12:46:47 +0000 Subject: Remove if_mirrored_queue/2, it's not buying us anything in terms of avoiding races. --- src/rabbit_mirror_queue_misc.erl | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 92ccc79d..ec00ecef 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -141,7 +141,7 @@ drop_mirrors(QName, Nodes) -> ok. drop_mirror(QName, MirrorNode) -> - if_mirrored_queue( + rabbit_amqqueue:with( QName, fun (#amqqueue { name = Name, pid = QPid, slave_pids = SPids }) -> case [Pid || Pid <- [QPid | SPids], node(Pid) =:= MirrorNode] of @@ -163,7 +163,7 @@ add_mirrors(QName, Nodes) -> ok. add_mirror(QName, MirrorNode) -> - if_mirrored_queue( + rabbit_amqqueue:with( QName, fun (#amqqueue { name = Name, pid = QPid, slave_pids = SPids } = Q) -> case [Pid || Pid <- [QPid | SPids], node(Pid) =:= MirrorNode] of @@ -206,14 +206,6 @@ start_child(Name, MirrorNode, Q) -> Other end. -if_mirrored_queue(QName, Fun) -> - rabbit_amqqueue:with(QName, fun (Q) -> - case is_mirrored(Q) of - false -> ok; - true -> Fun(Q) - end - end). - report_deaths(_MirrorPid, _IsMaster, _QueueName, []) -> ok; report_deaths(MirrorPid, IsMaster, QueueName, DeadPids) -> -- cgit v1.2.1 From 4ac78175b69a77eff0446ba372f969a31708261b Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 29 Oct 2012 12:51:33 +0000 Subject: stop reading when losing frame sync --- src/rabbit_reader.erl | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index f4e6865b..e9c18312 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -491,6 +491,14 @@ handle_exception(State, Channel, Reason) -> timer:sleep(?SILENT_CLOSE_DELAY * 1000), throw({handshake_error, State#v1.connection_state, Channel, Reason}). +%% we've "lost sync" with the client and hence must not accept any +%% more input +fatal_frame_error(Error, Type, Channel, Payload, State) -> + frame_error(Error, Type, Channel, Payload, State), + %% grace period to allow transmission of error + timer:sleep(?SILENT_CLOSE_DELAY * 1000), + throw(fatal_frame_error). + frame_error(Error, Type, Channel, Payload, State) -> {Str, Bin} = payload_snippet(Payload), handle_exception(State, Channel, @@ -621,8 +629,9 @@ handle_input(frame_header, <>, State = #v1{connection = #connection{frame_max = FrameMax}}) when FrameMax /= 0 andalso PayloadSize > FrameMax - ?EMPTY_FRAME_SIZE + ?FRAME_SIZE_FUDGE -> - frame_error({frame_too_large, PayloadSize, FrameMax - ?EMPTY_FRAME_SIZE}, - Type, Channel, <<>>, State); + fatal_frame_error( + {frame_too_large, PayloadSize, FrameMax - ?EMPTY_FRAME_SIZE}, + Type, Channel, <<>>, State); handle_input(frame_header, <>, State) -> ensure_stats_timer( switch_callback(State, {frame_payload, Type, Channel, PayloadSize}, @@ -633,8 +642,8 @@ handle_input({frame_payload, Type, Channel, PayloadSize}, Data, State) -> case EndMarker of ?FRAME_END -> State1 = handle_frame(Type, Channel, Payload, State), switch_callback(State1, frame_header, 7); - _ -> frame_error({invalid_frame_end_marker, EndMarker}, - Type, Channel, Payload, State) + _ -> fatal_frame_error({invalid_frame_end_marker, EndMarker}, + Type, Channel, Payload, State) end; %% The two rules pertaining to version negotiation: -- cgit v1.2.1 From 412d94bcdd6733f17849b08a467db1fdb56480d0 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 29 Oct 2012 13:59:40 +0000 Subject: migrate rabbit_basic tests to rabbit_tests --- src/rabbit_basic_tests.erl | 110 --------------------------------------------- src/rabbit_tests.erl | 85 ++++++++++++++++++++++++++++++++++- 2 files changed, 84 insertions(+), 111 deletions(-) delete mode 100644 src/rabbit_basic_tests.erl diff --git a/src/rabbit_basic_tests.erl b/src/rabbit_basic_tests.erl deleted file mode 100644 index 19c901c2..00000000 --- a/src/rabbit_basic_tests.erl +++ /dev/null @@ -1,110 +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 VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. -%% --module(rabbit_basic_tests). - --include("rabbit.hrl"). --include_lib("eunit/include/eunit.hrl"). - --compile(export_all). - --define(XDEATH_TABLE, - [{<<"reason">>, longstr, <<"blah">>}, - {<<"queue">>, longstr, <<"foo.bar.baz">>}, - {<<"exchange">>, longstr, <<"my-exchange">>}, - {<<"routing-keys">>, array, []}]). - --define(ROUTE_TABLE, [{<<"redelivered">>, bool, <<"true">>}]). - -write_table_with_invalid_existing_type_test_() -> - [{"existing entries with invalid types are moved to a table " - "stored as <<\"x-invalid-headers header\">>", - fun() -> - check_invalid(<<"x-death">>, - {longstr, <<"this should be a table!!!">>}, - ?XDEATH_TABLE) - end}, - {"if invalid existing headers are moved, newly added " - "ones are still stored correctly", - begin - BadHeaders = [{<<"x-received-from">>, - longstr, <<"this should be a table!!!">>}], - Headers = rabbit_basic:prepend_table_header( - <<"x-received-from">>, ?ROUTE_TABLE, BadHeaders), - ?_assertEqual({array, [{table, ?ROUTE_TABLE}]}, - rabbit_misc:table_lookup(Headers, <<"x-received-from">>)) - end}, - {"disparate invalid header entries are accumulated separately", - begin - BadHeaders = [{<<"x-received-from">>, - longstr, <<"this should be a table!!!">>}], - Headers = rabbit_basic:prepend_table_header( - <<"x-received-from">>, ?ROUTE_TABLE, BadHeaders), - BadHeaders2 = rabbit_basic:prepend_table_header( - <<"x-death">>, ?XDEATH_TABLE, - [{<<"x-death">>, - longstr, <<"and so should this!!!">>}|Headers]), - ?_assertMatch( - {table, - [{<<"x-death">>, array, [{longstr, <<"and so should this!!!">>}]}, - {<<"x-received-from">>, - array, [{longstr, <<"this should be a table!!!">>}]}]}, - rabbit_misc:table_lookup(BadHeaders2, ?INVALID_HEADERS_KEY)) - end}, - {"corrupt or invalid x-invalid-headers entries are overwritten!", - begin - Headers0 = [{<<"x-death">>, longstr, <<"this should be a table">>}, - {?INVALID_HEADERS_KEY, longstr, <<"what the!?">>}], - Headers1 = rabbit_basic:prepend_table_header( - <<"x-death">>, ?XDEATH_TABLE, Headers0), - ?_assertMatch( - {table, - [{<<"x-death">>, array, - [{longstr, <<"this should be a table">>}]}, - {?INVALID_HEADERS_KEY, array, - [{longstr, <<"what the!?">>}]}]}, - rabbit_misc:table_lookup(Headers1, ?INVALID_HEADERS_KEY)) - end}]. - -invalid_same_header_entry_accumulation_test() -> - Key = <<"x-received-from">>, - BadHeader1 = {longstr, <<"this should be a table!!!">>}, - Headers = check_invalid(Key, BadHeader1, ?ROUTE_TABLE), - Headers2 = rabbit_basic:prepend_table_header( - Key, - ?ROUTE_TABLE, - [{Key, longstr, - <<"this should also be a table!!!">>}|Headers]), - Invalid = rabbit_misc:table_lookup(Headers2, ?INVALID_HEADERS_KEY), - ?assertMatch({table, _}, Invalid), - {table, InvalidHeaders} = Invalid, - ?assertMatch({array, - [{longstr, <<"this should also be a table!!!">>},BadHeader1]}, - rabbit_misc:table_lookup(InvalidHeaders, Key)). - -assert_invalid(HeaderKey, Entry, Table) -> - fun() -> check_invalid(HeaderKey, Entry, Table) end. - -check_invalid(HeaderKey, {TBin, VBin}=InvalidEntry, HeaderTable) -> - Headers = rabbit_basic:prepend_table_header(HeaderKey, HeaderTable, - [{HeaderKey, TBin, VBin}]), - InvalidHeaders = rabbit_misc:table_lookup(Headers, ?INVALID_HEADERS_KEY), - ?assertMatch({table, _}, InvalidHeaders), - {_, Invalid} = InvalidHeaders, - InvalidArrayForKey = rabbit_misc:table_lookup(Invalid, HeaderKey), - ?assertMatch({array, _}, InvalidArrayForKey), - {_, Array} = InvalidArrayForKey, - ?assertMatch([InvalidEntry], Array), - Headers. diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index bea94c15..25402ca7 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -31,10 +31,19 @@ -define(CLEANUP_QUEUE_NAME, <<"cleanup-queue">>). -define(TIMEOUT, 5000). + +-define(XDEATH_TABLE, + [{<<"reason">>, longstr, <<"blah">>}, + {<<"queue">>, longstr, <<"foo.bar.baz">>}, + {<<"exchange">>, longstr, <<"my-exchange">>}, + {<<"routing-keys">>, array, []}]). + +-define(ROUTE_TABLE, [{<<"redelivered">>, bool, <<"true">>}]). + all_tests() -> ok = setup_cluster(), ok = supervisor2_tests:test_all(), - ok = rabbit_basic_tests:test(), + ok = rabbit_basic_tests(), passed = gm_tests:all_tests(), passed = mirrored_supervisor_tests:all_tests(), application:set_env(rabbit, file_handles_high_watermark, 10, infinity), @@ -72,6 +81,80 @@ all_tests() -> passed = test_configurable_server_properties(), passed. +rabbit_basic_tests() -> + ok = write_table_with_invalid_existing_type_test(), + ok = invalid_existing_headers_test(), + ok = disparate_invalid_header_entries_accumulate_separately_test(), + ok = corrupt_or_invalid_headers_are_overwritten_test(), + ok = invalid_same_header_entry_accumulation_test(). + +write_table_with_invalid_existing_type_test() -> + check_invalid(<<"x-death">>, + {longstr, <<"this should be a table!!!">>}, + ?XDEATH_TABLE), + ok. + +invalid_existing_headers_test() -> + BadHeaders = [{<<"x-received-from">>, + longstr, <<"this should be a table!!!">>}], + Headers = rabbit_basic:prepend_table_header( + <<"x-received-from">>, ?ROUTE_TABLE, BadHeaders), + {array, [{table, ?ROUTE_TABLE}]} = + rabbit_misc:table_lookup(Headers, <<"x-received-from">>), + ok. + +disparate_invalid_header_entries_accumulate_separately_test() -> + BadHeaders = [{<<"x-received-from">>, + longstr, <<"this should be a table!!!">>}], + Headers = rabbit_basic:prepend_table_header( + <<"x-received-from">>, ?ROUTE_TABLE, BadHeaders), + BadHeaders2 = rabbit_basic:prepend_table_header( + <<"x-death">>, ?XDEATH_TABLE, + [{<<"x-death">>, + longstr, <<"and so should this!!!">>}|Headers]), + + {table, [{<<"x-death">>, array, [{longstr, <<"and so should this!!!">>}]}, + {<<"x-received-from">>, + array, [{longstr, <<"this should be a table!!!">>}]}]} = + rabbit_misc:table_lookup(BadHeaders2, ?INVALID_HEADERS_KEY), + ok. + +corrupt_or_invalid_headers_are_overwritten_test() -> + Headers0 = [{<<"x-death">>, longstr, <<"this should be a table">>}, + {?INVALID_HEADERS_KEY, longstr, <<"what the!?">>}], + Headers1 = rabbit_basic:prepend_table_header( + <<"x-death">>, ?XDEATH_TABLE, Headers0), + {table,[{<<"x-death">>, array, + [{longstr, <<"this should be a table">>}]}, + {?INVALID_HEADERS_KEY, array, + [{longstr, <<"what the!?">>}]}]} = + rabbit_misc:table_lookup(Headers1, ?INVALID_HEADERS_KEY), + ok. + +invalid_same_header_entry_accumulation_test() -> + Key = <<"x-received-from">>, + BadHeader1 = {longstr, <<"this should be a table!!!">>}, + Headers = check_invalid(Key, BadHeader1, ?ROUTE_TABLE), + Headers2 = rabbit_basic:prepend_table_header( + Key, + ?ROUTE_TABLE, + [{Key, longstr, + <<"this should also be a table!!!">>}|Headers]), + Invalid = rabbit_misc:table_lookup(Headers2, ?INVALID_HEADERS_KEY), + {table, InvalidHeaders} = Invalid, + {array, [{longstr, <<"this should also be a table!!!">>},BadHeader1]} = + rabbit_misc:table_lookup(InvalidHeaders, Key), + ok. + +check_invalid(HeaderKey, {TBin, VBin}=InvalidEntry, HeaderTable) -> + Headers = rabbit_basic:prepend_table_header(HeaderKey, HeaderTable, + [{HeaderKey, TBin, VBin}]), + InvalidHeaders = rabbit_misc:table_lookup(Headers, ?INVALID_HEADERS_KEY), + {table, Invalid} = InvalidHeaders, + InvalidArrayForKey = rabbit_misc:table_lookup(Invalid, HeaderKey), + {array, [InvalidEntry]} = InvalidArrayForKey, + Headers. + do_if_secondary_node(Up, Down) -> SecondaryNode = rabbit_nodes:make("hare"), -- cgit v1.2.1 From 3dbe2cede1f87e420cce098595a779f366cd043f Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 29 Oct 2012 14:22:38 +0000 Subject: refactor --- src/rabbit_basic.erl | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/rabbit_basic.erl b/src/rabbit_basic.erl index 7cf3515e..c3e1ac99 100644 --- a/src/rabbit_basic.erl +++ b/src/rabbit_basic.erl @@ -197,30 +197,30 @@ set_invalid_header(Name, {_, _}=Value, Headers) when is_list(Headers) -> case rabbit_misc:table_lookup(Headers, ?INVALID_HEADERS_KEY) of undefined -> Invalid = [{Name, array, [Value]}], - rabbit_misc:set_table_value(Headers, ?INVALID_HEADERS_KEY, - table, Invalid); + set_invalid(Headers, Invalid); {table, InvalidEntries} -> case rabbit_misc:table_lookup(InvalidEntries, Name) of undefined -> - rabbit_misc:set_table_value( - Headers, ?INVALID_HEADERS_KEY, table, - rabbit_misc:set_table_value(InvalidEntries, - Name, array, [Value])); + set_invalid(Headers, InvalidEntries, Name, [Value]); {array, Prior} -> - rabbit_misc:set_table_value( - Headers, ?INVALID_HEADERS_KEY, table, - rabbit_misc:set_table_value(InvalidEntries, - Name, array, [Value | Prior])) + set_invalid(Headers, InvalidEntries, Name, [Value | Prior]) end; Other -> %% somehow the x-invalid-headers header is corrupt - set_invalid_header( - Name, Value, - rabbit_misc:set_table_value( - Headers, ?INVALID_HEADERS_KEY, - table, [{?INVALID_HEADERS_KEY, array, [Other]}])) + Invalid = [{?INVALID_HEADERS_KEY, array, [Other]}], + set_invalid_header(Name, Value, set_invalid(Headers, Invalid)) end. +set_invalid(Headers, Invalid) -> + rabbit_misc:set_table_value(Headers, ?INVALID_HEADERS_KEY, + table, Invalid). + +set_invalid(Headers, InvalidEntries, Name, Values) -> + rabbit_misc:set_table_value( + Headers, ?INVALID_HEADERS_KEY, table, + rabbit_misc:set_table_value(InvalidEntries, + Name, array, Values)). + extract_headers(Content) -> #content{properties = #'P_basic'{headers = Headers}} = rabbit_binary_parser:ensure_content_decoded(Content), -- cgit v1.2.1 From 8d72e0af5163596451aaa6fa5da1d4cfd71c1ef5 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 29 Oct 2012 14:22:58 +0000 Subject: Shorter. --- src/rabbit_plugins_main.erl | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/src/rabbit_plugins_main.erl b/src/rabbit_plugins_main.erl index 33092f39..33da1821 100644 --- a/src/rabbit_plugins_main.erl +++ b/src/rabbit_plugins_main.erl @@ -218,28 +218,19 @@ format_plugin(#plugin{name = Name, version = Version, {_, _, true} -> "[!]"; _ -> "[ ]" end, + Opt = fun (_F, A, A) -> ok; + ( F, A, _) -> io:format(F, [A]) + end, case Format of minimal -> io:format("~s~n", [Name]); - normal -> io:format("~s ~-" ++ integer_to_list(MaxWidth) ++ "w ~s~n", - [Glyph, Name, case Version of - undefined -> ""; - _ -> Version - end]); + normal -> io:format("~s ~-" ++ integer_to_list(MaxWidth) ++ "w", + [Glyph, Name]), + Opt("~s", Version, undefined), + io:format("~n"); verbose -> io:format("~s ~w~n", [Glyph, Name]), - case Version of - undefined -> ok; - _ -> io:format(" Version: \t~s~n", - [Version]) - end, - case Deps of - [] -> ok; - _ -> io:format(" Dependencies:\t~p~n", [Deps]) - end, - case Description of - undefined -> ok; - _ -> io:format(" Description:\t~s~n", - [Description]) - end, + Opt(" Version: \t~s~n", Version, undefined), + Opt(" Dependencies:\t~p~n", Deps, []), + Opt(" Description:\t~s~n", Description, undefined), io:format("~n") end. -- cgit v1.2.1 From 114ee456a1254cef2c4d3ec91ae37a9eea1a8018 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 29 Oct 2012 14:38:23 +0000 Subject: Formatting --- src/rabbit_plugins_main.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rabbit_plugins_main.erl b/src/rabbit_plugins_main.erl index 33da1821..2158d1da 100644 --- a/src/rabbit_plugins_main.erl +++ b/src/rabbit_plugins_main.erl @@ -223,14 +223,14 @@ format_plugin(#plugin{name = Name, version = Version, end, case Format of minimal -> io:format("~s~n", [Name]); - normal -> io:format("~s ~-" ++ integer_to_list(MaxWidth) ++ "w", + normal -> io:format("~s ~-" ++ integer_to_list(MaxWidth) ++ "w ", [Glyph, Name]), Opt("~s", Version, undefined), io:format("~n"); verbose -> io:format("~s ~w~n", [Glyph, Name]), - Opt(" Version: \t~s~n", Version, undefined), + Opt(" Version: \t~s~n", Version, undefined), Opt(" Dependencies:\t~p~n", Deps, []), - Opt(" Description:\t~s~n", Description, undefined), + Opt(" Description: \t~s~n", Description, undefined), io:format("~n") end. -- 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(-) 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 3cd7676de61a14675dc47ed842ea3ff004d4f375 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 29 Oct 2012 15:40:20 +0000 Subject: make basic tests fit into rabbit_tests a bit more cleanly --- src/rabbit_tests.erl | 150 ++++++++++++++++++++++++++------------------------- 1 file changed, 76 insertions(+), 74 deletions(-) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 25402ca7..13aff6e8 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -43,7 +43,6 @@ all_tests() -> ok = setup_cluster(), ok = supervisor2_tests:test_all(), - ok = rabbit_basic_tests(), passed = gm_tests:all_tests(), passed = mirrored_supervisor_tests:all_tests(), application:set_env(rabbit, file_handles_high_watermark, 10, infinity), @@ -51,6 +50,7 @@ all_tests() -> passed = test_multi_call(), passed = test_file_handle_cache(), passed = test_backing_queue(), + passed = test_rabbit_basic_header_handling(), passed = test_priority_queue(), passed = test_pg_local(), passed = test_unfold(), @@ -81,79 +81,6 @@ all_tests() -> passed = test_configurable_server_properties(), passed. -rabbit_basic_tests() -> - ok = write_table_with_invalid_existing_type_test(), - ok = invalid_existing_headers_test(), - ok = disparate_invalid_header_entries_accumulate_separately_test(), - ok = corrupt_or_invalid_headers_are_overwritten_test(), - ok = invalid_same_header_entry_accumulation_test(). - -write_table_with_invalid_existing_type_test() -> - check_invalid(<<"x-death">>, - {longstr, <<"this should be a table!!!">>}, - ?XDEATH_TABLE), - ok. - -invalid_existing_headers_test() -> - BadHeaders = [{<<"x-received-from">>, - longstr, <<"this should be a table!!!">>}], - Headers = rabbit_basic:prepend_table_header( - <<"x-received-from">>, ?ROUTE_TABLE, BadHeaders), - {array, [{table, ?ROUTE_TABLE}]} = - rabbit_misc:table_lookup(Headers, <<"x-received-from">>), - ok. - -disparate_invalid_header_entries_accumulate_separately_test() -> - BadHeaders = [{<<"x-received-from">>, - longstr, <<"this should be a table!!!">>}], - Headers = rabbit_basic:prepend_table_header( - <<"x-received-from">>, ?ROUTE_TABLE, BadHeaders), - BadHeaders2 = rabbit_basic:prepend_table_header( - <<"x-death">>, ?XDEATH_TABLE, - [{<<"x-death">>, - longstr, <<"and so should this!!!">>}|Headers]), - - {table, [{<<"x-death">>, array, [{longstr, <<"and so should this!!!">>}]}, - {<<"x-received-from">>, - array, [{longstr, <<"this should be a table!!!">>}]}]} = - rabbit_misc:table_lookup(BadHeaders2, ?INVALID_HEADERS_KEY), - ok. - -corrupt_or_invalid_headers_are_overwritten_test() -> - Headers0 = [{<<"x-death">>, longstr, <<"this should be a table">>}, - {?INVALID_HEADERS_KEY, longstr, <<"what the!?">>}], - Headers1 = rabbit_basic:prepend_table_header( - <<"x-death">>, ?XDEATH_TABLE, Headers0), - {table,[{<<"x-death">>, array, - [{longstr, <<"this should be a table">>}]}, - {?INVALID_HEADERS_KEY, array, - [{longstr, <<"what the!?">>}]}]} = - rabbit_misc:table_lookup(Headers1, ?INVALID_HEADERS_KEY), - ok. - -invalid_same_header_entry_accumulation_test() -> - Key = <<"x-received-from">>, - BadHeader1 = {longstr, <<"this should be a table!!!">>}, - Headers = check_invalid(Key, BadHeader1, ?ROUTE_TABLE), - Headers2 = rabbit_basic:prepend_table_header( - Key, - ?ROUTE_TABLE, - [{Key, longstr, - <<"this should also be a table!!!">>}|Headers]), - Invalid = rabbit_misc:table_lookup(Headers2, ?INVALID_HEADERS_KEY), - {table, InvalidHeaders} = Invalid, - {array, [{longstr, <<"this should also be a table!!!">>},BadHeader1]} = - rabbit_misc:table_lookup(InvalidHeaders, Key), - ok. - -check_invalid(HeaderKey, {TBin, VBin}=InvalidEntry, HeaderTable) -> - Headers = rabbit_basic:prepend_table_header(HeaderKey, HeaderTable, - [{HeaderKey, TBin, VBin}]), - InvalidHeaders = rabbit_misc:table_lookup(Headers, ?INVALID_HEADERS_KEY), - {table, Invalid} = InvalidHeaders, - InvalidArrayForKey = rabbit_misc:table_lookup(Invalid, HeaderKey), - {array, [InvalidEntry]} = InvalidArrayForKey, - Headers. do_if_secondary_node(Up, Down) -> SecondaryNode = rabbit_nodes:make("hare"), @@ -243,6 +170,81 @@ test_multi_call() -> exit(Pid3, bang), passed. +test_rabbit_basic_header_handling() -> + passed = write_table_with_invalid_existing_type_test(), + passed = invalid_existing_headers_test(), + passed = disparate_invalid_header_entries_accumulate_separately_test(), + passed = corrupt_or_invalid_headers_are_overwritten_test(), + passed = invalid_same_header_entry_accumulation_test(), + passed. + +write_table_with_invalid_existing_type_test() -> + check_invalid(<<"x-death">>, + {longstr, <<"this should be a table!!!">>}, + ?XDEATH_TABLE), + passed. + +invalid_existing_headers_test() -> + BadHeaders = [{<<"x-received-from">>, + longstr, <<"this should be a table!!!">>}], + Headers = rabbit_basic:prepend_table_header( + <<"x-received-from">>, ?ROUTE_TABLE, BadHeaders), + {array, [{table, ?ROUTE_TABLE}]} = + rabbit_misc:table_lookup(Headers, <<"x-received-from">>), + passed. + +disparate_invalid_header_entries_accumulate_separately_test() -> + BadHeaders = [{<<"x-received-from">>, + longstr, <<"this should be a table!!!">>}], + Headers = rabbit_basic:prepend_table_header( + <<"x-received-from">>, ?ROUTE_TABLE, BadHeaders), + BadHeaders2 = rabbit_basic:prepend_table_header( + <<"x-death">>, ?XDEATH_TABLE, + [{<<"x-death">>, + longstr, <<"and so should this!!!">>}|Headers]), + + {table, [{<<"x-death">>, array, [{longstr, <<"and so should this!!!">>}]}, + {<<"x-received-from">>, + array, [{longstr, <<"this should be a table!!!">>}]}]} = + rabbit_misc:table_lookup(BadHeaders2, ?INVALID_HEADERS_KEY), + passed. + +corrupt_or_invalid_headers_are_overwritten_test() -> + Headers0 = [{<<"x-death">>, longstr, <<"this should be a table">>}, + {?INVALID_HEADERS_KEY, longstr, <<"what the!?">>}], + Headers1 = rabbit_basic:prepend_table_header( + <<"x-death">>, ?XDEATH_TABLE, Headers0), + {table,[{<<"x-death">>, array, + [{longstr, <<"this should be a table">>}]}, + {?INVALID_HEADERS_KEY, array, + [{longstr, <<"what the!?">>}]}]} = + rabbit_misc:table_lookup(Headers1, ?INVALID_HEADERS_KEY), + passed. + +invalid_same_header_entry_accumulation_test() -> + Key = <<"x-received-from">>, + BadHeader1 = {longstr, <<"this should be a table!!!">>}, + Headers = check_invalid(Key, BadHeader1, ?ROUTE_TABLE), + Headers2 = rabbit_basic:prepend_table_header( + Key, + ?ROUTE_TABLE, + [{Key, longstr, + <<"this should also be a table!!!">>}|Headers]), + Invalid = rabbit_misc:table_lookup(Headers2, ?INVALID_HEADERS_KEY), + {table, InvalidHeaders} = Invalid, + {array, [{longstr, <<"this should also be a table!!!">>},BadHeader1]} = + rabbit_misc:table_lookup(InvalidHeaders, Key), + passed. + +check_invalid(HeaderKey, {TBin, VBin}=InvalidEntry, HeaderTable) -> + Headers = rabbit_basic:prepend_table_header(HeaderKey, HeaderTable, + [{HeaderKey, TBin, VBin}]), + InvalidHeaders = rabbit_misc:table_lookup(Headers, ?INVALID_HEADERS_KEY), + {table, Invalid} = InvalidHeaders, + InvalidArrayForKey = rabbit_misc:table_lookup(Invalid, HeaderKey), + {array, [InvalidEntry]} = InvalidArrayForKey, + Headers. + test_priority_queue() -> false = priority_queue:is_queue(not_a_queue), -- cgit v1.2.1 From 5fcd3b58a689f93d904177ef6f1817284e7a43e7 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 29 Oct 2012 15:57:52 +0000 Subject: Refactor a bit --- src/rabbit_basic.erl | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/src/rabbit_basic.erl b/src/rabbit_basic.erl index c3e1ac99..d95ceb8c 100644 --- a/src/rabbit_basic.erl +++ b/src/rabbit_basic.erl @@ -196,30 +196,25 @@ prepend_table(Headers, Name, Info, Prior) -> set_invalid_header(Name, {_, _}=Value, Headers) when is_list(Headers) -> case rabbit_misc:table_lookup(Headers, ?INVALID_HEADERS_KEY) of undefined -> - Invalid = [{Name, array, [Value]}], - set_invalid(Headers, Invalid); - {table, InvalidEntries} -> - case rabbit_misc:table_lookup(InvalidEntries, Name) of - undefined -> - set_invalid(Headers, InvalidEntries, Name, [Value]); - {array, Prior} -> - set_invalid(Headers, InvalidEntries, Name, [Value | Prior]) - end; + set_invalid(Headers, [{Name, array, [Value]}]); + {table, ExistingHdr} -> + update_invalid(Headers, ExistingHdr, Name, Value); Other -> %% somehow the x-invalid-headers header is corrupt Invalid = [{?INVALID_HEADERS_KEY, array, [Other]}], set_invalid_header(Name, Value, set_invalid(Headers, Invalid)) end. -set_invalid(Headers, Invalid) -> - rabbit_misc:set_table_value(Headers, ?INVALID_HEADERS_KEY, - table, Invalid). +set_invalid(Headers, NewHdr) -> + rabbit_misc:set_table_value(Headers, ?INVALID_HEADERS_KEY, table, NewHdr). -set_invalid(Headers, InvalidEntries, Name, Values) -> - rabbit_misc:set_table_value( - Headers, ?INVALID_HEADERS_KEY, table, - rabbit_misc:set_table_value(InvalidEntries, - Name, array, Values)). +update_invalid(Headers, ExistingHdr, Name, Value) -> + Values = case rabbit_misc:table_lookup(ExistingHdr, Name) of + undefined -> [Value]; + {array, Prior} -> [Value | Prior] + end, + NewHdr = rabbit_misc:set_table_value(ExistingHdr, Name, array, Values), + set_invalid(Headers, NewHdr). extract_headers(Content) -> #content{properties = #'P_basic'{headers = Headers}} = -- cgit v1.2.1 From 640d4e1409970bc7f0c591970f1093ee8e2f93a2 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 29 Oct 2012 16:02:05 +0000 Subject: Rearrange args to put the big structure we're working with at the end; more OTPish. Yes, rabbit_misc:table_lookup and rabbit_misc:set_table_value get this wrong, but we should not copy that. --- src/rabbit_basic.erl | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/rabbit_basic.erl b/src/rabbit_basic.erl index d95ceb8c..9966c0df 100644 --- a/src/rabbit_basic.erl +++ b/src/rabbit_basic.erl @@ -182,39 +182,39 @@ prepend_table_header(Name, Info, undefined) -> prepend_table_header(Name, Info, Headers) -> case rabbit_misc:table_lookup(Headers, Name) of {array, Existing} -> - prepend_table(Headers, Name, Info, Existing); + prepend_table(Name, Info, Existing, Headers); undefined -> - prepend_table(Headers, Name, Info, []); + prepend_table(Name, Info, [], Headers); Other -> - Headers2 = prepend_table(Headers, Name, Info, []), + Headers2 = prepend_table(Name, Info, [], Headers), set_invalid_header(Name, Other, Headers2) end. -prepend_table(Headers, Name, Info, Prior) -> +prepend_table(Name, Info, Prior, Headers) -> rabbit_misc:set_table_value(Headers, Name, array, [{table, Info} | Prior]). set_invalid_header(Name, {_, _}=Value, Headers) when is_list(Headers) -> case rabbit_misc:table_lookup(Headers, ?INVALID_HEADERS_KEY) of undefined -> - set_invalid(Headers, [{Name, array, [Value]}]); + set_invalid([{Name, array, [Value]}], Headers); {table, ExistingHdr} -> - update_invalid(Headers, ExistingHdr, Name, Value); + update_invalid(Name, Value, ExistingHdr, Headers); Other -> %% somehow the x-invalid-headers header is corrupt Invalid = [{?INVALID_HEADERS_KEY, array, [Other]}], - set_invalid_header(Name, Value, set_invalid(Headers, Invalid)) + set_invalid_header(Name, Value, set_invalid(Invalid, Headers)) end. -set_invalid(Headers, NewHdr) -> +set_invalid(NewHdr, Headers) -> rabbit_misc:set_table_value(Headers, ?INVALID_HEADERS_KEY, table, NewHdr). -update_invalid(Headers, ExistingHdr, Name, Value) -> +update_invalid(Name, Value, ExistingHdr, Header) -> Values = case rabbit_misc:table_lookup(ExistingHdr, Name) of undefined -> [Value]; {array, Prior} -> [Value | Prior] end, NewHdr = rabbit_misc:set_table_value(ExistingHdr, Name, array, Values), - set_invalid(Headers, NewHdr). + set_invalid(NewHdr, Header). extract_headers(Content) -> #content{properties = #'P_basic'{headers = Headers}} = -- cgit v1.2.1 From 751332c96618d1c0ac07fc8062ed86743d3706d9 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 29 Oct 2012 17:11:25 +0000 Subject: cosmetic --- src/rabbit_reader.erl | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index e9c18312..9f1f9c38 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -935,26 +935,27 @@ i(last_blocked_age, #v1{last_blocked_at = T}) -> timer:now_diff(erlang:now(), T) / 1000000; i(channels, #v1{}) -> length(all_channels()); -i(protocol, #v1{connection = #connection{protocol = none}}) -> - none; -i(protocol, #v1{connection = #connection{protocol = Protocol}}) -> - Protocol:version(); i(auth_mechanism, #v1{auth_mechanism = none}) -> none; i(auth_mechanism, #v1{auth_mechanism = Mechanism}) -> proplists:get_value(name, Mechanism:description()); -i(user, #v1{connection = #connection{user = #user{username = Username}}}) -> - Username; -i(user, #v1{connection = #connection{user = none}}) -> +i(protocol, #v1{connection = #connection{protocol = none}}) -> + none; +i(protocol, #v1{connection = #connection{protocol = Protocol}}) -> + Protocol:version(); +i(user, #v1{connection = #connection{user = none}}) -> ''; -i(vhost, #v1{connection = #connection{vhost = VHost}}) -> +i(user, #v1{connection = #connection{user = #user{ + username = Username}}}) -> + Username; +i(vhost, #v1{connection = #connection{vhost = VHost}}) -> VHost; -i(timeout, #v1{connection = #connection{timeout_sec = Timeout}}) -> +i(timeout, #v1{connection = #connection{timeout_sec = Timeout}}) -> Timeout; -i(frame_max, #v1{connection = #connection{frame_max = FrameMax}}) -> +i(frame_max, #v1{connection = #connection{frame_max = FrameMax}}) -> FrameMax; -i(client_properties, #v1{connection = #connection{ - client_properties = ClientProperties}}) -> +i(client_properties, #v1{connection = #connection{client_properties = + ClientProperties}}) -> ClientProperties; i(Item, #v1{}) -> throw({bad_argument, Item}). -- cgit v1.2.1 From 463230c0a3cb2f6483e024bd06fb768320706ec4 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 29 Oct 2012 17:28:03 +0000 Subject: An extensive list of transformations lead me here. I think this is more readable. --- src/rabbit_tests.erl | 104 +++++++++++++++++++++++---------------------------- 1 file changed, 46 insertions(+), 58 deletions(-) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 13aff6e8..33a75fec 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -31,15 +31,6 @@ -define(CLEANUP_QUEUE_NAME, <<"cleanup-queue">>). -define(TIMEOUT, 5000). - --define(XDEATH_TABLE, - [{<<"reason">>, longstr, <<"blah">>}, - {<<"queue">>, longstr, <<"foo.bar.baz">>}, - {<<"exchange">>, longstr, <<"my-exchange">>}, - {<<"routing-keys">>, array, []}]). - --define(ROUTE_TABLE, [{<<"redelivered">>, bool, <<"true">>}]). - all_tests() -> ok = setup_cluster(), ok = supervisor2_tests:test_all(), @@ -178,72 +169,69 @@ test_rabbit_basic_header_handling() -> passed = invalid_same_header_entry_accumulation_test(), passed. +-define(XDEATH_TABLE, + [{<<"reason">>, longstr, <<"blah">>}, + {<<"queue">>, longstr, <<"foo.bar.baz">>}, + {<<"exchange">>, longstr, <<"my-exchange">>}, + {<<"routing-keys">>, array, []}]). + +-define(ROUTE_TABLE, [{<<"redelivered">>, bool, <<"true">>}]). + +-define(BAD_HEADER(K), {<>, longstr, <<"bad ", K>>}). +-define(BAD_HEADER(K, Suf), {<>, longstr, <<"bad ", K, Suf>>}). +-define(FOUND_BAD_HEADER(K), {<>, array, [{longstr, <<"bad ", K>>}]}). + write_table_with_invalid_existing_type_test() -> - check_invalid(<<"x-death">>, - {longstr, <<"this should be a table!!!">>}, - ?XDEATH_TABLE), + prepend_check(<<"header1">>, ?XDEATH_TABLE, [?BAD_HEADER("header1")]), passed. invalid_existing_headers_test() -> - BadHeaders = [{<<"x-received-from">>, - longstr, <<"this should be a table!!!">>}], - Headers = rabbit_basic:prepend_table_header( - <<"x-received-from">>, ?ROUTE_TABLE, BadHeaders), + Headers = + prepend_check(<<"header2">>, ?ROUTE_TABLE, [?BAD_HEADER("header2")]), {array, [{table, ?ROUTE_TABLE}]} = - rabbit_misc:table_lookup(Headers, <<"x-received-from">>), + rabbit_misc:table_lookup(Headers, <<"header2">>), passed. disparate_invalid_header_entries_accumulate_separately_test() -> - BadHeaders = [{<<"x-received-from">>, - longstr, <<"this should be a table!!!">>}], - Headers = rabbit_basic:prepend_table_header( - <<"x-received-from">>, ?ROUTE_TABLE, BadHeaders), - BadHeaders2 = rabbit_basic:prepend_table_header( - <<"x-death">>, ?XDEATH_TABLE, - [{<<"x-death">>, - longstr, <<"and so should this!!!">>}|Headers]), - - {table, [{<<"x-death">>, array, [{longstr, <<"and so should this!!!">>}]}, - {<<"x-received-from">>, - array, [{longstr, <<"this should be a table!!!">>}]}]} = - rabbit_misc:table_lookup(BadHeaders2, ?INVALID_HEADERS_KEY), + BadHeaders = [?BAD_HEADER("header2")], + Headers = prepend_check(<<"header2">>, ?ROUTE_TABLE, BadHeaders), + Headers2 = prepend_check(<<"header1">>, ?XDEATH_TABLE, + [?BAD_HEADER("header1") | Headers]), + {table, [?FOUND_BAD_HEADER("header1"), + ?FOUND_BAD_HEADER("header2")]} = + rabbit_misc:table_lookup(Headers2, ?INVALID_HEADERS_KEY), passed. corrupt_or_invalid_headers_are_overwritten_test() -> - Headers0 = [{<<"x-death">>, longstr, <<"this should be a table">>}, - {?INVALID_HEADERS_KEY, longstr, <<"what the!?">>}], - Headers1 = rabbit_basic:prepend_table_header( - <<"x-death">>, ?XDEATH_TABLE, Headers0), - {table,[{<<"x-death">>, array, - [{longstr, <<"this should be a table">>}]}, - {?INVALID_HEADERS_KEY, array, - [{longstr, <<"what the!?">>}]}]} = + Headers0 = [?BAD_HEADER("header1"), + ?BAD_HEADER("x-invalid-headers")], + Headers1 = prepend_check(<<"header1">>, ?XDEATH_TABLE, Headers0), + {table,[?FOUND_BAD_HEADER("header1"), + ?FOUND_BAD_HEADER("x-invalid-headers")]} = rabbit_misc:table_lookup(Headers1, ?INVALID_HEADERS_KEY), passed. invalid_same_header_entry_accumulation_test() -> - Key = <<"x-received-from">>, - BadHeader1 = {longstr, <<"this should be a table!!!">>}, - Headers = check_invalid(Key, BadHeader1, ?ROUTE_TABLE), - Headers2 = rabbit_basic:prepend_table_header( - Key, - ?ROUTE_TABLE, - [{Key, longstr, - <<"this should also be a table!!!">>}|Headers]), - Invalid = rabbit_misc:table_lookup(Headers2, ?INVALID_HEADERS_KEY), - {table, InvalidHeaders} = Invalid, - {array, [{longstr, <<"this should also be a table!!!">>},BadHeader1]} = - rabbit_misc:table_lookup(InvalidHeaders, Key), + BadHeader1 = ?BAD_HEADER("header1", "a"), + Headers = prepend_check(<<"header1">>, ?ROUTE_TABLE, [BadHeader1]), + Headers2 = prepend_check(<<"header1">>, ?ROUTE_TABLE, + [?BAD_HEADER("header1", "b") | Headers]), + {table, InvalidHeaders} = + rabbit_misc:table_lookup(Headers2, ?INVALID_HEADERS_KEY), + {array, [{longstr,<<"bad header1b">>}, + {longstr,<<"bad header1a">>}]} = + rabbit_misc:table_lookup(InvalidHeaders, <<"header1">>), passed. -check_invalid(HeaderKey, {TBin, VBin}=InvalidEntry, HeaderTable) -> - Headers = rabbit_basic:prepend_table_header(HeaderKey, HeaderTable, - [{HeaderKey, TBin, VBin}]), - InvalidHeaders = rabbit_misc:table_lookup(Headers, ?INVALID_HEADERS_KEY), - {table, Invalid} = InvalidHeaders, - InvalidArrayForKey = rabbit_misc:table_lookup(Invalid, HeaderKey), - {array, [InvalidEntry]} = InvalidArrayForKey, - Headers. +prepend_check(HeaderKey, HeaderTable, Headers) -> + Headers1 = rabbit_basic:prepend_table_header( + HeaderKey, HeaderTable, Headers), + {table, Invalid} = + rabbit_misc:table_lookup(Headers1, ?INVALID_HEADERS_KEY), + {Type, Value} = rabbit_misc:table_lookup(Headers, HeaderKey), + {array, [{Type, Value} | _]} = + rabbit_misc:table_lookup(Invalid, HeaderKey), + Headers1. test_priority_queue() -> -- cgit v1.2.1 From d075a60988ba89a5302ab9724caeae4ac80d14f8 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 29 Oct 2012 17:37:56 +0000 Subject: These should be binary. --- src/rabbit_reader.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index e2cc4aeb..f94f5eca 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -896,9 +896,9 @@ i(pid, #v1{}) -> i(name, #v1{sock = Sock}) -> list_to_binary(name(Sock)); i(host, #v1{host = Host}) -> - Host; + list_to_binary(Host); i(peer_host, #v1{peer_host = PeerHost}) -> - PeerHost; + list_to_binary(PeerHost); i(address, #v1{sock = Sock}) -> socket_info(fun rabbit_net:sockname/1, fun ({A, _}) -> A end, Sock); i(port, #v1{sock = Sock}) -> -- cgit v1.2.1 From cb22d4298442b02f8d7e762d9f35c2e18655c7c7 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 29 Oct 2012 18:28:22 +0000 Subject: cosmetic --- src/rabbit_reader.erl | 103 ++++++++++++++++++++++---------------------------- 1 file changed, 45 insertions(+), 58 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 9f1f9c38..2c3638c7 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -890,83 +890,70 @@ auth_phase(Response, infos(Items, State) -> [{Item, i(Item, State)} || Item <- Items]. -i(pid, #v1{}) -> - self(); -i(name, #v1{sock = Sock}) -> - list_to_binary(name(Sock)); -i(address, #v1{sock = Sock}) -> - socket_info(fun rabbit_net:sockname/1, fun ({A, _}) -> A end, Sock); -i(port, #v1{sock = Sock}) -> - socket_info(fun rabbit_net:sockname/1, fun ({_, P}) -> P end, Sock); -i(peer_address, #v1{sock = Sock}) -> - socket_info(fun rabbit_net:peername/1, fun ({A, _}) -> A end, Sock); -i(peer_port, #v1{sock = Sock}) -> - socket_info(fun rabbit_net:peername/1, fun ({_, P}) -> P end, Sock); -i(ssl, #v1{sock = Sock}) -> - rabbit_net:is_ssl(Sock); -i(ssl_protocol, #v1{sock = Sock}) -> - ssl_info(fun ({P, _}) -> P end, Sock); -i(ssl_key_exchange, #v1{sock = Sock}) -> - ssl_info(fun ({_, {K, _, _}}) -> K end, Sock); -i(ssl_cipher, #v1{sock = Sock}) -> - ssl_info(fun ({_, {_, C, _}}) -> C end, Sock); -i(ssl_hash, #v1{sock = Sock}) -> - ssl_info(fun ({_, {_, _, H}}) -> H end, Sock); -i(peer_cert_issuer, #v1{sock = Sock}) -> - cert_info(fun rabbit_ssl:peer_cert_issuer/1, Sock); -i(peer_cert_subject, #v1{sock = Sock}) -> - cert_info(fun rabbit_ssl:peer_cert_subject/1, Sock); -i(peer_cert_validity, #v1{sock = Sock}) -> - cert_info(fun rabbit_ssl:peer_cert_validity/1, Sock); -i(SockStat, #v1{sock = Sock}) when SockStat =:= recv_oct; - SockStat =:= recv_cnt; - SockStat =:= send_oct; - SockStat =:= send_cnt; - SockStat =:= send_pend -> - socket_info(fun (S) -> rabbit_net:getstat(S, [SockStat]) end, - fun ([{_, I}]) -> I end, Sock); -i(state, #v1{connection_state = S}) -> - S; -i(last_blocked_by, #v1{last_blocked_by = By}) -> - By; -i(last_blocked_age, #v1{last_blocked_at = never}) -> +i(pid, #v1{}) -> self(); +i(name, #v1{sock = Sock}) -> list_to_binary(name(Sock)); +i(address, S) -> socket_info(fun rabbit_net:sockname/1, + fun ({A, _}) -> A end, S); +i(port, S) -> socket_info(fun rabbit_net:sockname/1, + fun ({_, P}) -> P end, S); +i(peer_address, S) -> socket_info(fun rabbit_net:peername/1, + fun ({A, _}) -> A end, S); +i(peer_port, S) -> socket_info(fun rabbit_net:peername/1, + fun ({_, P}) -> P end, S); +i(SockStat, S) when SockStat =:= recv_oct; + SockStat =:= recv_cnt; + SockStat =:= send_oct; + SockStat =:= send_cnt; + SockStat =:= send_pend -> + socket_info(fun (Sock) -> rabbit_net:getstat(Sock, [SockStat]) end, + fun ([{_, I}]) -> I end, S); +i(ssl, #v1{sock = Sock}) -> rabbit_net:is_ssl(Sock); +i(ssl_protocol, S) -> ssl_info(fun ({P, _}) -> P end, S); +i(ssl_key_exchange, S) -> ssl_info(fun ({_, {K, _, _}}) -> K end, S); +i(ssl_cipher, S) -> ssl_info(fun ({_, {_, C, _}}) -> C end, S); +i(ssl_hash, S) -> ssl_info(fun ({_, {_, _, H}}) -> H end, S); +i(peer_cert_issuer, S) -> cert_info(fun rabbit_ssl:peer_cert_issuer/1, S); +i(peer_cert_subject, S) -> cert_info(fun rabbit_ssl:peer_cert_subject/1, S); +i(peer_cert_validity, S) -> cert_info(fun rabbit_ssl:peer_cert_validity/1, S); +i(state, #v1{connection_state = CS}) -> CS; +i(last_blocked_by, #v1{last_blocked_by = By}) -> By; +i(last_blocked_age, #v1{last_blocked_at = never}) -> infinity; -i(last_blocked_age, #v1{last_blocked_at = T}) -> +i(last_blocked_age, #v1{last_blocked_at = T}) -> timer:now_diff(erlang:now(), T) / 1000000; -i(channels, #v1{}) -> - length(all_channels()); -i(auth_mechanism, #v1{auth_mechanism = none}) -> +i(channels, #v1{}) -> length(all_channels()); +i(auth_mechanism, #v1{auth_mechanism = none}) -> none; -i(auth_mechanism, #v1{auth_mechanism = Mechanism}) -> +i(auth_mechanism, #v1{auth_mechanism = Mechanism}) -> proplists:get_value(name, Mechanism:description()); -i(protocol, #v1{connection = #connection{protocol = none}}) -> +i(protocol, #v1{connection = #connection{protocol = none}}) -> none; -i(protocol, #v1{connection = #connection{protocol = Protocol}}) -> +i(protocol, #v1{connection = #connection{protocol = Protocol}}) -> Protocol:version(); -i(user, #v1{connection = #connection{user = none}}) -> +i(user, #v1{connection = #connection{user = none}}) -> ''; -i(user, #v1{connection = #connection{user = #user{ - username = Username}}}) -> +i(user, #v1{connection = #connection{user = #user{ + username = Username}}}) -> Username; -i(vhost, #v1{connection = #connection{vhost = VHost}}) -> +i(vhost, #v1{connection = #connection{vhost = VHost}}) -> VHost; -i(timeout, #v1{connection = #connection{timeout_sec = Timeout}}) -> +i(timeout, #v1{connection = #connection{timeout_sec = Timeout}}) -> Timeout; -i(frame_max, #v1{connection = #connection{frame_max = FrameMax}}) -> +i(frame_max, #v1{connection = #connection{frame_max = FrameMax}}) -> FrameMax; -i(client_properties, #v1{connection = #connection{client_properties = - ClientProperties}}) -> +i(client_properties, #v1{connection = #connection{client_properties = + ClientProperties}}) -> ClientProperties; i(Item, #v1{}) -> throw({bad_argument, Item}). -socket_info(Get, Select, Sock) -> +socket_info(Get, Select, #v1{sock = Sock}) -> case Get(Sock) of {ok, T} -> Select(T); {error, _} -> '' end. -ssl_info(F, Sock) -> +ssl_info(F, #v1{sock = Sock}) -> %% The first ok form is R14 %% The second is R13 - the extra term is exportability (by inspection, %% the docs are wrong) @@ -977,7 +964,7 @@ ssl_info(F, Sock) -> {ok, {P, {K, C, H, _}}} -> F({P, {K, C, H}}) end. -cert_info(F, Sock) -> +cert_info(F, #v1{sock = Sock}) -> case rabbit_net:peercert(Sock) of nossl -> ''; {error, no_peercert} -> ''; -- cgit v1.2.1 From 39b592cf5326e9dcaf9432431fee55643d7aa29e Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 29 Oct 2012 19:00:06 +0000 Subject: keep track of connection name in reader state That way we won't trip over socket errors when obtaining the name later on. Should also be more efficient. --- src/rabbit_reader.erl | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 2c3638c7..2c1eeb91 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -35,7 +35,7 @@ %%-------------------------------------------------------------------------- --record(v1, {parent, sock, connection, callback, recv_len, pending_recv, +-record(v1, {parent, sock, name, connection, callback, recv_len, pending_recv, connection_state, queue_collector, heartbeater, stats_timer, channel_sup_sup_pid, start_heartbeat_fun, buf, buf_len, auth_mechanism, auth_state, conserve_resources, @@ -195,13 +195,14 @@ name(Sock) -> start_connection(Parent, ChannelSupSupPid, Collector, StartHeartbeatFun, Deb, Sock, SockTransform) -> process_flag(trap_exit, true), - ConnStr = name(Sock), - log(info, "accepting AMQP connection ~p (~s)~n", [self(), ConnStr]), + Name = name(Sock), + log(info, "accepting AMQP connection ~p (~s)~n", [self(), Name]), ClientSock = socket_op(Sock, SockTransform), erlang:send_after(?HANDSHAKE_TIMEOUT * 1000, self(), handshake_timeout), State = #v1{parent = Parent, sock = ClientSock, + name = Name, connection = #connection{ protocol = none, user = none, @@ -230,13 +231,13 @@ start_connection(Parent, ChannelSupSupPid, Collector, StartHeartbeatFun, Deb, recvloop(Deb, switch_callback(rabbit_event:init_stats_timer( State, #v1.stats_timer), handshake, 8)), - log(info, "closing AMQP connection ~p (~s)~n", [self(), ConnStr]) + log(info, "closing AMQP connection ~p (~s)~n", [self(), Name]) catch Ex -> log(case Ex of connection_closed_abruptly -> warning; _ -> error end, "closing AMQP connection ~p (~s):~n~p~n", - [self(), ConnStr, Ex]) + [self(), Name, Ex]) after %% We don't call gen_tcp:close/1 here since it waits for %% pending output to be sent, which results in unnecessary @@ -521,7 +522,7 @@ payload_snippet(<>) -> %%-------------------------------------------------------------------------- create_channel(Channel, State) -> - #v1{sock = Sock, queue_collector = Collector, + #v1{sock = Sock, name = Name, queue_collector = Collector, channel_sup_sup_pid = ChanSupSup, connection = #connection{protocol = Protocol, frame_max = FrameMax, @@ -530,7 +531,7 @@ create_channel(Channel, State) -> capabilities = Capabilities}} = State, {ok, _ChSupPid, {ChPid, AState}} = rabbit_channel_sup_sup:start_channel( - ChanSupSup, {tcp, Sock, Channel, FrameMax, self(), name(Sock), + ChanSupSup, {tcp, Sock, Channel, FrameMax, self(), Name, Protocol, User, VHost, Capabilities, Collector}), MRef = erlang:monitor(process, ChPid), put({ch_pid, ChPid}, {Channel, MRef}), @@ -891,7 +892,7 @@ auth_phase(Response, infos(Items, State) -> [{Item, i(Item, State)} || Item <- Items]. i(pid, #v1{}) -> self(); -i(name, #v1{sock = Sock}) -> list_to_binary(name(Sock)); +i(name, #v1{name = Name}) -> list_to_binary(Name); i(address, S) -> socket_info(fun rabbit_net:sockname/1, fun ({A, _}) -> A end, S); i(port, S) -> socket_info(fun rabbit_net:sockname/1, -- cgit v1.2.1 From d8394776219dde818155f7359297ff3896d27b92 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 30 Oct 2012 08:12:56 +0000 Subject: fix R12B-3 build breakage looks like one cannot define two macros with the same name but different arity --- src/rabbit_tests.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 51ca6043..eb97f1d6 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -178,7 +178,7 @@ test_rabbit_basic_header_handling() -> -define(ROUTE_TABLE, [{<<"redelivered">>, bool, <<"true">>}]). -define(BAD_HEADER(K), {<>, longstr, <<"bad ", K>>}). --define(BAD_HEADER(K, Suf), {<>, longstr, <<"bad ", K, Suf>>}). +-define(BAD_HEADER2(K, Suf), {<>, longstr, <<"bad ", K, Suf>>}). -define(FOUND_BAD_HEADER(K), {<>, array, [{longstr, <<"bad ", K>>}]}). write_table_with_invalid_existing_type_test() -> @@ -212,10 +212,10 @@ corrupt_or_invalid_headers_are_overwritten_test() -> passed. invalid_same_header_entry_accumulation_test() -> - BadHeader1 = ?BAD_HEADER("header1", "a"), + BadHeader1 = ?BAD_HEADER2("header1", "a"), Headers = prepend_check(<<"header1">>, ?ROUTE_TABLE, [BadHeader1]), Headers2 = prepend_check(<<"header1">>, ?ROUTE_TABLE, - [?BAD_HEADER("header1", "b") | Headers]), + [?BAD_HEADER2("header1", "b") | Headers]), {table, InvalidHeaders} = rabbit_misc:table_lookup(Headers2, ?INVALID_HEADERS_KEY), {array, [{longstr,<<"bad header1b">>}, -- cgit v1.2.1 From a909713fc02ed762435de9cdc465ea9919b9928c Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 30 Oct 2012 11:56:31 +0000 Subject: Store name, host and peerhost as binary in the first place. --- src/rabbit_net.erl | 7 ++++--- src/rabbit_reader.erl | 8 ++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/rabbit_net.erl b/src/rabbit_net.erl index 24843aa0..c6fc9742 100644 --- a/src/rabbit_net.erl +++ b/src/rabbit_net.erl @@ -73,8 +73,8 @@ -spec(connection_string/2 :: (socket(), 'inbound' | 'outbound') -> ok_val_or_error(string())). -spec(rdns/2 :: - (socket(), 'inbound' | 'outbound') -> {string() | 'unknown', - string() | 'unknown'}). + (socket(), 'inbound' | 'outbound') -> {binary() | 'unknown', + binary() | 'unknown'}). -endif. @@ -223,7 +223,8 @@ rdns_lookup(Sock, Fun) -> {ok, Lookup} = application:get_env(rabbit, reverse_dns_lookups), case Lookup of true -> case Fun(Sock) of - {ok, {IP, _Port}} -> rabbit_networking:tcp_host(IP); + {ok, {IP, _Port}} -> list_to_binary( + rabbit_networking:tcp_host(IP)); _ -> unknown end; _ -> unknown diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 50ad2bf8..82781ad0 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -201,7 +201,7 @@ start_connection(Parent, ChannelSupSupPid, Collector, StartHeartbeatFun, Deb, {Host, PeerHost} = rabbit_net:rdns(Sock, inbound), State = #v1{parent = Parent, sock = ClientSock, - name = Name, + name = list_to_binary(Name), connection = #connection{ protocol = none, user = none, @@ -893,9 +893,9 @@ auth_phase(Response, infos(Items, State) -> [{Item, i(Item, State)} || Item <- Items]. i(pid, #v1{}) -> self(); -i(name, #v1{name = Name}) -> list_to_binary(Name); -i(host, #v1{host = Host}) -> list_to_binary(Host); -i(peer_host, #v1{peer_host = PeerHost}) -> list_to_binary(PeerHost); +i(name, #v1{name = Name}) -> Name; +i(host, #v1{host = Host}) -> Host; +i(peer_host, #v1{peer_host = PeerHost}) -> PeerHost; i(address, S) -> socket_info(fun rabbit_net:sockname/1, fun ({A, _}) -> A end, S); i(port, S) -> socket_info(fun rabbit_net:sockname/1, -- cgit v1.2.1 From 197d96e04e0fc278cfbebd7567500571328c364f Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Tue, 30 Oct 2012 16:33:48 +0000 Subject: verify the basic.expiration >= 0 --- src/rabbit_misc.erl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 7d3fb0ad..4dff62f0 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -982,5 +982,7 @@ term_to_json(V) when is_binary(V) orelse is_number(V) orelse V =:= null orelse check_expiry_size(N) when N > ?MAX_EXPIRY_TIMER -> {error, {value_too_big, N}}; +check_expiry_size(N) when N < 0 -> + {error, {negative_value, N}}; check_expiry_size(N) -> ok. -- cgit v1.2.1 From 4fc4d680b660fe8155c10ce44e605f0cf15b57f8 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Wed, 31 Oct 2012 11:42:48 +0000 Subject: messages can always 'potentially' expire --- src/rabbit_amqqueue_process.erl | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index c3d717fb..2cf3e9f1 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -719,8 +719,6 @@ calculate_msg_expiry(#basic_message{content = Content}, TTL) -> T -> now_micros() + T * 1000 end. -drop_expired_messages(State = #q{ttl = undefined}) -> - State; drop_expired_messages(State = #q{backing_queue_state = BQS, backing_queue = BQ }) -> Now = now_micros(), @@ -743,8 +741,6 @@ drop_expired_messages(State = #q{backing_queue_state = BQS, ensure_ttl_timer(undefined, State) -> State; -ensure_ttl_timer(_Expiry, State = #q{ttl = undefined}) -> - State; ensure_ttl_timer(Expiry, State = #q{ttl_timer_ref = undefined}) -> After = (case Expiry - now_micros() of V when V > 0 -> V + 999; %% always fire later -- cgit v1.2.1 From 684c55e36acc489030c1cb06489c69237f4363c4 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 31 Oct 2012 12:39:54 +0000 Subject: HA optimisation: don't broadcast 'drop' when nothing has been dropped --- src/rabbit_mirror_queue_master.erl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index 6a7a28f2..df733546 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -229,7 +229,10 @@ dropwhile(Pred, AckRequired, {Next, Msgs, BQS1} = BQ:dropwhile(Pred, AckRequired, BQS), Len1 = BQ:len(BQS1), Dropped = Len - Len1, - ok = gm:broadcast(GM, {drop, Len1, Dropped, AckRequired}), + case Dropped of + 0 -> ok; + _ -> ok = gm:broadcast(GM, {drop, Len1, Dropped, AckRequired}) + end, SetDelivered1 = lists:max([0, SetDelivered - Dropped]), {Next, Msgs, State #state { backing_queue_state = BQS1, set_delivered = SetDelivered1 } }. -- cgit v1.2.1 From 58ba1e08a08debf79f1413e73def76771500b709 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 31 Oct 2012 13:24:30 +0000 Subject: More clarity. --- docs/rabbitmqctl.1.xml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/rabbitmqctl.1.xml b/docs/rabbitmqctl.1.xml index ce3ae7d6..69d137b3 100644 --- a/docs/rabbitmqctl.1.xml +++ b/docs/rabbitmqctl.1.xml @@ -1291,7 +1291,8 @@ host - Server DNS host. + Server hostname obtained via reverse + DNS, or 'unknown'. peer_address @@ -1303,7 +1304,8 @@ peer_host - Peer DNS host. + Peer hostname obtained via reverse + DNS, or 'unknown'. ssl -- cgit v1.2.1 From a8741954d4090e5b8c989466bd332cf8868657d5 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Wed, 31 Oct 2012 16:17:52 +0000 Subject: if per-queue ttl is enabled, don't skip dropping expired messages --- src/rabbit_amqqueue_process.erl | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 2cf3e9f1..b52d2213 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -719,6 +719,9 @@ calculate_msg_expiry(#basic_message{content = Content}, TTL) -> T -> now_micros() + T * 1000 end. +drop_expired_messages(State = #q{ttl = undefined, + ttl_timer_ref = undefined}) -> + State; drop_expired_messages(State = #q{backing_queue_state = BQS, backing_queue = BQ }) -> Now = now_micros(), @@ -737,7 +740,8 @@ drop_expired_messages(State = #q{backing_queue_state = BQS, ensure_ttl_timer(case Props of undefined -> undefined; #message_properties{expiry = Exp} -> Exp - end, State#q{backing_queue_state = BQS1}). + end, State#q{backing_queue_state = BQS1, + ttl_timer_ref = undefined}). ensure_ttl_timer(undefined, State) -> State; @@ -1321,7 +1325,7 @@ handle_info(maybe_expire, State) -> end; handle_info(drop_expired, State) -> - noreply(drop_expired_messages(State#q{ttl_timer_ref = undefined})); + noreply(drop_expired_messages(State)); handle_info(emit_stats, State) -> %% Do not invoke noreply as it would see no timer and create a new one. -- cgit v1.2.1 From 74e70c287d0e5cef5c8e12d3c39b57e5f446ff4f Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Wed, 31 Oct 2012 16:45:43 +0000 Subject: revert 6982ce6874b3 => this broke ttl handling for rejects and ttl=0/immediate equivalence --- src/rabbit_amqqueue_process.erl | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index b52d2213..2cf3e9f1 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -719,9 +719,6 @@ calculate_msg_expiry(#basic_message{content = Content}, TTL) -> T -> now_micros() + T * 1000 end. -drop_expired_messages(State = #q{ttl = undefined, - ttl_timer_ref = undefined}) -> - State; drop_expired_messages(State = #q{backing_queue_state = BQS, backing_queue = BQ }) -> Now = now_micros(), @@ -740,8 +737,7 @@ drop_expired_messages(State = #q{backing_queue_state = BQS, ensure_ttl_timer(case Props of undefined -> undefined; #message_properties{expiry = Exp} -> Exp - end, State#q{backing_queue_state = BQS1, - ttl_timer_ref = undefined}). + end, State#q{backing_queue_state = BQS1}). ensure_ttl_timer(undefined, State) -> State; @@ -1325,7 +1321,7 @@ handle_info(maybe_expire, State) -> end; handle_info(drop_expired, State) -> - noreply(drop_expired_messages(State)); + noreply(drop_expired_messages(State#q{ttl_timer_ref = undefined})); handle_info(emit_stats, State) -> %% Do not invoke noreply as it would see no timer and create a new one. -- cgit v1.2.1 From 5effb392824fd27d2e3fea03004875283d36fbca Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 31 Oct 2012 23:37:06 +0000 Subject: refactor: s/lookup_absent/not_found_or_absent and s/not_found_or_absent/not_found_or_absent_dirty and make the signatures match --- src/rabbit_amqqueue.erl | 32 ++++++++++++++------------------ src/rabbit_binding.erl | 6 +++--- 2 files changed, 17 insertions(+), 21 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 2876550e..621da633 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -18,7 +18,7 @@ -export([start/0, stop/0, declare/5, delete_immediately/1, delete/3, purge/1]). -export([pseudo_queue/2]). --export([lookup/1, lookup_absent/1, with/2, with/3, with_or_die/2, +-export([lookup/1, not_found_or_absent/1, with/2, with/3, with_or_die/2, assert_equivalence/5, check_exclusive_access/2, with_exclusive_access_or_die/3, stat/1, deliver/2, deliver_flow/2, requeue/3, ack/3, reject/4]). @@ -83,9 +83,7 @@ (name()) -> rabbit_types:ok(rabbit_types:amqqueue()) | rabbit_types:error('not_found'); ([name()]) -> [rabbit_types:amqqueue()]). --spec(lookup_absent/1 :: - (name()) -> rabbit_types:ok(rabbit_types:amqqueue()) | - rabbit_types:error('not_found')). +-spec(not_found_or_absent/1 :: (name()) -> not_found_or_absent()). -spec(with/2 :: (name(), qfun(A)) -> A | rabbit_types:error(not_found_or_absent())). -spec(with/3 :: (name(), qfun(A), fun((not_found_or_absent()) -> B)) -> A | B). @@ -241,14 +239,12 @@ internal_declare(Q = #amqqueue{name = QueueName}, false) -> fun () -> case mnesia:wread({rabbit_queue, QueueName}) of [] -> - case lookup_absent(QueueName) of - {error, not_found} -> - Q1 = rabbit_policy:set(Q), - ok = store_queue(Q1), - B = add_default_binding(Q1), - fun () -> B(), Q1 end; - {ok, Q1} -> - rabbit_misc:const({absent, Q1}) + case not_found_or_absent(QueueName) of + not_found -> Q1 = rabbit_policy:set(Q), + ok = store_queue(Q1), + B = add_default_binding(Q1), + fun () -> B(), Q1 end; + {absent, _Q} = R -> rabbit_misc:const(R) end; [ExistingQ = #amqqueue{pid = QPid}] -> case rabbit_misc:is_process_alive(QPid) of @@ -302,15 +298,15 @@ lookup(Names) when is_list(Names) -> lookup(Name) -> rabbit_misc:dirty_read({rabbit_queue, Name}). -lookup_absent(Name) -> +not_found_or_absent(Name) -> %% NB: we assume that the caller has already performed a lookup on %% rabbit_queue and not found anything case mnesia:read({rabbit_durable_queue, Name}) of - [] -> {error, not_found}; - [Q] -> {ok, Q} %% Q exists on stopped node + [] -> not_found; + [Q] -> {absent, Q} %% Q exists on stopped node end. -not_found_or_absent(Name) -> +not_found_or_absent_dirty(Name) -> %% We should read from both tables inside a tx, to get a %% consistent view. But the chances of an inconsistency are small, %% and only affect the error kind. @@ -328,13 +324,13 @@ with(Name, F, E) -> rabbit_misc:with_exit_handler( fun () -> case rabbit_misc:is_process_alive(QPid) of - true -> E(not_found_or_absent(Name)); + true -> E(not_found_or_absent_dirty(Name)); false -> timer:sleep(25), with(Name, F, E) end end, fun () -> F(Q) end); {error, not_found} -> - E(not_found_or_absent(Name)) + E(not_found_or_absent_dirty(Name)) end. with(Name, F) -> with(Name, F, fun (E) -> {error, E} end). diff --git a/src/rabbit_binding.erl b/src/rabbit_binding.erl index 1375facb..2d486651 100644 --- a/src/rabbit_binding.erl +++ b/src/rabbit_binding.erl @@ -353,9 +353,9 @@ table_for_resource(#resource{kind = queue}) -> rabbit_queue. not_found_or_absent(#resource{kind = exchange} = Name) -> {not_found, Name}; not_found_or_absent(#resource{kind = queue} = Name) -> - case rabbit_amqqueue:lookup_absent(Name) of - {error, not_found} -> {not_found, Name}; - {ok, Q} -> {absent, Q} + case rabbit_amqqueue:not_found_or_absent(Name) of + not_found -> {not_found, Name}; + {absent, _Q} = R -> R end. contains(Table, MatchHead) -> -- cgit v1.2.1 From fdf36355954fee83b9f6ff6d71adfa008f4d4b44 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 1 Nov 2012 12:03:59 +0000 Subject: Unify address and host. Stick ports in the state, just because we're grabbing them anyway. --- docs/rabbitmqctl.1.xml | 16 +++++---------- src/rabbit_control_main.erl | 2 +- src/rabbit_net.erl | 49 ++++++++++++++++++++++----------------------- src/rabbit_reader.erl | 24 +++++++++++----------- 4 files changed, 42 insertions(+), 49 deletions(-) diff --git a/docs/rabbitmqctl.1.xml b/docs/rabbitmqctl.1.xml index 69d137b3..34947b66 100644 --- a/docs/rabbitmqctl.1.xml +++ b/docs/rabbitmqctl.1.xml @@ -1281,10 +1281,6 @@ name Readable name for the connection. - - address - Server IP address. - port Server port. @@ -1292,11 +1288,8 @@ host Server hostname obtained via reverse - DNS, or 'unknown'. - - - peer_address - Peer address. + DNS, or its IP address if reverse DNS failed or was + not enabled. peer_port @@ -1305,7 +1298,8 @@ peer_host Peer hostname obtained via reverse - DNS, or 'unknown'. + DNS, or its IP address if reverse DNS failed or was + not enabled. ssl @@ -1424,7 +1418,7 @@ If no connectioninfoitems are - specified then user, peer address, peer port, time since + specified then user, peer host, peer port, time since flow control and memory block state are displayed. diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index 25f7d758..bd18fa5f 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -386,7 +386,7 @@ action(list_bindings, Node, Args, Opts, Inform) -> action(list_connections, Node, Args, _Opts, Inform) -> Inform("Listing connections", []), - ArgAtoms = default_if_empty(Args, [user, peer_address, peer_port, state]), + ArgAtoms = default_if_empty(Args, [user, peer_host, peer_port, state]), display_info_list(rpc_call(Node, rabbit_networking, connection_info_all, [ArgAtoms]), ArgAtoms); diff --git a/src/rabbit_net.erl b/src/rabbit_net.erl index c6fc9742..562fc197 100644 --- a/src/rabbit_net.erl +++ b/src/rabbit_net.erl @@ -20,7 +20,7 @@ -export([is_ssl/1, ssl_info/1, controlling_process/2, getstat/2, recv/1, async_recv/3, port_command/2, getopts/2, setopts/2, send/2, close/1, fast_close/1, sockname/1, peername/1, peercert/1, - tune_buffer_size/1, connection_string/2, rdns/2]). + tune_buffer_size/1, connection_string/2, socket_ends/2]). %%--------------------------------------------------------------------------- @@ -36,7 +36,7 @@ -type(socket() :: port() | #ssl_socket{}). -type(opts() :: [{atom(), any()} | {raw, non_neg_integer(), non_neg_integer(), binary()}]). - +-type(host_or_ip() :: binary() | inet:ip_address()). -spec(is_ssl/1 :: (socket()) -> boolean()). -spec(ssl_info/1 :: (socket()) -> 'nossl' | ok_val_or_error( @@ -72,9 +72,10 @@ -spec(tune_buffer_size/1 :: (socket()) -> ok_or_any_error()). -spec(connection_string/2 :: (socket(), 'inbound' | 'outbound') -> ok_val_or_error(string())). --spec(rdns/2 :: - (socket(), 'inbound' | 'outbound') -> {binary() | 'unknown', - binary() | 'unknown'}). +-spec(socket_ends/2 :: + (socket(), 'inbound' | 'outbound') + -> ok_val_or_error({host_or_ip(), rabbit_networking:ip_port(), + host_or_ip(), rabbit_networking:ip_port()})). -endif. @@ -196,38 +197,36 @@ tune_buffer_size(Sock) -> end. connection_string(Sock, Direction) -> + case socket_ends(Sock, Direction) of + {ok, {FromAddress, FromPort, ToAddress, ToPort}} -> + {ok, rabbit_misc:format( + "~s:~p -> ~s:~p", + [maybe_ntoab(FromAddress), FromPort, + maybe_ntoab(ToAddress), ToPort])}; + Error -> + Error + end. + +socket_ends(Sock, Direction) -> {From, To} = sock_funs(Direction), case {From(Sock), To(Sock)} of {{ok, {FromAddress, FromPort}}, {ok, {ToAddress, ToPort}}} -> - {ok, rabbit_misc:format( - "~s:~p -> ~s:~p", - [maybe_rdns(FromAddress, Sock, From), FromPort, - maybe_rdns(ToAddress, Sock, To), ToPort])}; + {ok, {rdns(FromAddress), FromPort, + rdns(ToAddress), ToPort}}; {{error, _Reason} = Error, _} -> Error; {_, {error, _Reason} = Error} -> Error end. -rdns(Sock, Direction) -> - {From, To} = sock_funs(Direction), - {rdns_lookup(Sock, From), rdns_lookup(Sock, To)}. - -maybe_rdns(Addr, Sock, Fun) -> - case rdns_lookup(Sock, Fun) of - unknown -> rabbit_misc:ntoab(Addr); - Host -> Host - end. +maybe_ntoab(Addr) when is_tuple(Addr) -> rabbit_misc:ntoab(Addr); +maybe_ntoab(Host) -> Host. -rdns_lookup(Sock, Fun) -> +rdns(Addr) -> {ok, Lookup} = application:get_env(rabbit, reverse_dns_lookups), case Lookup of - true -> case Fun(Sock) of - {ok, {IP, _Port}} -> list_to_binary( - rabbit_networking:tcp_host(IP)); - _ -> unknown - end; - _ -> unknown + true -> list_to_binary(rabbit_networking:tcp_host(Addr)); + _ -> Addr end. sock_funs(inbound) -> {fun peername/1, fun sockname/1}; diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 82781ad0..f160b17a 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -39,14 +39,15 @@ connection_state, queue_collector, heartbeater, stats_timer, channel_sup_sup_pid, start_heartbeat_fun, buf, buf_len, auth_mechanism, auth_state, conserve_resources, - last_blocked_by, last_blocked_at, host, peer_host}). + last_blocked_by, last_blocked_at, host, peer_host, + port, peer_port}). -define(STATISTICS_KEYS, [pid, recv_oct, recv_cnt, send_oct, send_cnt, send_pend, state, last_blocked_by, last_blocked_age, channels]). -define(CREATION_EVENT_KEYS, - [pid, name, address, port, peer_address, peer_port, host, + [pid, name, port, peer_port, host, peer_host, ssl, peer_cert_subject, peer_cert_issuer, peer_cert_validity, auth_mechanism, ssl_protocol, ssl_key_exchange, ssl_cipher, ssl_hash, protocol, user, vhost, @@ -191,6 +192,9 @@ socket_op(Sock, Fun) -> name(Sock) -> socket_op(Sock, fun (S) -> rabbit_net:connection_string(S, inbound) end). +socket_ends(Sock) -> + socket_op(Sock, fun (S) -> rabbit_net:socket_ends(S, inbound) end). + start_connection(Parent, ChannelSupSupPid, Collector, StartHeartbeatFun, Deb, Sock, SockTransform) -> process_flag(trap_exit, true), @@ -198,7 +202,7 @@ start_connection(Parent, ChannelSupSupPid, Collector, StartHeartbeatFun, Deb, log(info, "accepting AMQP connection ~p (~s)~n", [self(), Name]), ClientSock = socket_op(Sock, SockTransform), erlang:send_after(?HANDSHAKE_TIMEOUT * 1000, self(), handshake_timeout), - {Host, PeerHost} = rabbit_net:rdns(Sock, inbound), + {Host, Port, PeerHost, PeerPort} = socket_ends(Sock), State = #v1{parent = Parent, sock = ClientSock, name = list_to_binary(Name), @@ -226,7 +230,9 @@ start_connection(Parent, ChannelSupSupPid, Collector, StartHeartbeatFun, Deb, last_blocked_by = none, last_blocked_at = never, host = Host, - peer_host = PeerHost}, + peer_host = PeerHost, + port = Port, + peer_port = PeerPort}, try ok = inet_op(fun () -> rabbit_net:tune_buffer_size(ClientSock) end), recvloop(Deb, switch_callback(rabbit_event:init_stats_timer( @@ -896,14 +902,8 @@ i(pid, #v1{}) -> self(); i(name, #v1{name = Name}) -> Name; i(host, #v1{host = Host}) -> Host; i(peer_host, #v1{peer_host = PeerHost}) -> PeerHost; -i(address, S) -> socket_info(fun rabbit_net:sockname/1, - fun ({A, _}) -> A end, S); -i(port, S) -> socket_info(fun rabbit_net:sockname/1, - fun ({_, P}) -> P end, S); -i(peer_address, S) -> socket_info(fun rabbit_net:peername/1, - fun ({A, _}) -> A end, S); -i(peer_port, S) -> socket_info(fun rabbit_net:peername/1, - fun ({_, P}) -> P end, S); +i(port, #v1{port = Port}) -> Port; +i(peer_port, #v1{peer_port = PeerPort}) -> PeerPort; i(SockStat, S) when SockStat =:= recv_oct; SockStat =:= recv_cnt; SockStat =:= send_oct; -- cgit v1.2.1 From b0d900e03df47b496e0a8de206f779fd40d2262c Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 1 Nov 2012 12:10:36 +0000 Subject: feeding of vertical alignment obsession --- src/rabbit_reader.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index f160b17a..67bfa42a 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -899,10 +899,10 @@ auth_phase(Response, infos(Items, State) -> [{Item, i(Item, State)} || Item <- Items]. i(pid, #v1{}) -> self(); -i(name, #v1{name = Name}) -> Name; -i(host, #v1{host = Host}) -> Host; +i(name, #v1{name = Name}) -> Name; +i(host, #v1{host = Host}) -> Host; i(peer_host, #v1{peer_host = PeerHost}) -> PeerHost; -i(port, #v1{port = Port}) -> Port; +i(port, #v1{port = Port}) -> Port; i(peer_port, #v1{peer_port = PeerPort}) -> PeerPort; i(SockStat, S) when SockStat =:= recv_oct; SockStat =:= recv_cnt; -- cgit v1.2.1 From 625d96198f16b1586b7f226ed614aa5c2688d39a Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 1 Nov 2012 14:32:03 +0000 Subject: I know, why don't we get those the right way round? *headdesk* --- src/rabbit_reader.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 67bfa42a..39502169 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -202,7 +202,7 @@ start_connection(Parent, ChannelSupSupPid, Collector, StartHeartbeatFun, Deb, log(info, "accepting AMQP connection ~p (~s)~n", [self(), Name]), ClientSock = socket_op(Sock, SockTransform), erlang:send_after(?HANDSHAKE_TIMEOUT * 1000, self(), handshake_timeout), - {Host, Port, PeerHost, PeerPort} = socket_ends(Sock), + {PeerHost, PeerPort, Host, Port} = socket_ends(Sock), State = #v1{parent = Parent, sock = ClientSock, name = list_to_binary(Name), -- cgit v1.2.1 From 96998d05cb0de99c46d60ea8c1388b6ec66ffa95 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 1 Nov 2012 14:45:10 +0000 Subject: ensure connection stats emission for write-only connections We set up a stats timer in the writers but rather emitting any stats directly from there we just get them to 'ping' the reader, which will then emit stats based on its own timer. The timer in the writer is created when a socket operation has been confirmed. a little bit of drive-by refactoring to make implementation easier: - move state creation into one place - move reader into state TODO: suppress all this when in the Erlang client --- src/rabbit_reader.erl | 2 ++ src/rabbit_writer.erl | 64 +++++++++++++++++++++++++++------------------------ 2 files changed, 36 insertions(+), 30 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 2c1eeb91..829e9e52 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -342,6 +342,8 @@ handle_other({'$gen_cast', force_event_refresh}, Deb, State) handle_other({'$gen_cast', force_event_refresh}, Deb, State) -> %% Ignore, we will emit a created event once we start running. mainloop(Deb, State); +handle_other(ensure_stats, Deb, State) -> + mainloop(Deb, ensure_stats_timer(State)); handle_other(emit_stats, Deb, State) -> mainloop(Deb, emit_stats(State)); handle_other({system, From, Request}, Deb, State = #v1{parent = Parent}) -> diff --git a/src/rabbit_writer.erl b/src/rabbit_writer.erl index f3a8cacf..c23f46f9 100644 --- a/src/rabbit_writer.erl +++ b/src/rabbit_writer.erl @@ -18,13 +18,17 @@ -include("rabbit.hrl"). -include("rabbit_framing.hrl"). --export([start/5, start_link/5, mainloop/2, mainloop1/2]). +-export([start/5, start_link/5]). -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]). -export([internal_send_command/4, internal_send_command/6]). --record(wstate, {sock, channel, frame_max, protocol, pending}). +%% internal +-export([mainloop/1, mainloop1/1]). + +-record(wstate, {sock, channel, frame_max, protocol, reader, + stats_timer, pending}). -define(HIBERNATE_AFTER, 5000). @@ -67,50 +71,47 @@ non_neg_integer(), rabbit_types:protocol()) -> 'ok'). --spec(mainloop/2 :: (_,_) -> 'done'). --spec(mainloop1/2 :: (_,_) -> any()). - -endif. %%--------------------------------------------------------------------------- start(Sock, Channel, FrameMax, Protocol, ReaderPid) -> - {ok, - proc_lib:spawn(?MODULE, mainloop, [ReaderPid, - #wstate{sock = Sock, - channel = Channel, - frame_max = FrameMax, - protocol = Protocol, - pending = []}])}. + State = initial_state(Sock, Channel, FrameMax, Protocol, ReaderPid), + {ok, proc_lib:spawn(?MODULE, mainloop, [State])}. start_link(Sock, Channel, FrameMax, Protocol, ReaderPid) -> - {ok, - proc_lib:spawn_link(?MODULE, mainloop, [ReaderPid, - #wstate{sock = Sock, - channel = Channel, - frame_max = FrameMax, - protocol = Protocol, - pending = []}])}. - -mainloop(ReaderPid, State) -> + State = initial_state(Sock, Channel, FrameMax, Protocol, ReaderPid), + {ok, proc_lib:spawn_link(?MODULE, mainloop, [State])}. + +initial_state(Sock, Channel, FrameMax, Protocol, ReaderPid) -> + rabbit_event:init_stats_timer(#wstate{sock = Sock, + channel = Channel, + frame_max = FrameMax, + protocol = Protocol, + reader = ReaderPid, + pending = []}, + #wstate.stats_timer). + +mainloop(State) -> try - mainloop1(ReaderPid, State) + mainloop1(State) catch - exit:Error -> ReaderPid ! {channel_exit, #wstate.channel, Error} + exit:Error -> #wstate{reader = ReaderPid, channel = Channel} = State, + ReaderPid ! {channel_exit, Channel, Error} end, done. -mainloop1(ReaderPid, State = #wstate{pending = []}) -> +mainloop1(State = #wstate{pending = []}) -> receive - Message -> ?MODULE:mainloop1(ReaderPid, handle_message(Message, State)) + Message -> ?MODULE:mainloop1(handle_message(Message, State)) after ?HIBERNATE_AFTER -> - erlang:hibernate(?MODULE, mainloop, [ReaderPid, State]) + erlang:hibernate(?MODULE, mainloop, [State]) end; -mainloop1(ReaderPid, State) -> +mainloop1(State) -> receive - Message -> ?MODULE:mainloop1(ReaderPid, handle_message(Message, State)) + Message -> ?MODULE:mainloop1(handle_message(Message, State)) after 0 -> - ?MODULE:mainloop1(ReaderPid, flush(State)) + ?MODULE:mainloop1(flush(State)) end. handle_message({send_command, MethodRecord}, State) -> @@ -139,9 +140,12 @@ handle_message({'DOWN', _MRef, process, QPid, _Reason}, State) -> rabbit_amqqueue:notify_sent_queue_down(QPid), State; handle_message({inet_reply, _, ok}, State) -> - State; + rabbit_event:ensure_stats_timer(State, #wstate.stats_timer, emit_stats); handle_message({inet_reply, _, Status}, _State) -> exit({writer, send_failed, Status}); +handle_message(emit_stats, State = #wstate{reader = ReaderPid}) -> + ReaderPid ! ensure_stats, + rabbit_event:reset_stats_timer(State, #wstate.stats_timer); handle_message(Message, _State) -> exit({writer, message_not_understood, Message}). -- cgit v1.2.1 From 795824fdd8facb660b965981d17eb155204a1458 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 1 Nov 2012 15:24:39 +0000 Subject: sort list_* results --- src/rabbit_control_main.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index 25f7d758..1b42a847 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -611,7 +611,7 @@ display_info_list(Results, InfoItemKeys) when is_list(Results) -> fun (Result) -> display_row( [format_info_item(proplists:get_value(X, Result)) || X <- InfoItemKeys]) - end, Results), + end, lists:sort(Results)), ok; display_info_list(Other, _) -> Other. -- cgit v1.2.1 From 347a76c0f3e19131d01555c2a682766c45147cbf Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 1 Nov 2012 16:00:01 +0000 Subject: fix heinous bug --- src/rabbit_writer.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_writer.erl b/src/rabbit_writer.erl index f3a8cacf..0e26a8a3 100644 --- a/src/rabbit_writer.erl +++ b/src/rabbit_writer.erl @@ -96,7 +96,7 @@ mainloop(ReaderPid, State) -> try mainloop1(ReaderPid, State) catch - exit:Error -> ReaderPid ! {channel_exit, #wstate.channel, Error} + exit:Error -> ReaderPid ! {channel_exit, State#wstate.channel, Error} end, done. -- cgit v1.2.1 From 43d796ee6ee1f162f0979b87c9b4ad75f123f1f1 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 2 Nov 2012 09:13:44 +0000 Subject: only trigger stats emission from writers in the server (and not the Erlang client) The server (and *only* the server) starts channels with rabbit_channel_sup:start_link({tcp, ...}). Writers are created as part of that. We pass an extra argument to rabbit_writer:start_link in this case, indicating that the reader wants be told by the writer to emit stats. Internally, the writer handles the stats/no_stats distinction by initialising the stats timer differently. We introduce a new function to create a disabled stats timer, thus taking advantage of all the existing logic in rabbit_event that suppresses timer/stats operations when stats are disabled. This approach requires inclusion of rabbit_event in rabbit_common. --- src/rabbit_channel_sup.erl | 2 +- src/rabbit_event.erl | 10 +++++++--- src/rabbit_writer.erl | 43 +++++++++++++++++++++++++++++++------------ 3 files changed, 39 insertions(+), 16 deletions(-) diff --git a/src/rabbit_channel_sup.erl b/src/rabbit_channel_sup.erl index bcb83851..42459833 100644 --- a/src/rabbit_channel_sup.erl +++ b/src/rabbit_channel_sup.erl @@ -83,7 +83,7 @@ init(Type) -> child_specs({tcp, Sock, Channel, FrameMax, ReaderPid, Protocol}) -> [{writer, {rabbit_writer, start_link, - [Sock, Channel, FrameMax, Protocol, ReaderPid]}, + [Sock, Channel, FrameMax, Protocol, ReaderPid, true]}, intrinsic, ?MAX_WAIT, worker, [rabbit_writer]} | child_specs(direct)]; child_specs(direct) -> [{limiter, {rabbit_limiter, start_link, []}, diff --git a/src/rabbit_event.erl b/src/rabbit_event.erl index 3f1b20fe..7d91b6fa 100644 --- a/src/rabbit_event.erl +++ b/src/rabbit_event.erl @@ -19,8 +19,8 @@ -include("rabbit.hrl"). -export([start_link/0]). --export([init_stats_timer/2, ensure_stats_timer/3, stop_stats_timer/2]). --export([reset_stats_timer/2]). +-export([init_stats_timer/2, init_disabled_stats_timer/2, + ensure_stats_timer/3, stop_stats_timer/2, reset_stats_timer/2]). -export([stats_level/2, if_enabled/3]). -export([notify/2, notify_if/3]). @@ -51,6 +51,7 @@ -spec(start_link/0 :: () -> rabbit_types:ok_pid_or_error()). -spec(init_stats_timer/2 :: (container(), pos()) -> container()). +-spec(init_disabled_stats_timer/2 :: (container(), pos()) -> container()). -spec(ensure_stats_timer/3 :: (container(), pos(), term()) -> container()). -spec(stop_stats_timer/2 :: (container(), pos()) -> container()). -spec(reset_stats_timer/2 :: (container(), pos()) -> container()). @@ -90,10 +91,13 @@ start_link() -> init_stats_timer(C, P) -> {ok, StatsLevel} = application:get_env(rabbit, collect_statistics), - {ok, Interval} = application:get_env(rabbit, collect_statistics_interval), + {ok, Interval} = application:get_env(rabbit, collect_statistics_interval), setelement(P, C, #state{level = StatsLevel, interval = Interval, timer = undefined}). +init_disabled_stats_timer(C, P) -> + setelement(P, C, #state{level = none, interval = 0, timer = undefined}). + ensure_stats_timer(C, P, Msg) -> case element(P, C) of #state{level = Level, interval = Interval, timer = undefined} = State diff --git a/src/rabbit_writer.erl b/src/rabbit_writer.erl index c23f46f9..a7ea3d99 100644 --- a/src/rabbit_writer.erl +++ b/src/rabbit_writer.erl @@ -18,7 +18,7 @@ -include("rabbit.hrl"). -include("rabbit_framing.hrl"). --export([start/5, start_link/5]). +-export([start/5, start_link/5, start/6, start_link/6]). -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]). @@ -44,6 +44,14 @@ (rabbit_net:socket(), rabbit_channel:channel_number(), non_neg_integer(), rabbit_types:protocol(), pid()) -> rabbit_types:ok(pid())). +-spec(start/6 :: + (rabbit_net:socket(), rabbit_channel:channel_number(), + non_neg_integer(), rabbit_types:protocol(), pid(), boolean()) + -> rabbit_types:ok(pid())). +-spec(start_link/6 :: + (rabbit_net:socket(), rabbit_channel:channel_number(), + non_neg_integer(), rabbit_types:protocol(), pid(), boolean()) + -> rabbit_types:ok(pid())). -spec(send_command/2 :: (pid(), rabbit_framing:amqp_method_record()) -> 'ok'). -spec(send_command/3 :: @@ -76,21 +84,32 @@ %%--------------------------------------------------------------------------- start(Sock, Channel, FrameMax, Protocol, ReaderPid) -> - State = initial_state(Sock, Channel, FrameMax, Protocol, ReaderPid), - {ok, proc_lib:spawn(?MODULE, mainloop, [State])}. + start(Sock, Channel, FrameMax, Protocol, ReaderPid, false). start_link(Sock, Channel, FrameMax, Protocol, ReaderPid) -> - State = initial_state(Sock, Channel, FrameMax, Protocol, ReaderPid), + start_link(Sock, Channel, FrameMax, Protocol, ReaderPid, false). + +start(Sock, Channel, FrameMax, Protocol, ReaderPid, ReaderWantsStats) -> + State = initial_state(Sock, Channel, FrameMax, Protocol, ReaderPid, + ReaderWantsStats), + {ok, proc_lib:spawn(?MODULE, mainloop, [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])}. -initial_state(Sock, Channel, FrameMax, Protocol, ReaderPid) -> - rabbit_event:init_stats_timer(#wstate{sock = Sock, - channel = Channel, - frame_max = FrameMax, - protocol = Protocol, - reader = ReaderPid, - pending = []}, - #wstate.stats_timer). +initial_state(Sock, Channel, FrameMax, Protocol, ReaderPid, ReaderWantsStats) -> + (case ReaderWantsStats of + true -> fun rabbit_event:init_stats_timer/2; + false -> fun rabbit_event:init_disabled_stats_timer/2 + end)(#wstate{sock = Sock, + channel = Channel, + frame_max = FrameMax, + protocol = Protocol, + reader = ReaderPid, + pending = []}, + #wstate.stats_timer). mainloop(State) -> try -- cgit v1.2.1 From 16dc5d1b4d8d213dd50b2e3b42cecbc17967441d Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 3 Nov 2012 17:28:54 +0000 Subject: cosmetic: vertical alignment / shrinkage --- src/rabbit_binary_generator.erl | 68 ++++++++++++----------------------------- src/rabbit_binary_parser.erl | 55 +++++++++++++-------------------- 2 files changed, 42 insertions(+), 81 deletions(-) diff --git a/src/rabbit_binary_generator.erl b/src/rabbit_binary_generator.erl index 4700fa31..c6db49ae 100644 --- a/src/rabbit_binary_generator.erl +++ b/src/rabbit_binary_generator.erl @@ -118,51 +118,24 @@ create_frame(TypeInt, ChannelInt, Payload) -> %% table_field_to_binary supports the AMQP 0-8/0-9 standard types, S, %% I, D, T and F, as well as the QPid extensions b, d, f, l, s, t, x, %% and V. - -table_field_to_binary({FName, Type, Value}) -> - [short_string_to_binary(FName) | field_value_to_binary(Type, Value)]. - -field_value_to_binary(longstr, Value) -> - ["S", long_string_to_binary(Value)]; - -field_value_to_binary(signedint, Value) -> - ["I", <>]; - -field_value_to_binary(decimal, {Before, After}) -> - ["D", Before, <>]; - -field_value_to_binary(timestamp, Value) -> - ["T", <>]; - -field_value_to_binary(table, Value) -> - ["F", table_to_binary(Value)]; - -field_value_to_binary(array, Value) -> - ["A", array_to_binary(Value)]; - -field_value_to_binary(byte, Value) -> - ["b", <>]; - -field_value_to_binary(double, Value) -> - ["d", <>]; - -field_value_to_binary(float, Value) -> - ["f", <>]; - -field_value_to_binary(long, Value) -> - ["l", <>]; - -field_value_to_binary(short, Value) -> - ["s", <>]; - -field_value_to_binary(bool, Value) -> - ["t", if Value -> 1; true -> 0 end]; - -field_value_to_binary(binary, Value) -> - ["x", long_string_to_binary(Value)]; - -field_value_to_binary(void, _Value) -> - ["V"]. +table_field_to_binary({FName, T, V}) -> + [short_string_to_binary(FName) | field_value_to_binary(T, V)]. + +field_value_to_binary(longstr, V) -> ["S", long_string_to_binary(V)]; +field_value_to_binary(signedint, V) -> ["I", <>]; +field_value_to_binary(decimal, V) -> {Before, After} = V, + ["D", Before, <>]; +field_value_to_binary(timestamp, V) -> ["T", <>]; +field_value_to_binary(table, V) -> ["F", table_to_binary(V)]; +field_value_to_binary(array, V) -> ["A", array_to_binary(V)]; +field_value_to_binary(byte, V) -> ["b", <>]; +field_value_to_binary(double, V) -> ["d", <>]; +field_value_to_binary(float, V) -> ["f", <>]; +field_value_to_binary(long, V) -> ["l", <>]; +field_value_to_binary(short, V) -> ["s", <>]; +field_value_to_binary(bool, V) -> ["t", if V -> 1; true -> 0 end]; +field_value_to_binary(binary, V) -> ["x", long_string_to_binary(V)]; +field_value_to_binary(void, _V) -> ["V"]. table_to_binary(Table) when is_list(Table) -> BinTable = generate_table(Table), @@ -176,9 +149,8 @@ generate_table(Table) when is_list(Table) -> list_to_binary(lists:map(fun table_field_to_binary/1, Table)). generate_array(Array) when is_list(Array) -> - list_to_binary(lists:map( - fun ({Type, Value}) -> field_value_to_binary(Type, Value) end, - Array)). + list_to_binary(lists:map(fun ({T, V}) -> field_value_to_binary(T, V) end, + Array)). short_string_to_binary(String) when is_binary(String) -> Len = size(String), diff --git a/src/rabbit_binary_parser.erl b/src/rabbit_binary_parser.erl index 5f0016b6..53878d6a 100644 --- a/src/rabbit_binary_parser.erl +++ b/src/rabbit_binary_parser.erl @@ -50,47 +50,36 @@ parse_array(<>) -> {Type, Value, Rest} = parse_field_value(ValueAndRest), [{Type, Value} | parse_array(Rest)]. -parse_field_value(<<"S", VLen:32/unsigned, ValueString:VLen/binary, Rest/binary>>) -> - {longstr, ValueString, Rest}; +parse_field_value(<<"S", VLen:32/unsigned, V:VLen/binary, R/binary>>) -> + {longstr, V, R}; -parse_field_value(<<"I", Value:32/signed, Rest/binary>>) -> - {signedint, Value, Rest}; +parse_field_value(<<"I", V:32/signed, R/binary>>) -> + {signedint, V, R}; -parse_field_value(<<"D", Before:8/unsigned, After:32/unsigned, Rest/binary>>) -> - {decimal, {Before, After}, Rest}; +parse_field_value(<<"D", Before:8/unsigned, After:32/unsigned, R/binary>>) -> + {decimal, {Before, After}, R}; -parse_field_value(<<"T", Value:64/unsigned, Rest/binary>>) -> - {timestamp, Value, Rest}; +parse_field_value(<<"T", V:64/unsigned, R/binary>>) -> + {timestamp, V, R}; -parse_field_value(<<"F", VLen:32/unsigned, Table:VLen/binary, Rest/binary>>) -> - {table, parse_table(Table), Rest}; +parse_field_value(<<"F", VLen:32/unsigned, Table:VLen/binary, R/binary>>) -> + {table, parse_table(Table), R}; -parse_field_value(<<"A", VLen:32/unsigned, Array:VLen/binary, Rest/binary>>) -> - {array, parse_array(Array), Rest}; +parse_field_value(<<"A", VLen:32/unsigned, Array:VLen/binary, R/binary>>) -> + {array, parse_array(Array), R}; -parse_field_value(<<"b", Value:8/unsigned, Rest/binary>>) -> - {byte, Value, Rest}; +parse_field_value(<<"b", V:8/unsigned, R/binary>>) -> {byte, V, R}; +parse_field_value(<<"d", V:64/float, R/binary>>) -> {double, V, R}; +parse_field_value(<<"f", V:32/float, R/binary>>) -> {float, V, R}; +parse_field_value(<<"l", V:64/signed, R/binary>>) -> {long, V, R}; +parse_field_value(<<"s", V:16/signed, R/binary>>) -> {short, V, R}; +parse_field_value(<<"t", V:8/unsigned, R/binary>>) -> {bool, (V /= 0), R}; -parse_field_value(<<"d", Value:64/float, Rest/binary>>) -> - {double, Value, Rest}; +parse_field_value(<<"x", VLen:32/unsigned, V:VLen/binary, R/binary>>) -> + {binary, V, R}; -parse_field_value(<<"f", Value:32/float, Rest/binary>>) -> - {float, Value, Rest}; - -parse_field_value(<<"l", Value:64/signed, Rest/binary>>) -> - {long, Value, Rest}; - -parse_field_value(<<"s", Value:16/signed, Rest/binary>>) -> - {short, Value, Rest}; - -parse_field_value(<<"t", Value:8/unsigned, Rest/binary>>) -> - {bool, (Value /= 0), Rest}; - -parse_field_value(<<"x", VLen:32/unsigned, ValueString:VLen/binary, Rest/binary>>) -> - {binary, ValueString, Rest}; - -parse_field_value(<<"V", Rest/binary>>) -> - {void, undefined, Rest}. +parse_field_value(<<"V", R/binary>>) -> + {void, undefined, R}. ensure_content_decoded(Content = #content{properties = Props}) when Props =/= none -> -- cgit v1.2.1 From 1a583b92379645f98c37be43894649c2f49f62ee Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 3 Nov 2012 19:13:37 +0000 Subject: cosmetic --- src/rabbit_binary_generator.erl | 84 +++++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 41 deletions(-) diff --git a/src/rabbit_binary_generator.erl b/src/rabbit_binary_generator.erl index c6db49ae..6b6b395a 100644 --- a/src/rabbit_binary_generator.erl +++ b/src/rabbit_binary_generator.erl @@ -173,58 +173,60 @@ encode_properties([], []) -> encode_properties(TypeList, ValueList) -> encode_properties(0, TypeList, ValueList, 0, [], []). -encode_properties(_Bit, [], [], FirstShortAcc, FlagsAcc, PropsAcc) -> - list_to_binary([lists:reverse(FlagsAcc), <>, lists:reverse(PropsAcc)]); -encode_properties(_Bit, [], _ValueList, _FirstShortAcc, _FlagsAcc, _PropsAcc) -> +encode_properties(_Bit, [], [], + FirstShortAcc, FlagsAcc, PropsAcc) -> + list_to_binary([lists:reverse(FlagsAcc), + <>, + lists:reverse(PropsAcc)]); +encode_properties(_Bit, [], _ValueList, + _FirstShortAcc, _FlagsAcc, _PropsAcc) -> exit(content_properties_values_overflow); -encode_properties(15, TypeList, ValueList, FirstShortAcc, FlagsAcc, PropsAcc) -> +encode_properties(15, TypeList, ValueList, + FirstShortAcc, FlagsAcc, PropsAcc) -> NewFlagsShort = FirstShortAcc bor 1, % set the continuation low bit - encode_properties(0, TypeList, ValueList, 0, [<> | FlagsAcc], PropsAcc); -encode_properties(Bit, [bit | TypeList], [Value | ValueList], FirstShortAcc, FlagsAcc, PropsAcc) -> - case Value of - true -> encode_properties(Bit + 1, TypeList, ValueList, - FirstShortAcc bor (1 bsl (15 - Bit)), FlagsAcc, PropsAcc); - false -> encode_properties(Bit + 1, TypeList, ValueList, - FirstShortAcc, FlagsAcc, PropsAcc); - Other -> exit({content_properties_illegal_bit_value, Other}) - end; -encode_properties(Bit, [T | TypeList], [Value | ValueList], FirstShortAcc, FlagsAcc, PropsAcc) -> - case Value of - undefined -> encode_properties(Bit + 1, TypeList, ValueList, - FirstShortAcc, FlagsAcc, PropsAcc); - _ -> encode_properties(Bit + 1, TypeList, ValueList, - FirstShortAcc bor (1 bsl (15 - Bit)), - FlagsAcc, - [encode_property(T, Value) | PropsAcc]) - end. + encode_properties(0, TypeList, ValueList, + 0, [<> | FlagsAcc], PropsAcc); +encode_properties(Bit, [bit | TypeList], [true | ValueList], + FirstShortAcc, FlagsAcc, PropsAcc) -> + encode_properties(Bit + 1, TypeList, ValueList, + FirstShortAcc bor (1 bsl (15 - Bit)), FlagsAcc, PropsAcc); +encode_properties(Bit, [bit | TypeList], [false | ValueList], + FirstShortAcc, FlagsAcc, PropsAcc) -> + encode_properties(Bit + 1, TypeList, ValueList, + FirstShortAcc, FlagsAcc, PropsAcc); +encode_properties(_Bit, [bit | _TypeList], [Other | _ValueList], + _FirstShortAcc, _FlagsAcc, _PropsAcc) -> + exit({content_properties_illegal_bit_value, Other}); +encode_properties(Bit, [_Type | TypeList], [undefined | ValueList], + FirstShortAcc, FlagsAcc, PropsAcc) -> + encode_properties(Bit + 1, TypeList, ValueList, + FirstShortAcc, FlagsAcc, PropsAcc); +encode_properties(Bit, [Type | TypeList], [Value | ValueList], + FirstShortAcc, FlagsAcc, PropsAcc) -> + encode_properties(Bit + 1, TypeList, ValueList, + FirstShortAcc bor (1 bsl (15 - Bit)), FlagsAcc, + [encode_property(Type, Value) | PropsAcc]). encode_property(shortstr, String) -> Len = size(String), if Len < 256 -> <>; true -> exit(content_properties_shortstr_overflow) end; -encode_property(longstr, String) -> - Len = size(String), <>; -encode_property(octet, Int) -> - <>; -encode_property(shortint, Int) -> - <>; -encode_property(longint, Int) -> - <>; -encode_property(longlongint, Int) -> - <>; -encode_property(timestamp, Int) -> - <>; -encode_property(table, Table) -> - table_to_binary(Table). +encode_property(longstr, String) -> Len = size(String), + <>; +encode_property(octet, Int) -> <>; +encode_property(shortint, Int) -> <>; +encode_property(longint, Int) -> <>; +encode_property(longlongint, Int) -> <>; +encode_property(timestamp, Int) -> <>; +encode_property(table, Table) -> table_to_binary(Table). check_empty_frame_size() -> %% Intended to ensure that EMPTY_FRAME_SIZE is defined correctly. - ComputedSize = iolist_size(create_frame(?FRAME_BODY, 0, <<>>)), - if ComputedSize == ?EMPTY_FRAME_SIZE -> - ok; - true -> - exit({incorrect_empty_frame_size, ComputedSize, ?EMPTY_FRAME_SIZE}) + case iolist_size(create_frame(?FRAME_BODY, 0, <<>>)) of + ?EMPTY_FRAME_SIZE -> ok; + ComputedSize -> exit({incorrect_empty_frame_size, + ComputedSize, ?EMPTY_FRAME_SIZE}) end. ensure_content_encoded(Content = #content{properties_bin = PropBin, -- cgit v1.2.1 From cabb62c333032ec5a1a499e2336d8c6bb10aa580 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 3 Nov 2012 21:04:32 +0000 Subject: ditch superfluous type mapping in codegen --- codegen.py | 27 ++++++--------------------- src/rabbit_binary_generator.erl | 6 +++--- 2 files changed, 9 insertions(+), 24 deletions(-) diff --git a/codegen.py b/codegen.py index 9483e854..68f530c2 100644 --- a/codegen.py +++ b/codegen.py @@ -24,18 +24,6 @@ from amqp_codegen import * import string import re -erlangTypeMap = { - 'octet': 'octet', - 'shortstr': 'shortstr', - 'longstr': 'longstr', - 'short': 'shortint', - 'long': 'longint', - 'longlong': 'longlongint', - 'bit': 'bit', - 'table': 'table', - 'timestamp': 'timestamp', -} - # Coming up with a proper encoding of AMQP tables in JSON is too much # hassle at this stage. Given that the only default value we are # interested in is for the empty table, we only support that. @@ -123,7 +111,7 @@ def printFileHeader(): def genErl(spec): def erlType(domain): - return erlangTypeMap[spec.resolveDomain(domain)] + return erlangize(spec.resolveDomain(domain)) def fieldTypeList(fields): return '[' + ', '.join([erlType(f.domain) for f in fields]) + ']' @@ -186,11 +174,11 @@ def genErl(spec): return p+'Len:32/unsigned, '+p+':'+p+'Len/binary' elif type == 'octet': return p+':8/unsigned' - elif type == 'shortint': + elif type == 'short': return p+':16/unsigned' - elif type == 'longint': + elif type == 'long': return p+':32/unsigned' - elif type == 'longlongint': + elif type == 'longlong': return p+':64/unsigned' elif type == 'timestamp': return p+':64/unsigned' @@ -350,8 +338,8 @@ def genErl(spec): 'table' | 'byte' | 'double' | 'float' | 'long' | 'short' | 'bool' | 'binary' | 'void' | 'array'). -type(amqp_property_type() :: - 'shortstr' | 'longstr' | 'octet' | 'shortint' | 'longint' | - 'longlongint' | 'timestamp' | 'bit' | 'table'). + 'shortstr' | 'longstr' | 'octet' | 'short' | 'long' | + 'longlong' | 'timestamp' | 'bit' | 'table'). -type(amqp_table() :: [{binary(), amqp_field_type(), amqp_value()}]). -type(amqp_array() :: [{amqp_field_type(), amqp_value()}]). @@ -497,9 +485,6 @@ shortstr_size(S) -> print "amqp_exception(_Code) -> undefined." def genHrl(spec): - def erlType(domain): - return erlangTypeMap[spec.resolveDomain(domain)] - def fieldNameList(fields): return ', '.join([erlangize(f.name) for f in fields]) diff --git a/src/rabbit_binary_generator.erl b/src/rabbit_binary_generator.erl index 6b6b395a..2ece8696 100644 --- a/src/rabbit_binary_generator.erl +++ b/src/rabbit_binary_generator.erl @@ -215,9 +215,9 @@ encode_property(shortstr, String) -> encode_property(longstr, String) -> Len = size(String), <>; encode_property(octet, Int) -> <>; -encode_property(shortint, Int) -> <>; -encode_property(longint, Int) -> <>; -encode_property(longlongint, Int) -> <>; +encode_property(short, Int) -> <>; +encode_property(long, Int) -> <>; +encode_property(longlong, Int) -> <>; encode_property(timestamp, Int) -> <>; encode_property(table, Table) -> table_to_binary(Table). -- cgit v1.2.1 From bd2bd07fd063e6ce2b0d7e8c1589fff7ac1cd2ae Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 3 Nov 2012 21:09:33 +0000 Subject: fix tests --- src/rabbit_tests.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index eb97f1d6..715aa186 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -444,10 +444,10 @@ test_content_properties() -> test_content_prop_encoding([{bit, true}, {octet, 123}, {octet, 123}, {bit, true}], <<16#F0,0,123,123>>), test_content_prop_encoding([{bit, true}, {shortstr, <<"hi">>}, {bit, true}, - {shortint, 54321}, {bit, true}], + {short, 54321}, {bit, true}], <<16#F8,0,2,"hi",16#D4,16#31>>), test_content_prop_encoding([{bit, true}, {shortstr, undefined}, {bit, true}, - {shortint, 54321}, {bit, true}], + {short, 54321}, {bit, true}], <<16#B8,0,16#D4,16#31>>), test_content_prop_encoding([{table, [{<<"a signedint">>, signedint, 12345678}, {<<"a longstr">>, longstr, <<"yes please">>}, -- cgit v1.2.1 From c0574c50ecf4250df2900c96cdcd1cc27de4baa9 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sun, 4 Nov 2012 09:29:58 +0000 Subject: simplify property decoder functions instead of macros, which gives the same performance in the fast "no properties" path and only causes a tiny loss (if any) otherwise. --- codegen.py | 51 ++++++++++++++++----------------------------------- 1 file changed, 16 insertions(+), 35 deletions(-) diff --git a/codegen.py b/codegen.py index 9483e854..1cd20fb9 100644 --- a/codegen.py +++ b/codegen.py @@ -233,29 +233,19 @@ def genErl(spec): def presentBin(fields): ps = ', '.join(['P' + str(f.index) + ':1' for f in fields]) return '<<' + ps + ', _:%d, R0/binary>>' % (16 - len(fields),) - def mkMacroName(field): - return '?' + field.domain.upper() + '_PROP' - def writePropFieldLine(field, bin_next = None): + def writePropFieldLine(field): i = str(field.index) - if not bin_next: - bin_next = 'R' + str(field.index + 1) - if field.domain in ['octet', 'timestamp']: - print (" {%s, %s} = %s(%s, %s, %s, %s)," % - ('F' + i, bin_next, mkMacroName(field), 'P' + i, - 'R' + i, 'I' + i, 'X' + i)) - else: - print (" {%s, %s} = %s(%s, %s, %s, %s, %s)," % - ('F' + i, bin_next, mkMacroName(field), 'P' + i, - 'R' + i, 'L' + i, 'S' + i, 'X' + i)) + print " {F%s, R%s} = if P%s =:= 0 -> {undefined, R%s}; true -> %s_prop(R%s) end," % \ + (i, str(field.index + 1), i, i, erlType(field.domain), i) if len(c.fields) == 0: print "decode_properties(%d, _) ->" % (c.index,) else: print ("decode_properties(%d, %s) ->" % (c.index, presentBin(c.fields))) - for field in c.fields[:-1]: + for field in c.fields: writePropFieldLine(field) - writePropFieldLine(c.fields[-1], "<<>>") + print " <<>> = %s," % ('R' + str(len(c.fields))) print " #'P_%s'{%s};" % (erlangize(c.name), fieldMapList(c.fields)) def genFieldPreprocessing(packed): @@ -429,26 +419,17 @@ shortstr_size(S) -> _ -> exit(method_field_shortstr_overflow) end. --define(SHORTSTR_PROP(P, R, L, S, X), - if P =:= 0 -> {undefined, R}; - true -> <> = R, - {S, X} - end). --define(TABLE_PROP(P, R, L, T, X), - if P =:= 0 -> {undefined, R}; - true -> <> = R, - {rabbit_binary_parser:parse_table(T), X} - end). --define(OCTET_PROP(P, R, I, X), - if P =:= 0 -> {undefined, R}; - true -> <> = R, - {I, X} - end). --define(TIMESTAMP_PROP(P, R, I, X), - if P =:= 0 -> {undefined, R}; - true -> <> = R, - {I, X} - end). +shortstr_prop(<>) -> + {S, X}. + +table_prop(<>) -> + {rabbit_binary_parser:parse_table(T), X}. + +octet_prop(<>) -> + {I, X}. + +timestamp_prop(<>) -> + {I, X}. """ version = "{%d, %d, %d}" % (spec.major, spec.minor, spec.revision) if version == '{8, 0, 0}': version = '{0, 8, 0}' -- cgit v1.2.1 From 829dbc813af97f23796ba30a8ecbf87be1b9acb5 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sun, 4 Nov 2012 09:32:01 +0000 Subject: be more assertive in property decoding --- codegen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codegen.py b/codegen.py index 9483e854..d418e76d 100644 --- a/codegen.py +++ b/codegen.py @@ -249,7 +249,7 @@ def genErl(spec): 'R' + i, 'L' + i, 'S' + i, 'X' + i)) if len(c.fields) == 0: - print "decode_properties(%d, _) ->" % (c.index,) + print "decode_properties(%d, <<>>) ->" % (c.index,) else: print ("decode_properties(%d, %s) ->" % (c.index, presentBin(c.fields))) -- cgit v1.2.1 From aa6908c906ab8d0e647c5d0963d86c3a69e38267 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sun, 4 Nov 2012 16:48:13 +0000 Subject: handle all types in property decoder --- codegen.py | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/codegen.py b/codegen.py index 0adec1ab..2ff78420 100644 --- a/codegen.py +++ b/codegen.py @@ -223,8 +223,12 @@ def genErl(spec): return '<<' + ps + ', _:%d, R0/binary>>' % (16 - len(fields),) def writePropFieldLine(field): i = str(field.index) - print " {F%s, R%s} = if P%s =:= 0 -> {undefined, R%s}; true -> %s_prop(R%s) end," % \ - (i, str(field.index + 1), i, i, erlType(field.domain), i) + if field.domain == 'bit': + print " {F%s, R%s} = {P%s =/= 0, R%s}," % \ + (i, str(field.index + 1), i, i) + else: + print " {F%s, R%s} = if P%s =:= 0 -> {undefined, R%s}; true -> %s_prop(R%s) end," % \ + (i, str(field.index + 1), i, i, erlType(field.domain), i) if len(c.fields) == 0: print "decode_properties(%d, <<>>) ->" % (c.index,) @@ -407,17 +411,28 @@ shortstr_size(S) -> _ -> exit(method_field_shortstr_overflow) end. -shortstr_prop(<>) -> - {S, X}. +%% use of these functions by the codec depends on the protocol spec +-compile({nowarn_unused_function, + [{shortstr_prop, 1}, {longstr_prop, 1}, + {short_prop, 1}, {long_prop, 1}, {longlong_prop, 1}, + {octet_prop, 1}, {table_prop, 1}, {timestamp_prop, 1}]}). + +shortstr_prop(<>) -> {S, X}. + +longstr_prop(<>) -> {S, X}. + +short_prop(<>) -> {I, X}. + +long_prop(<>) -> {I, X}. + +longlong_prop(<>) -> {I, X}. + +octet_prop(<>) -> {I, X}. table_prop(<>) -> {rabbit_binary_parser:parse_table(T), X}. -octet_prop(<>) -> - {I, X}. - -timestamp_prop(<>) -> - {I, X}. +timestamp_prop(<>) -> {I, X}. """ version = "{%d, %d, %d}" % (spec.major, spec.minor, spec.revision) if version == '{8, 0, 0}': version = '{0, 8, 0}' -- cgit v1.2.1 From e405cc15ba10e44b9ea235fb2dd2fcd165188420 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 5 Nov 2012 10:57:53 +0000 Subject: use precondition_failed for error notification --- src/rabbit_channel.erl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 3309094b..053ab3de 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -477,9 +477,8 @@ check_user_id_header(#'P_basic'{user_id = Claimed}, check_expiration_header(Props) -> case rabbit_basic:parse_expiration(Props) of {ok, _} -> ok; - {error, E} -> rabbit_misc:protocol_error( - invalid_expiration, "cannot parse expiration '~p': ~p", - [Props#'P_basic'.expiration, E]) + {error, E} -> precondition_failed("cannot parse expiration '~p': ~p", + [Props#'P_basic'.expiration, E]) end. check_internal_exchange(#exchange{name = Name, internal = true}) -> -- cgit v1.2.1 From 72abeaef0006c6829c955eadd416f5eabb4e6914 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 5 Nov 2012 11:01:01 +0000 Subject: consistent error messages for invalid params --- src/rabbit_channel.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 053ab3de..79815d68 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -477,7 +477,7 @@ check_user_id_header(#'P_basic'{user_id = Claimed}, check_expiration_header(Props) -> case rabbit_basic:parse_expiration(Props) of {ok, _} -> ok; - {error, E} -> precondition_failed("cannot parse expiration '~p': ~p", + {error, E} -> precondition_failed("invalid expiration '~s': ~w", [Props#'P_basic'.expiration, E]) end. -- cgit v1.2.1 From 56a0fd8ea270f15e7a78b190cc8c9ea14170ec6b Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 5 Nov 2012 11:03:01 +0000 Subject: refactor parse_expiration --- src/rabbit_basic.erl | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/rabbit_basic.erl b/src/rabbit_basic.erl index 6e5c503b..ca18087d 100644 --- a/src/rabbit_basic.erl +++ b/src/rabbit_basic.erl @@ -259,19 +259,18 @@ header_routes(HeadersTable) -> binary_to_list(HeaderKey), Type}}) end || HeaderKey <- ?ROUTING_HEADERS]). +parse_expiration(#'P_basic'{expiration = undefined}) -> + {ok, undefined}; parse_expiration(#'P_basic'{expiration = Expiration}) -> - case Expiration of - undefined -> {ok, undefined}; - B -> case string:to_integer(binary_to_list(B)) of - {error, no_integer} = E -> - E; - {N, ""} -> - case rabbit_misc:check_expiry_size(N) of - ok -> {ok, N}; - E = {error, _} -> E - end; - {_, S} -> - {error, {leftover_string, S}} - end + case string:to_integer(binary_to_list(Expiration)) of + {error, no_integer} = E -> + E; + {N, ""} -> + case rabbit_misc:check_expiry_size(N) of + ok -> {ok, N}; + E = {error, _} -> E + end; + {_, S} -> + {error, {leftover_string, S}} end. -- cgit v1.2.1 From 1c1c55f36a072f27c106972d4fbd6ee2f21709ea Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 5 Nov 2012 11:20:56 +0000 Subject: fix type specs --- src/rabbit_basic.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_basic.erl b/src/rabbit_basic.erl index ca18087d..a1f52480 100644 --- a/src/rabbit_basic.erl +++ b/src/rabbit_basic.erl @@ -75,7 +75,7 @@ {rabbit_framing:amqp_property_record(), binary()}). -spec(parse_expiration/1 :: (rabbit_framing:amqp_property_record()) - -> rabbit_types:ok_or_error2(non_neg_integer(), any())). + -> rabbit_types:ok_or_error2('undefined' | non_neg_integer(), any())). -endif. -- cgit v1.2.1 From fdc57bfb07cdcfc31eb5a9f487196c22c828c5a4 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 5 Nov 2012 11:25:10 +0000 Subject: better function names for ttl and expires checking --- src/rabbit_amqqueue.erl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index df182796..2b99f712 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -371,8 +371,8 @@ assert_args_equivalence(#amqqueue{name = QueueName, arguments = Args}, Args, RequiredArgs, QueueName, [<<"x-expires">>, <<"x-message-ttl">>]). check_declare_arguments(QueueName, Args) -> - Checks = [{<<"x-expires">>, fun check_positive_int_arg/2}, - {<<"x-message-ttl">>, fun check_non_neg_int_arg/2}, + Checks = [{<<"x-expires">>, fun check_positive_expires/2}, + {<<"x-message-ttl">>, fun check_non_neg_ttl/2}, {<<"x-dead-letter-exchange">>, fun check_string_arg/2}, {<<"x-dead-letter-routing-key">>, fun check_dlxrk_arg/2}], [case rabbit_misc:table_lookup(Args, Key) of @@ -399,14 +399,14 @@ check_int_arg({Type, _}, _) -> false -> {error, {unacceptable_type, Type}} end. -check_positive_int_arg({Type, Val}, Args) -> +check_positive_expires({Type, Val}, Args) -> case check_int_arg({Type, Val}, Args) of ok when Val > 0 -> rabbit_misc:check_expiry_size(Val); ok -> {error, {value_zero_or_less, Val}}; Error -> Error end. -check_non_neg_int_arg({Type, Val}, Args) -> +check_non_neg_ttl({Type, Val}, Args) -> case check_int_arg({Type, Val}, Args) of ok when Val >= 0 -> rabbit_misc:check_expiry_size(Val); ok -> {error, {value_less_than_zero, Val}}; -- cgit v1.2.1 From a631594844300830dc629c5e1045ab4eca9d2344 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 5 Nov 2012 11:33:24 +0000 Subject: simply >= 0 checking --- src/rabbit_amqqueue.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 2b99f712..f185fc67 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -401,9 +401,9 @@ check_int_arg({Type, _}, _) -> check_positive_expires({Type, Val}, Args) -> case check_int_arg({Type, Val}, Args) of - ok when Val > 0 -> rabbit_misc:check_expiry_size(Val); - ok -> {error, {value_zero_or_less, Val}}; - Error -> Error + ok when Val == 0 -> {error, {value_zero_or_less, Val}}; + ok -> rabbit_misc:check_expiry_size(Val); + Error -> Error end. check_non_neg_ttl({Type, Val}, Args) -> -- cgit v1.2.1 From 5acff88d14e61b034184de8e5dce13c7b42e9132 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 5 Nov 2012 11:36:35 +0000 Subject: check_expiry_size => check_expiry --- src/rabbit_amqqueue.erl | 4 ++-- src/rabbit_basic.erl | 2 +- src/rabbit_misc.erl | 10 +++++----- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index f185fc67..5e163800 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -402,13 +402,13 @@ check_int_arg({Type, _}, _) -> check_positive_expires({Type, Val}, Args) -> case check_int_arg({Type, Val}, Args) of ok when Val == 0 -> {error, {value_zero_or_less, Val}}; - ok -> rabbit_misc:check_expiry_size(Val); + ok -> rabbit_misc:check_expiry(Val); Error -> Error end. check_non_neg_ttl({Type, Val}, Args) -> case check_int_arg({Type, Val}, Args) of - ok when Val >= 0 -> rabbit_misc:check_expiry_size(Val); + ok when Val >= 0 -> rabbit_misc:check_expiry(Val); ok -> {error, {value_less_than_zero, Val}}; Error -> Error end. diff --git a/src/rabbit_basic.erl b/src/rabbit_basic.erl index a1f52480..9bd1fad9 100644 --- a/src/rabbit_basic.erl +++ b/src/rabbit_basic.erl @@ -266,7 +266,7 @@ parse_expiration(#'P_basic'{expiration = Expiration}) -> {error, no_integer} = E -> E; {N, ""} -> - case rabbit_misc:check_expiry_size(N) of + case rabbit_misc:check_expiry(N) of ok -> {ok, N}; E = {error, _} -> E end; diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index a707b2b1..7ea345e1 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -63,7 +63,7 @@ -export([version/0]). -export([sequence_error/1]). -export([json_encode/1, json_decode/1, json_to_term/1, term_to_json/1]). --export([check_expiry_size/1]). +-export([check_expiry/1]). -export([base64url/1]). %% Horrible macro to use in guards @@ -233,7 +233,7 @@ -spec(json_decode/1 :: (string()) -> {'ok', any()} | 'error'). -spec(json_to_term/1 :: (any()) -> any()). -spec(term_to_json/1 :: (any()) -> any()). --spec(check_expiry_size/1 :: (integer()) -> rabbit_types:ok_or_error(any())). +-spec(check_expiry/1 :: (integer()) -> rabbit_types:ok_or_error(any())). -spec(base64url/1 :: (binary()) -> string()). -endif. @@ -1005,11 +1005,11 @@ term_to_json(V) when is_binary(V) orelse is_number(V) orelse V =:= null orelse V =:= true orelse V =:= false -> V. -check_expiry_size(N) when N > ?MAX_EXPIRY_TIMER -> +check_expiry(N) when N > ?MAX_EXPIRY_TIMER -> {error, {value_too_big, N}}; -check_expiry_size(N) when N < 0 -> +check_expiry(N) when N < 0 -> {error, {negative_value, N}}; -check_expiry_size(_N) -> +check_expiry(_N) -> ok. base64url(In) -> -- cgit v1.2.1 From f8f55a8fa0b89571a70839af8bd0be8c219f7d02 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 5 Nov 2012 12:22:21 +0000 Subject: code gen property encoder pretty much symmetric to decoder Involves renaming of the decoder helper funs, since those names are more meaningful for the encoder --- codegen.py | 70 ++++++++++++++++++++++++++++++++--------- src/rabbit_binary_generator.erl | 57 +-------------------------------- 2 files changed, 57 insertions(+), 70 deletions(-) diff --git a/codegen.py b/codegen.py index 2ff78420..6c36753c 100644 --- a/codegen.py +++ b/codegen.py @@ -227,7 +227,7 @@ def genErl(spec): print " {F%s, R%s} = {P%s =/= 0, R%s}," % \ (i, str(field.index + 1), i, i) else: - print " {F%s, R%s} = if P%s =:= 0 -> {undefined, R%s}; true -> %s_prop(R%s) end," % \ + print " {F%s, R%s} = if P%s =:= 0 -> {undefined, R%s}; true -> %s_val(R%s) end," % \ (i, str(field.index + 1), i, i, erlType(field.domain), i) if len(c.fields) == 0: @@ -265,9 +265,27 @@ def genErl(spec): print " <<%s>>;" % (', '.join([methodFieldFragment(f) for f in packedFields])) def genEncodeProperties(c): + def presentBin(fields): + ps = ', '.join(['P' + str(f.index) + ':1' for f in fields]) + return '<<' + ps + ', 0:%d>>' % (16 - len(fields),) + def writePropFieldLine(field): + i = str(field.index) + if field.domain == 'bit': + print " {P%s, R%s} = {F%s =:= 1, R%s}," % \ + (i, str(field.index + 1), i, i) + else: + print " {P%s, R%s} = if F%s =:= undefined -> {0, R%s}; true -> {1, [?%s_PROP(F%s) | R%s]} end," % \ + (i, str(field.index + 1), i, i, erlType(field.domain).upper(), i, i) + print "encode_properties(#'P_%s'{%s}) ->" % (erlangize(c.name), fieldMapList(c.fields)) - print " rabbit_binary_generator:encode_properties(%s, %s);" % \ - (fieldTypeList(c.fields), fieldTempList(c.fields)) + if len(c.fields) == 0: + print " <<>>;" + else: + print " R0 = <<>>," + for field in c.fields: + writePropFieldLine(field) + print " list_to_binary([%s | lists:reverse(R%s)]);" % \ + (presentBin(c.fields), str(len(c.fields))) def messageConstantClass(cls): # We do this because 0.8 uses "soft error" and 8.1 uses "soft-error". @@ -413,26 +431,50 @@ shortstr_size(S) -> %% use of these functions by the codec depends on the protocol spec -compile({nowarn_unused_function, - [{shortstr_prop, 1}, {longstr_prop, 1}, - {short_prop, 1}, {long_prop, 1}, {longlong_prop, 1}, - {octet_prop, 1}, {table_prop, 1}, {timestamp_prop, 1}]}). + [{shortstr_val, 1}, {longstr_val, 1}, + {short_val, 1}, {long_val, 1}, {longlong_val, 1}, + {octet_val, 1}, {table_val, 1}, {timestamp_val, 1}, + {shortstr_prop, 1}, {longstr_prop, 1}, {table_prop, 1}]}). -shortstr_prop(<>) -> {S, X}. +shortstr_val(<>) -> {S, X}. -longstr_prop(<>) -> {S, X}. +longstr_val(<>) -> {S, X}. -short_prop(<>) -> {I, X}. +short_val(<>) -> {I, X}. -long_prop(<>) -> {I, X}. +long_val(<>) -> {I, X}. -longlong_prop(<>) -> {I, X}. +longlong_val(<>) -> {I, X}. -octet_prop(<>) -> {I, X}. +octet_val(<>) -> {I, X}. -table_prop(<>) -> +table_val(<>) -> {rabbit_binary_parser:parse_table(T), X}. -timestamp_prop(<>) -> {I, X}. +timestamp_val(<>) -> {I, X}. + +shortstr_prop(S) -> + L = size(S), + if L < 256 -> <>; + true -> exit(content_properties_shortstr_overflow) + end. + +longstr_prop(S) -> + L = size(S), + <>. + +table_prop(T) -> + BinT = rabbit_binary_generator:generate_table(T), + <<(size(BinT)):32, BinT/binary>>. + +-define(SHORTSTR_PROP(X), shortstr_prop(X)). +-define(LONGSTR_PROP(X), longstr_prop(X)). +-define(OCTET_PROP(X), <>). +-define(SHORT_PROP(X), <>). +-define(LONG_PROP(X), <>). +-define(LONGLONG_PROP(X), <>). +-define(TIMESTAMP_PROP(X), <>). +-define(TABLE_PROP(X), table_prop(X)). """ version = "{%d, %d, %d}" % (spec.major, spec.minor, spec.revision) if version == '{8, 0, 0}': version = '{0, 8, 0}' diff --git a/src/rabbit_binary_generator.erl b/src/rabbit_binary_generator.erl index 2ece8696..a333c1ce 100644 --- a/src/rabbit_binary_generator.erl +++ b/src/rabbit_binary_generator.erl @@ -21,7 +21,7 @@ -export([build_simple_method_frame/3, build_simple_content_frames/4, build_heartbeat_frame/0]). --export([generate_table/1, encode_properties/2]). +-export([generate_table/1]). -export([check_empty_frame_size/0]). -export([ensure_content_encoded/2, clear_encoded_content/1]). -export([map_exception/3]). @@ -42,8 +42,6 @@ -> [frame()]). -spec(build_heartbeat_frame/0 :: () -> frame()). -spec(generate_table/1 :: (rabbit_framing:amqp_table()) -> binary()). --spec(encode_properties/2 :: - ([rabbit_framing:amqp_property_type()], [any()]) -> binary()). -spec(check_empty_frame_size/0 :: () -> 'ok'). -spec(ensure_content_encoded/2 :: (rabbit_types:content(), rabbit_types:protocol()) -> @@ -168,59 +166,6 @@ long_string_to_binary(String) when is_binary(String) -> long_string_to_binary(String) -> [<<(length(String)):32>>, String]. -encode_properties([], []) -> - <<0, 0>>; -encode_properties(TypeList, ValueList) -> - encode_properties(0, TypeList, ValueList, 0, [], []). - -encode_properties(_Bit, [], [], - FirstShortAcc, FlagsAcc, PropsAcc) -> - list_to_binary([lists:reverse(FlagsAcc), - <>, - lists:reverse(PropsAcc)]); -encode_properties(_Bit, [], _ValueList, - _FirstShortAcc, _FlagsAcc, _PropsAcc) -> - exit(content_properties_values_overflow); -encode_properties(15, TypeList, ValueList, - FirstShortAcc, FlagsAcc, PropsAcc) -> - NewFlagsShort = FirstShortAcc bor 1, % set the continuation low bit - encode_properties(0, TypeList, ValueList, - 0, [<> | FlagsAcc], PropsAcc); -encode_properties(Bit, [bit | TypeList], [true | ValueList], - FirstShortAcc, FlagsAcc, PropsAcc) -> - encode_properties(Bit + 1, TypeList, ValueList, - FirstShortAcc bor (1 bsl (15 - Bit)), FlagsAcc, PropsAcc); -encode_properties(Bit, [bit | TypeList], [false | ValueList], - FirstShortAcc, FlagsAcc, PropsAcc) -> - encode_properties(Bit + 1, TypeList, ValueList, - FirstShortAcc, FlagsAcc, PropsAcc); -encode_properties(_Bit, [bit | _TypeList], [Other | _ValueList], - _FirstShortAcc, _FlagsAcc, _PropsAcc) -> - exit({content_properties_illegal_bit_value, Other}); -encode_properties(Bit, [_Type | TypeList], [undefined | ValueList], - FirstShortAcc, FlagsAcc, PropsAcc) -> - encode_properties(Bit + 1, TypeList, ValueList, - FirstShortAcc, FlagsAcc, PropsAcc); -encode_properties(Bit, [Type | TypeList], [Value | ValueList], - FirstShortAcc, FlagsAcc, PropsAcc) -> - encode_properties(Bit + 1, TypeList, ValueList, - FirstShortAcc bor (1 bsl (15 - Bit)), FlagsAcc, - [encode_property(Type, Value) | PropsAcc]). - -encode_property(shortstr, String) -> - Len = size(String), - if Len < 256 -> <>; - true -> exit(content_properties_shortstr_overflow) - end; -encode_property(longstr, String) -> Len = size(String), - <>; -encode_property(octet, Int) -> <>; -encode_property(short, Int) -> <>; -encode_property(long, Int) -> <>; -encode_property(longlong, Int) -> <>; -encode_property(timestamp, Int) -> <>; -encode_property(table, Table) -> table_to_binary(Table). - check_empty_frame_size() -> %% Intended to ensure that EMPTY_FRAME_SIZE is defined correctly. case iolist_size(create_frame(?FRAME_BODY, 0, <<>>)) of -- cgit v1.2.1 From 1ce15a6c129007c84e48ed9c20733b7165cb988c Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 5 Nov 2012 13:21:59 +0000 Subject: drop property codec tests since that part of the code is now internal and code gen'ed but we can still test the table codec --- src/rabbit_tests.erl | 146 ++++++++++++++------------------------------------- 1 file changed, 39 insertions(+), 107 deletions(-) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 715aa186..f802a5a0 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -18,7 +18,7 @@ -compile([export_all]). --export([all_tests/0, test_parsing/0]). +-export([all_tests/0]). -import(rabbit_misc, [pget/2]). @@ -46,7 +46,7 @@ all_tests() -> passed = test_pg_local(), passed = test_unfold(), passed = test_supervisor_delayed_restart(), - passed = test_parsing(), + passed = test_table_codec(), passed = test_content_framing(), passed = test_content_transcoding(), passed = test_topic_matching(), @@ -424,113 +424,45 @@ test_unfold() -> end, 10), passed. -test_parsing() -> - passed = test_content_properties(), - passed = test_field_values(), - passed. - -test_content_prop_encoding(Datum, Binary) -> - Types = [element(1, E) || E <- Datum], - Values = [element(2, E) || E <- Datum], - Binary = rabbit_binary_generator:encode_properties(Types, Values). %% assertion - -test_content_properties() -> - test_content_prop_encoding([], <<0, 0>>), - test_content_prop_encoding([{bit, true}, {bit, false}, {bit, true}, {bit, false}], - <<16#A0, 0>>), - test_content_prop_encoding([{bit, true}, {octet, 123}, {bit, true}, {octet, undefined}, - {bit, true}], - <<16#E8,0,123>>), - test_content_prop_encoding([{bit, true}, {octet, 123}, {octet, 123}, {bit, true}], - <<16#F0,0,123,123>>), - test_content_prop_encoding([{bit, true}, {shortstr, <<"hi">>}, {bit, true}, - {short, 54321}, {bit, true}], - <<16#F8,0,2,"hi",16#D4,16#31>>), - test_content_prop_encoding([{bit, true}, {shortstr, undefined}, {bit, true}, - {short, 54321}, {bit, true}], - <<16#B8,0,16#D4,16#31>>), - test_content_prop_encoding([{table, [{<<"a signedint">>, signedint, 12345678}, - {<<"a longstr">>, longstr, <<"yes please">>}, - {<<"a decimal">>, decimal, {123, 12345678}}, - {<<"a timestamp">>, timestamp, 123456789012345}, - {<<"a nested table">>, table, - [{<<"one">>, signedint, 1}, - {<<"two">>, signedint, 2}]}]}], - << - %% property-flags - 16#8000:16, - - %% property-list: - - %% table - 117:32, % table length in bytes - - 11,"a signedint", % name - "I",12345678:32, % type and value - - 9,"a longstr", - "S",10:32,"yes please", - - 9,"a decimal", - "D",123,12345678:32, - - 11,"a timestamp", - "T", 123456789012345:64, - - 14,"a nested table", - "F", - 18:32, - - 3,"one", - "I",1:32, - - 3,"two", - "I",2:32 >>), - passed. - -test_field_values() -> +test_table_codec() -> %% FIXME this does not test inexact numbers (double and float) yet, %% because they won't pass the equality assertions - test_content_prop_encoding( - [{table, [{<<"longstr">>, longstr, <<"Here is a long string">>}, - {<<"signedint">>, signedint, 12345}, - {<<"decimal">>, decimal, {3, 123456}}, - {<<"timestamp">>, timestamp, 109876543209876}, - {<<"table">>, table, [{<<"one">>, signedint, 54321}, - {<<"two">>, longstr, <<"A long string">>}]}, - {<<"byte">>, byte, 255}, - {<<"long">>, long, 1234567890}, - {<<"short">>, short, 655}, - {<<"bool">>, bool, true}, - {<<"binary">>, binary, <<"a binary string">>}, - {<<"void">>, void, undefined}, - {<<"array">>, array, [{signedint, 54321}, - {longstr, <<"A long string">>}]} - - ]}], - << - %% property-flags - 16#8000:16, - %% table length in bytes - 228:32, - - 7,"longstr", "S", 21:32, "Here is a long string", % = 34 - 9,"signedint", "I", 12345:32/signed, % + 15 = 49 - 7,"decimal", "D", 3, 123456:32, % + 14 = 63 - 9,"timestamp", "T", 109876543209876:64, % + 19 = 82 - 5,"table", "F", 31:32, % length of table % + 11 = 93 - 3,"one", "I", 54321:32, % + 9 = 102 - 3,"two", "S", 13:32, "A long string", % + 22 = 124 - 4,"byte", "b", 255:8, % + 7 = 131 - 4,"long", "l", 1234567890:64, % + 14 = 145 - 5,"short", "s", 655:16, % + 9 = 154 - 4,"bool", "t", 1, % + 7 = 161 - 6,"binary", "x", 15:32, "a binary string", % + 27 = 188 - 4,"void", "V", % + 6 = 194 - 5,"array", "A", 23:32, % + 11 = 205 - "I", 54321:32, % + 5 = 210 - "S", 13:32, "A long string" % + 18 = 228 - >>), + Table = [{<<"longstr">>, longstr, <<"Here is a long string">>}, + {<<"signedint">>, signedint, 12345}, + {<<"decimal">>, decimal, {3, 123456}}, + {<<"timestamp">>, timestamp, 109876543209876}, + {<<"table">>, table, [{<<"one">>, signedint, 54321}, + {<<"two">>, longstr, + <<"A long string">>}]}, + {<<"byte">>, byte, 255}, + {<<"long">>, long, 1234567890}, + {<<"short">>, short, 655}, + {<<"bool">>, bool, true}, + {<<"binary">>, binary, <<"a binary string">>}, + {<<"void">>, void, undefined}, + {<<"array">>, array, [{signedint, 54321}, + {longstr, <<"A long string">>}]} + ], + Binary = << + 7,"longstr", "S", 21:32, "Here is a long string", + 9,"signedint", "I", 12345:32/signed, + 7,"decimal", "D", 3, 123456:32, + 9,"timestamp", "T", 109876543209876:64, + 5,"table", "F", 31:32, % length of table + 3,"one", "I", 54321:32, + 3,"two", "S", 13:32, "A long string", + 4,"byte", "b", 255:8, + 4,"long", "l", 1234567890:64, + 5,"short", "s", 655:16, + 4,"bool", "t", 1, + 6,"binary", "x", 15:32, "a binary string", + 4,"void", "V", + 5,"array", "A", 23:32, + "I", 54321:32, + "S", 13:32, "A long string" + >>, + Binary = rabbit_binary_generator:generate_table(Table), + Table = rabbit_binary_parser:parse_table(Binary), passed. %% Test that content frames don't exceed frame-max -- cgit v1.2.1 From 228cb7396f4eaf8ac323f40a0bc5ff98cd0dba22 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 5 Nov 2012 14:08:13 +0000 Subject: fix bug --- codegen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codegen.py b/codegen.py index 6c36753c..255e7204 100644 --- a/codegen.py +++ b/codegen.py @@ -281,7 +281,7 @@ def genErl(spec): if len(c.fields) == 0: print " <<>>;" else: - print " R0 = <<>>," + print " R0 = [<<>>]," for field in c.fields: writePropFieldLine(field) print " list_to_binary([%s | lists:reverse(R%s)]);" % \ -- cgit v1.2.1 From 7e9680a7ac410a29be7562526b052f4b6acce04e Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 5 Nov 2012 14:55:54 +0000 Subject: tweak --- src/rabbit_amqqueue.erl | 15 +++++++-------- src/rabbit_misc.erl | 2 +- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 5e163800..922951be 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -371,8 +371,8 @@ assert_args_equivalence(#amqqueue{name = QueueName, arguments = Args}, Args, RequiredArgs, QueueName, [<<"x-expires">>, <<"x-message-ttl">>]). check_declare_arguments(QueueName, Args) -> - Checks = [{<<"x-expires">>, fun check_positive_expires/2}, - {<<"x-message-ttl">>, fun check_non_neg_ttl/2}, + Checks = [{<<"x-expires">>, fun check_expires_arg/2}, + {<<"x-message-ttl">>, fun check_message_ttl_arg/2}, {<<"x-dead-letter-exchange">>, fun check_string_arg/2}, {<<"x-dead-letter-routing-key">>, fun check_dlxrk_arg/2}], [case rabbit_misc:table_lookup(Args, Key) of @@ -399,18 +399,17 @@ check_int_arg({Type, _}, _) -> false -> {error, {unacceptable_type, Type}} end. -check_positive_expires({Type, Val}, Args) -> +check_expires_arg({Type, Val}, Args) -> case check_int_arg({Type, Val}, Args) of - ok when Val == 0 -> {error, {value_zero_or_less, Val}}; + ok when Val == 0 -> {error, {value_zero, Val}}; ok -> rabbit_misc:check_expiry(Val); Error -> Error end. -check_non_neg_ttl({Type, Val}, Args) -> +check_message_ttl_arg({Type, Val}, Args) -> case check_int_arg({Type, Val}, Args) of - ok when Val >= 0 -> rabbit_misc:check_expiry(Val); - ok -> {error, {value_less_than_zero, Val}}; - Error -> Error + ok -> rabbit_misc:check_expiry(Val); + Error -> Error end. check_dlxrk_arg({longstr, _}, Args) -> diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 7ea345e1..bdc52852 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -1008,7 +1008,7 @@ term_to_json(V) when is_binary(V) orelse is_number(V) orelse V =:= null orelse check_expiry(N) when N > ?MAX_EXPIRY_TIMER -> {error, {value_too_big, N}}; check_expiry(N) when N < 0 -> - {error, {negative_value, N}}; + {error, {value_negative, N}}; check_expiry(_N) -> ok. -- cgit v1.2.1 From 7f23856e81433e12a3ecfba5044662744b21d7c6 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 5 Nov 2012 15:00:50 +0000 Subject: cosmetic --- src/rabbit_misc.erl | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index bdc52852..7e3cc3d7 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -1005,12 +1005,9 @@ term_to_json(V) when is_binary(V) orelse is_number(V) orelse V =:= null orelse V =:= true orelse V =:= false -> V. -check_expiry(N) when N > ?MAX_EXPIRY_TIMER -> - {error, {value_too_big, N}}; -check_expiry(N) when N < 0 -> - {error, {value_negative, N}}; -check_expiry(_N) -> - ok. +check_expiry(N) when N > ?MAX_EXPIRY_TIMER -> {error, {value_too_big, N}}; +check_expiry(N) when N < 0 -> {error, {value_negative, N}}; +check_expiry(_N) -> ok. base64url(In) -> lists:reverse(lists:foldl(fun ($\+, Acc) -> [$\- | Acc]; -- cgit v1.2.1 From d7432000dfda38dec0f42fd8729058f06c38521d Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 5 Nov 2012 17:24:58 +0000 Subject: switch back to using macros in order to eek out a bit more performance but at least make the macro signature uniform and pull out the conditional --- codegen.py | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/codegen.py b/codegen.py index 1cd20fb9..5234396f 100644 --- a/codegen.py +++ b/codegen.py @@ -235,8 +235,8 @@ def genErl(spec): return '<<' + ps + ', _:%d, R0/binary>>' % (16 - len(fields),) def writePropFieldLine(field): i = str(field.index) - print " {F%s, R%s} = if P%s =:= 0 -> {undefined, R%s}; true -> %s_prop(R%s) end," % \ - (i, str(field.index + 1), i, i, erlType(field.domain), i) + print " {F%s, R%s} = if P%s =:= 0 -> {undefined, R%s}; true -> ?%s_PROP(R%s, L%s, V%s, X%s) end," % \ + (i, str(field.index + 1), i, i, erlType(field.domain).upper(), i, i, i, i) if len(c.fields) == 0: print "decode_properties(%d, _) ->" % (c.index,) @@ -419,17 +419,29 @@ shortstr_size(S) -> _ -> exit(method_field_shortstr_overflow) end. -shortstr_prop(<>) -> - {S, X}. - -table_prop(<>) -> - {rabbit_binary_parser:parse_table(T), X}. - -octet_prop(<>) -> - {I, X}. - -timestamp_prop(<>) -> - {I, X}. +-define(SHORTSTR_PROP(R, L, V, X), + begin + <> = R, + {V, X} + end). + +-define(TABLE_PROP(R, L, V, X), + begin + <> = R, + {rabbit_binary_parser:parse_table(V), X} + end). + +-define(OCTET_PROP(R, L, V, X), + begin + <> = R, + {V, X} + end). + +-define(TIMESTAMP_PROP(R, L, V, X), + begin + <> = R, + {V, X} + end). """ version = "{%d, %d, %d}" % (spec.major, spec.minor, spec.revision) if version == '{8, 0, 0}': version = '{0, 8, 0}' -- cgit v1.2.1 From f8abe894b16efddcc501aad25d12934f1bd27c0d Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 5 Nov 2012 18:07:34 +0000 Subject: macrofy --- codegen.py | 51 +++++++++++++++++++++++++-------------------------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/codegen.py b/codegen.py index 8c6e42a1..5624658b 100644 --- a/codegen.py +++ b/codegen.py @@ -274,8 +274,8 @@ def genErl(spec): print " {P%s, R%s} = {F%s =:= 1, R%s}," % \ (i, str(field.index + 1), i, i) else: - print " {P%s, R%s} = if F%s =:= undefined -> {0, R%s}; true -> {1, [?%s_PROP(F%s) | R%s]} end," % \ - (i, str(field.index + 1), i, i, erlType(field.domain).upper(), i, i) + print " {P%s, R%s} = if F%s =:= undefined -> {0, R%s}; true -> {1, [?%s_PROP(F%s, L%s) | R%s]} end," % \ + (i, str(field.index + 1), i, i, erlType(field.domain).upper(), i, i, i) print "encode_properties(#'P_%s'{%s}) ->" % (erlangize(c.name), fieldMapList(c.fields)) if len(c.fields) == 0: @@ -477,32 +477,31 @@ shortstr_size(S) -> {V, X} end). -%% use of these functions by the codec depends on the protocol spec --compile({nowarn_unused_function, - [{shortstr_prop, 1}, {longstr_prop, 1}, {table_prop, 1}]}). +-define(SHORTSTR_PROP(X, L), + begin + L = size(X), + if L < 256 -> <>; + true -> exit(content_properties_shortstr_overflow) + end + end). -shortstr_prop(S) -> - L = size(S), - if L < 256 -> <>; - true -> exit(content_properties_shortstr_overflow) - end. +-define(LONGSTR_PROP(X, L), + begin + L = size(X), + <> + end). + +-define(OCTET_PROP(X, L), <>). +-define(SHORT_PROP(X, L), <>). +-define(LONG_PROP(X, L), <>). +-define(LONGLONG_PROP(X, L), <>). +-define(TIMESTAMP_PROP(X, L), <>). -longstr_prop(S) -> - L = size(S), - <>. - -table_prop(T) -> - BinT = rabbit_binary_generator:generate_table(T), - <<(size(BinT)):32, BinT/binary>>. - --define(SHORTSTR_PROP(X), shortstr_prop(X)). --define(LONGSTR_PROP(X), longstr_prop(X)). --define(OCTET_PROP(X), <>). --define(SHORT_PROP(X), <>). --define(LONG_PROP(X), <>). --define(LONGLONG_PROP(X), <>). --define(TIMESTAMP_PROP(X), <>). --define(TABLE_PROP(X), table_prop(X)). +-define(TABLE_PROP(X, T), + begin + T = rabbit_binary_generator:generate_table(X), + <<(size(T)):32, T/binary>> + end). """ version = "{%d, %d, %d}" % (spec.major, spec.minor, spec.revision) if version == '{8, 0, 0}': version = '{0, 8, 0}' -- cgit v1.2.1 From 0444acb2f8966bbbb6a8f0270a3a364151ac94ae Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 6 Nov 2012 12:33:56 +0000 Subject: GC the world when memory alarm goes off. --- src/rabbit_alarm.erl | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/rabbit_alarm.erl b/src/rabbit_alarm.erl index e6625b2b..7b706bd8 100644 --- a/src/rabbit_alarm.erl +++ b/src/rabbit_alarm.erl @@ -18,7 +18,7 @@ -behaviour(gen_event). --export([start_link/0, start/0, stop/0, register/2, set_alarm/1, +-export([start_link/0, start/0, stop/0, register/2, set_alarm/1, set_alarm_gc/1, clear_alarm/1, get_alarms/0, on_node_up/1, on_node_down/1]). -export([init/1, handle_call/2, handle_event/2, handle_info/2, @@ -38,6 +38,7 @@ -spec(start/0 :: () -> 'ok'). -spec(stop/0 :: () -> 'ok'). -spec(register/2 :: (pid(), rabbit_types:mfargs()) -> boolean()). +-spec(set_alarm_gc/1 :: (any()) -> 'ok'). -spec(set_alarm/1 :: (any()) -> 'ok'). -spec(clear_alarm/1 :: (any()) -> 'ok'). -spec(on_node_up/1 :: (node()) -> 'ok'). @@ -55,7 +56,7 @@ start() -> ok = gen_event:add_handler(?SERVER, ?MODULE, []), {ok, MemoryWatermark} = application:get_env(vm_memory_high_watermark), rabbit_sup:start_restartable_child( - vm_memory_monitor, [MemoryWatermark, fun rabbit_alarm:set_alarm/1, + vm_memory_monitor, [MemoryWatermark, fun rabbit_alarm:set_alarm_gc/1, fun rabbit_alarm:clear_alarm/1]), {ok, DiskLimit} = application:get_env(disk_free_limit), rabbit_sup:start_restartable_child(rabbit_disk_monitor, [DiskLimit]), @@ -67,8 +68,10 @@ register(Pid, HighMemMFA) -> gen_event:call(?SERVER, ?MODULE, {register, Pid, HighMemMFA}, infinity). -set_alarm(Alarm) -> gen_event:notify(?SERVER, {set_alarm, Alarm}). -clear_alarm(Alarm) -> gen_event:notify(?SERVER, {clear_alarm, Alarm}). +set_alarm_gc(Alarm) -> [erlang:garbage_collect(P) || P <- processes()], + set_alarm(Alarm). +set_alarm(Alarm) -> gen_event:notify(?SERVER, {set_alarm, Alarm}). +clear_alarm(Alarm) -> gen_event:notify(?SERVER, {clear_alarm, Alarm}). get_alarms() -> gen_event:call(?SERVER, ?MODULE, get_alarms, infinity). -- cgit v1.2.1 From 2fe207ca62b74de667a32740c28689454bb2b515 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 6 Nov 2012 12:46:15 +0000 Subject: Simplify --- src/rabbit_alarm.erl | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/rabbit_alarm.erl b/src/rabbit_alarm.erl index 7b706bd8..183bb538 100644 --- a/src/rabbit_alarm.erl +++ b/src/rabbit_alarm.erl @@ -18,7 +18,7 @@ -behaviour(gen_event). --export([start_link/0, start/0, stop/0, register/2, set_alarm/1, set_alarm_gc/1, +-export([start_link/0, start/0, stop/0, register/2, set_alarm/1, clear_alarm/1, get_alarms/0, on_node_up/1, on_node_down/1]). -export([init/1, handle_call/2, handle_event/2, handle_info/2, @@ -38,7 +38,6 @@ -spec(start/0 :: () -> 'ok'). -spec(stop/0 :: () -> 'ok'). -spec(register/2 :: (pid(), rabbit_types:mfargs()) -> boolean()). --spec(set_alarm_gc/1 :: (any()) -> 'ok'). -spec(set_alarm/1 :: (any()) -> 'ok'). -spec(clear_alarm/1 :: (any()) -> 'ok'). -spec(on_node_up/1 :: (node()) -> 'ok'). @@ -56,8 +55,10 @@ start() -> ok = gen_event:add_handler(?SERVER, ?MODULE, []), {ok, MemoryWatermark} = application:get_env(vm_memory_high_watermark), rabbit_sup:start_restartable_child( - vm_memory_monitor, [MemoryWatermark, fun rabbit_alarm:set_alarm_gc/1, - fun rabbit_alarm:clear_alarm/1]), + vm_memory_monitor, [MemoryWatermark, fun (Alarm) -> + gc_all(), + set_alarm(Alarm) + end, fun clear_alarm/1]), {ok, DiskLimit} = application:get_env(disk_free_limit), rabbit_sup:start_restartable_child(rabbit_disk_monitor, [DiskLimit]), ok. @@ -68,10 +69,8 @@ register(Pid, HighMemMFA) -> gen_event:call(?SERVER, ?MODULE, {register, Pid, HighMemMFA}, infinity). -set_alarm_gc(Alarm) -> [erlang:garbage_collect(P) || P <- processes()], - set_alarm(Alarm). -set_alarm(Alarm) -> gen_event:notify(?SERVER, {set_alarm, Alarm}). -clear_alarm(Alarm) -> gen_event:notify(?SERVER, {clear_alarm, Alarm}). +set_alarm(Alarm) -> gen_event:notify(?SERVER, {set_alarm, Alarm}). +clear_alarm(Alarm) -> gen_event:notify(?SERVER, {clear_alarm, Alarm}). get_alarms() -> gen_event:call(?SERVER, ?MODULE, get_alarms, infinity). @@ -228,3 +227,5 @@ handle_clear_alarm(file_descriptor_limit, State) -> handle_clear_alarm(Alarm, State) -> rabbit_log:warning("alarm '~p' cleared~n", [Alarm]), {ok, State}. + +gc_all() -> [erlang:garbage_collect(P) || P <- processes()]. -- cgit v1.2.1 From a63d98a89019e686c2ce2f59e88f1817d608fa22 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 6 Nov 2012 12:52:42 +0000 Subject: use ~p after all otherwise we end up with {leftover_string,[102,111,111,98,97,114]} in the logs --- src/rabbit_channel.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 79815d68..a94d2ab5 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -477,7 +477,7 @@ check_user_id_header(#'P_basic'{user_id = Claimed}, check_expiration_header(Props) -> case rabbit_basic:parse_expiration(Props) of {ok, _} -> ok; - {error, E} -> precondition_failed("invalid expiration '~s': ~w", + {error, E} -> precondition_failed("invalid expiration '~s': ~p", [Props#'P_basic'.expiration, E]) end. -- cgit v1.2.1 From ea80281bd7da6a6c89b184393dee1f60774aeba4 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 6 Nov 2012 12:57:35 +0000 Subject: cosmetic --- src/rabbit_alarm.erl | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/rabbit_alarm.erl b/src/rabbit_alarm.erl index 183bb538..f350f2b2 100644 --- a/src/rabbit_alarm.erl +++ b/src/rabbit_alarm.erl @@ -55,10 +55,9 @@ start() -> ok = gen_event:add_handler(?SERVER, ?MODULE, []), {ok, MemoryWatermark} = application:get_env(vm_memory_high_watermark), rabbit_sup:start_restartable_child( - vm_memory_monitor, [MemoryWatermark, fun (Alarm) -> - gc_all(), - set_alarm(Alarm) - end, fun clear_alarm/1]), + vm_memory_monitor, [MemoryWatermark, + fun (Alarm) -> gc_all(), set_alarm(Alarm) end, + fun clear_alarm/1]), {ok, DiskLimit} = application:get_env(disk_free_limit), rabbit_sup:start_restartable_child(rabbit_disk_monitor, [DiskLimit]), ok. -- cgit v1.2.1 From fc90dd715ad70a9c5764f09d1cbc55057df500b1 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 6 Nov 2012 13:04:31 +0000 Subject: send out the alarm event before gc so subscribers can react to it quickly --- src/rabbit_alarm.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_alarm.erl b/src/rabbit_alarm.erl index f350f2b2..9d3798d8 100644 --- a/src/rabbit_alarm.erl +++ b/src/rabbit_alarm.erl @@ -56,7 +56,7 @@ start() -> {ok, MemoryWatermark} = application:get_env(vm_memory_high_watermark), rabbit_sup:start_restartable_child( vm_memory_monitor, [MemoryWatermark, - fun (Alarm) -> gc_all(), set_alarm(Alarm) end, + fun (Alarm) -> ok = set_alarm(Alarm), gc(), ok end, fun clear_alarm/1]), {ok, DiskLimit} = application:get_env(disk_free_limit), rabbit_sup:start_restartable_child(rabbit_disk_monitor, [DiskLimit]), @@ -227,4 +227,4 @@ handle_clear_alarm(Alarm, State) -> rabbit_log:warning("alarm '~p' cleared~n", [Alarm]), {ok, State}. -gc_all() -> [erlang:garbage_collect(P) || P <- processes()]. +gc() -> [erlang:garbage_collect(P) || P <- processes()]. -- cgit v1.2.1 From 6d2246365ddeb028503f70dce556bd6a41fe6de1 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 6 Nov 2012 13:05:40 +0000 Subject: propagate result --- src/rabbit_alarm.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_alarm.erl b/src/rabbit_alarm.erl index 9d3798d8..2f182423 100644 --- a/src/rabbit_alarm.erl +++ b/src/rabbit_alarm.erl @@ -56,7 +56,7 @@ start() -> {ok, MemoryWatermark} = application:get_env(vm_memory_high_watermark), rabbit_sup:start_restartable_child( vm_memory_monitor, [MemoryWatermark, - fun (Alarm) -> ok = set_alarm(Alarm), gc(), ok end, + fun (Alarm) -> R = set_alarm(Alarm), gc(), R end, fun clear_alarm/1]), {ok, DiskLimit} = application:get_env(disk_free_limit), rabbit_sup:start_restartable_child(rabbit_disk_monitor, [DiskLimit]), -- cgit v1.2.1 From fd9e7679a8df897c06dfb9e26ad6ae79d7b0b170 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 6 Nov 2012 16:00:07 +0000 Subject: remove cruft --- src/rabbit_plugins.erl | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/rabbit_plugins.erl b/src/rabbit_plugins.erl index abe9b089..9f94af7d 100644 --- a/src/rabbit_plugins.erl +++ b/src/rabbit_plugins.erl @@ -19,18 +19,6 @@ -export([setup/0, active/0, read_enabled/1, list/1, dependencies/3]). --define(VERBOSE_DEF, {?VERBOSE_OPT, flag}). --define(MINIMAL_DEF, {?MINIMAL_OPT, flag}). --define(ENABLED_DEF, {?ENABLED_OPT, flag}). --define(ENABLED_ALL_DEF, {?ENABLED_ALL_OPT, flag}). - --define(GLOBAL_DEFS, []). - --define(COMMANDS, - [{list, [?VERBOSE_DEF, ?MINIMAL_DEF, ?ENABLED_DEF, ?ENABLED_ALL_DEF]}, - enable, - disable]). - %%---------------------------------------------------------------------------- -ifdef(use_specs). -- cgit v1.2.1 From 1a4262f574568e17a8702ff6772a7ca8eed8c2d6 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 7 Nov 2012 10:24:59 +0000 Subject: inline --- src/rabbit_alarm.erl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/rabbit_alarm.erl b/src/rabbit_alarm.erl index 2f182423..675d3697 100644 --- a/src/rabbit_alarm.erl +++ b/src/rabbit_alarm.erl @@ -56,7 +56,11 @@ start() -> {ok, MemoryWatermark} = application:get_env(vm_memory_high_watermark), rabbit_sup:start_restartable_child( vm_memory_monitor, [MemoryWatermark, - fun (Alarm) -> R = set_alarm(Alarm), gc(), R end, + fun (Alarm) -> + R = set_alarm(Alarm), + [garbage_collect(P) || P <- processes()], + R + end, fun clear_alarm/1]), {ok, DiskLimit} = application:get_env(disk_free_limit), rabbit_sup:start_restartable_child(rabbit_disk_monitor, [DiskLimit]), @@ -226,5 +230,3 @@ handle_clear_alarm(file_descriptor_limit, State) -> handle_clear_alarm(Alarm, State) -> rabbit_log:warning("alarm '~p' cleared~n", [Alarm]), {ok, State}. - -gc() -> [erlang:garbage_collect(P) || P <- processes()]. -- cgit v1.2.1 From a8cb4048fafb489cccd57a96f6668837d8d9098e Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 7 Nov 2012 14:54:33 +0000 Subject: Apply TTL after DLX, during recovery we want to have a working DLX when messages are expired. --- src/rabbit_amqqueue_process.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 43fe3578..fb457478 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -247,9 +247,9 @@ process_args(State = #q{q = #amqqueue{arguments = Arguments}}) -> end end, State, [{<<"x-expires">>, fun init_expires/2}, - {<<"x-message-ttl">>, fun init_ttl/2}, {<<"x-dead-letter-exchange">>, fun init_dlx/2}, - {<<"x-dead-letter-routing-key">>, fun init_dlx_routing_key/2}]). + {<<"x-dead-letter-routing-key">>, fun init_dlx_routing_key/2}, + {<<"x-message-ttl">>, fun init_ttl/2}]). init_expires(Expires, State) -> ensure_expiry_timer(State#q{expires = Expires}). -- cgit v1.2.1 From 403786326c589a18968f2d3d1cb318e5a09a52b8 Mon Sep 17 00:00:00 2001 From: Alvaro Videla Date: Wed, 7 Nov 2012 16:17:50 +0000 Subject: adds the erlang base dir before calling erl --- scripts/rabbitmq-defaults | 8 ++++++++ scripts/rabbitmq-plugins | 2 +- scripts/rabbitmq-server | 4 ++-- scripts/rabbitmqctl | 2 +- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/scripts/rabbitmq-defaults b/scripts/rabbitmq-defaults index 4763f086..f618f584 100644 --- a/scripts/rabbitmq-defaults +++ b/scripts/rabbitmq-defaults @@ -18,6 +18,14 @@ ### next line potentially updated in package install steps SYS_PREFIX= +### next line will be updated when generating a standalone release +ERL_DIR= + +### TODO fix these values +CLEAN_BOOT_FILE="${SYS_PREFIX}/releases/${RELEASE_VERSION}/start_clean" +SASL_BOOT_FILE="${SYS_PREFIX}/releases/${RELEASE_VERSION}/start_sasl" + + ## Set default values CONFIG_FILE=${SYS_PREFIX}/etc/rabbitmq/rabbitmq diff --git a/scripts/rabbitmq-plugins b/scripts/rabbitmq-plugins index 97c74791..d9347258 100755 --- a/scripts/rabbitmq-plugins +++ b/scripts/rabbitmq-plugins @@ -26,7 +26,7 @@ ##--- End of overridden variables -exec erl \ +exec ${ERL_DIR}erl \ -pa "${RABBITMQ_HOME}/ebin" \ -noinput \ -hidden \ diff --git a/scripts/rabbitmq-server b/scripts/rabbitmq-server index e1686627..c3d61868 100755 --- a/scripts/rabbitmq-server +++ b/scripts/rabbitmq-server @@ -82,7 +82,7 @@ case "$(uname -s)" in esac RABBITMQ_EBIN_ROOT="${RABBITMQ_HOME}/ebin" -if ! erl -pa "$RABBITMQ_EBIN_ROOT" \ +if ! ${ERL_DIR}erl -pa "$RABBITMQ_EBIN_ROOT" \ -noinput \ -hidden \ -s rabbit_prelaunch \ @@ -103,7 +103,7 @@ RABBITMQ_LISTEN_ARG= # there is no other way of preventing their expansion. set -f -exec erl \ +exec ${ERL_DIR}erl \ -pa ${RABBITMQ_EBIN_ROOT} \ ${RABBITMQ_START_RABBIT} \ -sname ${RABBITMQ_NODENAME} \ diff --git a/scripts/rabbitmqctl b/scripts/rabbitmqctl index a5fade72..b7351624 100755 --- a/scripts/rabbitmqctl +++ b/scripts/rabbitmqctl @@ -26,7 +26,7 @@ ##--- End of overridden variables -exec erl \ +exec ${ERL_DIR}erl \ -pa "${RABBITMQ_HOME}/ebin" \ -noinput \ -hidden \ -- cgit v1.2.1 From 266d92fc1fba50e54aa4842a4423e7fb3be35fa0 Mon Sep 17 00:00:00 2001 From: Alvaro Videla Date: Wed, 7 Nov 2012 16:18:32 +0000 Subject: adds Makefile for standalone mac release --- packaging/mac/Makefile | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 packaging/mac/Makefile diff --git a/packaging/mac/Makefile b/packaging/mac/Makefile new file mode 100644 index 00000000..a21ec88a --- /dev/null +++ b/packaging/mac/Makefile @@ -0,0 +1,61 @@ +VERSION=0.0.0 +SOURCE_DIR=rabbitmq-server-$(VERSION) +TARGET_DIR=rabbitmq_server-$(VERSION) +TARGET_TARBALL=rabbitmq-server-mac-standalone-$(VERSION) +RLS_DIR=$(TARGET_DIR)/release + +ERTS_VSN=$(shell erl -noshell -eval 'io:format("~s", [erlang:system_info(version)]), halt().') +ERTS_ROOT_DIR=$(shell erl -noshell -eval 'io:format("~s", [code:root_dir()]), halt().') + +# used to generate the erlang release +RABBITMQ_HOME=$(TARGET_DIR) +RABBITMQ_EBIN_ROOT=$(RABBITMQ_HOME)/ebin +RABBITMQ_PLUGINS_DIR=$(RABBITMQ_HOME)/plugins +RABBITMQ_PLUGINS_EXPAND_DIR=$(RABBITMQ_PLUGINS_DIR)/expand + +dist: + tar -zxf ../../dist/$(SOURCE_DIR).tar.gz + + $(MAKE) -C $(SOURCE_DIR) \ + TARGET_DIR=`pwd`/$(TARGET_DIR) \ + SBIN_DIR=`pwd`/$(TARGET_DIR)/sbin \ + MAN_DIR=`pwd`/$(TARGET_DIR)/share/man \ + install + + sed -e 's:^SYS_PREFIX=$$:SYS_PREFIX=\$${RABBITMQ_HOME}:' \ + $(TARGET_DIR)/sbin/rabbitmq-defaults >$(TARGET_DIR)/sbin/rabbitmq-defaults.tmp \ + && sed -e 's:^ERL_DIR=$$:ERL_DIR=\$${RABBITMQ_HOME}/erts-$(ERTS_VSN)/bin/:' \ + $(TARGET_DIR)/sbin/rabbitmq-defaults.tmp >$(TARGET_DIR)/sbin/rabbitmq-defaults + + chmod 0755 $(TARGET_DIR)/sbin/rabbitmq-defaults + + mkdir -p $(TARGET_DIR)/etc/rabbitmq + + $(MAKE) generate_release + +## todo see where the .tar is being created + mkdirp -p $(RLS_DIR) + tar -C $(RLS_DIR) -xzf $(RABBITMQ_HOME)/rabbit.tar.gz + +# add minimal boot file + cp $(ERTS_ROOT_DIR)/bin/start_clean.boot $(RLS_DIR)/releases/$(VERSION) + cp $(ERTS_ROOT_DIR)/bin/start_sasl.boot $(RLS_DIR)/releases/$(VERSION) + + tar -zcf $(TARGET_TARBALL).tar.gz $(RLS_DIR) + rm -rf $(SOURCE_DIR) $(TARGET_DIR) + +clean: clean_partial + rm -f rabbitmq-server-generic-unix-*.tar.gz + +clean_partial: + rm -rf $(SOURCE_DIR) + rm -rf $(TARGET_DIR) + +.PHONY : generate_release +generate_release: + erl \ + -pa "$(RABBITMQ_EBIN_ROOT)" \ + -noinput \ + -hidden \ + -s rabbit_release \ + -extra "$(RABBITMQ_PLUGINS_DIR)" "$(RABBITMQ_PLUGINS_EXPAND_DIR)" "$(RABBITMQ_HOME)" -- cgit v1.2.1 From 888215642285b60f4960bafbeb52f355a7fd04d7 Mon Sep 17 00:00:00 2001 From: Alvaro Videla Date: Wed, 7 Nov 2012 16:24:22 +0000 Subject: adds file to generate the erlang release --- src/rabbit_release.erl | 148 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 src/rabbit_release.erl diff --git a/src/rabbit_release.erl b/src/rabbit_release.erl new file mode 100644 index 00000000..bcba2699 --- /dev/null +++ b/src/rabbit_release.erl @@ -0,0 +1,148 @@ +%% based on rabbit_prelaunch.erl from rabbitmq-server source code +-module(rabbit_release). + +-export([start/0, stop/0, make_tar/2]). + +-include("rabbit.hrl"). + +-define(BaseApps, [rabbit]). +-define(ERROR_CODE, 1). + +start() -> + %% Determine our various directories + [PluginsDistDir, UnpackedPluginDir, RabbitHome] = + init:get_plain_arguments(), + RootName = UnpackedPluginDir ++ "/rabbit", + + prepare_plugins(PluginsDistDir, UnpackedPluginDir), + + PluginAppNames = [ P#plugin.name || P <- rabbit_plugins:list(PluginsDistDir) ], + + %% we need to call find_plugins because it has the secondary effect of adding the + %% plugin ebin folder to the code path. We need that in order to load the plugin app + RequiredApps = find_plugins(UnpackedPluginDir), + + %% Build the entire set of dependencies - this will load the + %% applications along the way + AllApps = case catch sets:to_list(expand_dependencies(RequiredApps)) of + {failed_to_load_app, App, Err} -> + terminate("failed to load application ~s:~n~p", + [App, Err]); + AppList -> + AppList + end, + + %% we need a list of ERTS apps we need to ship with rabbit + BaseApps = AllApps -- PluginAppNames, + + AppVersions = [determine_version(App) || App <- BaseApps], + RabbitVersion = proplists:get_value(rabbit, AppVersions), + + %% Build the overall release descriptor + RDesc = {release, + {"rabbit", RabbitVersion}, + {erts, erlang:system_info(version)}, + AppVersions}, + + %% Write it out to $RABBITMQ_PLUGINS_EXPAND_DIR/rabbit.rel + rabbit_file:write_file(RootName ++ ".rel", io_lib:format("~p.~n", [RDesc])), + + %% Compile the script + systools:make_script(RootName), + systools:script2boot(RootName), + %% Make release tarfile + make_tar(RootName, RabbitHome), + terminate(0), + ok. + +stop() -> + ok. + +make_tar(Release, RabbitHome) -> + systools:make_tar(Release, + [ + {dirs, [docs, etc, include, plugins, sbin, share]}, + {erts, code:root_dir()}, + {outdir, RabbitHome} + ]). + +determine_version(App) -> + application:load(App), + {ok, Vsn} = application:get_key(App, vsn), + {App, Vsn}. + +delete_recursively(Fn) -> + case rabbit_file:recursive_delete([Fn]) of + ok -> ok; + {error, {Path, E}} -> {error, {cannot_delete, Path, E}}; + Error -> Error + end. + +prepare_plugins(PluginsDistDir, DestDir) -> + %% Eliminate the contents of the destination directory + case delete_recursively(DestDir) of + ok -> ok; + {error, E} -> terminate("Could not delete dir ~s (~p)", [DestDir, E]) + end, + case filelib:ensure_dir(DestDir ++ "/") of + ok -> ok; + {error, E2} -> terminate("Could not create dir ~s (~p)", [DestDir, E2]) + end, + + [prepare_plugin(Plugin, DestDir) || Plugin <- rabbit_plugins:list(PluginsDistDir)]. + +prepare_plugin(#plugin{type = ez, location = Location}, PluginDestDir) -> + zip:unzip(Location, [{cwd, PluginDestDir}]); +prepare_plugin(#plugin{type = dir, name = Name, location = Location}, + PluginsDestDir) -> + rabbit_file:recursive_copy(Location, + filename:join([PluginsDestDir, Name])). + +expand_dependencies(Pending) -> + expand_dependencies(sets:new(), Pending). +expand_dependencies(Current, []) -> + Current; +expand_dependencies(Current, [Next|Rest]) -> + case sets:is_element(Next, Current) of + true -> + expand_dependencies(Current, Rest); + false -> + case application:load(Next) of + ok -> + ok; + {error, {already_loaded, _}} -> + ok; + {error, Reason} -> + throw({failed_to_load_app, Next, Reason}) + end, + {ok, Required} = application:get_key(Next, applications), + Unique = [A || A <- Required, not(sets:is_element(A, Current))], + expand_dependencies(sets:add_element(Next, Current), Rest ++ Unique) + end. + +find_plugins(PluginDir) -> + [prepare_dir_plugin(PluginName) || + PluginName <- filelib:wildcard(PluginDir ++ "/*/ebin/*.app")]. + +prepare_dir_plugin(PluginAppDescFn) -> + %% Add the plugin ebin directory to the load path + PluginEBinDirN = filename:dirname(PluginAppDescFn), + code:add_path(PluginEBinDirN), + + %% We want the second-last token + NameTokens = string:tokens(PluginAppDescFn,"/."), + PluginNameString = lists:nth(length(NameTokens) - 1, NameTokens), + list_to_atom(PluginNameString). + +terminate(Fmt, Args) -> + io:format("ERROR: " ++ Fmt ++ "~n", Args), + terminate(?ERROR_CODE). + +terminate(Status) -> + case os:type() of + {unix, _} -> halt(Status); + {win32, _} -> init:stop(Status), + receive + after infinity -> ok + end + end. -- cgit v1.2.1 From a9a928fdce4d0bdf748a0d42f9fb0e178dfda51c Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 7 Nov 2012 21:46:02 +0000 Subject: cosmetic --- src/rabbit_amqqueue_process.erl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index fb457478..6de2ae1e 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -219,17 +219,17 @@ declare(Recover, From, State = #q{q = Q, {stop, normal, Err, State} end. -matches(true, Q, Q) -> true; +matches(true, Q, Q) -> true; matches(true, _Q, _Q1) -> false; matches(false, Q1, Q2) -> %% i.e. not policy - Q1#amqqueue.name =:= Q2#amqqueue.name andalso - Q1#amqqueue.durable =:= Q2#amqqueue.durable andalso - Q1#amqqueue.auto_delete =:= Q2#amqqueue.auto_delete andalso + Q1#amqqueue.name =:= Q2#amqqueue.name andalso + Q1#amqqueue.durable =:= Q2#amqqueue.durable andalso + Q1#amqqueue.auto_delete =:= Q2#amqqueue.auto_delete andalso Q1#amqqueue.exclusive_owner =:= Q2#amqqueue.exclusive_owner andalso - Q1#amqqueue.arguments =:= Q2#amqqueue.arguments andalso - Q1#amqqueue.pid =:= Q2#amqqueue.pid andalso - Q1#amqqueue.slave_pids =:= Q2#amqqueue.slave_pids. + Q1#amqqueue.arguments =:= Q2#amqqueue.arguments andalso + Q1#amqqueue.pid =:= Q2#amqqueue.pid andalso + Q1#amqqueue.slave_pids =:= Q2#amqqueue.slave_pids. bq_init(BQ, Q, Recover) -> Self = self(), -- cgit v1.2.1 From 78be28fe7d58d32c8c6cf70e5d5775e513e9bd91 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 8 Nov 2012 12:11:25 +0000 Subject: defer final phase of queue recovery until routing recovery has completed That way dead-lettering during the final phase has all the correct routing in place --- src/rabbit.erl | 4 +++- src/rabbit_amqqueue.erl | 20 +++++++++++++++----- src/rabbit_amqqueue_process.erl | 28 +++++++++++++++++++--------- 3 files changed, 37 insertions(+), 15 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index c52c296a..d156d570 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -570,7 +570,9 @@ boot_delegate() -> rabbit_sup:start_supervisor_child(delegate_sup, [Count]). recover() -> - rabbit_binding:recover(rabbit_exchange:recover(), rabbit_amqqueue:start()). + RecoveredQNames = rabbit_amqqueue:recover(), + ok = rabbit_binding:recover(rabbit_exchange:recover(), RecoveredQNames), + rabbit_amqqueue:start(RecoveredQNames). maybe_insert_default_data() -> case rabbit_table:is_empty() of diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 922951be..16cd2bfd 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -16,7 +16,8 @@ -module(rabbit_amqqueue). --export([start/0, stop/0, declare/5, delete_immediately/1, delete/3, purge/1]). +-export([recover/0, stop/0, start/1, declare/5, + delete_immediately/1, delete/3, purge/1]). -export([pseudo_queue/2]). -export([lookup/1, not_found_or_absent/1, with/2, with/3, with_or_die/2, assert_equivalence/5, @@ -64,8 +65,9 @@ {'absent', rabbit_types:amqqueue()}). -type(not_found_or_absent() :: 'not_found' | {'absent', rabbit_types:amqqueue()}). --spec(start/0 :: () -> [name()]). +-spec(recover/0 :: () -> [name()]). -spec(stop/0 :: () -> 'ok'). +-spec(start/1 :: ([name()]) -> 'ok'). -spec(declare/5 :: (name(), boolean(), boolean(), rabbit_framing:amqp_table(), rabbit_types:maybe(pid())) @@ -179,7 +181,7 @@ -define(CONSUMER_INFO_KEYS, [queue_name, channel_pid, consumer_tag, ack_required]). -start() -> +recover() -> %% Clear out remnants of old incarnation, in case we restarted %% faster than other nodes handled DOWN messages from us. on_node_down(node()), @@ -199,6 +201,14 @@ stop() -> {ok, BQ} = application:get_env(rabbit, backing_queue_module), ok = BQ:stop(). +start(QNames) -> + %% At this point all recovered queues and their bindings are + %% visible to routing, so now it is safe for them to complete + %% their initialisation (which may involve interacting with other + %% queues). + [Pid ! {self(), go} || #amqqueue{pid = Pid} <- lookup(QNames)], + ok. + find_durable_queues() -> Node = node(), %% TODO: use dirty ops instead @@ -212,7 +222,7 @@ find_durable_queues() -> recover_durable_queues(DurableQueues) -> Qs = [start_queue_process(node(), Q) || Q <- DurableQueues], [QName || Q = #amqqueue{name = QName, pid = Pid} <- Qs, - gen_server2:call(Pid, {init, true}, infinity) == {new, Q}]. + gen_server2:call(Pid, {init, self()}, infinity) == {new, Q}]. declare(QueueName, Durable, AutoDelete, Args, Owner) -> ok = check_declare_arguments(QueueName, Args), @@ -227,7 +237,7 @@ declare(QueueName, Durable, AutoDelete, Args, Owner) -> gm_pids = []}), {Node, _MNodes} = rabbit_mirror_queue_misc:suggested_queue_nodes(Q0), Q1 = start_queue_process(Node, Q0), - gen_server2:call(Q1#amqqueue.pid, {init, false}, infinity). + gen_server2:call(Q1#amqqueue.pid, {init, new}, infinity). internal_declare(Q, true) -> rabbit_misc:execute_mnesia_tx_with_tail( diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index fb457478..15d7146a 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -195,7 +195,7 @@ code_change(_OldVsn, State, _Extra) -> declare(Recover, From, State = #q{q = Q, backing_queue = BQ, backing_queue_state = undefined}) -> - case rabbit_amqqueue:internal_declare(Q, Recover) of + case rabbit_amqqueue:internal_declare(Q, Recover =/= new) of #amqqueue{} = Q1 -> case matches(Recover, Q, Q1) of true -> @@ -206,6 +206,7 @@ declare(Recover, From, State = #q{q = Q, self(), {rabbit_amqqueue, set_ram_duration_target, [self()]}), BQS = bq_init(BQ, Q, Recover), + recovery_barrier(Recover), State1 = process_args(State#q{backing_queue_state = BQS}), rabbit_event:notify(queue_created, infos(?CREATION_EVENT_KEYS, State1)), @@ -219,9 +220,7 @@ declare(Recover, From, State = #q{q = Q, {stop, normal, Err, State} end. -matches(true, Q, Q) -> true; -matches(true, _Q, _Q1) -> false; -matches(false, Q1, Q2) -> +matches(new, Q1, Q2) -> %% i.e. not policy Q1#amqqueue.name =:= Q2#amqqueue.name andalso Q1#amqqueue.durable =:= Q2#amqqueue.durable andalso @@ -229,15 +228,26 @@ matches(false, Q1, Q2) -> Q1#amqqueue.exclusive_owner =:= Q2#amqqueue.exclusive_owner andalso Q1#amqqueue.arguments =:= Q2#amqqueue.arguments andalso Q1#amqqueue.pid =:= Q2#amqqueue.pid andalso - Q1#amqqueue.slave_pids =:= Q2#amqqueue.slave_pids. + Q1#amqqueue.slave_pids =:= Q2#amqqueue.slave_pids; +matches(_, Q, Q) -> true; +matches(_, _Q, _Q1) -> false. bq_init(BQ, Q, Recover) -> Self = self(), - BQ:init(Q, Recover, + BQ:init(Q, Recover =/= new, fun (Mod, Fun) -> rabbit_amqqueue:run_backing_queue(Self, Mod, Fun) end). +recovery_barrier(new) -> + ok; +recovery_barrier(BarrierPid) -> + MRef = erlang:monitor(process, BarrierPid), + receive + {BarrierPid, go} -> erlang:demonitor(MRef, [flush]); + {'DOWN', MRef, process, _, _} -> ok + end. + process_args(State = #q{q = #amqqueue{arguments = Arguments}}) -> lists:foldl( fun({Arg, Fun}, State1) -> @@ -1001,9 +1011,9 @@ handle_call({init, Recover}, From, q = #amqqueue{name = QName} = Q} = State, gen_server2:reply(From, not_found), case Recover of - true -> ok; - _ -> rabbit_log:warning( - "Queue ~p exclusive owner went away~n", [QName]) + new -> rabbit_log:warning( + "Queue ~p exclusive owner went away~n", [QName]); + _ -> ok end, BQS = bq_init(BQ, Q, Recover), %% Rely on terminate to delete the queue. -- cgit v1.2.1 From d622d03adf677904623eca12a92cb2e9cfca35c6 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 8 Nov 2012 12:41:39 +0000 Subject: pass q records instead of names between recovery phases That way we can guarantee that the qpids we are talking to in the start phase are the same as in the recovery phase --- src/rabbit.erl | 7 ++++--- src/rabbit_amqqueue.erl | 12 ++++++------ 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index d156d570..ef9f5f56 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -570,9 +570,10 @@ boot_delegate() -> rabbit_sup:start_supervisor_child(delegate_sup, [Count]). recover() -> - RecoveredQNames = rabbit_amqqueue:recover(), - ok = rabbit_binding:recover(rabbit_exchange:recover(), RecoveredQNames), - rabbit_amqqueue:start(RecoveredQNames). + Qs = rabbit_amqqueue:recover(), + ok = rabbit_binding:recover(rabbit_exchange:recover(), + [QName || #amqqueue{name = QName} <- Qs]), + rabbit_amqqueue:start(Qs). maybe_insert_default_data() -> case rabbit_table:is_empty() of diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 16cd2bfd..9fb453c1 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -65,9 +65,9 @@ {'absent', rabbit_types:amqqueue()}). -type(not_found_or_absent() :: 'not_found' | {'absent', rabbit_types:amqqueue()}). --spec(recover/0 :: () -> [name()]). +-spec(recover/0 :: () -> [rabbit_types:amqqueue()]). -spec(stop/0 :: () -> 'ok'). --spec(start/1 :: ([name()]) -> 'ok'). +-spec(start/1 :: ([rabbit_types:amqqueue()]) -> 'ok'). -spec(declare/5 :: (name(), boolean(), boolean(), rabbit_framing:amqp_table(), rabbit_types:maybe(pid())) @@ -201,12 +201,12 @@ stop() -> {ok, BQ} = application:get_env(rabbit, backing_queue_module), ok = BQ:stop(). -start(QNames) -> +start(Qs) -> %% At this point all recovered queues and their bindings are %% visible to routing, so now it is safe for them to complete %% their initialisation (which may involve interacting with other %% queues). - [Pid ! {self(), go} || #amqqueue{pid = Pid} <- lookup(QNames)], + [Pid ! {self(), go} || #amqqueue{pid = Pid} <- Qs], ok. find_durable_queues() -> @@ -221,8 +221,8 @@ find_durable_queues() -> recover_durable_queues(DurableQueues) -> Qs = [start_queue_process(node(), Q) || Q <- DurableQueues], - [QName || Q = #amqqueue{name = QName, pid = Pid} <- Qs, - gen_server2:call(Pid, {init, self()}, infinity) == {new, Q}]. + [Q || Q = #amqqueue{pid = Pid} <- Qs, + gen_server2:call(Pid, {init, self()}, infinity) == {new, Q}]. declare(QueueName, Durable, AutoDelete, Args, Owner) -> ok = check_declare_arguments(QueueName, Args), -- cgit v1.2.1 From 0aca77c2ad5ab599d74723704bda3355b06f5af4 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 8 Nov 2012 12:53:27 +0000 Subject: Pass length to prioritise_* --- src/file_handle_cache.erl | 4 ++-- src/gen_server2.erl | 12 ++++++++---- src/gm.erl | 8 ++++---- src/rabbit_amqqueue_process.erl | 10 +++++----- src/rabbit_channel.erl | 10 +++++----- src/rabbit_limiter.erl | 6 +++--- src/rabbit_mirror_queue_slave.erl | 10 +++++----- src/rabbit_msg_store.erl | 10 +++++----- src/rabbit_msg_store_gc.erl | 6 +++--- src/worker_pool_worker.erl | 6 +++--- 10 files changed, 43 insertions(+), 39 deletions(-) diff --git a/src/file_handle_cache.erl b/src/file_handle_cache.erl index 3260d369..f39ba01d 100644 --- a/src/file_handle_cache.erl +++ b/src/file_handle_cache.erl @@ -152,7 +152,7 @@ -export([ulimit/0]). -export([start_link/0, start_link/2, init/1, handle_call/3, handle_cast/2, - handle_info/2, terminate/2, code_change/3, prioritise_cast/2]). + handle_info/2, terminate/2, code_change/3, prioritise_cast/3]). -define(SERVER, ?MODULE). -define(RESERVED_FOR_OTHERS, 100). @@ -848,7 +848,7 @@ init([AlarmSet, AlarmClear]) -> alarm_set = AlarmSet, alarm_clear = AlarmClear }}. -prioritise_cast(Msg, _State) -> +prioritise_cast(Msg, _Len, _State) -> case Msg of {release, _, _} -> 5; _ -> 0 diff --git a/src/gen_server2.erl b/src/gen_server2.erl index 78bbbe06..7bdfa91a 100644 --- a/src/gen_server2.erl +++ b/src/gen_server2.erl @@ -1179,16 +1179,20 @@ find_prioritisers(GS2State = #gs2_state { mod = Mod }) -> function_exported_or_default(Mod, Fun, Arity, Default) -> case erlang:function_exported(Mod, Fun, Arity) of true -> case Arity of - 2 -> fun (Msg, GS2State = #gs2_state { state = State }) -> - case catch Mod:Fun(Msg, State) of + 2 -> fun (Msg, GS2State = #gs2_state { queue = Queue, + state = State }) -> + Length = priority_queue:len(Queue), + case catch Mod:Fun(Msg, Length, State) of Res when is_integer(Res) -> Res; Err -> handle_common_termination(Err, Msg, GS2State) end end; - 3 -> fun (Msg, From, GS2State = #gs2_state { state = State }) -> - case catch Mod:Fun(Msg, From, State) of + 3 -> fun (Msg, From, GS2State = #gs2_state { queue = Queue, + state = State }) -> + Length = priority_queue:len(Queue), + case catch Mod:Fun(Msg, From, Length, State) of Res when is_integer(Res) -> Res; Err -> diff --git a/src/gm.erl b/src/gm.erl index 4a95de0d..98685ebb 100644 --- a/src/gm.erl +++ b/src/gm.erl @@ -380,7 +380,7 @@ confirmed_broadcast/2, info/1, forget_group/1]). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, - code_change/3, prioritise_info/2]). + code_change/3, prioritise_info/3]). -ifndef(use_specs). -export([behaviour_info/1]). @@ -718,12 +718,12 @@ terminate(Reason, State = #state { module = Module, code_change(_OldVsn, State, _Extra) -> {ok, State}. -prioritise_info(flush, _State) -> +prioritise_info(flush, _Len, _State) -> 1; -prioritise_info({'DOWN', _MRef, process, _Pid, _Reason}, +prioritise_info({'DOWN', _MRef, process, _Pid, _Reason}, _Len, #state { members_state = MS }) when MS /= undefined -> 1; -prioritise_info(_, _State) -> +prioritise_info(_, _Len, _State) -> 0. diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 43fe3578..d706de3c 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -29,8 +29,8 @@ -export([init_with_backing_queue_state/7]). -export([init/1, terminate/2, code_change/3, handle_call/3, handle_cast/2, - handle_info/2, handle_pre_hibernate/1, prioritise_call/3, - prioritise_cast/2, prioritise_info/2, format_message_queue/2]). + handle_info/2, handle_pre_hibernate/1, prioritise_call/4, + prioritise_cast/3, prioritise_info/3, format_message_queue/2]). %% Queue's state -record(q, {q, @@ -956,7 +956,7 @@ emit_consumer_deleted(ChPid, ConsumerTag) -> %%---------------------------------------------------------------------------- -prioritise_call(Msg, _From, _State) -> +prioritise_call(Msg, _From, _Len, _State) -> case Msg of info -> 9; {info, _Items} -> 9; @@ -965,7 +965,7 @@ prioritise_call(Msg, _From, _State) -> _ -> 0 end. -prioritise_cast(Msg, _State) -> +prioritise_cast(Msg, _Len, _State) -> case Msg of delete_immediately -> 8; {set_ram_duration_target, _Duration} -> 8; @@ -974,7 +974,7 @@ prioritise_cast(Msg, _State) -> _ -> 0 end. -prioritise_info(Msg, #q{q = #amqqueue{exclusive_owner = DownPid}}) -> +prioritise_info(Msg, _Len, #q{q = #amqqueue{exclusive_owner = DownPid}}) -> case Msg of {'DOWN', _, process, DownPid, _} -> 8; update_ram_duration -> 8; diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index a94d2ab5..c14a8b34 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -27,8 +27,8 @@ -export([force_event_refresh/0]). -export([init/1, terminate/2, code_change/3, handle_call/3, handle_cast/2, - handle_info/2, handle_pre_hibernate/1, prioritise_call/3, - prioritise_cast/2, prioritise_info/2, format_message_queue/2]). + handle_info/2, handle_pre_hibernate/1, prioritise_call/4, + prioritise_cast/3, prioritise_info/3, format_message_queue/2]). %% Internal -export([list_local/0]). @@ -213,20 +213,20 @@ init([Channel, ReaderPid, WriterPid, ConnPid, ConnName, Protocol, User, VHost, {ok, State1, hibernate, {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}. -prioritise_call(Msg, _From, _State) -> +prioritise_call(Msg, _From, _Len, _State) -> case Msg of info -> 9; {info, _Items} -> 9; _ -> 0 end. -prioritise_cast(Msg, _State) -> +prioritise_cast(Msg, _Len, _State) -> case Msg of {confirm, _MsgSeqNos, _QPid} -> 5; _ -> 0 end. -prioritise_info(Msg, _State) -> +prioritise_info(Msg, _Len, _State) -> case Msg of emit_stats -> 7; _ -> 0 diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index 2b15498e..05c27250 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -19,7 +19,7 @@ -behaviour(gen_server2). -export([init/1, terminate/2, code_change/3, handle_call/3, handle_cast/2, - handle_info/2, prioritise_call/3]). + handle_info/2, prioritise_call/4]). -export([start_link/0, make_token/0, make_token/1, is_enabled/1, enable/2, disable/1]). -export([limit/2, can_send/3, ack/2, register/2, unregister/2]). @@ -126,8 +126,8 @@ is_blocked(Limiter) -> init([]) -> {ok, #lim{}}. -prioritise_call(get_limit, _From, _State) -> 9; -prioritise_call(_Msg, _From, _State) -> 0. +prioritise_call(get_limit, _From, _Len, _State) -> 9; +prioritise_call(_Msg, _From, _Len, _State) -> 0. handle_call({can_send, QPid, _AckRequired}, _From, State = #lim{blocked = true}) -> diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 1ba1420f..1d733cf7 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -27,8 +27,8 @@ -export([start_link/1, set_maximum_since_use/2, info/1]). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, - code_change/3, handle_pre_hibernate/1, prioritise_call/3, - prioritise_cast/2, prioritise_info/2]). + code_change/3, handle_pre_hibernate/1, prioritise_call/4, + prioritise_cast/3, prioritise_info/3]). -export([joined/2, members_changed/3, handle_msg/3]). @@ -305,14 +305,14 @@ handle_pre_hibernate(State = #state { backing_queue = BQ, BQS3 = BQ:handle_pre_hibernate(BQS2), {hibernate, stop_rate_timer(State #state { backing_queue_state = BQS3 })}. -prioritise_call(Msg, _From, _State) -> +prioritise_call(Msg, _From, _Len, _State) -> case Msg of info -> 9; {gm_deaths, _Deaths} -> 5; _ -> 0 end. -prioritise_cast(Msg, _State) -> +prioritise_cast(Msg, _Len, _State) -> case Msg of {set_ram_duration_target, _Duration} -> 8; {set_maximum_since_use, _Age} -> 8; @@ -322,7 +322,7 @@ prioritise_cast(Msg, _State) -> _ -> 0 end. -prioritise_info(Msg, _State) -> +prioritise_info(Msg, _Len, _State) -> case Msg of update_ram_duration -> 8; sync_timeout -> 6; diff --git a/src/rabbit_msg_store.erl b/src/rabbit_msg_store.erl index c2e55022..d656098a 100644 --- a/src/rabbit_msg_store.erl +++ b/src/rabbit_msg_store.erl @@ -29,8 +29,8 @@ -export([transform_dir/3, force_recovery/2]). %% upgrade -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, - code_change/3, prioritise_call/3, prioritise_cast/2, - prioritise_info/2, format_message_queue/2]). + code_change/3, prioritise_call/4, prioritise_cast/3, + prioritise_info/3, format_message_queue/2]). %%---------------------------------------------------------------------------- @@ -738,7 +738,7 @@ init([Server, BaseDir, ClientRefs, StartupFunState]) -> hibernate, {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}. -prioritise_call(Msg, _From, _State) -> +prioritise_call(Msg, _From, _Len, _State) -> case Msg of successfully_recovered_state -> 7; {new_client_state, _Ref, _Pid, _MODC, _CloseFDsFun} -> 7; @@ -746,7 +746,7 @@ prioritise_call(Msg, _From, _State) -> _ -> 0 end. -prioritise_cast(Msg, _State) -> +prioritise_cast(Msg, _Len, _State) -> case Msg of {combine_files, _Source, _Destination, _Reclaimed} -> 8; {delete_file, _File, _Reclaimed} -> 8; @@ -755,7 +755,7 @@ prioritise_cast(Msg, _State) -> _ -> 0 end. -prioritise_info(Msg, _State) -> +prioritise_info(Msg, _Len, _State) -> case Msg of sync -> 8; _ -> 0 diff --git a/src/rabbit_msg_store_gc.erl b/src/rabbit_msg_store_gc.erl index 3b61ed0b..bdabf406 100644 --- a/src/rabbit_msg_store_gc.erl +++ b/src/rabbit_msg_store_gc.erl @@ -23,7 +23,7 @@ -export([set_maximum_since_use/2]). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, - terminate/2, code_change/3, prioritise_cast/2]). + terminate/2, code_change/3, prioritise_cast/3]). -record(state, { pending_no_readers, @@ -79,8 +79,8 @@ init([MsgStoreState]) -> msg_store_state = MsgStoreState }, hibernate, {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}. -prioritise_cast({set_maximum_since_use, _Age}, _State) -> 8; -prioritise_cast(_Msg, _State) -> 0. +prioritise_cast({set_maximum_since_use, _Age}, _Len, _State) -> 8; +prioritise_cast(_Msg, _Len, _State) -> 0. handle_call(stop, _From, State) -> {stop, normal, ok, State}. diff --git a/src/worker_pool_worker.erl b/src/worker_pool_worker.erl index 1ddcebb2..6db6d156 100644 --- a/src/worker_pool_worker.erl +++ b/src/worker_pool_worker.erl @@ -23,7 +23,7 @@ -export([set_maximum_since_use/2]). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, - terminate/2, code_change/3, prioritise_cast/2]). + terminate/2, code_change/3, prioritise_cast/3]). %%---------------------------------------------------------------------------- @@ -73,8 +73,8 @@ init([WId]) -> {ok, WId, hibernate, {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}. -prioritise_cast({set_maximum_since_use, _Age}, _State) -> 8; -prioritise_cast(_Msg, _State) -> 0. +prioritise_cast({set_maximum_since_use, _Age}, _Len, _State) -> 8; +prioritise_cast(_Msg, _Len, _State) -> 0. handle_call({submit, Fun}, From, WId) -> gen_server2:reply(From, run(Fun)), -- cgit v1.2.1 From c59aad743c8bef4b4f2611292acc16e3f886ef49 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 8 Nov 2012 13:08:55 +0000 Subject: fix tests --- src/rabbit_tests.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index f802a5a0..8a24d388 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -2524,7 +2524,7 @@ test_queue_recover() -> after 10000 -> exit(timeout_waiting_for_queue_death) end, rabbit_amqqueue:stop(), - rabbit_amqqueue:start(), + rabbit_amqqueue:start(rabbit_amqqueue:recover()), rabbit_amqqueue:with_or_die( QName, fun (Q1 = #amqqueue { pid = QPid1 }) -> -- cgit v1.2.1 From 044bfbcecbc7e2a86f331d9a493d8d2daed5e260 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 8 Nov 2012 13:34:10 +0000 Subject: Fix arities, support 'drop'. --- src/gen_server2.erl | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/src/gen_server2.erl b/src/gen_server2.erl index 7bdfa91a..3ef45062 100644 --- a/src/gen_server2.erl +++ b/src/gen_server2.erl @@ -16,12 +16,14 @@ %% The original code could reorder messages when communicating with a %% process on a remote node that was not currently connected. %% -%% 4) The callback module can optionally implement prioritise_call/3, -%% prioritise_cast/2 and prioritise_info/2. These functions take -%% Message, From and State or just Message and State and return a -%% single integer representing the priority attached to the message. -%% Messages with higher priorities are processed before requests with -%% lower priorities. The default priority is 0. +%% 4) The callback module can optionally implement prioritise_call/4, +%% prioritise_cast/3 and prioritise_info/3. These functions take +%% Message, From, Length and State or just Message, Length and State +%% (where Length is the current number of messages waiting to be +%% processed) and return a single integer representing the priority +%% attached to the message, or 'drop' to ignore it. Messages with +%% higher priorities are processed before requests with lower +%% priorities. The default priority is 0. %% %% 5) The callback module can optionally implement %% handle_pre_hibernate/1 and handle_post_hibernate/1. These will be @@ -650,6 +652,9 @@ in({system, _From, _Req} = Input, GS2State) -> in(Input, GS2State = #gs2_state { prioritise_info = PI }) -> in(Input, PI(Input, GS2State), GS2State). +in(_Input, drop, GS2State) -> + GS2State; + in(Input, Priority, GS2State = #gs2_state { queue = Queue }) -> GS2State # gs2_state { queue = priority_queue:in(Input, Priority, Queue) }. @@ -1166,11 +1171,11 @@ whereis_name(Name) -> find_prioritisers(GS2State = #gs2_state { mod = Mod }) -> PrioriCall = function_exported_or_default( - Mod, 'prioritise_call', 3, + Mod, 'prioritise_call', 4, fun (_Msg, _From, _State) -> 0 end), - PrioriCast = function_exported_or_default(Mod, 'prioritise_cast', 2, + PrioriCast = function_exported_or_default(Mod, 'prioritise_cast', 3, fun (_Msg, _State) -> 0 end), - PrioriInfo = function_exported_or_default(Mod, 'prioritise_info', 2, + PrioriInfo = function_exported_or_default(Mod, 'prioritise_info', 3, fun (_Msg, _State) -> 0 end), GS2State #gs2_state { prioritise_call = PrioriCall, prioritise_cast = PrioriCast, @@ -1179,20 +1184,24 @@ find_prioritisers(GS2State = #gs2_state { mod = Mod }) -> function_exported_or_default(Mod, Fun, Arity, Default) -> case erlang:function_exported(Mod, Fun, Arity) of true -> case Arity of - 2 -> fun (Msg, GS2State = #gs2_state { queue = Queue, + 3 -> fun (Msg, GS2State = #gs2_state { queue = Queue, state = State }) -> Length = priority_queue:len(Queue), case catch Mod:Fun(Msg, Length, State) of + drop -> + drop; Res when is_integer(Res) -> Res; Err -> handle_common_termination(Err, Msg, GS2State) end end; - 3 -> fun (Msg, From, GS2State = #gs2_state { queue = Queue, + 4 -> fun (Msg, From, GS2State = #gs2_state { queue = Queue, state = State }) -> Length = priority_queue:len(Queue), case catch Mod:Fun(Msg, From, Length, State) of + drop -> + drop; Res when is_integer(Res) -> Res; Err -> -- cgit v1.2.1 From 7c6415eda10ea30feb706d9b38de354560abc504 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 8 Nov 2012 13:42:44 +0000 Subject: Doesn't make sense to drop calls. --- src/gen_server2.erl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/gen_server2.erl b/src/gen_server2.erl index 3ef45062..12390051 100644 --- a/src/gen_server2.erl +++ b/src/gen_server2.erl @@ -21,7 +21,8 @@ %% Message, From, Length and State or just Message, Length and State %% (where Length is the current number of messages waiting to be %% processed) and return a single integer representing the priority -%% attached to the message, or 'drop' to ignore it. Messages with +%% attached to the message, or 'drop' to ignore it (for +%% prioritise_cast/3 and prioritise_info/3 only). Messages with %% higher priorities are processed before requests with lower %% priorities. The default priority is 0. %% @@ -1200,8 +1201,6 @@ function_exported_or_default(Mod, Fun, Arity, Default) -> state = State }) -> Length = priority_queue:len(Queue), case catch Mod:Fun(Msg, From, Length, State) of - drop -> - drop; Res when is_integer(Res) -> Res; Err -> -- cgit v1.2.1 From 0f8065523d649cfb5420818e1d2ab60aa705bbcd Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 8 Nov 2012 13:47:01 +0000 Subject: Make priority_queue:len/1 fast. --- src/priority_queue.erl | 70 +++++++++++++++++++++++++------------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/src/priority_queue.erl b/src/priority_queue.erl index 780fa2e9..14da39ad 100644 --- a/src/priority_queue.erl +++ b/src/priority_queue.erl @@ -69,9 +69,9 @@ %%---------------------------------------------------------------------------- new() -> - {queue, [], []}. + {queue, [], [], 0}. -is_queue({queue, R, F}) when is_list(R), is_list(F) -> +is_queue({queue, R, F, L}) when is_list(R), is_list(F), is_integer(L) -> true; is_queue({pqueue, Queues}) when is_list(Queues) -> lists:all(fun ({infinity, Q}) -> is_queue(Q); @@ -80,17 +80,17 @@ is_queue({pqueue, Queues}) when is_list(Queues) -> is_queue(_) -> false. -is_empty({queue, [], []}) -> +is_empty({queue, [], [], 0}) -> true; is_empty(_) -> false. -len({queue, R, F}) when is_list(R), is_list(F) -> - length(R) + length(F); +len({queue, _R, _F, L}) -> + L; len({pqueue, Queues}) -> lists:sum([len(Q) || {_, Q} <- Queues]). -to_list({queue, In, Out}) when is_list(In), is_list(Out) -> +to_list({queue, In, Out, _Len}) when is_list(In), is_list(Out) -> [{0, V} || V <- Out ++ lists:reverse(In, [])]; to_list({pqueue, Queues}) -> [{maybe_negate_priority(P), V} || {P, Q} <- Queues, @@ -99,13 +99,13 @@ to_list({pqueue, Queues}) -> in(Item, Q) -> in(Item, 0, Q). -in(X, 0, {queue, [_] = In, []}) -> - {queue, [X], In}; -in(X, 0, {queue, In, Out}) when is_list(In), is_list(Out) -> - {queue, [X|In], Out}; -in(X, Priority, _Q = {queue, [], []}) -> +in(X, 0, {queue, [_] = In, [], 1}) -> + {queue, [X], In, 2}; +in(X, 0, {queue, In, Out, Len}) when is_list(In), is_list(Out) -> + {queue, [X|In], Out, Len + 1}; +in(X, Priority, _Q = {queue, [], [], 0}) -> in(X, Priority, {pqueue, []}); -in(X, Priority, Q = {queue, _, _}) -> +in(X, Priority, Q = {queue, _, _, _}) -> in(X, Priority, {pqueue, [{0, Q}]}); in(X, Priority, {pqueue, Queues}) -> P = maybe_negate_priority(Priority), @@ -113,33 +113,33 @@ in(X, Priority, {pqueue, Queues}) -> {value, {_, Q}} -> lists:keyreplace(P, 1, Queues, {P, in(X, Q)}); false when P == infinity -> - [{P, {queue, [X], []}} | Queues]; + [{P, {queue, [X], [], 1}} | Queues]; false -> case Queues of [{infinity, InfQueue} | Queues1] -> [{infinity, InfQueue} | - lists:keysort(1, [{P, {queue, [X], []}} | Queues1])]; + lists:keysort(1, [{P, {queue, [X], [], 1}} | Queues1])]; _ -> - lists:keysort(1, [{P, {queue, [X], []}} | Queues]) + lists:keysort(1, [{P, {queue, [X], [], 1}} | Queues]) end end}. -out({queue, [], []} = Q) -> +out({queue, [], [], 0} = Q) -> {empty, Q}; -out({queue, [V], []}) -> - {{value, V}, {queue, [], []}}; -out({queue, [Y|In], []}) -> +out({queue, [V], [], 1}) -> + {{value, V}, {queue, [], [], 0}}; +out({queue, [Y|In], [], Len}) -> [V|Out] = lists:reverse(In, []), - {{value, V}, {queue, [Y], Out}}; -out({queue, In, [V]}) when is_list(In) -> - {{value,V}, r2f(In)}; -out({queue, In,[V|Out]}) when is_list(In) -> - {{value, V}, {queue, In, Out}}; + {{value, V}, {queue, [Y], Out}, Len - 1}; +out({queue, In, [V], Len}) when is_list(In) -> + {{value,V}, r2f(In, Len - 1)}; +out({queue, In,[V|Out], Len}) when is_list(In) -> + {{value, V}, {queue, In, Out, Len - 1}}; out({pqueue, [{P, Q} | Queues]}) -> {R, Q1} = out(Q), NewQ = case is_empty(Q1) of true -> case Queues of - [] -> {queue, [], []}; + [] -> {queue, [], [], 0}; [{0, OnlyQ}] -> OnlyQ; [_|_] -> {pqueue, Queues} end; @@ -147,13 +147,13 @@ out({pqueue, [{P, Q} | Queues]}) -> end, {R, NewQ}. -join(A, {queue, [], []}) -> +join(A, {queue, [], [], 0}) -> A; -join({queue, [], []}, B) -> +join({queue, [], [], 0}, B) -> B; -join({queue, AIn, AOut}, {queue, BIn, BOut}) -> - {queue, BIn, AOut ++ lists:reverse(AIn, BOut)}; -join(A = {queue, _, _}, {pqueue, BPQ}) -> +join({queue, AIn, AOut, ALen}, {queue, BIn, BOut, BLen}) -> + {queue, BIn, AOut ++ lists:reverse(AIn, BOut), ALen + BLen}; +join(A = {queue, _, _, _}, {pqueue, BPQ}) -> {Pre, Post} = lists:splitwith(fun ({P, _}) -> P < 0 orelse P == infinity end, BPQ), Post1 = case Post of @@ -162,7 +162,7 @@ join(A = {queue, _, _}, {pqueue, BPQ}) -> _ -> [ {0, A} | Post ] end, {pqueue, Pre ++ Post1}; -join({pqueue, APQ}, B = {queue, _, _}) -> +join({pqueue, APQ}, B = {queue, _, _, _}) -> {Pre, Post} = lists:splitwith(fun ({P, _}) -> P < 0 orelse P == infinity end, APQ), Post1 = case Post of @@ -185,10 +185,10 @@ 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 ]). -r2f([]) -> {queue, [], []}; -r2f([_] = R) -> {queue, [], R}; -r2f([X,Y]) -> {queue, [X], [Y]}; -r2f([X,Y|R]) -> {queue, [X,Y], lists:reverse(R, [])}. +r2f([], 0) -> {queue, [], [], 0}; +r2f([_] = R, 1) -> {queue, [], R, 1}; +r2f([X,Y], 2) -> {queue, [X], [Y], 2}; +r2f([X,Y|R], L) -> {queue, [X,Y], lists:reverse(R, []), L}. maybe_negate_priority(infinity) -> infinity; maybe_negate_priority(P) -> -P. -- cgit v1.2.1 From 0c9e49560393811e6137ea9e5c454ed10ec5760b Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Thu, 8 Nov 2012 14:52:05 +0000 Subject: introduce a noop process at the head of rabbit_sup's children, which we now monitor instead of the rabbit application's pid --- src/rabbit.erl | 22 +++++++++++++++-- src/rabbit_node_monitor.erl | 58 ++++++++++++++++++++++++--------------------- 2 files changed, 51 insertions(+), 29 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index c52c296a..8c13224f 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -21,7 +21,7 @@ -export([start/0, boot/0, stop/0, stop_and_halt/0, await_startup/0, status/0, is_running/0, is_running/1, environment/0, rotate_logs/1, force_event_refresh/0, - start_fhc/0]). + start_fhc/0, start_app_marker/1, hibernate/0]). -export([start/2, stop/1]). @@ -174,10 +174,15 @@ [{mfa, {rabbit_networking, boot, []}}, {requires, log_relay}]}). +-rabbit_boot_step({app_running, + [{description, "cluster membership"}, + {mfa, {rabbit, start_app_marker, [boot]}}, + {requires, networking}]}). + -rabbit_boot_step({notify_cluster, [{description, "notify cluster nodes"}, {mfa, {rabbit_node_monitor, notify_node_up, []}}, - {requires, networking}]}). + {requires, app_running}]}). %%--------------------------------------------------------------------------- @@ -770,3 +775,16 @@ start_fhc() -> rabbit_sup:start_restartable_child( file_handle_cache, [fun rabbit_alarm:set_alarm/1, fun rabbit_alarm:clear_alarm/1]). + +start_app_marker(boot) -> + supervisor:start_child(rabbit_sup, + {rabbit_app, {?MODULE, start_app_marker, [spawn]}, + transient, ?MAX_WAIT, worker, [?MODULE]}); +start_app_marker(spawn) -> + Pid = spawn_link(fun() -> erlang:hibernate(?MODULE, hibernate, []) end), + register(rabbit_running, Pid), + {ok, Pid}. + +hibernate() -> + erlang:hibernate(?MODULE, hibernate, []). + diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index b11c9d04..ec2f8159 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -85,10 +85,10 @@ cluster_status_filename() -> prepare_cluster_status_files() -> rabbit_mnesia:ensure_mnesia_dir(), - CorruptFiles = fun () -> throw({error, corrupt_cluster_status_files}) end, + Corrupt = fun(F) -> throw({error, corrupt_cluster_status_files, F}) end, RunningNodes1 = case try_read_file(running_nodes_filename()) of {ok, [Nodes]} when is_list(Nodes) -> Nodes; - {ok, _ } -> CorruptFiles(); + {ok, Other} -> Corrupt(Other); {error, enoent} -> [] end, ThisNode = [node()], @@ -102,8 +102,8 @@ prepare_cluster_status_files() -> {ok, [AllNodes0]} when is_list(AllNodes0) -> {legacy_cluster_nodes(AllNodes0), legacy_should_be_disc_node(AllNodes0)}; - {ok, _} -> - CorruptFiles(); + {ok, Files} -> + Corrupt(Files); {error, enoent} -> {legacy_cluster_nodes([]), true} end, @@ -114,7 +114,7 @@ prepare_cluster_status_files() -> end, ok = write_cluster_status({AllNodes2, DiscNodes, RunningNodes2}). -write_cluster_status({All, Disc, Running}) -> +write_cluster_status({All, Disc, Running}=St) -> ClusterStatusFN = cluster_status_filename(), Res = case rabbit_file:write_term_file(ClusterStatusFN, [{All, Disc}]) of ok -> @@ -134,8 +134,8 @@ read_cluster_status() -> try_read_file(running_nodes_filename())} of {{ok, [{All, Disc}]}, {ok, [Running]}} when is_list(Running) -> {All, Disc, Running}; - {_, _} -> - throw({error, corrupt_or_missing_cluster_files}) + {Stat, Run} -> + throw({error, {corrupt_or_missing_cluster_files, Stat, Run}}) end. update_cluster_status() -> @@ -199,44 +199,48 @@ handle_call(_Request, _From, State) -> %% mnesia propagation. handle_cast({node_up, Node, NodeType}, State = #state{monitors = Monitors}) -> - case pmon:is_monitored({rabbit, Node}, Monitors) of + case pmon:is_monitored({rabbit_running, Node}, Monitors) of true -> {noreply, State}; false -> rabbit_log:info("rabbit on node ~p up~n", [Node]), {AllNodes, DiscNodes, RunningNodes} = read_cluster_status(), - write_cluster_status({add_node(Node, AllNodes), - case NodeType of - disc -> add_node(Node, DiscNodes); - ram -> DiscNodes - end, - add_node(Node, RunningNodes)}), + ok = write_cluster_status({add_node(Node, AllNodes), + case NodeType of + disc -> add_node(Node, DiscNodes); + ram -> DiscNodes + end, + add_node(Node, RunningNodes)}), ok = handle_live_rabbit(Node), - {noreply, State#state{ - monitors = pmon:monitor({rabbit, Node}, Monitors)}} + {noreply, + State#state{ + monitors = pmon:monitor({rabbit_running, Node}, Monitors)}} end; handle_cast({joined_cluster, Node, NodeType}, State) -> {AllNodes, DiscNodes, RunningNodes} = read_cluster_status(), - write_cluster_status({add_node(Node, AllNodes), - case NodeType of - disc -> add_node(Node, DiscNodes); - ram -> DiscNodes - end, - RunningNodes}), + ok = write_cluster_status({add_node(Node, AllNodes), + case NodeType of + disc -> add_node(Node, DiscNodes); + ram -> DiscNodes + end, + RunningNodes}), {noreply, State}; handle_cast({left_cluster, Node}, State) -> {AllNodes, DiscNodes, RunningNodes} = read_cluster_status(), - write_cluster_status({del_node(Node, AllNodes), del_node(Node, DiscNodes), - del_node(Node, RunningNodes)}), + ok = write_cluster_status({del_node(Node, AllNodes), + del_node(Node, DiscNodes), + del_node(Node, RunningNodes)}), {noreply, State}; handle_cast(_Msg, State) -> {noreply, State}. -handle_info({'DOWN', _MRef, process, {rabbit, Node}, _Reason}, +handle_info({'DOWN', _MRef, process, {rabbit_running, Node}, _Reason}, State = #state{monitors = Monitors}) -> rabbit_log:info("rabbit on node ~p down~n", [Node]), {AllNodes, DiscNodes, RunningNodes} = read_cluster_status(), - write_cluster_status({AllNodes, DiscNodes, del_node(Node, RunningNodes)}), + ok = write_cluster_status({AllNodes, DiscNodes, + del_node(Node, RunningNodes)}), ok = handle_dead_rabbit(Node), - {noreply, State#state{monitors = pmon:erase({rabbit, Node}, Monitors)}}; + {noreply, State#state{monitors = pmon:erase( + {rabbit_running, Node}, Monitors)}}; handle_info({mnesia_system_event, {inconsistent_database, running_partitioned_network, Node}}, -- cgit v1.2.1 From 4def2d6d249f309e64376c8c8de80b1e7c545cf8 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 8 Nov 2012 15:09:50 +0000 Subject: reduce distance to default --- src/rabbit_node_monitor.erl | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index ec2f8159..6fa652ee 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -112,7 +112,7 @@ prepare_cluster_status_files() -> true -> ThisNode; false -> [] end, - ok = write_cluster_status({AllNodes2, DiscNodes, RunningNodes2}). + write_cluster_status({AllNodes2, DiscNodes, RunningNodes2}). write_cluster_status({All, Disc, Running}=St) -> ClusterStatusFN = cluster_status_filename(), @@ -203,12 +203,12 @@ handle_cast({node_up, Node, NodeType}, true -> {noreply, State}; false -> rabbit_log:info("rabbit on node ~p up~n", [Node]), {AllNodes, DiscNodes, RunningNodes} = read_cluster_status(), - ok = write_cluster_status({add_node(Node, AllNodes), - case NodeType of - disc -> add_node(Node, DiscNodes); - ram -> DiscNodes - end, - add_node(Node, RunningNodes)}), + write_cluster_status({add_node(Node, AllNodes), + case NodeType of + disc -> add_node(Node, DiscNodes); + ram -> DiscNodes + end, + add_node(Node, RunningNodes)}), ok = handle_live_rabbit(Node), {noreply, State#state{ @@ -216,18 +216,17 @@ handle_cast({node_up, Node, NodeType}, end; handle_cast({joined_cluster, Node, NodeType}, State) -> {AllNodes, DiscNodes, RunningNodes} = read_cluster_status(), - ok = write_cluster_status({add_node(Node, AllNodes), - case NodeType of - disc -> add_node(Node, DiscNodes); - ram -> DiscNodes - end, - RunningNodes}), + write_cluster_status({add_node(Node, AllNodes), + case NodeType of + disc -> add_node(Node, DiscNodes); + ram -> DiscNodes + end, + RunningNodes}), {noreply, State}; handle_cast({left_cluster, Node}, State) -> {AllNodes, DiscNodes, RunningNodes} = read_cluster_status(), - ok = write_cluster_status({del_node(Node, AllNodes), - del_node(Node, DiscNodes), - del_node(Node, RunningNodes)}), + write_cluster_status({del_node(Node, AllNodes), del_node(Node, DiscNodes), + del_node(Node, RunningNodes)}), {noreply, State}; handle_cast(_Msg, State) -> {noreply, State}. @@ -236,8 +235,7 @@ handle_info({'DOWN', _MRef, process, {rabbit_running, Node}, _Reason}, State = #state{monitors = Monitors}) -> rabbit_log:info("rabbit on node ~p down~n", [Node]), {AllNodes, DiscNodes, RunningNodes} = read_cluster_status(), - ok = write_cluster_status({AllNodes, DiscNodes, - del_node(Node, RunningNodes)}), + write_cluster_status({AllNodes, DiscNodes, del_node(Node, RunningNodes)}), ok = handle_dead_rabbit(Node), {noreply, State#state{monitors = pmon:erase( {rabbit_running, Node}, Monitors)}}; -- cgit v1.2.1 From c5a64acac12ba209f07d06c3316847c3f9c6e137 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 8 Nov 2012 15:11:36 +0000 Subject: Further clean --- src/rabbit_node_monitor.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index 6fa652ee..d316e6a7 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -114,7 +114,7 @@ prepare_cluster_status_files() -> end, write_cluster_status({AllNodes2, DiscNodes, RunningNodes2}). -write_cluster_status({All, Disc, Running}=St) -> +write_cluster_status({All, Disc, Running}) -> ClusterStatusFN = cluster_status_filename(), Res = case rabbit_file:write_term_file(ClusterStatusFN, [{All, Disc}]) of ok -> -- cgit v1.2.1 From 46e4a2b5ac16428e69be16e6fd61632885dc5ef1 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 8 Nov 2012 16:20:26 +0000 Subject: Make the app marker into a gen_server --- src/rabbit.erl | 18 +++--------------- src/rabbit_app_marker.erl | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 15 deletions(-) create mode 100644 src/rabbit_app_marker.erl diff --git a/src/rabbit.erl b/src/rabbit.erl index 8c13224f..66adcca3 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -21,7 +21,7 @@ -export([start/0, boot/0, stop/0, stop_and_halt/0, await_startup/0, status/0, is_running/0, is_running/1, environment/0, rotate_logs/1, force_event_refresh/0, - start_fhc/0, start_app_marker/1, hibernate/0]). + start_fhc/0]). -export([start/2, stop/1]). @@ -176,7 +176,8 @@ -rabbit_boot_step({app_running, [{description, "cluster membership"}, - {mfa, {rabbit, start_app_marker, [boot]}}, + {mfa, {rabbit_sup, start_restartable_child, + [rabbit_app_marker]}}, {requires, networking}]}). -rabbit_boot_step({notify_cluster, @@ -775,16 +776,3 @@ start_fhc() -> rabbit_sup:start_restartable_child( file_handle_cache, [fun rabbit_alarm:set_alarm/1, fun rabbit_alarm:clear_alarm/1]). - -start_app_marker(boot) -> - supervisor:start_child(rabbit_sup, - {rabbit_app, {?MODULE, start_app_marker, [spawn]}, - transient, ?MAX_WAIT, worker, [?MODULE]}); -start_app_marker(spawn) -> - Pid = spawn_link(fun() -> erlang:hibernate(?MODULE, hibernate, []) end), - register(rabbit_running, Pid), - {ok, Pid}. - -hibernate() -> - erlang:hibernate(?MODULE, hibernate, []). - diff --git a/src/rabbit_app_marker.erl b/src/rabbit_app_marker.erl new file mode 100644 index 00000000..14daa98c --- /dev/null +++ b/src/rabbit_app_marker.erl @@ -0,0 +1,41 @@ +%% 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 VMware, Inc. +%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% + +-module(rabbit_app_marker). + +-behaviour(gen_server). + +-export([start_link/0]). + +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3]). + +-include("rabbit.hrl"). + +start_link() -> + gen_server:start_link({local, rabbit_running}, ?MODULE, [], []). + +%%---------------------------------------------------------------------------- + +init([]) -> {ok, state, hibernate}. + +handle_call(_Msg, _From, State) -> {stop, not_understood, State}. +handle_cast(_Msg, State) -> {stop, not_understood, State}. +handle_info(_Msg, State) -> {stop, not_understood, State}. + +terminate(_Arg, _State) -> ok. + +code_change(_OldVsn, State, _Extra) -> {ok, State}. -- cgit v1.2.1 From 7711b085cb94c8903b9eb6787bdc509745ea80f4 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 8 Nov 2012 16:36:35 +0000 Subject: Explain --- src/rabbit_app_marker.erl | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/rabbit_app_marker.erl b/src/rabbit_app_marker.erl index 14daa98c..296c2918 100644 --- a/src/rabbit_app_marker.erl +++ b/src/rabbit_app_marker.erl @@ -25,6 +25,14 @@ -include("rabbit.hrl"). +%%---------------------------------------------------------------------------- + +%% We want to know when another node has *started* shutting down (to +%% write the cluster status file). The rabbit application goes away +%% pretty much when we have *finished* shutting down. So we have this +%% process to monitor instead - it;s the last thing to be started so +%% the first thing to go. + start_link() -> gen_server:start_link({local, rabbit_running}, ?MODULE, [], []). -- cgit v1.2.1 From a496dd6dba2faf56a5934120c1b92041ac45edba Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 8 Nov 2012 16:58:16 +0000 Subject: Take two monitors into the shower. --- src/rabbit_node_monitor.erl | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index d316e6a7..e82a4aaa 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -210,9 +210,12 @@ handle_cast({node_up, Node, NodeType}, end, add_node(Node, RunningNodes)}), ok = handle_live_rabbit(Node), - {noreply, - State#state{ - monitors = pmon:monitor({rabbit_running, Node}, Monitors)}} + State1 = mon({rabbit_running, Node}, State), + State2 = case pmon:is_monitored({rabbit, Node}) of + true -> State1; + false -> mon({rabbit, Node}, State1) + end, + {noreply, State2} end; handle_cast({joined_cluster, Node, NodeType}, State) -> {AllNodes, DiscNodes, RunningNodes} = read_cluster_status(), @@ -231,14 +234,20 @@ handle_cast({left_cluster, Node}, State) -> handle_cast(_Msg, State) -> {noreply, State}. -handle_info({'DOWN', _MRef, process, {rabbit_running, Node}, _Reason}, - State = #state{monitors = Monitors}) -> - rabbit_log:info("rabbit on node ~p down~n", [Node]), +handle_info({'DOWN', _MRef, process, {rabbit_running, Node}, _Reason}, State) -> + %% The node has started to stop, remove it from the cluster status + %% file. We want to do this "early" to stand a better chance of + %% recording anything when all the nodes are shut down + %% simultaneously. {AllNodes, DiscNodes, RunningNodes} = read_cluster_status(), write_cluster_status({AllNodes, DiscNodes, del_node(Node, RunningNodes)}), + {noreply, unmon({rabbit_running, Node}, State)}; + +handle_info({'DOWN', _MRef, process, {rabbit, Node}, _Reason}, State) -> + %% The node has finished stopping (rabbit anyway), treat it as dead. + rabbit_log:info("rabbit on node ~p down~n", [Node]), ok = handle_dead_rabbit(Node), - {noreply, State#state{monitors = pmon:erase( - {rabbit_running, Node}, Monitors)}}; + {noreply, unmon({rabbit, Node}, State)}; handle_info({mnesia_system_event, {inconsistent_database, running_partitioned_network, Node}}, @@ -296,3 +305,9 @@ legacy_should_be_disc_node(DiscNodes) -> add_node(Node, Nodes) -> lists:usort([Node | Nodes]). del_node(Node, Nodes) -> Nodes -- [Node]. + +mon(Item, State = #state{monitors = Monitors}) -> + State#state{monitors = pmon:monitor(Item, Monitors)}. + +unmon(Item, State = #state{monitors = Monitors}) -> + State#state{monitors = pmon:erase(Item, Monitors)}. -- cgit v1.2.1 From 1640bb6af3b1a568de2da9bdf70b983269e2bd63 Mon Sep 17 00:00:00 2001 From: Alvaro Videla Date: Fri, 9 Nov 2012 10:56:47 +0000 Subject: specifies boot file for erl calls --- scripts/rabbitmq-defaults | 6 ++---- scripts/rabbitmq-plugins | 1 + scripts/rabbitmq-server | 2 +- scripts/rabbitmqctl | 1 + 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/rabbitmq-defaults b/scripts/rabbitmq-defaults index f618f584..c6923586 100644 --- a/scripts/rabbitmq-defaults +++ b/scripts/rabbitmq-defaults @@ -21,10 +21,8 @@ SYS_PREFIX= ### next line will be updated when generating a standalone release ERL_DIR= -### TODO fix these values -CLEAN_BOOT_FILE="${SYS_PREFIX}/releases/${RELEASE_VERSION}/start_clean" -SASL_BOOT_FILE="${SYS_PREFIX}/releases/${RELEASE_VERSION}/start_sasl" - +CLEAN_BOOT_FILE=start_clean +SASL_BOOT_FILE=start_sasl ## Set default values diff --git a/scripts/rabbitmq-plugins b/scripts/rabbitmq-plugins index d9347258..985c7122 100755 --- a/scripts/rabbitmq-plugins +++ b/scripts/rabbitmq-plugins @@ -31,6 +31,7 @@ exec ${ERL_DIR}erl \ -noinput \ -hidden \ -sname rabbitmq-plugins$$ \ + -boot "${CLEAN_BOOT_FILE}" \ -s rabbit_plugins_main \ -enabled_plugins_file "$RABBITMQ_ENABLED_PLUGINS_FILE" \ -plugins_dist_dir "$RABBITMQ_PLUGINS_DIR" \ diff --git a/scripts/rabbitmq-server b/scripts/rabbitmq-server index c3d61868..3253bd7b 100755 --- a/scripts/rabbitmq-server +++ b/scripts/rabbitmq-server @@ -107,7 +107,7 @@ exec ${ERL_DIR}erl \ -pa ${RABBITMQ_EBIN_ROOT} \ ${RABBITMQ_START_RABBIT} \ -sname ${RABBITMQ_NODENAME} \ - -boot start_sasl \ + -boot "${SASL_BOOT_FILE}" \ ${RABBITMQ_CONFIG_ARG} \ +W w \ ${RABBITMQ_SERVER_ERL_ARGS} \ diff --git a/scripts/rabbitmqctl b/scripts/rabbitmqctl index b7351624..5471abf7 100755 --- a/scripts/rabbitmqctl +++ b/scripts/rabbitmqctl @@ -32,6 +32,7 @@ exec ${ERL_DIR}erl \ -hidden \ ${RABBITMQ_CTL_ERL_ARGS} \ -sname rabbitmqctl$$ \ + -boot "${CLEAN_BOOT_FILE}" \ -s rabbit_control_main \ -nodename $RABBITMQ_NODENAME \ -extra "$@" -- cgit v1.2.1 From 79a2ec921ee4e2d2873a247a9414655a73aea154 Mon Sep 17 00:00:00 2001 From: Alvaro Videla Date: Fri, 9 Nov 2012 10:58:28 +0000 Subject: replaces boot files accordingly in rabbitmq-defaults --- packaging/mac/Makefile | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/packaging/mac/Makefile b/packaging/mac/Makefile index a21ec88a..ea16e72d 100644 --- a/packaging/mac/Makefile +++ b/packaging/mac/Makefile @@ -2,7 +2,7 @@ VERSION=0.0.0 SOURCE_DIR=rabbitmq-server-$(VERSION) TARGET_DIR=rabbitmq_server-$(VERSION) TARGET_TARBALL=rabbitmq-server-mac-standalone-$(VERSION) -RLS_DIR=$(TARGET_DIR)/release +RLS_DIR=$(TARGET_DIR)/release/$(TARGET_DIR) ERTS_VSN=$(shell erl -noshell -eval 'io:format("~s", [erlang:system_info(version)]), halt().') ERTS_ROOT_DIR=$(shell erl -noshell -eval 'io:format("~s", [code:root_dir()]), halt().') @@ -25,6 +25,10 @@ dist: sed -e 's:^SYS_PREFIX=$$:SYS_PREFIX=\$${RABBITMQ_HOME}:' \ $(TARGET_DIR)/sbin/rabbitmq-defaults >$(TARGET_DIR)/sbin/rabbitmq-defaults.tmp \ && sed -e 's:^ERL_DIR=$$:ERL_DIR=\$${RABBITMQ_HOME}/erts-$(ERTS_VSN)/bin/:' \ + $(TARGET_DIR)/sbin/rabbitmq-defaults.tmp >$(TARGET_DIR)/sbin/rabbitmq-defaults.tmp1 \ + && sed -e 's:start_clean$$:"\$${SYS_PREFIX}/releases/$(VERSION)/start_clean":' \ + $(TARGET_DIR)/sbin/rabbitmq-defaults.tmp1 >$(TARGET_DIR)/sbin/rabbitmq-defaults.tmp \ + && sed -e 's:start_sasl:"\$${SYS_PREFIX}/releases/$(VERSION)/start_sasl":' \ $(TARGET_DIR)/sbin/rabbitmq-defaults.tmp >$(TARGET_DIR)/sbin/rabbitmq-defaults chmod 0755 $(TARGET_DIR)/sbin/rabbitmq-defaults @@ -34,18 +38,21 @@ dist: $(MAKE) generate_release ## todo see where the .tar is being created - mkdirp -p $(RLS_DIR) + mkdir -p $(RLS_DIR) tar -C $(RLS_DIR) -xzf $(RABBITMQ_HOME)/rabbit.tar.gz # add minimal boot file cp $(ERTS_ROOT_DIR)/bin/start_clean.boot $(RLS_DIR)/releases/$(VERSION) cp $(ERTS_ROOT_DIR)/bin/start_sasl.boot $(RLS_DIR)/releases/$(VERSION) - tar -zcf $(TARGET_TARBALL).tar.gz $(RLS_DIR) +# move rabbitmq files to top level folder + mv $(RLS_DIR)/lib/rabbit-$(VERSION)/* $(RLS_DIR) + + tar -zcf $(TARGET_TARBALL).tar.gz -C $(TARGET_DIR)/release $(TARGET_DIR) rm -rf $(SOURCE_DIR) $(TARGET_DIR) clean: clean_partial - rm -f rabbitmq-server-generic-unix-*.tar.gz + rm -f rabbitmq-server-mac-standalone-*.tar.gz clean_partial: rm -rf $(SOURCE_DIR) -- cgit v1.2.1 From fcd59347940d894cae4351783307357758c3525a Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 9 Nov 2012 15:58:26 +0000 Subject: Fix idiocy --- src/rabbit_node_monitor.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index e82a4aaa..b89d4dc4 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -211,7 +211,7 @@ handle_cast({node_up, Node, NodeType}, add_node(Node, RunningNodes)}), ok = handle_live_rabbit(Node), State1 = mon({rabbit_running, Node}, State), - State2 = case pmon:is_monitored({rabbit, Node}) of + State2 = case pmon:is_monitored({rabbit, Node}, Monitors) of true -> State1; false -> mon({rabbit, Node}, State1) end, -- cgit v1.2.1 From 9f3e80195c4cf485908e2a50823539657180dacd Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 12 Nov 2012 13:16:08 +0000 Subject: Background GC. --- src/background_gc.erl | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/rabbit.erl | 6 ++++ src/rabbit_misc.erl | 18 ++++++++++++ 3 files changed, 105 insertions(+) create mode 100644 src/background_gc.erl diff --git a/src/background_gc.erl b/src/background_gc.erl new file mode 100644 index 00000000..36a3c387 --- /dev/null +++ b/src/background_gc.erl @@ -0,0 +1,81 @@ +%% 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 VMware, Inc. +%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% + +-module(background_gc). + +-behaviour(gen_server2). + +-export([start_link/0]). + +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3]). + +-define(HIBERNATE_AFTER_MIN, 1000). +-define(DESIRED_HIBERNATE, 10000). + +-define(MAX_RATIO, 0.01). +-define(IDEAL_INTERVAL, 5000). + +-record(state, {last_interval}). + +%%---------------------------------------------------------------------------- + +start_link() -> + gen_server2:start_link(?MODULE, [], [{timeout, infinity}]). + +%%---------------------------------------------------------------------------- + +init([]) -> + {ok, run_gc(#state{last_interval = ?IDEAL_INTERVAL}), hibernate, + {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}. + +handle_call(Msg, _From, State) -> + {stop, {unexpected_call, Msg}, State}. + +handle_cast(Msg, State) -> + {stop, {unexpected_cast, Msg}, State}. + +handle_info(run_gc, State) -> + {noreply, run_gc(State)}; + +handle_info(Msg, State) -> + {stop, {unexpected_info, Msg}, State}. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +terminate(_Reason, State) -> + State. + +%%---------------------------------------------------------------------------- + +run_gc(State = #state{last_interval = LastInterval}) -> + {ok, Interval} = rabbit_misc:interval_operation( + fun do_gc/0, ?MAX_RATIO, ?IDEAL_INTERVAL, LastInterval), + erlang:send_after(Interval, self(), run_gc), + State#state{last_interval = Interval}. + +do_gc() -> + Sorted = lists:reverse(lists:sort( + [{memory(Pid), Pid} || Pid <- processes()])), + %% TODO select probabilisticly. + garbage_collect(element(2, hd(Sorted))), + ok. + +memory(Pid) -> case process_info(Pid, memory) of + {memory, M} -> M; + _ -> 0 + end. diff --git a/src/rabbit.erl b/src/rabbit.erl index ef9f5f56..ba0fceaf 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -179,6 +179,12 @@ {mfa, {rabbit_node_monitor, notify_node_up, []}}, {requires, networking}]}). +-rabbit_boot_step({background_gc, + [{description, "background garbage collection"}, + {mfa, {rabbit_sup, start_restartable_child, + [background_gc]}}, + {requires, networking}]}). + %%--------------------------------------------------------------------------- -include("rabbit_framing.hrl"). diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 7e3cc3d7..956b6feb 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -65,6 +65,7 @@ -export([json_encode/1, json_decode/1, json_to_term/1, term_to_json/1]). -export([check_expiry/1]). -export([base64url/1]). +-export([interval_operation/4]). %% Horrible macro to use in guards -define(IS_BENIGN_EXIT(R), @@ -235,6 +236,9 @@ -spec(term_to_json/1 :: (any()) -> any()). -spec(check_expiry/1 :: (integer()) -> rabbit_types:ok_or_error(any())). -spec(base64url/1 :: (binary()) -> string()). +-spec(interval_operation/4 :: + (thunk(A), float(), non_neg_integer(), non_neg_integer()) + -> {A, non_neg_integer()}). -endif. @@ -1015,3 +1019,17 @@ base64url(In) -> ($\=, Acc) -> Acc; (Chr, Acc) -> [Chr | Acc] end, [], base64:encode_to_string(In))). + +%% Ideally, you'd want Fun to run every IdealInterval. but you don't +%% want it to take more than MaxRatio of IdealInterval. So if it takes +%% more then you want to run it less often. So we time how long it +%% takes to run, and then suggest how long you should wait before +%% running it again. Times are in millis. +interval_operation(Fun, MaxRatio, IdealInterval, LastInterval) -> + {Micros, Res} = timer:tc(Fun), + {Res, case {Micros > 1000 * (MaxRatio * IdealInterval), + Micros > 1000 * (MaxRatio * LastInterval)} of + {true, true} -> round(LastInterval * 1.5); + {true, false} -> LastInterval; + {false, false} -> IdealInterval + end}. -- cgit v1.2.1 From 9d552a8fd2b6ab12830eaf83d7a84680eb392953 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 12 Nov 2012 14:39:39 +0000 Subject: Only GC idle (waiting) processes. Filter tiny processes from the list (to make generating the list more efficient when there are huge numbers of processes). Probabilistic process selection. --- src/background_gc.erl | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/background_gc.erl b/src/background_gc.erl index 36a3c387..4722997a 100644 --- a/src/background_gc.erl +++ b/src/background_gc.erl @@ -28,6 +28,7 @@ -define(MAX_RATIO, 0.01). -define(IDEAL_INTERVAL, 5000). +-define(MIN_BYTES, 50000). -record(state, {last_interval}). @@ -69,13 +70,14 @@ run_gc(State = #state{last_interval = LastInterval}) -> State#state{last_interval = Interval}. do_gc() -> - Sorted = lists:reverse(lists:sort( - [{memory(Pid), Pid} || Pid <- processes()])), - %% TODO select probabilisticly. - garbage_collect(element(2, hd(Sorted))), + MPs = rs([{M, P} || P <- processes(), + [{status, waiting}, {memory, M}] <- stats(P), + M > ?MIN_BYTES]), + Idx = trunc(math:pow(length(MPs) + 1, random:uniform())), + {_, Pid} = lists:nth(Idx, MPs), + garbage_collect(Pid), ok. -memory(Pid) -> case process_info(Pid, memory) of - {memory, M} -> M; - _ -> 0 - end. +rs(L) -> lists:reverse(lists:sort(L)). + +stats(P) -> [erlang:process_info(P, [status, memory])]. -- cgit v1.2.1 From 91357ca49377138bcc66c8c4a631763118f17493 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 12 Nov 2012 15:37:20 +0000 Subject: Profiling suggests that garbage_collect/1 is in fact far cheaper than processes/0, so just GC all waiting processes. And therefore reduce the frequency we call this with. --- src/background_gc.erl | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/src/background_gc.erl b/src/background_gc.erl index 4722997a..0753dd67 100644 --- a/src/background_gc.erl +++ b/src/background_gc.erl @@ -27,7 +27,7 @@ -define(DESIRED_HIBERNATE, 10000). -define(MAX_RATIO, 0.01). --define(IDEAL_INTERVAL, 5000). +-define(IDEAL_INTERVAL, 60000). -define(MIN_BYTES, 50000). -record(state, {last_interval}). @@ -35,7 +35,8 @@ %%---------------------------------------------------------------------------- start_link() -> - gen_server2:start_link(?MODULE, [], [{timeout, infinity}]). + gen_server2:start_link({local, ?MODULE}, ?MODULE, [], + [{timeout, infinity}]). %%---------------------------------------------------------------------------- @@ -70,14 +71,6 @@ run_gc(State = #state{last_interval = LastInterval}) -> State#state{last_interval = Interval}. do_gc() -> - MPs = rs([{M, P} || P <- processes(), - [{status, waiting}, {memory, M}] <- stats(P), - M > ?MIN_BYTES]), - Idx = trunc(math:pow(length(MPs) + 1, random:uniform())), - {_, Pid} = lists:nth(Idx, MPs), - garbage_collect(Pid), + [garbage_collect(P) || P <- processes(), + {status, waiting} == process_info(P, status)], ok. - -rs(L) -> lists:reverse(lists:sort(L)). - -stats(P) -> [erlang:process_info(P, [status, memory])]. -- cgit v1.2.1 From 4cf850b2a27fb9c1122074848db734241bc71105 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 12 Nov 2012 15:37:35 +0000 Subject: Decrease geometrically as well. --- src/rabbit_misc.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 956b6feb..137ccf20 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -1031,5 +1031,6 @@ interval_operation(Fun, MaxRatio, IdealInterval, LastInterval) -> Micros > 1000 * (MaxRatio * LastInterval)} of {true, true} -> round(LastInterval * 1.5); {true, false} -> LastInterval; - {false, false} -> IdealInterval + {false, false} -> lists:max([IdealInterval, + round(LastInterval / 1.5)]) end}. -- cgit v1.2.1 From a6f80fe5cab43561afcf8a9853e3111eaa5664bb Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 12 Nov 2012 15:44:41 +0000 Subject: Use background_gc when the memory alarm goes off. --- src/background_gc.erl | 9 ++++++++- src/rabbit_alarm.erl | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/background_gc.erl b/src/background_gc.erl index 0753dd67..64b86b18 100644 --- a/src/background_gc.erl +++ b/src/background_gc.erl @@ -18,7 +18,7 @@ -behaviour(gen_server2). --export([start_link/0]). +-export([start_link/0, gc_all/0]). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). @@ -38,12 +38,19 @@ start_link() -> gen_server2:start_link({local, ?MODULE}, ?MODULE, [], [{timeout, infinity}]). +gc_all() -> + gen_server2:call(?MODULE, gc_all, infinity). + %%---------------------------------------------------------------------------- init([]) -> {ok, run_gc(#state{last_interval = ?IDEAL_INTERVAL}), hibernate, {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}. +handle_call(gc_all, _From, State) -> + do_gc(), + {reply, ok, State, hibernate}; + handle_call(Msg, _From, State) -> {stop, {unexpected_call, Msg}, State}. diff --git a/src/rabbit_alarm.erl b/src/rabbit_alarm.erl index 675d3697..e5f8a1c4 100644 --- a/src/rabbit_alarm.erl +++ b/src/rabbit_alarm.erl @@ -58,7 +58,7 @@ start() -> vm_memory_monitor, [MemoryWatermark, fun (Alarm) -> R = set_alarm(Alarm), - [garbage_collect(P) || P <- processes()], + background_gc:gc_all(), R end, fun clear_alarm/1]), -- cgit v1.2.1 From 053d4728cf24d979a397cb5215954049ec4ac496 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 12 Nov 2012 15:59:28 +0000 Subject: cast not call. --- src/background_gc.erl | 14 +++++++------- src/rabbit_alarm.erl | 5 ++--- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/background_gc.erl b/src/background_gc.erl index 64b86b18..34741eba 100644 --- a/src/background_gc.erl +++ b/src/background_gc.erl @@ -18,7 +18,7 @@ -behaviour(gen_server2). --export([start_link/0, gc_all/0]). +-export([start_link/0, run/0]). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). @@ -38,8 +38,8 @@ start_link() -> gen_server2:start_link({local, ?MODULE}, ?MODULE, [], [{timeout, infinity}]). -gc_all() -> - gen_server2:call(?MODULE, gc_all, infinity). +run() -> + gen_server2:cast(?MODULE, gc_all). %%---------------------------------------------------------------------------- @@ -47,13 +47,13 @@ init([]) -> {ok, run_gc(#state{last_interval = ?IDEAL_INTERVAL}), hibernate, {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}. -handle_call(gc_all, _From, State) -> - do_gc(), - {reply, ok, State, hibernate}; - handle_call(Msg, _From, State) -> {stop, {unexpected_call, Msg}, State}. +handle_cast(gc_all, State) -> + do_gc(), + {noreply, State, hibernate}; + handle_cast(Msg, State) -> {stop, {unexpected_cast, Msg}, State}. diff --git a/src/rabbit_alarm.erl b/src/rabbit_alarm.erl index e5f8a1c4..d7d4d82a 100644 --- a/src/rabbit_alarm.erl +++ b/src/rabbit_alarm.erl @@ -57,9 +57,8 @@ start() -> rabbit_sup:start_restartable_child( vm_memory_monitor, [MemoryWatermark, fun (Alarm) -> - R = set_alarm(Alarm), - background_gc:gc_all(), - R + background_gc:run(), + set_alarm(Alarm) end, fun clear_alarm/1]), {ok, DiskLimit} = application:get_env(disk_free_limit), -- cgit v1.2.1 From 3e422eca2856eb6ef64e1f0ab8019c11c8c12e5f Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 12 Nov 2012 16:48:47 +0000 Subject: Specs --- src/background_gc.erl | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/background_gc.erl b/src/background_gc.erl index 34741eba..e053a677 100644 --- a/src/background_gc.erl +++ b/src/background_gc.erl @@ -34,6 +34,15 @@ %%---------------------------------------------------------------------------- +-ifdef(use_specs). + +-spec(start_link/0 :: () -> {'ok', pid()} | {'error', any()}). +-spec(run/0 :: () -> 'ok'). + +-endif. + +%%---------------------------------------------------------------------------- + start_link() -> gen_server2:start_link({local, ?MODULE}, ?MODULE, [], [{timeout, infinity}]). -- cgit v1.2.1 From 7174bb59f32ffe4fe1db6fb7f653e90d4f989f93 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 12 Nov 2012 16:51:18 +0000 Subject: Remove define, change message, silence dialyser. --- src/background_gc.erl | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/background_gc.erl b/src/background_gc.erl index e053a677..d781a290 100644 --- a/src/background_gc.erl +++ b/src/background_gc.erl @@ -28,7 +28,6 @@ -define(MAX_RATIO, 0.01). -define(IDEAL_INTERVAL, 60000). --define(MIN_BYTES, 50000). -record(state, {last_interval}). @@ -48,7 +47,7 @@ start_link() -> [{timeout, infinity}]). run() -> - gen_server2:cast(?MODULE, gc_all). + gen_server2:cast(?MODULE, run). %%---------------------------------------------------------------------------- @@ -57,9 +56,9 @@ init([]) -> {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}. handle_call(Msg, _From, State) -> - {stop, {unexpected_call, Msg}, State}. + {stop, {unexpected_call, Msg}, {unexpected_call, Msg}, State}. -handle_cast(gc_all, State) -> +handle_cast(run, State) -> do_gc(), {noreply, State, hibernate}; -- cgit v1.2.1 From 5be572e8357f3519053ea7320cbeeea51cfc556e Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 12 Nov 2012 16:52:08 +0000 Subject: Start background_gc earlier --- src/rabbit.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index ba0fceaf..c3a6d283 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -183,7 +183,7 @@ [{description, "background garbage collection"}, {mfa, {rabbit_sup, start_restartable_child, [background_gc]}}, - {requires, networking}]}). + {enables, networking}]}). %%--------------------------------------------------------------------------- -- cgit v1.2.1 From 57fc2f428c28909a75ad1ef9a0cf3b81fc42fde0 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 12 Nov 2012 16:55:09 +0000 Subject: Don't hibernate, GC self instead. --- src/background_gc.erl | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/background_gc.erl b/src/background_gc.erl index d781a290..0275700f 100644 --- a/src/background_gc.erl +++ b/src/background_gc.erl @@ -23,9 +23,6 @@ -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). --define(HIBERNATE_AFTER_MIN, 1000). --define(DESIRED_HIBERNATE, 10000). - -define(MAX_RATIO, 0.01). -define(IDEAL_INTERVAL, 60000). @@ -52,15 +49,14 @@ run() -> %%---------------------------------------------------------------------------- init([]) -> - {ok, run_gc(#state{last_interval = ?IDEAL_INTERVAL}), hibernate, - {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}. + {ok, run_gc(#state{last_interval = ?IDEAL_INTERVAL})}. handle_call(Msg, _From, State) -> {stop, {unexpected_call, Msg}, {unexpected_call, Msg}, State}. handle_cast(run, State) -> do_gc(), - {noreply, State, hibernate}; + {noreply, State}; handle_cast(Msg, State) -> {stop, {unexpected_cast, Msg}, State}. @@ -88,4 +84,5 @@ run_gc(State = #state{last_interval = LastInterval}) -> do_gc() -> [garbage_collect(P) || P <- processes(), {status, waiting} == process_info(P, status)], + garbage_collect(self()), %% Since we will never be waiting... ok. -- cgit v1.2.1 From 0cfaf879a427c252c970e21ec6135ab898672402 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 13 Nov 2012 11:19:02 +0000 Subject: test triggering of memory alarms --- src/rabbit_tests.erl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 8a24d388..096f9490 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -1131,6 +1131,9 @@ test_server_status() -> HWM = vm_memory_monitor:get_vm_memory_high_watermark(), ok = control_action(set_vm_memory_high_watermark, ["1"]), ok = control_action(set_vm_memory_high_watermark, ["1.0"]), + %% this will trigger an alarm + ok = control_action(set_vm_memory_high_watermark, ["0.0"]), + %% reset ok = control_action(set_vm_memory_high_watermark, [float_to_list(HWM)]), %% eval -- cgit v1.2.1 From c6e0ca5ed0c5a0c75bf004d316bf87a15053bcbc Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Tue, 13 Nov 2012 11:46:59 +0000 Subject: roll back to (almost) default - keeping the extra error information when corrupt cluster status files are found --- src/rabbit.erl | 8 +------- src/rabbit_node_monitor.erl | 33 ++++++++------------------------- 2 files changed, 9 insertions(+), 32 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index f3d31b22..ef9f5f56 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -174,16 +174,10 @@ [{mfa, {rabbit_networking, boot, []}}, {requires, log_relay}]}). --rabbit_boot_step({app_running, - [{description, "cluster membership"}, - {mfa, {rabbit_sup, start_restartable_child, - [rabbit_app_marker]}}, - {requires, networking}]}). - -rabbit_boot_step({notify_cluster, [{description, "notify cluster nodes"}, {mfa, {rabbit_node_monitor, notify_node_up, []}}, - {requires, app_running}]}). + {requires, networking}]}). %%--------------------------------------------------------------------------- diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index b89d4dc4..97feb2f2 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -112,7 +112,7 @@ prepare_cluster_status_files() -> true -> ThisNode; false -> [] end, - write_cluster_status({AllNodes2, DiscNodes, RunningNodes2}). + ok = write_cluster_status({AllNodes2, DiscNodes, RunningNodes2}). write_cluster_status({All, Disc, Running}) -> ClusterStatusFN = cluster_status_filename(), @@ -199,7 +199,7 @@ handle_call(_Request, _From, State) -> %% mnesia propagation. handle_cast({node_up, Node, NodeType}, State = #state{monitors = Monitors}) -> - case pmon:is_monitored({rabbit_running, Node}, Monitors) of + case pmon:is_monitored({rabbit, Node}, Monitors) of true -> {noreply, State}; false -> rabbit_log:info("rabbit on node ~p up~n", [Node]), {AllNodes, DiscNodes, RunningNodes} = read_cluster_status(), @@ -210,12 +210,8 @@ handle_cast({node_up, Node, NodeType}, end, add_node(Node, RunningNodes)}), ok = handle_live_rabbit(Node), - State1 = mon({rabbit_running, Node}, State), - State2 = case pmon:is_monitored({rabbit, Node}, Monitors) of - true -> State1; - false -> mon({rabbit, Node}, State1) - end, - {noreply, State2} + {noreply, State#state{ + monitors = pmon:monitor({rabbit, Node}, Monitors)}} end; handle_cast({joined_cluster, Node, NodeType}, State) -> {AllNodes, DiscNodes, RunningNodes} = read_cluster_status(), @@ -234,20 +230,13 @@ handle_cast({left_cluster, Node}, State) -> handle_cast(_Msg, State) -> {noreply, State}. -handle_info({'DOWN', _MRef, process, {rabbit_running, Node}, _Reason}, State) -> - %% The node has started to stop, remove it from the cluster status - %% file. We want to do this "early" to stand a better chance of - %% recording anything when all the nodes are shut down - %% simultaneously. +handle_info({'DOWN', _MRef, process, {rabbit, Node}, _Reason}, + State = #state{monitors = Monitors}) -> + rabbit_log:info("rabbit on node ~p down~n", [Node]), {AllNodes, DiscNodes, RunningNodes} = read_cluster_status(), write_cluster_status({AllNodes, DiscNodes, del_node(Node, RunningNodes)}), - {noreply, unmon({rabbit_running, Node}, State)}; - -handle_info({'DOWN', _MRef, process, {rabbit, Node}, _Reason}, State) -> - %% The node has finished stopping (rabbit anyway), treat it as dead. - rabbit_log:info("rabbit on node ~p down~n", [Node]), ok = handle_dead_rabbit(Node), - {noreply, unmon({rabbit, Node}, State)}; + {noreply, State#state{monitors = pmon:erase({rabbit, Node}, Monitors)}}; handle_info({mnesia_system_event, {inconsistent_database, running_partitioned_network, Node}}, @@ -305,9 +294,3 @@ legacy_should_be_disc_node(DiscNodes) -> add_node(Node, Nodes) -> lists:usort([Node | Nodes]). del_node(Node, Nodes) -> Nodes -- [Node]. - -mon(Item, State = #state{monitors = Monitors}) -> - State#state{monitors = pmon:monitor(Item, Monitors)}. - -unmon(Item, State = #state{monitors = Monitors}) -> - State#state{monitors = pmon:erase(Item, Monitors)}. -- cgit v1.2.1 From 236aff09ce9dc77367540a8a0acac88c0c3384da Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Tue, 13 Nov 2012 11:48:12 +0000 Subject: rabbit_node_monitor traps exits, so shutdown allows us time to flush any pending file system operations before the process dies --- src/rabbit_node_monitor.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index 97feb2f2..21389583 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -184,6 +184,7 @@ partitions() -> %%---------------------------------------------------------------------------- init([]) -> + process_flag(trap_exit, true), {ok, _} = mnesia:subscribe(system), {ok, #state{monitors = pmon:new(), partitions = []}}. -- cgit v1.2.1 From e150aa15f9bb8d21081d75495ca79378ac4e26b1 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 13 Nov 2012 12:02:44 +0000 Subject: Explain why --- src/rabbit_node_monitor.erl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index 21389583..8d0e4456 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -184,6 +184,10 @@ partitions() -> %%---------------------------------------------------------------------------- init([]) -> + %% We trap exits so that the supervisor will not just kill us. We + %% want to be sure that we are not going to be killed while + %% writing out the cluster status files - bad things can then + %% happen. process_flag(trap_exit, true), {ok, _} = mnesia:subscribe(system), {ok, #state{monitors = pmon:new(), -- cgit v1.2.1 From 9ab849d1c407688a211b7506bafb2c3c6ff3d2a8 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 13 Nov 2012 12:03:36 +0000 Subject: And, err, this thing isn't needed any more. --- src/rabbit_app_marker.erl | 49 ----------------------------------------------- 1 file changed, 49 deletions(-) delete mode 100644 src/rabbit_app_marker.erl diff --git a/src/rabbit_app_marker.erl b/src/rabbit_app_marker.erl deleted file mode 100644 index 296c2918..00000000 --- a/src/rabbit_app_marker.erl +++ /dev/null @@ -1,49 +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 VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. -%% - --module(rabbit_app_marker). - --behaviour(gen_server). - --export([start_link/0]). - --export([init/1, handle_call/3, handle_cast/2, handle_info/2, - terminate/2, code_change/3]). - --include("rabbit.hrl"). - -%%---------------------------------------------------------------------------- - -%% We want to know when another node has *started* shutting down (to -%% write the cluster status file). The rabbit application goes away -%% pretty much when we have *finished* shutting down. So we have this -%% process to monitor instead - it;s the last thing to be started so -%% the first thing to go. - -start_link() -> - gen_server:start_link({local, rabbit_running}, ?MODULE, [], []). - -%%---------------------------------------------------------------------------- - -init([]) -> {ok, state, hibernate}. - -handle_call(_Msg, _From, State) -> {stop, not_understood, State}. -handle_cast(_Msg, State) -> {stop, not_understood, State}. -handle_info(_Msg, State) -> {stop, not_understood, State}. - -terminate(_Arg, _State) -> ok. - -code_change(_OldVsn, State, _Extra) -> {ok, State}. -- cgit v1.2.1 From 447fbe031b1bd41cc78ea3203fdad11ad137f6b8 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 13 Nov 2012 12:42:54 +0000 Subject: tweak - garbage_collect(self()) -> garbage_collect() - rename some funs / msg tags - cosmetics --- src/background_gc.erl | 40 +++++++++++++++------------------------- 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/src/background_gc.erl b/src/background_gc.erl index 0275700f..7c68a177 100644 --- a/src/background_gc.erl +++ b/src/background_gc.erl @@ -39,50 +39,40 @@ %%---------------------------------------------------------------------------- -start_link() -> - gen_server2:start_link({local, ?MODULE}, ?MODULE, [], - [{timeout, infinity}]). +start_link() -> gen_server2:start_link({local, ?MODULE}, ?MODULE, [], + [{timeout, infinity}]). -run() -> - gen_server2:cast(?MODULE, run). +run() -> gen_server2:cast(?MODULE, run). %%---------------------------------------------------------------------------- -init([]) -> - {ok, run_gc(#state{last_interval = ?IDEAL_INTERVAL})}. +init([]) -> {ok, interval_gc(#state{last_interval = ?IDEAL_INTERVAL})}. handle_call(Msg, _From, State) -> {stop, {unexpected_call, Msg}, {unexpected_call, Msg}, State}. -handle_cast(run, State) -> - do_gc(), - {noreply, State}; +handle_cast(run, State) -> gc(), {noreply, State}; -handle_cast(Msg, State) -> - {stop, {unexpected_cast, Msg}, State}. +handle_cast(Msg, State) -> {stop, {unexpected_cast, Msg}, State}. -handle_info(run_gc, State) -> - {noreply, run_gc(State)}; +handle_info(run, State) -> {noreply, interval_gc(State)}; -handle_info(Msg, State) -> - {stop, {unexpected_info, Msg}, State}. +handle_info(Msg, State) -> {stop, {unexpected_info, Msg}, State}. -code_change(_OldVsn, State, _Extra) -> - {ok, State}. +code_change(_OldVsn, State, _Extra) -> {ok, State}. -terminate(_Reason, State) -> - State. +terminate(_Reason, State) -> State. %%---------------------------------------------------------------------------- -run_gc(State = #state{last_interval = LastInterval}) -> +interval_gc(State = #state{last_interval = LastInterval}) -> {ok, Interval} = rabbit_misc:interval_operation( - fun do_gc/0, ?MAX_RATIO, ?IDEAL_INTERVAL, LastInterval), - erlang:send_after(Interval, self(), run_gc), + fun gc/0, ?MAX_RATIO, ?IDEAL_INTERVAL, LastInterval), + erlang:send_after(Interval, self(), run), State#state{last_interval = Interval}. -do_gc() -> +gc() -> [garbage_collect(P) || P <- processes(), {status, waiting} == process_info(P, status)], - garbage_collect(self()), %% Since we will never be waiting... + garbage_collect(), %% since we will never be waiting... ok. -- cgit v1.2.1 From 563ef0ee5e839b4c1b5567bd8bc2f00e8e1b6b43 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 14 Nov 2012 11:14:39 +0000 Subject: Work on older Erlang versions --- src/background_gc.erl | 5 ++++- src/rabbit_misc.erl | 8 ++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/background_gc.erl b/src/background_gc.erl index 7c68a177..3dbce330 100644 --- a/src/background_gc.erl +++ b/src/background_gc.erl @@ -19,6 +19,7 @@ -behaviour(gen_server2). -export([start_link/0, run/0]). +-export([gc/0]). %% For run_interval only -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). @@ -34,6 +35,7 @@ -spec(start_link/0 :: () -> {'ok', pid()} | {'error', any()}). -spec(run/0 :: () -> 'ok'). +-spec(gc/0 :: () -> 'ok'). -endif. @@ -67,7 +69,8 @@ terminate(_Reason, State) -> State. interval_gc(State = #state{last_interval = LastInterval}) -> {ok, Interval} = rabbit_misc:interval_operation( - fun gc/0, ?MAX_RATIO, ?IDEAL_INTERVAL, LastInterval), + {?MODULE, gc, []}, + ?MAX_RATIO, ?IDEAL_INTERVAL, LastInterval), erlang:send_after(Interval, self(), run), State#state{last_interval = Interval}. diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 137ccf20..81bb6769 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -237,8 +237,8 @@ -spec(check_expiry/1 :: (integer()) -> rabbit_types:ok_or_error(any())). -spec(base64url/1 :: (binary()) -> string()). -spec(interval_operation/4 :: - (thunk(A), float(), non_neg_integer(), non_neg_integer()) - -> {A, non_neg_integer()}). + ({atom(), atom(), any()}, float(), non_neg_integer(), non_neg_integer()) + -> {any(), non_neg_integer()}). -endif. @@ -1025,8 +1025,8 @@ base64url(In) -> %% more then you want to run it less often. So we time how long it %% takes to run, and then suggest how long you should wait before %% running it again. Times are in millis. -interval_operation(Fun, MaxRatio, IdealInterval, LastInterval) -> - {Micros, Res} = timer:tc(Fun), +interval_operation({M, F, A}, MaxRatio, IdealInterval, LastInterval) -> + {Micros, Res} = timer:tc(M, F, A), {Res, case {Micros > 1000 * (MaxRatio * IdealInterval), Micros > 1000 * (MaxRatio * LastInterval)} of {true, true} -> round(LastInterval * 1.5); -- cgit v1.2.1 -- cgit v1.2.1 From 3684e4d03b6af3a7face8319b4bcaf7949f557d8 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 15 Nov 2012 11:56:11 +0000 Subject: emit names instead of pids in queue process' queue events --- src/rabbit_amqqueue.erl | 26 ++++++++++++-------------- src/rabbit_amqqueue_process.erl | 10 ++++------ 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 9fb453c1..4684ad7c 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -34,7 +34,7 @@ -export([start_mirroring/1, stop_mirroring/1]). %% internal --export([internal_declare/2, internal_delete/2, run_backing_queue/3, +-export([internal_declare/2, internal_delete/1, run_backing_queue/3, set_ram_duration_target/2, set_maximum_since_use/2]). -include("rabbit.hrl"). @@ -156,11 +156,11 @@ -spec(notify_sent_queue_down/1 :: (pid()) -> 'ok'). -spec(unblock/2 :: (pid(), pid()) -> 'ok'). -spec(flush_all/2 :: (qpids(), pid()) -> 'ok'). --spec(internal_delete/2 :: - (name(), pid()) -> rabbit_types:ok_or_error('not_found') | - rabbit_types:connection_exit() | - fun (() -> rabbit_types:ok_or_error('not_found') | - rabbit_types:connection_exit())). +-spec(internal_delete/1 :: + (name()) -> rabbit_types:ok_or_error('not_found') | + rabbit_types:connection_exit() | + fun (() -> rabbit_types:ok_or_error('not_found') | + rabbit_types:connection_exit())). -spec(run_backing_queue/3 :: (pid(), atom(), (fun ((atom(), A) -> {[rabbit_types:msg_id()], A}))) -> 'ok'). @@ -257,7 +257,7 @@ internal_declare(Q = #amqqueue{name = QueueName}, false) -> [ExistingQ = #amqqueue{pid = QPid}] -> case rabbit_misc:is_process_alive(QPid) of true -> rabbit_misc:const(ExistingQ); - false -> TailFun = internal_delete(QueueName, QPid), + false -> TailFun = internal_delete(QueueName), fun () -> TailFun(), ExistingQ end end end @@ -574,7 +574,7 @@ internal_delete1(QueueName) -> %% after the transaction. rabbit_binding:remove_for_destination(QueueName). -internal_delete(QueueName, QPid) -> +internal_delete(QueueName) -> rabbit_misc:execute_mnesia_tx_with_tail( fun () -> case mnesia:wread({rabbit_queue, QueueName}) of @@ -584,8 +584,7 @@ internal_delete(QueueName, QPid) -> fun() -> ok = T(), ok = rabbit_event:notify(queue_deleted, - [{pid, QPid}, - {name, QueueName}]) + [{name, QueueName}]) end end end). @@ -605,7 +604,7 @@ stop_mirroring(QPid) -> ok = delegate_call(QPid, stop_mirroring). on_node_down(Node) -> rabbit_misc:execute_mnesia_tx_with_tail( fun () -> QsDels = - qlc:e(qlc:q([{{QName, Pid}, delete_queue(QName)} || + qlc:e(qlc:q([{QName, delete_queue(QName)} || #amqqueue{name = QName, pid = Pid, slave_pids = []} <- mnesia:table(rabbit_queue), @@ -618,10 +617,9 @@ on_node_down(Node) -> fun () -> T(), lists:foreach( - fun({QName, QPid}) -> + fun(QName) -> ok = rabbit_event:notify(queue_deleted, - [{pid, QPid}, - {name, QName}]) + [{name, QName}]) end, Qs) end end). diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 92b00db0..67f925a4 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -85,7 +85,7 @@ %%---------------------------------------------------------------------------- -define(STATISTICS_KEYS, - [pid, + [name, policy, exclusive_consumer_pid, exclusive_consumer_tag, @@ -101,16 +101,14 @@ ]). -define(CREATION_EVENT_KEYS, - [pid, - name, + [name, durable, auto_delete, arguments, owner_pid ]). --define(INFO_KEYS, - ?CREATION_EVENT_KEYS ++ ?STATISTICS_KEYS -- [pid]). +-define(INFO_KEYS, ?CREATION_EVENT_KEYS ++ ?STATISTICS_KEYS). %%---------------------------------------------------------------------------- @@ -183,7 +181,7 @@ terminate(Reason, State = #q{q = #amqqueue{name = QName}, fun (BQS) -> BQS1 = BQ:delete_and_terminate(Reason, BQS), %% don't care if the internal delete doesn't return 'ok'. - rabbit_amqqueue:internal_delete(QName, self()), + rabbit_amqqueue:internal_delete(QName), BQS1 end, State). -- cgit v1.2.1 From 919323e6f72f587fc9493c2884e07203f8aa6ff1 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 15 Nov 2012 12:18:32 +0000 Subject: reference queue by name in consumer events --- src/rabbit_amqqueue_process.erl | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 67f925a4..9bd465dd 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -275,7 +275,8 @@ terminate_shutdown(Fun, State) -> case BQS of undefined -> State1; _ -> ok = rabbit_memory_monitor:deregister(self()), - [emit_consumer_deleted(Ch, CTag) + QName = qname(State), + [emit_consumer_deleted(Ch, CTag, QName) || {Ch, CTag, _} <- consumers(State1)], State1#q{backing_queue_state = Fun(BQS)} end. @@ -589,9 +590,9 @@ remove_consumer(ChPid, ConsumerTag, Queue) -> (CP /= ChPid) or (CTag /= ConsumerTag) end, Queue). -remove_consumers(ChPid, Queue) -> +remove_consumers(ChPid, Queue, QName) -> queue:filter(fun ({CP, #consumer{tag = CTag}}) when CP =:= ChPid -> - emit_consumer_deleted(ChPid, CTag), + emit_consumer_deleted(ChPid, CTag, QName), false; (_) -> true @@ -632,7 +633,8 @@ handle_ch_down(DownPid, State = #q{exclusive_consumer = Holder, C = #cr{ch_pid = ChPid, acktags = ChAckTags, blocked_consumers = Blocked} -> - _ = remove_consumers(ChPid, Blocked), %% for stats emission + QName = qname(State), + _ = remove_consumers(ChPid, Blocked, QName), %% for stats emission ok = erase_ch_record(C), State1 = State#q{ exclusive_consumer = case Holder of @@ -640,7 +642,8 @@ handle_ch_down(DownPid, State = #q{exclusive_consumer = Holder, Other -> Other end, active_consumers = remove_consumers( - ChPid, State#q.active_consumers), + ChPid, State#q.active_consumers, + QName), senders = Senders1}, case should_auto_delete(State1) of true -> {stop, State1}; @@ -948,19 +951,19 @@ emit_stats(State) -> emit_stats(State, Extra) -> rabbit_event:notify(queue_stats, Extra ++ infos(?STATISTICS_KEYS, State)). -emit_consumer_created(ChPid, ConsumerTag, Exclusive, AckRequired) -> +emit_consumer_created(ChPid, ConsumerTag, Exclusive, AckRequired, QName) -> rabbit_event:notify(consumer_created, [{consumer_tag, ConsumerTag}, {exclusive, Exclusive}, {ack_required, AckRequired}, {channel, ChPid}, - {queue, self()}]). + {queue, QName}]). -emit_consumer_deleted(ChPid, ConsumerTag) -> +emit_consumer_deleted(ChPid, ConsumerTag, QName) -> rabbit_event:notify(consumer_deleted, [{consumer_tag, ConsumerTag}, {channel, ChPid}, - {queue, self()}]). + {queue, QName}]). %%---------------------------------------------------------------------------- @@ -1068,9 +1071,8 @@ handle_call({basic_get, ChPid, NoAck}, _From, handle_call({basic_consume, NoAck, ChPid, Limiter, ConsumerTag, ExclusiveConsume, OkMsg}, - _From, State = #q{exclusive_consumer = ExistingHolder}) -> - case check_exclusive_access(ExistingHolder, ExclusiveConsume, - State) of + _From, State = #q{exclusive_consumer = Holder}) -> + case check_exclusive_access(Holder, ExclusiveConsume, State) of in_use -> reply({error, exclusive_consume_unavailable}, State); ok -> @@ -1079,7 +1081,7 @@ handle_call({basic_consume, NoAck, ChPid, Limiter, Consumer = #consumer{tag = ConsumerTag, ack_required = not NoAck}, ExclusiveConsumer = if ExclusiveConsume -> {ChPid, ConsumerTag}; - true -> ExistingHolder + true -> Holder end, State1 = State#q{has_had_consumers = true, exclusive_consumer = ExclusiveConsumer}, @@ -1094,7 +1096,7 @@ handle_call({basic_consume, NoAck, ChPid, Limiter, run_message_queue(State1#q{active_consumers = AC1}) end, emit_consumer_created(ChPid, ConsumerTag, ExclusiveConsume, - not NoAck), + not NoAck, qname(State2)), reply(ok, State2) end; @@ -1105,7 +1107,7 @@ handle_call({basic_cancel, ChPid, ConsumerTag, OkMsg}, From, not_found -> reply(ok, State); C = #cr{blocked_consumers = Blocked} -> - emit_consumer_deleted(ChPid, ConsumerTag), + emit_consumer_deleted(ChPid, ConsumerTag, qname(State)), Blocked1 = remove_consumer(ChPid, ConsumerTag, Blocked), update_consumer_count(C#cr{blocked_consumers = Blocked1}, -1), State1 = State#q{ @@ -1115,7 +1117,7 @@ handle_call({basic_cancel, ChPid, ConsumerTag, OkMsg}, From, end, active_consumers = remove_consumer( ChPid, ConsumerTag, - State#q.active_consumers)}, + State#q.active_consumers)}, case should_auto_delete(State1) of false -> reply(ok, ensure_expiry_timer(State1)); true -> stop_later(normal, From, ok, State1) @@ -1169,11 +1171,13 @@ handle_call(stop_mirroring, _From, State = #q{backing_queue = BQ, handle_call(force_event_refresh, _From, State = #q{exclusive_consumer = Exclusive}) -> rabbit_event:notify(queue_created, infos(?CREATION_EVENT_KEYS, State)), + QName = qname(State), case Exclusive of - none -> [emit_consumer_created(Ch, CTag, false, AckRequired) || + 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) + emit_consumer_created(Ch, CTag, true, AckRequired, QName) end, reply(ok, State). -- cgit v1.2.1 From 24e4abdbabcd2e47c219d1f5e554214517601f60 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 15 Nov 2012 13:04:30 +0000 Subject: inline --- src/rabbit_channel.erl | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index a94d2ab5..53610e6d 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -1242,7 +1242,10 @@ record_sent(ConsumerTag, AckRequired, {_ , true} -> deliver; {_ , false} -> deliver_no_ack end, State), - maybe_incr_redeliver_stats(Redelivered, QPid, State), + case Redelivered of + true -> maybe_incr_stats([{QPid, 1}], redeliver, State); + false -> ok + end, rabbit_trace:tap_trace_out(Msg, TraceState), UAMQ1 = case AckRequired of true -> queue:in({DeliveryTag, ConsumerTag, {QPid, MsgId}}, @@ -1462,11 +1465,6 @@ i(Item, _) -> name(#ch{conn_name = ConnName, channel = Channel}) -> list_to_binary(rabbit_misc:format("~s (~p)", [ConnName, Channel])). -maybe_incr_redeliver_stats(true, QPid, State) -> - maybe_incr_stats([{QPid, 1}], redeliver, State); -maybe_incr_redeliver_stats(_, _, _State) -> - ok. - maybe_incr_stats(QXIncs, Measure, State) -> case rabbit_event:stats_level(State, #ch.stats_timer) of fine -> [incr_stats(QX, Inc, Measure) || {QX, Inc} <- QXIncs]; -- cgit v1.2.1 From f1e03821a9b62e1f28ffddf0a4ee4fa31006461b Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 15 Nov 2012 13:53:46 +0000 Subject: refactor stats recording in channel - specify stats type explicitly rather than inferring it from the structure of the key (yuck!) - s/maybe_incr_stats/incr_stats --- src/rabbit_channel.erl | 64 +++++++++++++++++++++++--------------------------- 1 file changed, 29 insertions(+), 35 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 53610e6d..9dbfbdea 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -1236,14 +1236,14 @@ record_sent(ConsumerTag, AckRequired, State = #ch{unacked_message_q = UAMQ, next_tag = DeliveryTag, trace_state = TraceState}) -> - maybe_incr_stats([{QPid, 1}], case {ConsumerTag, AckRequired} of - {none, true} -> get; - {none, false} -> get_no_ack; - {_ , true} -> deliver; - {_ , false} -> deliver_no_ack - end, State), + incr_stats([{queue_stats, QPid, 1}], case {ConsumerTag, AckRequired} of + {none, true} -> get; + {none, false} -> get_no_ack; + {_ , true} -> deliver; + {_ , false} -> deliver_no_ack + end, State), case Redelivered of - true -> maybe_incr_stats([{QPid, 1}], redeliver, State); + true -> incr_stats([{queue_stats, QPid, 1}], redeliver, State); false -> ok end, rabbit_trace:tap_trace_out(Msg, TraceState), @@ -1277,13 +1277,13 @@ collect_acks(ToAcc, PrefixAcc, Q, DeliveryTag, Multiple) -> end. ack(Acked, State) -> - QIncs = fold_per_queue( - fun (QPid, MsgIds, L) -> - ok = rabbit_amqqueue:ack(QPid, MsgIds, self()), - [{QPid, length(MsgIds)} | L] - end, [], Acked), + Incs = fold_per_queue( + fun (QPid, MsgIds, L) -> + ok = rabbit_amqqueue:ack(QPid, MsgIds, self()), + [{queue_stats, QPid, length(MsgIds)} | L] + end, [], Acked), ok = notify_limiter(State#ch.limiter, Acked), - maybe_incr_stats(QIncs, ack, State). + incr_stats(Incs, ack, State). new_tx(State) -> State#ch{uncommitted_message_q = queue:new(), uncommitted_acks = [], @@ -1346,15 +1346,15 @@ deliver_to_queues({Delivery = #delivery{message = Message = #basic_message{ State#ch.queue_monitors)}, State2 = process_routing_result(RoutingRes, DeliveredQPids, XName, MsgSeqNo, Message, State1), - maybe_incr_stats([{XName, 1} | - [{{QPid, XName}, 1} || - QPid <- DeliveredQPids]], publish, State2), + incr_stats([{exchange_stats, XName, 1} | + [{queue_exchange_stats, {QPid, XName}, 1} || + QPid <- DeliveredQPids]], publish, State2), State2. process_routing_result(unroutable, _, XName, MsgSeqNo, Msg, State) -> ok = basic_return(Msg, State, no_route), - maybe_incr_stats([{Msg#basic_message.exchange_name, 1}], - return_unroutable, State), + incr_stats([{exchange_stats, Msg#basic_message.exchange_name, 1}], + return_unroutable, State), record_confirm(MsgSeqNo, XName, State); process_routing_result(routed, [], XName, MsgSeqNo, _, State) -> record_confirm(MsgSeqNo, XName, State); @@ -1379,10 +1379,11 @@ send_confirms(State = #ch{tx_status = none, confirmed = []}) -> State; send_confirms(State = #ch{tx_status = none, confirmed = C}) -> MsgSeqNos = - lists:foldl(fun ({MsgSeqNo, XName}, MSNs) -> - maybe_incr_stats([{XName, 1}], confirm, State), - [MsgSeqNo | MSNs] - end, [], lists:append(C)), + lists:foldl( + fun ({MsgSeqNo, XName}, MSNs) -> + incr_stats([{exchange_stats, XName, 1}], confirm, State), + [MsgSeqNo | MSNs] + end, [], lists:append(C)), send_confirms(MsgSeqNos, State#ch{confirmed = []}); send_confirms(State) -> maybe_complete_tx(State). @@ -1465,21 +1466,15 @@ i(Item, _) -> name(#ch{conn_name = ConnName, channel = Channel}) -> list_to_binary(rabbit_misc:format("~s (~p)", [ConnName, Channel])). -maybe_incr_stats(QXIncs, Measure, State) -> +incr_stats(Incs, Measure, State) -> case rabbit_event:stats_level(State, #ch.stats_timer) of - fine -> [incr_stats(QX, Inc, Measure) || {QX, Inc} <- QXIncs]; + fine -> [update_measures(Type, Key, Inc, Measure) || + {Type, Key, Inc} <- Incs]; _ -> ok end. -incr_stats({_, _} = QX, Inc, Measure) -> - update_measures(queue_exchange_stats, QX, Inc, Measure); -incr_stats(QPid, Inc, Measure) when is_pid(QPid) -> - update_measures(queue_stats, QPid, Inc, Measure); -incr_stats(X, Inc, Measure) -> - update_measures(exchange_stats, X, Inc, Measure). - -update_measures(Type, QX, Inc, Measure) -> - Measures = case get({Type, QX}) of +update_measures(Type, Key, Inc, Measure) -> + Measures = case get({Type, Key}) of undefined -> []; D -> D end, @@ -1487,8 +1482,7 @@ update_measures(Type, QX, Inc, Measure) -> error -> 0; {ok, C} -> C end, - put({Type, QX}, - orddict:store(Measure, Cur + Inc, Measures)). + put({Type, Key}, orddict:store(Measure, Cur + Inc, Measures)). emit_stats(State) -> emit_stats(State, []). -- cgit v1.2.1 From f546b647743c8a1ffa311ac9a940992b550ee0d5 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 15 Nov 2012 15:13:16 +0000 Subject: always monitor queues from which we are (possibly) delivering messages so that stats associated with these queues are cleared out from the channel's process dictionary when these queues disappear. --- src/rabbit_channel.erl | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 9dbfbdea..2afc4c36 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -1126,12 +1126,13 @@ consumer_monitor(ConsumerTag, State end. -monitor_delivering_queue(true, _QPid, State) -> - State; -monitor_delivering_queue(false, QPid, State = #ch{queue_monitors = QMons, +monitor_delivering_queue(NoAck, QPid, State = #ch{queue_monitors = QMons, delivering_queues = DQ}) -> State#ch{queue_monitors = pmon:monitor(QPid, QMons), - delivering_queues = sets:add_element(QPid, DQ)}. + delivering_queues = case NoAck of + true -> DQ; + false -> sets:add_element(QPid, DQ) + end}. handle_publishing_queue_down(QPid, Reason, State = #ch{unconfirmed = UC}) -> case rabbit_misc:is_abnormal_exit(Reason) of -- cgit v1.2.1 From 4edf76b369491de4e9bb18a4f5df571c10e411de Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 15 Nov 2012 18:05:55 +0000 Subject: inline (and avoid misleading var name in the process) --- src/rabbit_amqqueue_process.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 92b00db0..8d7535f8 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -762,8 +762,8 @@ dead_letter_publish(Msg, Reason, X, State = #q{publish_seqno = MsgSeqNo}) -> {Queues, Cycles} = detect_dead_letter_cycles( DLMsg, rabbit_exchange:route(X, Delivery)), lists:foreach(fun log_cycle_once/1, Cycles), - QPids = rabbit_amqqueue:lookup(Queues), - {_, DeliveredQPids} = rabbit_amqqueue:deliver(QPids, Delivery), + {_, DeliveredQPids} = rabbit_amqqueue:deliver( + rabbit_amqqueue:lookup(Queues), Delivery), DeliveredQPids. handle_queue_down(QPid, Reason, State = #q{queue_monitors = QMons, -- cgit v1.2.1 From 583967bd7c0050416645716d34729a2a48dd5e74 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 15 Nov 2012 18:34:14 +0000 Subject: cosmetic --- src/rabbit_amqqueue.erl | 37 +++++++++++++------------------------ 1 file changed, 13 insertions(+), 24 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 9fb453c1..8ce1160c 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -284,8 +284,7 @@ store_queue(Q = #amqqueue{durable = false}) -> ok = mnesia:write(rabbit_queue, Q, write), ok. -policy_changed(Q1, Q2) -> - rabbit_mirror_queue_misc:update_mirrors(Q1, Q2). +policy_changed(Q1, Q2) -> rabbit_mirror_queue_misc:update_mirrors(Q1, Q2). start_queue_process(Node, Q) -> {ok, Pid} = rabbit_amqqueue_sup:start_child(Node, [Q]), @@ -398,10 +397,8 @@ check_declare_arguments(QueueName, Args) -> end || {Key, Fun} <- Checks], ok. -check_string_arg({longstr, _}, _Args) -> - ok; -check_string_arg({Type, _}, _) -> - {error, {unacceptable_type, Type}}. +check_string_arg({longstr, _}, _Args) -> ok; +check_string_arg({Type, _}, _Args) -> {error, {unacceptable_type, Type}}. check_int_arg({Type, _}, _) -> case lists:member(Type, ?INTEGER_ARG_TYPES) of @@ -427,11 +424,10 @@ check_dlxrk_arg({longstr, _}, Args) -> undefined -> {error, routing_key_but_no_dlx_defined}; _ -> ok end; -check_dlxrk_arg({Type, _}, _Args) -> +check_dlxrk_arg({Type, _}, _Args) -> {error, {unacceptable_type, Type}}. -list() -> - mnesia:dirty_match_object(rabbit_queue, #amqqueue{_ = '_'}). +list() -> mnesia:dirty_match_object(rabbit_queue, #amqqueue{_ = '_'}). list(VHostPath) -> mnesia:dirty_match_object( @@ -442,8 +438,7 @@ info_keys() -> rabbit_amqqueue_process:info_keys(). map(VHostPath, F) -> rabbit_misc:filter_exit_map(F, list(VHostPath)). -info(#amqqueue{ pid = QPid }) -> - delegate_call(QPid, info). +info(#amqqueue{ pid = QPid }) -> delegate_call(QPid, info). info(#amqqueue{ pid = QPid }, Items) -> case delegate_call(QPid, {info, Items}) of @@ -460,8 +455,7 @@ info_all(VHostPath, Items) -> map(VHostPath, fun (Q) -> info(Q, Items) end). %% the first place since a node failed). Therefore we keep poking at %% the list of queues until we were able to talk to a live process or %% the queue no longer exists. -force_event_refresh() -> - force_event_refresh([Q#amqqueue.name || Q <- list()]). +force_event_refresh() -> force_event_refresh([Q#amqqueue.name || Q <- list()]). force_event_refresh(QNames) -> Qs = [Q || Q <- list(), lists:member(Q#amqqueue.name, QNames)], @@ -478,8 +472,7 @@ force_event_refresh(QNames) -> wake_up(#amqqueue{pid = QPid}) -> gen_server2:cast(QPid, wake_up). -consumers(#amqqueue{ pid = QPid }) -> - delegate_call(QPid, consumers). +consumers(#amqqueue{ pid = QPid }) -> delegate_call(QPid, consumers). consumer_info_keys() -> ?CONSUMER_INFO_KEYS. @@ -493,8 +486,7 @@ consumers_all(VHostPath) -> {ChPid, ConsumerTag, AckRequired} <- consumers(Q)] end)). -stat(#amqqueue{pid = QPid}) -> - delegate_call(QPid, stat). +stat(#amqqueue{pid = QPid}) -> delegate_call(QPid, stat). delete_immediately(QPids) -> [gen_server2:cast(QPid, delete_immediately) || QPid <- QPids], @@ -509,11 +501,9 @@ deliver(Qs, Delivery) -> deliver(Qs, Delivery, noflow). deliver_flow(Qs, Delivery) -> deliver(Qs, Delivery, flow). -requeue(QPid, MsgIds, ChPid) -> - delegate_call(QPid, {requeue, MsgIds, ChPid}). +requeue(QPid, MsgIds, ChPid) -> delegate_call(QPid, {requeue, MsgIds, ChPid}). -ack(QPid, MsgIds, ChPid) -> - delegate_cast(QPid, {ack, MsgIds, ChPid}). +ack(QPid, MsgIds, ChPid) -> delegate_cast(QPid, {ack, MsgIds, ChPid}). reject(QPid, MsgIds, Requeue, ChPid) -> delegate_cast(QPid, {reject, MsgIds, Requeue, ChPid}). @@ -555,8 +545,7 @@ notify_sent_queue_down(QPid) -> erase({consumer_credit_to, QPid}), ok. -unblock(QPid, ChPid) -> - delegate_cast(QPid, {unblock, ChPid}). +unblock(QPid, ChPid) -> delegate_cast(QPid, {unblock, ChPid}). flush_all(QPids, ChPid) -> delegate:invoke_no_result( @@ -600,7 +589,7 @@ set_maximum_since_use(QPid, Age) -> gen_server2:cast(QPid, {set_maximum_since_use, Age}). start_mirroring(QPid) -> ok = delegate_call(QPid, start_mirroring). -stop_mirroring(QPid) -> ok = delegate_call(QPid, stop_mirroring). +stop_mirroring(QPid) -> ok = delegate_call(QPid, stop_mirroring). on_node_down(Node) -> rabbit_misc:execute_mnesia_tx_with_tail( -- cgit v1.2.1 From 278a90478f558d98f48b58dd7c9e6ee376e0a6ab Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 16 Nov 2012 02:22:50 +0000 Subject: cosmetic --- src/rabbit_mirror_queue_misc.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 2f75ef2e..2b3bd027 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -315,8 +315,8 @@ update_mirrors(OldQ = #amqqueue{pid = QPid}, case {is_mirrored(OldQ), is_mirrored(NewQ)} of {false, false} -> ok; {true, false} -> rabbit_amqqueue:stop_mirroring(QPid); - {false, true} -> rabbit_amqqueue:start_mirroring(QPid); - {true, true} -> update_mirrors0(OldQ, NewQ) + {false, true} -> rabbit_amqqueue:start_mirroring(QPid); + {true, true} -> update_mirrors0(OldQ, NewQ) end. update_mirrors0(OldQ = #amqqueue{name = QName}, -- cgit v1.2.1 From 98957fd60dd3d1693071478079f9e1e049358ee3 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 16 Nov 2012 02:23:45 +0000 Subject: refactor: simplify dlx logic top half --- src/rabbit_amqqueue_process.erl | 50 +++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 30 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 8d7535f8..b679cb08 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -705,17 +705,18 @@ calculate_msg_expiry(#basic_message{content = Content}, TTL) -> T -> now_micros() + T * 1000 end. -drop_expired_messages(State = #q{backing_queue_state = BQS, +drop_expired_messages(State = #q{dlx = DLX, + backing_queue_state = BQS, backing_queue = BQ }) -> Now = now_micros(), - DLXFun = dead_letter_fun(expired, State), ExpirePred = fun (#message_properties{expiry = Exp}) -> Now >= Exp end, - {Props, BQS1} = case DLXFun of + {Props, BQS1} = case DLX of undefined -> {Next, undefined, BQS2} = BQ:dropwhile(ExpirePred, false, BQS), {Next, BQS2}; _ -> {Next, Msgs, BQS2} = BQ:dropwhile(ExpirePred, true, BQS), + DLXFun = dead_letter_fun(expired), DLXFun(Msgs), {Next, BQS2} end, @@ -743,17 +744,7 @@ ensure_ttl_timer(Expiry, State = #q{ttl_timer_ref = TRef, ensure_ttl_timer(_Expiry, State) -> State. -ack_if_no_dlx(AckTags, State = #q{dlx = undefined, - backing_queue = BQ, - backing_queue_state = BQS }) -> - {_Guids, BQS1} = BQ:ack(AckTags, BQS), - State#q{backing_queue_state = BQS1}; -ack_if_no_dlx(_AckTags, State) -> - State. - -dead_letter_fun(_Reason, #q{dlx = undefined}) -> - undefined; -dead_letter_fun(Reason, _State) -> +dead_letter_fun(Reason) -> fun(Msgs) -> gen_server2:cast(self(), {dead_letter, Msgs, Reason}) end. dead_letter_publish(Msg, Reason, X, State = #q{publish_seqno = MsgSeqNo}) -> @@ -1217,24 +1208,23 @@ handle_cast({ack, AckTags, ChPid}, State) -> State1#q{backing_queue_state = BQS1} end)); -handle_cast({reject, AckTags, Requeue, ChPid}, State) -> +handle_cast({reject, AckTags, true, ChPid}, State) -> noreply(subtract_acks( ChPid, AckTags, State, - case Requeue of - true -> fun (State1) -> requeue_and_run(AckTags, State1) end; - false -> fun (State1 = #q{backing_queue = BQ, - backing_queue_state = BQS}) -> - Fun = - case dead_letter_fun(rejected, State1) of - undefined -> undefined; - F -> fun(M, A) -> F([{M, A}]) - end - end, - BQS1 = BQ:fold(Fun, BQS, AckTags), - ack_if_no_dlx( - AckTags, - State1#q{backing_queue_state = BQS1}) - end + fun (State1) -> requeue_and_run(AckTags, State1) end)); + +handle_cast({reject, AckTags, false, ChPid}, State = #q{dlx = undefined}) -> + handle_cast({ack, AckTags, ChPid}, State); + +handle_cast({reject, AckTags, false, ChPid}, State) -> + DLXFun = dead_letter_fun(rejected), + noreply(subtract_acks( + ChPid, AckTags, State, + fun (State1 = #q{backing_queue = BQ, + backing_queue_state = BQS}) -> + BQS1 = BQ:fold(fun(M, A) -> DLXFun([{M, A}]) end, + BQS, AckTags), + State1#q{backing_queue_state = BQS1} end)); handle_cast(delete_immediately, State) -> -- cgit v1.2.1 From de5e29ea4bf0ab0c14de26395f3501e7d9ce48b2 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 16 Nov 2012 03:29:11 +0000 Subject: refactor: extract some helpers ...to make it abundantly clear which queue operations amount to an ack/requeue. --- src/rabbit_amqqueue_process.erl | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index b679cb08..ac357663 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -586,6 +586,18 @@ fetch(AckRequired, State = #q{backing_queue = BQ, {Result, BQS1} = BQ:fetch(AckRequired, BQS), {Result, State#q{backing_queue_state = BQS1}}. +ack(AckTags, ChPid, State) -> + subtract_acks(ChPid, AckTags, State, + fun (State1 = #q{backing_queue = BQ, + backing_queue_state = BQS}) -> + {_Guids, BQS1} = BQ:ack(AckTags, BQS), + State1#q{backing_queue_state = BQS1} + end). + +requeue(AckTags, ChPid, State) -> + subtract_acks(ChPid, AckTags, 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) @@ -1138,9 +1150,7 @@ handle_call(purge, _From, State = #q{backing_queue = BQ, handle_call({requeue, AckTags, ChPid}, From, State) -> gen_server2:reply(From, ok), - noreply(subtract_acks( - ChPid, AckTags, State, - fun (State1) -> requeue_and_run(AckTags, State1) end)); + noreply(requeue(AckTags, ChPid, State)); handle_call(start_mirroring, _From, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> @@ -1200,21 +1210,13 @@ handle_cast({deliver, Delivery = #delivery{sender = Sender}, Delivered, Flow}, noreply(deliver_or_enqueue(Delivery, Delivered, State1)); handle_cast({ack, AckTags, ChPid}, State) -> - noreply(subtract_acks( - ChPid, AckTags, State, - fun (State1 = #q{backing_queue = BQ, - backing_queue_state = BQS}) -> - {_Guids, BQS1} = BQ:ack(AckTags, BQS), - State1#q{backing_queue_state = BQS1} - end)); + noreply(ack(AckTags, ChPid, State)); handle_cast({reject, AckTags, true, ChPid}, State) -> - noreply(subtract_acks( - ChPid, AckTags, State, - fun (State1) -> requeue_and_run(AckTags, State1) end)); + noreply(requeue(AckTags, ChPid, State)); handle_cast({reject, AckTags, false, ChPid}, State = #q{dlx = undefined}) -> - handle_cast({ack, AckTags, ChPid}, State); + noreply(ack(AckTags, ChPid, State)); handle_cast({reject, AckTags, false, ChPid}, State) -> DLXFun = dead_letter_fun(rejected), -- cgit v1.2.1 From ed581bc28021574bf0356ec3f96104777203aa75 Mon Sep 17 00:00:00 2001 From: Alvaro Videla Date: Fri, 16 Nov 2012 12:43:06 +0100 Subject: moves files from mac folder to release folder --- packaging/mac/Makefile | 68 ---------------------------------------------- packaging/release/Makefile | 68 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 68 deletions(-) delete mode 100644 packaging/mac/Makefile create mode 100644 packaging/release/Makefile diff --git a/packaging/mac/Makefile b/packaging/mac/Makefile deleted file mode 100644 index ea16e72d..00000000 --- a/packaging/mac/Makefile +++ /dev/null @@ -1,68 +0,0 @@ -VERSION=0.0.0 -SOURCE_DIR=rabbitmq-server-$(VERSION) -TARGET_DIR=rabbitmq_server-$(VERSION) -TARGET_TARBALL=rabbitmq-server-mac-standalone-$(VERSION) -RLS_DIR=$(TARGET_DIR)/release/$(TARGET_DIR) - -ERTS_VSN=$(shell erl -noshell -eval 'io:format("~s", [erlang:system_info(version)]), halt().') -ERTS_ROOT_DIR=$(shell erl -noshell -eval 'io:format("~s", [code:root_dir()]), halt().') - -# used to generate the erlang release -RABBITMQ_HOME=$(TARGET_DIR) -RABBITMQ_EBIN_ROOT=$(RABBITMQ_HOME)/ebin -RABBITMQ_PLUGINS_DIR=$(RABBITMQ_HOME)/plugins -RABBITMQ_PLUGINS_EXPAND_DIR=$(RABBITMQ_PLUGINS_DIR)/expand - -dist: - tar -zxf ../../dist/$(SOURCE_DIR).tar.gz - - $(MAKE) -C $(SOURCE_DIR) \ - TARGET_DIR=`pwd`/$(TARGET_DIR) \ - SBIN_DIR=`pwd`/$(TARGET_DIR)/sbin \ - MAN_DIR=`pwd`/$(TARGET_DIR)/share/man \ - install - - sed -e 's:^SYS_PREFIX=$$:SYS_PREFIX=\$${RABBITMQ_HOME}:' \ - $(TARGET_DIR)/sbin/rabbitmq-defaults >$(TARGET_DIR)/sbin/rabbitmq-defaults.tmp \ - && sed -e 's:^ERL_DIR=$$:ERL_DIR=\$${RABBITMQ_HOME}/erts-$(ERTS_VSN)/bin/:' \ - $(TARGET_DIR)/sbin/rabbitmq-defaults.tmp >$(TARGET_DIR)/sbin/rabbitmq-defaults.tmp1 \ - && sed -e 's:start_clean$$:"\$${SYS_PREFIX}/releases/$(VERSION)/start_clean":' \ - $(TARGET_DIR)/sbin/rabbitmq-defaults.tmp1 >$(TARGET_DIR)/sbin/rabbitmq-defaults.tmp \ - && sed -e 's:start_sasl:"\$${SYS_PREFIX}/releases/$(VERSION)/start_sasl":' \ - $(TARGET_DIR)/sbin/rabbitmq-defaults.tmp >$(TARGET_DIR)/sbin/rabbitmq-defaults - - chmod 0755 $(TARGET_DIR)/sbin/rabbitmq-defaults - - mkdir -p $(TARGET_DIR)/etc/rabbitmq - - $(MAKE) generate_release - -## todo see where the .tar is being created - mkdir -p $(RLS_DIR) - tar -C $(RLS_DIR) -xzf $(RABBITMQ_HOME)/rabbit.tar.gz - -# add minimal boot file - cp $(ERTS_ROOT_DIR)/bin/start_clean.boot $(RLS_DIR)/releases/$(VERSION) - cp $(ERTS_ROOT_DIR)/bin/start_sasl.boot $(RLS_DIR)/releases/$(VERSION) - -# move rabbitmq files to top level folder - mv $(RLS_DIR)/lib/rabbit-$(VERSION)/* $(RLS_DIR) - - tar -zcf $(TARGET_TARBALL).tar.gz -C $(TARGET_DIR)/release $(TARGET_DIR) - rm -rf $(SOURCE_DIR) $(TARGET_DIR) - -clean: clean_partial - rm -f rabbitmq-server-mac-standalone-*.tar.gz - -clean_partial: - rm -rf $(SOURCE_DIR) - rm -rf $(TARGET_DIR) - -.PHONY : generate_release -generate_release: - erl \ - -pa "$(RABBITMQ_EBIN_ROOT)" \ - -noinput \ - -hidden \ - -s rabbit_release \ - -extra "$(RABBITMQ_PLUGINS_DIR)" "$(RABBITMQ_PLUGINS_EXPAND_DIR)" "$(RABBITMQ_HOME)" diff --git a/packaging/release/Makefile b/packaging/release/Makefile new file mode 100644 index 00000000..ea16e72d --- /dev/null +++ b/packaging/release/Makefile @@ -0,0 +1,68 @@ +VERSION=0.0.0 +SOURCE_DIR=rabbitmq-server-$(VERSION) +TARGET_DIR=rabbitmq_server-$(VERSION) +TARGET_TARBALL=rabbitmq-server-mac-standalone-$(VERSION) +RLS_DIR=$(TARGET_DIR)/release/$(TARGET_DIR) + +ERTS_VSN=$(shell erl -noshell -eval 'io:format("~s", [erlang:system_info(version)]), halt().') +ERTS_ROOT_DIR=$(shell erl -noshell -eval 'io:format("~s", [code:root_dir()]), halt().') + +# used to generate the erlang release +RABBITMQ_HOME=$(TARGET_DIR) +RABBITMQ_EBIN_ROOT=$(RABBITMQ_HOME)/ebin +RABBITMQ_PLUGINS_DIR=$(RABBITMQ_HOME)/plugins +RABBITMQ_PLUGINS_EXPAND_DIR=$(RABBITMQ_PLUGINS_DIR)/expand + +dist: + tar -zxf ../../dist/$(SOURCE_DIR).tar.gz + + $(MAKE) -C $(SOURCE_DIR) \ + TARGET_DIR=`pwd`/$(TARGET_DIR) \ + SBIN_DIR=`pwd`/$(TARGET_DIR)/sbin \ + MAN_DIR=`pwd`/$(TARGET_DIR)/share/man \ + install + + sed -e 's:^SYS_PREFIX=$$:SYS_PREFIX=\$${RABBITMQ_HOME}:' \ + $(TARGET_DIR)/sbin/rabbitmq-defaults >$(TARGET_DIR)/sbin/rabbitmq-defaults.tmp \ + && sed -e 's:^ERL_DIR=$$:ERL_DIR=\$${RABBITMQ_HOME}/erts-$(ERTS_VSN)/bin/:' \ + $(TARGET_DIR)/sbin/rabbitmq-defaults.tmp >$(TARGET_DIR)/sbin/rabbitmq-defaults.tmp1 \ + && sed -e 's:start_clean$$:"\$${SYS_PREFIX}/releases/$(VERSION)/start_clean":' \ + $(TARGET_DIR)/sbin/rabbitmq-defaults.tmp1 >$(TARGET_DIR)/sbin/rabbitmq-defaults.tmp \ + && sed -e 's:start_sasl:"\$${SYS_PREFIX}/releases/$(VERSION)/start_sasl":' \ + $(TARGET_DIR)/sbin/rabbitmq-defaults.tmp >$(TARGET_DIR)/sbin/rabbitmq-defaults + + chmod 0755 $(TARGET_DIR)/sbin/rabbitmq-defaults + + mkdir -p $(TARGET_DIR)/etc/rabbitmq + + $(MAKE) generate_release + +## todo see where the .tar is being created + mkdir -p $(RLS_DIR) + tar -C $(RLS_DIR) -xzf $(RABBITMQ_HOME)/rabbit.tar.gz + +# add minimal boot file + cp $(ERTS_ROOT_DIR)/bin/start_clean.boot $(RLS_DIR)/releases/$(VERSION) + cp $(ERTS_ROOT_DIR)/bin/start_sasl.boot $(RLS_DIR)/releases/$(VERSION) + +# move rabbitmq files to top level folder + mv $(RLS_DIR)/lib/rabbit-$(VERSION)/* $(RLS_DIR) + + tar -zcf $(TARGET_TARBALL).tar.gz -C $(TARGET_DIR)/release $(TARGET_DIR) + rm -rf $(SOURCE_DIR) $(TARGET_DIR) + +clean: clean_partial + rm -f rabbitmq-server-mac-standalone-*.tar.gz + +clean_partial: + rm -rf $(SOURCE_DIR) + rm -rf $(TARGET_DIR) + +.PHONY : generate_release +generate_release: + erl \ + -pa "$(RABBITMQ_EBIN_ROOT)" \ + -noinput \ + -hidden \ + -s rabbit_release \ + -extra "$(RABBITMQ_PLUGINS_DIR)" "$(RABBITMQ_PLUGINS_EXPAND_DIR)" "$(RABBITMQ_HOME)" -- cgit v1.2.1 From 71e0da5f582eb7b7900cf09ca65a4a189453d4b7 Mon Sep 17 00:00:00 2001 From: Alvaro Videla Date: Fri, 16 Nov 2012 13:00:48 +0100 Subject: renames release folder to standalone --- packaging/release/Makefile | 68 ----------------------------------------- packaging/standalone/Makefile | 70 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 68 deletions(-) delete mode 100644 packaging/release/Makefile create mode 100644 packaging/standalone/Makefile diff --git a/packaging/release/Makefile b/packaging/release/Makefile deleted file mode 100644 index ea16e72d..00000000 --- a/packaging/release/Makefile +++ /dev/null @@ -1,68 +0,0 @@ -VERSION=0.0.0 -SOURCE_DIR=rabbitmq-server-$(VERSION) -TARGET_DIR=rabbitmq_server-$(VERSION) -TARGET_TARBALL=rabbitmq-server-mac-standalone-$(VERSION) -RLS_DIR=$(TARGET_DIR)/release/$(TARGET_DIR) - -ERTS_VSN=$(shell erl -noshell -eval 'io:format("~s", [erlang:system_info(version)]), halt().') -ERTS_ROOT_DIR=$(shell erl -noshell -eval 'io:format("~s", [code:root_dir()]), halt().') - -# used to generate the erlang release -RABBITMQ_HOME=$(TARGET_DIR) -RABBITMQ_EBIN_ROOT=$(RABBITMQ_HOME)/ebin -RABBITMQ_PLUGINS_DIR=$(RABBITMQ_HOME)/plugins -RABBITMQ_PLUGINS_EXPAND_DIR=$(RABBITMQ_PLUGINS_DIR)/expand - -dist: - tar -zxf ../../dist/$(SOURCE_DIR).tar.gz - - $(MAKE) -C $(SOURCE_DIR) \ - TARGET_DIR=`pwd`/$(TARGET_DIR) \ - SBIN_DIR=`pwd`/$(TARGET_DIR)/sbin \ - MAN_DIR=`pwd`/$(TARGET_DIR)/share/man \ - install - - sed -e 's:^SYS_PREFIX=$$:SYS_PREFIX=\$${RABBITMQ_HOME}:' \ - $(TARGET_DIR)/sbin/rabbitmq-defaults >$(TARGET_DIR)/sbin/rabbitmq-defaults.tmp \ - && sed -e 's:^ERL_DIR=$$:ERL_DIR=\$${RABBITMQ_HOME}/erts-$(ERTS_VSN)/bin/:' \ - $(TARGET_DIR)/sbin/rabbitmq-defaults.tmp >$(TARGET_DIR)/sbin/rabbitmq-defaults.tmp1 \ - && sed -e 's:start_clean$$:"\$${SYS_PREFIX}/releases/$(VERSION)/start_clean":' \ - $(TARGET_DIR)/sbin/rabbitmq-defaults.tmp1 >$(TARGET_DIR)/sbin/rabbitmq-defaults.tmp \ - && sed -e 's:start_sasl:"\$${SYS_PREFIX}/releases/$(VERSION)/start_sasl":' \ - $(TARGET_DIR)/sbin/rabbitmq-defaults.tmp >$(TARGET_DIR)/sbin/rabbitmq-defaults - - chmod 0755 $(TARGET_DIR)/sbin/rabbitmq-defaults - - mkdir -p $(TARGET_DIR)/etc/rabbitmq - - $(MAKE) generate_release - -## todo see where the .tar is being created - mkdir -p $(RLS_DIR) - tar -C $(RLS_DIR) -xzf $(RABBITMQ_HOME)/rabbit.tar.gz - -# add minimal boot file - cp $(ERTS_ROOT_DIR)/bin/start_clean.boot $(RLS_DIR)/releases/$(VERSION) - cp $(ERTS_ROOT_DIR)/bin/start_sasl.boot $(RLS_DIR)/releases/$(VERSION) - -# move rabbitmq files to top level folder - mv $(RLS_DIR)/lib/rabbit-$(VERSION)/* $(RLS_DIR) - - tar -zcf $(TARGET_TARBALL).tar.gz -C $(TARGET_DIR)/release $(TARGET_DIR) - rm -rf $(SOURCE_DIR) $(TARGET_DIR) - -clean: clean_partial - rm -f rabbitmq-server-mac-standalone-*.tar.gz - -clean_partial: - rm -rf $(SOURCE_DIR) - rm -rf $(TARGET_DIR) - -.PHONY : generate_release -generate_release: - erl \ - -pa "$(RABBITMQ_EBIN_ROOT)" \ - -noinput \ - -hidden \ - -s rabbit_release \ - -extra "$(RABBITMQ_PLUGINS_DIR)" "$(RABBITMQ_PLUGINS_EXPAND_DIR)" "$(RABBITMQ_HOME)" diff --git a/packaging/standalone/Makefile b/packaging/standalone/Makefile new file mode 100644 index 00000000..4658afd5 --- /dev/null +++ b/packaging/standalone/Makefile @@ -0,0 +1,70 @@ +VERSION=0.0.0 +SOURCE_DIR=rabbitmq-server-$(VERSION) +TARGET_DIR=rabbitmq_server-$(VERSION) +TARGET_TARBALL=rabbitmq-server-mac-standalone-$(VERSION) +RLS_DIR=$(TARGET_DIR)/release/$(TARGET_DIR) + +ERTS_VSN=$(shell erl -noshell -eval 'io:format("~s", [erlang:system_info(version)]), halt().') +ERTS_ROOT_DIR=$(shell erl -noshell -eval 'io:format("~s", [code:root_dir()]), halt().') + +# used to generate the erlang release +RABBITMQ_HOME=$(TARGET_DIR) +RABBITMQ_EBIN_ROOT=$(RABBITMQ_HOME)/ebin +RABBITMQ_PLUGINS_DIR=$(RABBITMQ_HOME)/plugins +RABBITMQ_PLUGINS_EXPAND_DIR=$(RABBITMQ_PLUGINS_DIR)/expand + +dist: + tar -zxf ../../dist/$(SOURCE_DIR).tar.gz + + $(MAKE) -C $(SOURCE_DIR) \ + TARGET_DIR=`pwd`/$(TARGET_DIR) \ + SBIN_DIR=`pwd`/$(TARGET_DIR)/sbin \ + MAN_DIR=`pwd`/$(TARGET_DIR)/share/man \ + install + + sed -e 's:^SYS_PREFIX=$$:SYS_PREFIX=\$${RABBITMQ_HOME}:' \ + $(TARGET_DIR)/sbin/rabbitmq-defaults >$(TARGET_DIR)/sbin/rabbitmq-defaults.tmp \ + && sed -e 's:^ERL_DIR=$$:ERL_DIR=\$${RABBITMQ_HOME}/erts-$(ERTS_VSN)/bin/:' \ + $(TARGET_DIR)/sbin/rabbitmq-defaults.tmp >$(TARGET_DIR)/sbin/rabbitmq-defaults.tmp1 \ + && sed -e 's:start_clean$$:"\$${SYS_PREFIX}/releases/$(VERSION)/start_clean":' \ + $(TARGET_DIR)/sbin/rabbitmq-defaults.tmp1 >$(TARGET_DIR)/sbin/rabbitmq-defaults.tmp \ + && sed -e 's:start_sasl:"\$${SYS_PREFIX}/releases/$(VERSION)/start_sasl":' \ + $(TARGET_DIR)/sbin/rabbitmq-defaults.tmp >$(TARGET_DIR)/sbin/rabbitmq-defaults + + chmod 0755 $(TARGET_DIR)/sbin/rabbitmq-defaults + + mkdir -p $(TARGET_DIR)/etc/rabbitmq + + $(MAKE) generate_release + +## todo see where the .tar is being created + mkdir -p $(RLS_DIR) + tar -C $(RLS_DIR) -xzf $(RABBITMQ_HOME)/rabbit.tar.gz + +# add minimal boot file + cp $(ERTS_ROOT_DIR)/bin/start_clean.boot $(RLS_DIR)/releases/$(VERSION) + cp $(ERTS_ROOT_DIR)/bin/start_sasl.boot $(RLS_DIR)/releases/$(VERSION) + +# move rabbitmq files to top level folder + mv $(RLS_DIR)/lib/rabbit-$(VERSION)/* $(RLS_DIR) + + tar -zcf $(TARGET_TARBALL).tar.gz -C $(TARGET_DIR)/release $(TARGET_DIR) + rm -rf $(SOURCE_DIR) $(TARGET_DIR) + +clean: clean_partial + rm -f rabbitmq-server-mac-standalone-*.tar.gz + +clean_partial: + rm -rf $(SOURCE_DIR) + rm -rf $(TARGET_DIR) + +.PHONY : generate_release +generate_release: + erlc -I $(TARGET_DIR)/include/ -o erlang -Wall -v +debug_info -Duse_specs -Duse_proper_qc -pa $(TARGET_DIR)/ebin/ src/rabbit_release.erl + erl \ + -pa "$(RABBITMQ_EBIN_ROOT)" \ + -pa src \ + -noinput \ + -hidden \ + -s rabbit_release \ + -extra "$(RABBITMQ_PLUGINS_DIR)" "$(RABBITMQ_PLUGINS_EXPAND_DIR)" "$(RABBITMQ_HOME)" -- cgit v1.2.1 From a7442cc212a9b3d6065f80fcdeb5fe18142d7e8b Mon Sep 17 00:00:00 2001 From: Alvaro Videla Date: Fri, 16 Nov 2012 13:09:12 +0100 Subject: renames generated tarball --- packaging/standalone/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packaging/standalone/Makefile b/packaging/standalone/Makefile index 4658afd5..077f67b7 100644 --- a/packaging/standalone/Makefile +++ b/packaging/standalone/Makefile @@ -1,7 +1,7 @@ VERSION=0.0.0 SOURCE_DIR=rabbitmq-server-$(VERSION) TARGET_DIR=rabbitmq_server-$(VERSION) -TARGET_TARBALL=rabbitmq-server-mac-standalone-$(VERSION) +TARGET_TARBALL=rabbitmq-server-standalone-$(VERSION) RLS_DIR=$(TARGET_DIR)/release/$(TARGET_DIR) ERTS_VSN=$(shell erl -noshell -eval 'io:format("~s", [erlang:system_info(version)]), halt().') @@ -52,7 +52,7 @@ dist: rm -rf $(SOURCE_DIR) $(TARGET_DIR) clean: clean_partial - rm -f rabbitmq-server-mac-standalone-*.tar.gz + rm -f rabbitmq-server-standalone-*.tar.gz clean_partial: rm -rf $(SOURCE_DIR) -- cgit v1.2.1 From ffdfc1f15f811a1ba4642359f930a266b371c990 Mon Sep 17 00:00:00 2001 From: Alvaro Videla Date: Fri, 16 Nov 2012 13:09:49 +0100 Subject: moves rabbit_release to the standalone packaging folder --- packaging/standalone/src/rabbit_release.erl | 148 ++++++++++++++++++++++++++++ src/rabbit_release.erl | 148 ---------------------------- 2 files changed, 148 insertions(+), 148 deletions(-) create mode 100644 packaging/standalone/src/rabbit_release.erl delete mode 100644 src/rabbit_release.erl diff --git a/packaging/standalone/src/rabbit_release.erl b/packaging/standalone/src/rabbit_release.erl new file mode 100644 index 00000000..bcba2699 --- /dev/null +++ b/packaging/standalone/src/rabbit_release.erl @@ -0,0 +1,148 @@ +%% based on rabbit_prelaunch.erl from rabbitmq-server source code +-module(rabbit_release). + +-export([start/0, stop/0, make_tar/2]). + +-include("rabbit.hrl"). + +-define(BaseApps, [rabbit]). +-define(ERROR_CODE, 1). + +start() -> + %% Determine our various directories + [PluginsDistDir, UnpackedPluginDir, RabbitHome] = + init:get_plain_arguments(), + RootName = UnpackedPluginDir ++ "/rabbit", + + prepare_plugins(PluginsDistDir, UnpackedPluginDir), + + PluginAppNames = [ P#plugin.name || P <- rabbit_plugins:list(PluginsDistDir) ], + + %% we need to call find_plugins because it has the secondary effect of adding the + %% plugin ebin folder to the code path. We need that in order to load the plugin app + RequiredApps = find_plugins(UnpackedPluginDir), + + %% Build the entire set of dependencies - this will load the + %% applications along the way + AllApps = case catch sets:to_list(expand_dependencies(RequiredApps)) of + {failed_to_load_app, App, Err} -> + terminate("failed to load application ~s:~n~p", + [App, Err]); + AppList -> + AppList + end, + + %% we need a list of ERTS apps we need to ship with rabbit + BaseApps = AllApps -- PluginAppNames, + + AppVersions = [determine_version(App) || App <- BaseApps], + RabbitVersion = proplists:get_value(rabbit, AppVersions), + + %% Build the overall release descriptor + RDesc = {release, + {"rabbit", RabbitVersion}, + {erts, erlang:system_info(version)}, + AppVersions}, + + %% Write it out to $RABBITMQ_PLUGINS_EXPAND_DIR/rabbit.rel + rabbit_file:write_file(RootName ++ ".rel", io_lib:format("~p.~n", [RDesc])), + + %% Compile the script + systools:make_script(RootName), + systools:script2boot(RootName), + %% Make release tarfile + make_tar(RootName, RabbitHome), + terminate(0), + ok. + +stop() -> + ok. + +make_tar(Release, RabbitHome) -> + systools:make_tar(Release, + [ + {dirs, [docs, etc, include, plugins, sbin, share]}, + {erts, code:root_dir()}, + {outdir, RabbitHome} + ]). + +determine_version(App) -> + application:load(App), + {ok, Vsn} = application:get_key(App, vsn), + {App, Vsn}. + +delete_recursively(Fn) -> + case rabbit_file:recursive_delete([Fn]) of + ok -> ok; + {error, {Path, E}} -> {error, {cannot_delete, Path, E}}; + Error -> Error + end. + +prepare_plugins(PluginsDistDir, DestDir) -> + %% Eliminate the contents of the destination directory + case delete_recursively(DestDir) of + ok -> ok; + {error, E} -> terminate("Could not delete dir ~s (~p)", [DestDir, E]) + end, + case filelib:ensure_dir(DestDir ++ "/") of + ok -> ok; + {error, E2} -> terminate("Could not create dir ~s (~p)", [DestDir, E2]) + end, + + [prepare_plugin(Plugin, DestDir) || Plugin <- rabbit_plugins:list(PluginsDistDir)]. + +prepare_plugin(#plugin{type = ez, location = Location}, PluginDestDir) -> + zip:unzip(Location, [{cwd, PluginDestDir}]); +prepare_plugin(#plugin{type = dir, name = Name, location = Location}, + PluginsDestDir) -> + rabbit_file:recursive_copy(Location, + filename:join([PluginsDestDir, Name])). + +expand_dependencies(Pending) -> + expand_dependencies(sets:new(), Pending). +expand_dependencies(Current, []) -> + Current; +expand_dependencies(Current, [Next|Rest]) -> + case sets:is_element(Next, Current) of + true -> + expand_dependencies(Current, Rest); + false -> + case application:load(Next) of + ok -> + ok; + {error, {already_loaded, _}} -> + ok; + {error, Reason} -> + throw({failed_to_load_app, Next, Reason}) + end, + {ok, Required} = application:get_key(Next, applications), + Unique = [A || A <- Required, not(sets:is_element(A, Current))], + expand_dependencies(sets:add_element(Next, Current), Rest ++ Unique) + end. + +find_plugins(PluginDir) -> + [prepare_dir_plugin(PluginName) || + PluginName <- filelib:wildcard(PluginDir ++ "/*/ebin/*.app")]. + +prepare_dir_plugin(PluginAppDescFn) -> + %% Add the plugin ebin directory to the load path + PluginEBinDirN = filename:dirname(PluginAppDescFn), + code:add_path(PluginEBinDirN), + + %% We want the second-last token + NameTokens = string:tokens(PluginAppDescFn,"/."), + PluginNameString = lists:nth(length(NameTokens) - 1, NameTokens), + list_to_atom(PluginNameString). + +terminate(Fmt, Args) -> + io:format("ERROR: " ++ Fmt ++ "~n", Args), + terminate(?ERROR_CODE). + +terminate(Status) -> + case os:type() of + {unix, _} -> halt(Status); + {win32, _} -> init:stop(Status), + receive + after infinity -> ok + end + end. diff --git a/src/rabbit_release.erl b/src/rabbit_release.erl deleted file mode 100644 index bcba2699..00000000 --- a/src/rabbit_release.erl +++ /dev/null @@ -1,148 +0,0 @@ -%% based on rabbit_prelaunch.erl from rabbitmq-server source code --module(rabbit_release). - --export([start/0, stop/0, make_tar/2]). - --include("rabbit.hrl"). - --define(BaseApps, [rabbit]). --define(ERROR_CODE, 1). - -start() -> - %% Determine our various directories - [PluginsDistDir, UnpackedPluginDir, RabbitHome] = - init:get_plain_arguments(), - RootName = UnpackedPluginDir ++ "/rabbit", - - prepare_plugins(PluginsDistDir, UnpackedPluginDir), - - PluginAppNames = [ P#plugin.name || P <- rabbit_plugins:list(PluginsDistDir) ], - - %% we need to call find_plugins because it has the secondary effect of adding the - %% plugin ebin folder to the code path. We need that in order to load the plugin app - RequiredApps = find_plugins(UnpackedPluginDir), - - %% Build the entire set of dependencies - this will load the - %% applications along the way - AllApps = case catch sets:to_list(expand_dependencies(RequiredApps)) of - {failed_to_load_app, App, Err} -> - terminate("failed to load application ~s:~n~p", - [App, Err]); - AppList -> - AppList - end, - - %% we need a list of ERTS apps we need to ship with rabbit - BaseApps = AllApps -- PluginAppNames, - - AppVersions = [determine_version(App) || App <- BaseApps], - RabbitVersion = proplists:get_value(rabbit, AppVersions), - - %% Build the overall release descriptor - RDesc = {release, - {"rabbit", RabbitVersion}, - {erts, erlang:system_info(version)}, - AppVersions}, - - %% Write it out to $RABBITMQ_PLUGINS_EXPAND_DIR/rabbit.rel - rabbit_file:write_file(RootName ++ ".rel", io_lib:format("~p.~n", [RDesc])), - - %% Compile the script - systools:make_script(RootName), - systools:script2boot(RootName), - %% Make release tarfile - make_tar(RootName, RabbitHome), - terminate(0), - ok. - -stop() -> - ok. - -make_tar(Release, RabbitHome) -> - systools:make_tar(Release, - [ - {dirs, [docs, etc, include, plugins, sbin, share]}, - {erts, code:root_dir()}, - {outdir, RabbitHome} - ]). - -determine_version(App) -> - application:load(App), - {ok, Vsn} = application:get_key(App, vsn), - {App, Vsn}. - -delete_recursively(Fn) -> - case rabbit_file:recursive_delete([Fn]) of - ok -> ok; - {error, {Path, E}} -> {error, {cannot_delete, Path, E}}; - Error -> Error - end. - -prepare_plugins(PluginsDistDir, DestDir) -> - %% Eliminate the contents of the destination directory - case delete_recursively(DestDir) of - ok -> ok; - {error, E} -> terminate("Could not delete dir ~s (~p)", [DestDir, E]) - end, - case filelib:ensure_dir(DestDir ++ "/") of - ok -> ok; - {error, E2} -> terminate("Could not create dir ~s (~p)", [DestDir, E2]) - end, - - [prepare_plugin(Plugin, DestDir) || Plugin <- rabbit_plugins:list(PluginsDistDir)]. - -prepare_plugin(#plugin{type = ez, location = Location}, PluginDestDir) -> - zip:unzip(Location, [{cwd, PluginDestDir}]); -prepare_plugin(#plugin{type = dir, name = Name, location = Location}, - PluginsDestDir) -> - rabbit_file:recursive_copy(Location, - filename:join([PluginsDestDir, Name])). - -expand_dependencies(Pending) -> - expand_dependencies(sets:new(), Pending). -expand_dependencies(Current, []) -> - Current; -expand_dependencies(Current, [Next|Rest]) -> - case sets:is_element(Next, Current) of - true -> - expand_dependencies(Current, Rest); - false -> - case application:load(Next) of - ok -> - ok; - {error, {already_loaded, _}} -> - ok; - {error, Reason} -> - throw({failed_to_load_app, Next, Reason}) - end, - {ok, Required} = application:get_key(Next, applications), - Unique = [A || A <- Required, not(sets:is_element(A, Current))], - expand_dependencies(sets:add_element(Next, Current), Rest ++ Unique) - end. - -find_plugins(PluginDir) -> - [prepare_dir_plugin(PluginName) || - PluginName <- filelib:wildcard(PluginDir ++ "/*/ebin/*.app")]. - -prepare_dir_plugin(PluginAppDescFn) -> - %% Add the plugin ebin directory to the load path - PluginEBinDirN = filename:dirname(PluginAppDescFn), - code:add_path(PluginEBinDirN), - - %% We want the second-last token - NameTokens = string:tokens(PluginAppDescFn,"/."), - PluginNameString = lists:nth(length(NameTokens) - 1, NameTokens), - list_to_atom(PluginNameString). - -terminate(Fmt, Args) -> - io:format("ERROR: " ++ Fmt ++ "~n", Args), - terminate(?ERROR_CODE). - -terminate(Status) -> - case os:type() of - {unix, _} -> halt(Status); - {win32, _} -> init:stop(Status), - receive - after infinity -> ok - end - end. -- cgit v1.2.1 From 8a70f56318fc2dc9c053fea7113fd0370b853977 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 16 Nov 2012 12:18:27 +0000 Subject: refactor stop_later - s/stop_later/stop - since it's always called with Reason=normal there is no point passing that around / storing it --- src/rabbit_amqqueue_process.erl | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index ac357663..af634dc3 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -788,17 +788,16 @@ handle_queue_down(QPid, Reason, State = #q{queue_monitors = QMons, unconfirmed = UC1}) end. -stop_later(Reason, State) -> - stop_later(Reason, undefined, noreply, State). +stop(State) -> stop(undefined, noreply, State). -stop_later(Reason, From, Reply, State = #q{unconfirmed = UC}) -> +stop(From, Reply, State = #q{unconfirmed = UC}) -> case {dtree:is_empty(UC), Reply} of {true, noreply} -> - {stop, Reason, State}; + {stop, normal, State}; {true, _} -> - {stop, Reason, Reply, State}; + {stop, normal, Reply, State}; {false, _} -> - noreply(State#q{delayed_stop = {Reason, {From, Reply}}}) + noreply(State#q{delayed_stop = {From, Reply}}) end. cleanup_after_confirm(AckTags, State = #q{delayed_stop = DS, @@ -809,11 +808,10 @@ cleanup_after_confirm(AckTags, State = #q{delayed_stop = DS, State1 = State#q{backing_queue_state = BQS1}, case dtree:is_empty(UC) andalso DS =/= undefined of true -> case DS of - {_, {_, noreply}} -> ok; - {_, {From, Reply}} -> gen_server2:reply(From, Reply) + {_, noreply} -> ok; + {From, Reply} -> gen_server2:reply(From, Reply) end, - {Reason, _} = DS, - {stop, Reason, State1}; + {stop, normal, State1}; false -> noreply(State1) end. @@ -1048,7 +1046,7 @@ handle_call({notify_down, ChPid}, From, State) -> %% gen_server2 *before* the reply is sent. case handle_ch_down(ChPid, State) of {ok, State1} -> reply(ok, State1); - {stop, State1} -> stop_later(normal, From, ok, State1) + {stop, State1} -> stop(From, ok, State1) end; handle_call({basic_get, ChPid, NoAck}, _From, @@ -1123,7 +1121,7 @@ handle_call({basic_cancel, ChPid, ConsumerTag, OkMsg}, From, State#q.active_consumers)}, case should_auto_delete(State1) of false -> reply(ok, ensure_expiry_timer(State1)); - true -> stop_later(normal, From, ok, State1) + true -> stop(From, ok, State1) end end; @@ -1139,8 +1137,7 @@ handle_call({delete, IfUnused, IfEmpty}, From, if IfEmpty and not(IsEmpty) -> reply({error, not_empty}, State); IfUnused and not(IsUnused) -> reply({error, in_use}, State); - true -> stop_later(normal, From, - {ok, BQ:len(BQS)}, State) + true -> stop(From, {ok, BQ:len(BQS)}, State) end; handle_call(purge, _From, State = #q{backing_queue = BQ, @@ -1230,7 +1227,7 @@ handle_cast({reject, AckTags, false, ChPid}, State) -> end)); handle_cast(delete_immediately, State) -> - stop_later(normal, State); + stop(State); handle_cast({unblock, ChPid}, State) -> noreply( @@ -1306,7 +1303,7 @@ handle_info(_, State = #q{delayed_stop = DS}) when DS =/= undefined -> handle_info(maybe_expire, State) -> case is_unused(State) of - true -> stop_later(normal, State); + true -> stop(State); false -> noreply(ensure_expiry_timer(State)) end; @@ -1328,12 +1325,12 @@ handle_info({'DOWN', _MonitorRef, process, DownPid, _Reason}, %% match what people expect (see bug 21824). However we need this %% monitor-and-async- delete in case the connection goes away %% unexpectedly. - stop_later(normal, State); + stop(State); handle_info({'DOWN', _MonitorRef, process, DownPid, Reason}, State) -> case handle_ch_down(DownPid, State) of {ok, State1} -> handle_queue_down(DownPid, Reason, State1); - {stop, State1} -> stop_later(normal, State1) + {stop, State1} -> stop(State1) end; handle_info(update_ram_duration, State = #q{backing_queue = BQ, -- cgit v1.2.1 From 61f9eb7b33cbaa05cb64f0158a3bd40bd9f0c050 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 16 Nov 2012 12:21:05 +0000 Subject: fixme --- src/rabbit_amqqueue_process.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index af634dc3..f87f5777 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1043,7 +1043,8 @@ handle_call({notify_down, ChPid}, From, State) -> %% are no longer visible by the time we send a response to the %% client. The queue is ultimately deleted in terminate/2; if we %% return stop with a reply, terminate/2 will be called by - %% gen_server2 *before* the reply is sent. + %% gen_server2 *before* the reply is sent. FIXME: in case of a + %% delayed stop the reply is sent earlier. case handle_ch_down(ChPid, State) of {ok, State1} -> reply(ok, State1); {stop, State1} -> stop(From, ok, State1) -- cgit v1.2.1 From d7358f7873a1520251f5ee37597445ec824ac114 Mon Sep 17 00:00:00 2001 From: Alvaro Videla Date: Fri, 16 Nov 2012 13:24:29 +0100 Subject: fixes erlc invocation for rabbit_release.erl --- packaging/standalone/Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packaging/standalone/Makefile b/packaging/standalone/Makefile index 077f67b7..c57387d8 100644 --- a/packaging/standalone/Makefile +++ b/packaging/standalone/Makefile @@ -60,7 +60,7 @@ clean_partial: .PHONY : generate_release generate_release: - erlc -I $(TARGET_DIR)/include/ -o erlang -Wall -v +debug_info -Duse_specs -Duse_proper_qc -pa $(TARGET_DIR)/ebin/ src/rabbit_release.erl + erlc -I $(TARGET_DIR)/include/ -o src -Wall -v +debug_info -Duse_specs -Duse_proper_qc -pa $(TARGET_DIR)/ebin/ src/rabbit_release.erl erl \ -pa "$(RABBITMQ_EBIN_ROOT)" \ -pa src \ @@ -68,3 +68,4 @@ generate_release: -hidden \ -s rabbit_release \ -extra "$(RABBITMQ_PLUGINS_DIR)" "$(RABBITMQ_PLUGINS_EXPAND_DIR)" "$(RABBITMQ_HOME)" + rm src/rabbit_release.beam -- cgit v1.2.1 From 287dc46703160f83e34e186c807058f1fd381699 Mon Sep 17 00:00:00 2001 From: Alvaro Videla Date: Fri, 16 Nov 2012 13:27:59 +0100 Subject: moves the sed invocation to its own target --- packaging/standalone/Makefile | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/packaging/standalone/Makefile b/packaging/standalone/Makefile index c57387d8..f979bb0c 100644 --- a/packaging/standalone/Makefile +++ b/packaging/standalone/Makefile @@ -22,16 +22,7 @@ dist: MAN_DIR=`pwd`/$(TARGET_DIR)/share/man \ install - sed -e 's:^SYS_PREFIX=$$:SYS_PREFIX=\$${RABBITMQ_HOME}:' \ - $(TARGET_DIR)/sbin/rabbitmq-defaults >$(TARGET_DIR)/sbin/rabbitmq-defaults.tmp \ - && sed -e 's:^ERL_DIR=$$:ERL_DIR=\$${RABBITMQ_HOME}/erts-$(ERTS_VSN)/bin/:' \ - $(TARGET_DIR)/sbin/rabbitmq-defaults.tmp >$(TARGET_DIR)/sbin/rabbitmq-defaults.tmp1 \ - && sed -e 's:start_clean$$:"\$${SYS_PREFIX}/releases/$(VERSION)/start_clean":' \ - $(TARGET_DIR)/sbin/rabbitmq-defaults.tmp1 >$(TARGET_DIR)/sbin/rabbitmq-defaults.tmp \ - && sed -e 's:start_sasl:"\$${SYS_PREFIX}/releases/$(VERSION)/start_sasl":' \ - $(TARGET_DIR)/sbin/rabbitmq-defaults.tmp >$(TARGET_DIR)/sbin/rabbitmq-defaults - - chmod 0755 $(TARGET_DIR)/sbin/rabbitmq-defaults + $(MAKE) fix_rabbitmq_defaults mkdir -p $(TARGET_DIR)/etc/rabbitmq @@ -69,3 +60,19 @@ generate_release: -s rabbit_release \ -extra "$(RABBITMQ_PLUGINS_DIR)" "$(RABBITMQ_PLUGINS_EXPAND_DIR)" "$(RABBITMQ_HOME)" rm src/rabbit_release.beam + +## Here we set the RABBITMQ_HOME variable, +## then we make ERL_DIR point to our released erl +## and we add the paths to our released start_clean and start_sasl boot scripts +.PHONY : fix_rabbitmq_defaults +fix_rabbitmq_defaults: + sed -e 's:^SYS_PREFIX=$$:SYS_PREFIX=\$${RABBITMQ_HOME}:' \ + $(TARGET_DIR)/sbin/rabbitmq-defaults >$(TARGET_DIR)/sbin/rabbitmq-defaults.tmp \ + && sed -e 's:^ERL_DIR=$$:ERL_DIR=\$${RABBITMQ_HOME}/erts-$(ERTS_VSN)/bin/:' \ + $(TARGET_DIR)/sbin/rabbitmq-defaults.tmp >$(TARGET_DIR)/sbin/rabbitmq-defaults.tmp1 \ + && sed -e 's:start_clean$$:"\$${SYS_PREFIX}/releases/$(VERSION)/start_clean":' \ + $(TARGET_DIR)/sbin/rabbitmq-defaults.tmp1 >$(TARGET_DIR)/sbin/rabbitmq-defaults.tmp \ + && sed -e 's:start_sasl:"\$${SYS_PREFIX}/releases/$(VERSION)/start_sasl":' \ + $(TARGET_DIR)/sbin/rabbitmq-defaults.tmp >$(TARGET_DIR)/sbin/rabbitmq-defaults + + chmod 0755 $(TARGET_DIR)/sbin/rabbitmq-defaults -- cgit v1.2.1 From f3654f1ece558874e1e5500c95b92884947caff8 Mon Sep 17 00:00:00 2001 From: Alvaro Videla Date: Fri, 16 Nov 2012 13:30:44 +0100 Subject: removes todo comment --- packaging/standalone/Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/packaging/standalone/Makefile b/packaging/standalone/Makefile index f979bb0c..cd85ffca 100644 --- a/packaging/standalone/Makefile +++ b/packaging/standalone/Makefile @@ -28,7 +28,6 @@ dist: $(MAKE) generate_release -## todo see where the .tar is being created mkdir -p $(RLS_DIR) tar -C $(RLS_DIR) -xzf $(RABBITMQ_HOME)/rabbit.tar.gz -- cgit v1.2.1 From 73110798abe6b3ca4a1091f8597d0a09d9a07026 Mon Sep 17 00:00:00 2001 From: Alvaro Videla Date: Fri, 16 Nov 2012 13:58:30 +0100 Subject: cosmetics + comments --- packaging/standalone/src/rabbit_release.erl | 34 ++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/packaging/standalone/src/rabbit_release.erl b/packaging/standalone/src/rabbit_release.erl index bcba2699..a7569595 100644 --- a/packaging/standalone/src/rabbit_release.erl +++ b/packaging/standalone/src/rabbit_release.erl @@ -1,4 +1,18 @@ -%% based on rabbit_prelaunch.erl from rabbitmq-server source code +%% 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 VMware, Inc. +%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% -module(rabbit_release). -export([start/0, stop/0, make_tar/2]). @@ -8,6 +22,13 @@ -define(BaseApps, [rabbit]). -define(ERROR_CODE, 1). +%% This module is based on rabbit_prelaunch.erl from rabbitmq-server source code +%% We need to calculate all the ERTS apps we need to ship with a +%% standalone rabbit. To acomplish that we need to unpack and load the plugins +%% apps that are shiped with rabbit. +%% Once we get that we generate an erlang release inside a tarball. +%% Our make file will work with that release to generate our final rabbitmq +%% package. start() -> %% Determine our various directories [PluginsDistDir, UnpackedPluginDir, RabbitHome] = @@ -16,10 +37,12 @@ start() -> prepare_plugins(PluginsDistDir, UnpackedPluginDir), - PluginAppNames = [ P#plugin.name || P <- rabbit_plugins:list(PluginsDistDir) ], + PluginAppNames = [P#plugin.name || + P <- rabbit_plugins:list(PluginsDistDir)], - %% we need to call find_plugins because it has the secondary effect of adding the - %% plugin ebin folder to the code path. We need that in order to load the plugin app + %% we need to call find_plugins because it has the secondary effect of + %% adding the plugin ebin folder to the code path. + %% We need that in order to load the plugin app RequiredApps = find_plugins(UnpackedPluginDir), %% Build the entire set of dependencies - this will load the @@ -89,7 +112,8 @@ prepare_plugins(PluginsDistDir, DestDir) -> {error, E2} -> terminate("Could not create dir ~s (~p)", [DestDir, E2]) end, - [prepare_plugin(Plugin, DestDir) || Plugin <- rabbit_plugins:list(PluginsDistDir)]. + [prepare_plugin(Plugin, DestDir) || + Plugin <- rabbit_plugins:list(PluginsDistDir)]. prepare_plugin(#plugin{type = ez, location = Location}, PluginDestDir) -> zip:unzip(Location, [{cwd, PluginDestDir}]); -- cgit v1.2.1 From fa35905bc4010f326ae0d817e2a0aedc457e381c Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 16 Nov 2012 14:04:27 +0000 Subject: plug leak --- src/rabbit_channel.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 2afc4c36..b97af6d8 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -335,7 +335,7 @@ handle_info({'DOWN', _MRef, process, QPid, Reason}, State) -> State4 = handle_delivering_queue_down(QPid, State3), credit_flow:peer_down(QPid), erase_queue_stats(QPid), - noreply(State3#ch{queue_monitors = pmon:erase( + noreply(State4#ch{queue_monitors = pmon:erase( QPid, State4#ch.queue_monitors)}); handle_info({'EXIT', _Pid, Reason}, State) -> -- cgit v1.2.1 From 7d94f4241fcb354c60aed2b50d6a08fb192d9191 Mon Sep 17 00:00:00 2001 From: Alvaro Videla Date: Fri, 16 Nov 2012 15:19:23 +0100 Subject: cleans up unused/duplicated functions --- packaging/standalone/src/rabbit_release.erl | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/packaging/standalone/src/rabbit_release.erl b/packaging/standalone/src/rabbit_release.erl index a7569595..877fd73e 100644 --- a/packaging/standalone/src/rabbit_release.erl +++ b/packaging/standalone/src/rabbit_release.erl @@ -15,7 +15,7 @@ %% -module(rabbit_release). --export([start/0, stop/0, make_tar/2]). +-export([start/0]). -include("rabbit.hrl"). @@ -75,10 +75,7 @@ start() -> systools:script2boot(RootName), %% Make release tarfile make_tar(RootName, RabbitHome), - terminate(0), - ok. - -stop() -> + rabbit_misc:quit(0), ok. make_tar(Release, RabbitHome) -> @@ -160,13 +157,4 @@ prepare_dir_plugin(PluginAppDescFn) -> terminate(Fmt, Args) -> io:format("ERROR: " ++ Fmt ++ "~n", Args), - terminate(?ERROR_CODE). - -terminate(Status) -> - case os:type() of - {unix, _} -> halt(Status); - {win32, _} -> init:stop(Status), - receive - after infinity -> ok - end - end. + rabbit_misc:quit(?ERROR_CODE). -- cgit v1.2.1 From e2f5beaf5d2f9db139fc8cfa4e61d4d5f06bbbff Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 16 Nov 2012 14:54:28 +0000 Subject: propagate API change --- src/rabbit_tests.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 096f9490..e25843c3 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -2539,7 +2539,7 @@ test_queue_recover() -> {{_Msg1, true, _AckTag1, CountMinusOne}, VQ2} = rabbit_variable_queue:fetch(true, VQ1), _VQ3 = rabbit_variable_queue:delete_and_terminate(shutdown, VQ2), - rabbit_amqqueue:internal_delete(QName, QPid1) + rabbit_amqqueue:internal_delete(QName) end), passed. -- cgit v1.2.1 From 97491563e8d8fb1cac3b6f01b5471b78307bc11e Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 16 Nov 2012 16:53:56 +0000 Subject: identify queues by name rather than pid in channel stats - in deliver_to_queues, take the result of rabbit_amqqueue:lookup and use it to a) add entries to a QPid -> QName mapping in the state for all master pids, and b) setup monitors for all master pids - for the stats creation in deliver_to_queues, map the DeliveredQPids to the associated QNames, via the mapping in the State. Note that this will ignore slave pids, hence we only get one stat per QName (which is good). Also, in the event that the master died between lookup and delivery (and the delivery was 'mandatory'), we will not record any stats at all. - in monitor_delivering_queue, which is called by basic.{consume,get} we add to the mapping - in ack/2 we use the mapping to obtain QNames from QPids, and use that in stats. Since a queue may have vanished prior to the ack/reject arriving, we need to handle the case of no entry being present in the mapping for the given QPid. - in record_sent we have the QName anyway, so can just record stats against that instead of the QPid. - in the 'DOWN' handler we use the mapping to determine the QName from the pid, and pass that to erase_queue_stats, which can now remove entries based on the QName. We then remove the entry from the mapping. --- src/rabbit_channel.erl | 124 ++++++++++++++++++++++++++++--------------------- src/rabbit_tests.erl | 29 ++++++------ 2 files changed, 86 insertions(+), 67 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index b97af6d8..f8f099f6 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -35,8 +35,9 @@ -record(ch, {state, protocol, channel, reader_pid, writer_pid, conn_pid, conn_name, limiter, tx_status, next_tag, unacked_message_q, uncommitted_message_q, uncommitted_acks, uncommitted_nacks, user, - virtual_host, most_recently_declared_queue, queue_monitors, - consumer_mapping, blocking, queue_consumers, delivering_queues, + virtual_host, most_recently_declared_queue, + queue_names, queue_monitors, consumer_mapping, + blocking, queue_consumers, delivering_queues, queue_collector_pid, stats_timer, confirm_enabled, publish_seqno, unconfirmed, confirmed, capabilities, trace_state}). @@ -194,6 +195,7 @@ init([Channel, ReaderPid, WriterPid, ConnPid, ConnName, Protocol, User, VHost, user = User, virtual_host = VHost, most_recently_declared_queue = <<>>, + queue_names = dict:new(), queue_monitors = pmon:new(), consumer_mapping = dict:new(), blocking = sets:new(), @@ -334,9 +336,13 @@ handle_info({'DOWN', _MRef, process, QPid, Reason}, State) -> State3 = handle_consuming_queue_down(QPid, State2), State4 = handle_delivering_queue_down(QPid, State3), credit_flow:peer_down(QPid), - erase_queue_stats(QPid), - noreply(State4#ch{queue_monitors = pmon:erase( - QPid, State4#ch.queue_monitors)}); + #ch{queue_names = QNames, queue_monitors = QMons} = State4, + case dict:find(QPid, QNames) of + {ok, QName} -> erase_queue_stats(QName); + error -> ok + end, + noreply(State4#ch{queue_names = dict:erase(QPid, QNames), + queue_monitors = pmon:erase(QPid, QMons)}); handle_info({'EXIT', _Pid, Reason}, State) -> {stop, Reason, State}. @@ -677,7 +683,7 @@ handle_method(#'basic.get'{queue = QueueNameBin, QueueName, ConnPid, fun (Q) -> rabbit_amqqueue:basic_get(Q, self(), NoAck) end) of {ok, MessageCount, - Msg = {_QName, QPid, _MsgId, Redelivered, + Msg = {QName, QPid, _MsgId, Redelivered, #basic_message{exchange_name = ExchangeName, routing_keys = [RoutingKey | _CcRoutes], content = Content}}} -> @@ -689,7 +695,7 @@ handle_method(#'basic.get'{queue = QueueNameBin, routing_key = RoutingKey, message_count = MessageCount}, Content), - State1 = monitor_delivering_queue(NoAck, QPid, State), + State1 = monitor_delivering_queue(NoAck, QPid, QName, State), {noreply, record_sent(none, not(NoAck), Msg, State1)}; empty -> {reply, #'basic.get_empty'{}, State} @@ -728,10 +734,11 @@ handle_method(#'basic.consume'{queue = QueueNameBin, consumer_tag = ActualConsumerTag})), Q} end) of - {ok, Q = #amqqueue{pid = QPid}} -> + {ok, Q = #amqqueue{pid = QPid, name = QName}} -> CM1 = dict:store(ActualConsumerTag, Q, ConsumerMapping), State1 = monitor_delivering_queue( - NoAck, QPid, State#ch{consumer_mapping = CM1}), + NoAck, QPid, QName, + State#ch{consumer_mapping = CM1}), {noreply, case NoWait of true -> consumer_monitor(ActualConsumerTag, State1); @@ -1126,9 +1133,12 @@ consumer_monitor(ConsumerTag, State end. -monitor_delivering_queue(NoAck, QPid, State = #ch{queue_monitors = QMons, - delivering_queues = DQ}) -> - State#ch{queue_monitors = pmon:monitor(QPid, QMons), +monitor_delivering_queue(NoAck, QPid, QName, + State = #ch{queue_names = QNames, + queue_monitors = QMons, + delivering_queues = DQ}) -> + State#ch{queue_names = dict:store(QPid, QName, QNames), + queue_monitors = pmon:monitor(QPid, QMons), delivering_queues = case NoAck of true -> DQ; false -> sets:add_element(QPid, DQ) @@ -1233,18 +1243,18 @@ reject(Requeue, Acked, Limiter) -> ok = notify_limiter(Limiter, Acked). record_sent(ConsumerTag, AckRequired, - Msg = {_QName, QPid, MsgId, Redelivered, _Message}, + Msg = {QName, QPid, MsgId, Redelivered, _Message}, State = #ch{unacked_message_q = UAMQ, next_tag = DeliveryTag, trace_state = TraceState}) -> - incr_stats([{queue_stats, QPid, 1}], case {ConsumerTag, AckRequired} of - {none, true} -> get; - {none, false} -> get_no_ack; - {_ , true} -> deliver; - {_ , false} -> deliver_no_ack - end, State), + incr_stats([{queue_stats, QName, 1}], case {ConsumerTag, AckRequired} of + {none, true} -> get; + {none, false} -> get_no_ack; + {_ , true} -> deliver; + {_ , false} -> deliver_no_ack + end, State), case Redelivered of - true -> incr_stats([{queue_stats, QPid, 1}], redeliver, State); + true -> incr_stats([{queue_stats, QName, 1}], redeliver, State); false -> ok end, rabbit_trace:tap_trace_out(Msg, TraceState), @@ -1277,11 +1287,15 @@ collect_acks(ToAcc, PrefixAcc, Q, DeliveryTag, Multiple) -> precondition_failed("unknown delivery tag ~w", [DeliveryTag]) end. -ack(Acked, State) -> +ack(Acked, State = #ch{queue_names = QNames}) -> Incs = fold_per_queue( fun (QPid, MsgIds, L) -> ok = rabbit_amqqueue:ack(QPid, MsgIds, self()), - [{queue_stats, QPid, length(MsgIds)} | L] + case dict:find(QPid, QNames) of + {ok, QName} -> Count = length(MsgIds), + [{queue_stats, QName, Count} | L]; + error -> L + end end, [], Acked), ok = notify_limiter(State#ch.limiter, Acked), incr_stats(Incs, ack, State). @@ -1339,23 +1353,30 @@ notify_limiter(Limiter, Acked) -> deliver_to_queues({Delivery = #delivery{message = Message = #basic_message{ exchange_name = XName}, msg_seq_no = MsgSeqNo}, - QNames}, State) -> - {RoutingRes, DeliveredQPids} = - rabbit_amqqueue:deliver_flow(rabbit_amqqueue:lookup(QNames), Delivery), - State1 = State#ch{queue_monitors = - pmon:monitor_all(DeliveredQPids, - State#ch.queue_monitors)}, - State2 = process_routing_result(RoutingRes, DeliveredQPids, - XName, MsgSeqNo, Message, State1), + DelQNames}, State = #ch{queue_names = QNames, + queue_monitors = QMons}) -> + Qs = rabbit_amqqueue:lookup(DelQNames), + {RoutingRes, DeliveredQPids} = rabbit_amqqueue:deliver_flow(Qs, Delivery), + {QNames1, QMons1} = + lists:foldl(fun (#amqqueue{pid = QPid, name = QName}, + {QNames0, QMons0}) -> + {dict:store(QPid, QName, QNames0), + pmon:monitor(QPid, QMons0)} + end, {QNames, pmon:monitor_all(DeliveredQPids, QMons)}, Qs), + State1 = process_routing_result(RoutingRes, DeliveredQPids, + XName, MsgSeqNo, Message, + State#ch{queue_names = QNames1, + queue_monitors = QMons1}), incr_stats([{exchange_stats, XName, 1} | - [{queue_exchange_stats, {QPid, XName}, 1} || - QPid <- DeliveredQPids]], publish, State2), - State2. + [{queue_exchange_stats, {QName, XName}, 1} || + QPid <- DeliveredQPids, + {ok, QName} <- [dict:find(QPid, QNames1)]]], + publish, State1), + State1. process_routing_result(unroutable, _, XName, MsgSeqNo, Msg, State) -> ok = basic_return(Msg, State, no_route), - incr_stats([{exchange_stats, Msg#basic_message.exchange_name, 1}], - return_unroutable, State), + incr_stats([{exchange_stats, XName, 1}], return_unroutable, State), record_confirm(MsgSeqNo, XName, State); process_routing_result(routed, [], XName, MsgSeqNo, _, State) -> record_confirm(MsgSeqNo, XName, State); @@ -1489,24 +1510,23 @@ emit_stats(State) -> emit_stats(State, []). emit_stats(State, Extra) -> - CoarseStats = infos(?STATISTICS_KEYS, State), + Coarse = infos(?STATISTICS_KEYS, State), case rabbit_event:stats_level(State, #ch.stats_timer) of - coarse -> - rabbit_event:notify(channel_stats, Extra ++ CoarseStats); - fine -> - FineStats = - [{channel_queue_stats, - [{QPid, Stats} || {{queue_stats, QPid}, Stats} <- get()]}, - {channel_exchange_stats, - [{X, Stats} || {{exchange_stats, X}, Stats} <- get()]}, - {channel_queue_exchange_stats, - [{QX, Stats} || - {{queue_exchange_stats, QX}, Stats} <- get()]}], - rabbit_event:notify(channel_stats, - Extra ++ CoarseStats ++ FineStats) + coarse -> rabbit_event:notify(channel_stats, Extra ++ Coarse); + fine -> Fine = [{channel_queue_stats, + [{QName, Stats} || + {{queue_stats, QName}, Stats} <- get()]}, + {channel_exchange_stats, + [{XName, Stats} || + {{exchange_stats, XName}, Stats} <- get()]}, + {channel_queue_exchange_stats, + [{QX, Stats} || + {{queue_exchange_stats, QX}, Stats} <- get()]}], + rabbit_event:notify(channel_stats, Extra ++ Coarse ++ Fine) end. -erase_queue_stats(QPid) -> - erase({queue_stats, QPid}), +erase_queue_stats(QName) -> + erase({queue_stats, QName}), [erase({queue_exchange_stats, QX}) || - {{queue_exchange_stats, QX = {QPid0, _}}, _} <- get(), QPid =:= QPid0]. + {{queue_exchange_stats, QX = {QName0, _}}, _} <- get(), + QName0 =:= QName]. diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index e25843c3..8b2f3b3a 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -1286,8 +1286,7 @@ test_statistics() -> QName = receive #'queue.declare_ok'{queue = Q0} -> Q0 after ?TIMEOUT -> throw(failed_to_receive_queue_declare_ok) end, - {ok, Q} = rabbit_amqqueue:lookup(rabbit_misc:r(<<"/">>, queue, QName)), - QPid = Q#amqqueue.pid, + QRes = rabbit_misc:r(<<"/">>, queue, QName), X = rabbit_misc:r(<<"/">>, exchange, <<"">>), rabbit_tests_event_receiver:start(self(), [node()], [channel_stats]), @@ -1311,9 +1310,9 @@ test_statistics() -> length(proplists:get_value( channel_queue_exchange_stats, E)) > 0 end), - [{QPid,[{get,1}]}] = proplists:get_value(channel_queue_stats, Event2), + [{QRes, [{get,1}]}] = proplists:get_value(channel_queue_stats, Event2), [{X,[{publish,1}]}] = proplists:get_value(channel_exchange_stats, Event2), - [{{QPid,X},[{publish,1}]}] = + [{{QRes,X},[{publish,1}]}] = proplists:get_value(channel_queue_exchange_stats, Event2), %% Check the stats remove stuff on queue deletion @@ -1338,31 +1337,31 @@ test_refresh_events(SecondaryNode) -> [channel_created, queue_created]), {_Writer, Ch} = test_spawn(), - expect_events(Ch, channel_created), + expect_events(pid, Ch, channel_created), rabbit_channel:shutdown(Ch), {_Writer2, Ch2} = test_spawn(SecondaryNode), - expect_events(Ch2, channel_created), + expect_events(pid, Ch2, channel_created), rabbit_channel:shutdown(Ch2), - {new, #amqqueue { pid = QPid } = Q} = + {new, #amqqueue{name = QName} = Q} = rabbit_amqqueue:declare(test_queue(), false, false, [], none), - expect_events(QPid, queue_created), + expect_events(name, QName, queue_created), rabbit_amqqueue:delete(Q, false, false), rabbit_tests_event_receiver:stop(), passed. -expect_events(Pid, Type) -> - expect_event(Pid, Type), +expect_events(Tag, Key, Type) -> + expect_event(Tag, Key, Type), rabbit:force_event_refresh(), - expect_event(Pid, Type). + expect_event(Tag, Key, Type). -expect_event(Pid, Type) -> +expect_event(Tag, Key, Type) -> receive #event{type = Type, props = Props} -> - case pget(pid, Props) of - Pid -> ok; - _ -> expect_event(Pid, Type) + case pget(Tag, Props) of + Key -> ok; + _ -> expect_event(Tag, Key, Type) end after ?TIMEOUT -> throw({failed_to_receive_event, Type}) end. -- cgit v1.2.1 From 3ea53152cc47f3f126aaa7c8683d4a39d7d39062 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 16 Nov 2012 17:43:42 +0000 Subject: optimise this brings perf roughly on par with default --- src/rabbit_channel.erl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index f8f099f6..6ccc2e65 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -1360,8 +1360,10 @@ deliver_to_queues({Delivery = #delivery{message = Message = #basic_message{ {QNames1, QMons1} = lists:foldl(fun (#amqqueue{pid = QPid, name = QName}, {QNames0, QMons0}) -> - {dict:store(QPid, QName, QNames0), - pmon:monitor(QPid, QMons0)} + {case dict:is_key(QPid, QNames0) of + true -> QNames0; + false -> dict:store(QPid, QName, QNames0) + end, pmon:monitor(QPid, QMons0)} end, {QNames, pmon:monitor_all(DeliveredQPids, QMons)}, Qs), State1 = process_routing_result(RoutingRes, DeliveredQPids, XName, MsgSeqNo, Message, -- cgit v1.2.1 From ae1de506132fa0d2a929c7a48d79e9545540b345 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 19 Nov 2012 11:07:59 +0000 Subject: Changelogs for 3.0.0 --- packaging/RPMS/Fedora/rabbitmq-server.spec | 3 +++ packaging/debs/Debian/debian/changelog | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/packaging/RPMS/Fedora/rabbitmq-server.spec b/packaging/RPMS/Fedora/rabbitmq-server.spec index d73c5634..5d9b9e2e 100644 --- a/packaging/RPMS/Fedora/rabbitmq-server.spec +++ b/packaging/RPMS/Fedora/rabbitmq-server.spec @@ -123,6 +123,9 @@ done rm -rf %{buildroot} %changelog +* Fri Nov 16 2012 simon@rabbitmq.com 3.0.0-1 +- New Upstream Release + * Fri Dec 16 2011 steve@rabbitmq.com 2.7.1-1 - New Upstream Release diff --git a/packaging/debs/Debian/debian/changelog b/packaging/debs/Debian/debian/changelog index b3743c39..17327133 100644 --- a/packaging/debs/Debian/debian/changelog +++ b/packaging/debs/Debian/debian/changelog @@ -1,3 +1,9 @@ +rabbitmq-server (3.0.0-1) unstable; urgency=low + + * New Upstream Release + + -- Simon MacMullen Fri, 16 Nov 2012 14:15:29 +0000 + rabbitmq-server (2.7.1-1) natty; urgency=low * New Upstream Release -- cgit v1.2.1 -- cgit v1.2.1 From 27ff7e9bc045e187942c6484e4391e422b1d0bfe Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 19 Nov 2012 12:18:13 +0000 Subject: optimise "no messages dead-lettered during expiry" case --- src/rabbit_amqqueue_process.erl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index f87f5777..1c324bbf 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -728,8 +728,10 @@ drop_expired_messages(State = #q{dlx = DLX, {Next, BQS2}; _ -> {Next, Msgs, BQS2} = BQ:dropwhile(ExpirePred, true, BQS), - DLXFun = dead_letter_fun(expired), - DLXFun(Msgs), + case Msgs of + [] -> ok; + _ -> (dead_letter_fun(expired))(Msgs) + end, {Next, BQS2} end, ensure_ttl_timer(case Props of -- cgit v1.2.1 From 4655ba493b26a70075944ade32d764f2f32cffa7 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 19 Nov 2012 16:15:26 +0000 Subject: cosmetic --- src/rabbit_amqqueue_process.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 1c324bbf..abdbd24b 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1135,11 +1135,11 @@ handle_call(stat, _From, State) -> handle_call({delete, IfUnused, IfEmpty}, From, State = #q{backing_queue_state = BQS, backing_queue = BQ}) -> - IsEmpty = BQ:is_empty(BQS), + IsEmpty = BQ:is_empty(BQS), IsUnused = is_unused(State), if - IfEmpty and not(IsEmpty) -> reply({error, not_empty}, State); - IfUnused and not(IsUnused) -> reply({error, in_use}, State); + IfEmpty and not(IsEmpty) -> reply({error, not_empty}, State); + IfUnused and not(IsUnused) -> reply({error, in_use}, State); true -> stop(From, {ok, BQ:len(BQS)}, State) end; -- cgit v1.2.1 From 4885f41e4f37537f8fcf5c33ccdb964fcdc5bf1a Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 19 Nov 2012 18:16:49 +0000 Subject: format mq slave message queue to facilitate debugging --- src/rabbit_mirror_queue_slave.erl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 1ba1420f..bea4758c 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -28,7 +28,7 @@ -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3, handle_pre_hibernate/1, prioritise_call/3, - prioritise_cast/2, prioritise_info/2]). + prioritise_cast/2, prioritise_info/2, format_message_queue/2]). -export([joined/2, members_changed/3, handle_msg/3]). @@ -329,6 +329,8 @@ prioritise_info(Msg, _State) -> _ -> 0 end. +format_message_queue(Opt, MQ) -> rabbit_misc:format_message_queue(Opt, MQ). + %% --------------------------------------------------------------------------- %% GM %% --------------------------------------------------------------------------- -- cgit v1.2.1 From 17d021928a1bdfcff280fe1e6c44d5556cbf3bf1 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 20 Nov 2012 13:34:59 +0000 Subject: introduce bq:drop/2 and use it in slaves to prevent msg fetching - 'drop' is the same as 'fetch' except it doesn't read messages from the msg store - slaves never fetch messages, they only drop them - technically, mq_master:drop doesn't need to exist, since 'drop' is only invoked by the slaves, but we provide an implementation for completeness. --- src/rabbit_backing_queue.erl | 8 ++++++++ src/rabbit_mirror_queue_master.erl | 38 ++++++++++++++++++++++++++++---------- src/rabbit_mirror_queue_slave.erl | 19 ++----------------- src/rabbit_variable_queue.erl | 12 +++++++++++- 4 files changed, 49 insertions(+), 28 deletions(-) diff --git a/src/rabbit_backing_queue.erl b/src/rabbit_backing_queue.erl index af660c60..00de3e17 100644 --- a/src/rabbit_backing_queue.erl +++ b/src/rabbit_backing_queue.erl @@ -29,6 +29,10 @@ ('empty' | %% Message, IsDelivered, AckTag, Remaining_Len {rabbit_types:basic_message(), boolean(), Ack, non_neg_integer()})). +-type(drop_result(Ack) :: + ('empty' | + %% MessageId, AckTag, Remaining_Len + {rabbit_types:msg_id(), Ack, non_neg_integer()})). -type(attempt_recovery() :: boolean()). -type(purged_msg_count() :: non_neg_integer()). -type(async_callback() :: @@ -139,6 +143,10 @@ -callback fetch(true, state()) -> {fetch_result(ack()), state()}; (false, state()) -> {fetch_result(undefined), state()}. +%% Remove the next message. +-callback drop(true, state()) -> {drop_result(ack()), state()}; + (false, state()) -> {drop_result(undefined), state()}. + %% Acktags supplied are for messages which can now be forgotten %% about. Must return 1 msg_id per Ack, in the same order as Acks. -callback ack([ack()], state()) -> {msg_ids(), state()}. diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index df733546..961636b1 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -17,7 +17,8 @@ -module(rabbit_mirror_queue_master). -export([init/3, terminate/2, delete_and_terminate/2, - purge/1, publish/4, publish_delivered/4, discard/3, fetch/2, ack/2, + purge/1, publish/4, publish_delivered/4, + discard/3, fetch/2, drop/2, ack/2, requeue/2, len/1, is_empty/1, depth/1, drain_confirmed/1, dropwhile/3, set_ram_duration_target/2, ram_duration/1, needs_timeout/1, timeout/1, handle_pre_hibernate/1, @@ -270,22 +271,30 @@ drain_confirmed(State = #state { backing_queue = BQ, fetch(AckRequired, State = #state { gm = GM, backing_queue = BQ, backing_queue_state = BQS, - set_delivered = SetDelivered, - ack_msg_id = AM }) -> + set_delivered = SetDelivered }) -> {Result, BQS1} = BQ:fetch(AckRequired, BQS), State1 = State #state { backing_queue_state = BQS1 }, case Result of empty -> {Result, State1}; - {#basic_message { id = MsgId } = Message, IsDelivered, AckTag, - Remaining} -> - ok = gm:broadcast(GM, {fetch, AckRequired, MsgId, Remaining}), + {Message, IsDelivered, AckTag, Remaining} -> + ok = gm:broadcast(GM, {drop, Remaining, 1, AckRequired}), IsDelivered1 = IsDelivered orelse SetDelivered > 0, - SetDelivered1 = lists:max([0, SetDelivered - 1]), - AM1 = maybe_store_acktag(AckTag, MsgId, AM), {{Message, IsDelivered1, AckTag, Remaining}, - State1 #state { set_delivered = SetDelivered1, - ack_msg_id = AM1 }} + drop(Message#basic_message.id, AckTag, State1)} + end. + +drop(AckRequired, State = #state { gm = GM, + backing_queue = BQ, + backing_queue_state = BQS }) -> + {Result, BQS1} = BQ:drop(AckRequired, BQS), + State1 = State #state { backing_queue_state = BQS1 }, + case Result of + empty -> + {Result, State1}; + {MsgId, AckTag, Remaining} -> + ok = gm:broadcast(GM, {drop, Remaining, 1, AckRequired}), + {Result, drop(MsgId, AckTag, State1)} end. ack(AckTags, State = #state { gm = GM, @@ -440,6 +449,15 @@ depth_fun() -> end) end. +%% --------------------------------------------------------------------------- +%% Helpers +%% --------------------------------------------------------------------------- + +drop(MsgId, AckTag, State = #state { set_delivered = SetDelivered, + ack_msg_id = AM }) -> + State #state { set_delivered = lists:max([0, SetDelivered - 1]), + ack_msg_id = maybe_store_acktag(AckTag, MsgId, AM) }. + maybe_store_acktag(undefined, _MsgId, AM) -> AM; maybe_store_acktag(AckTag, MsgId, AM) -> dict:store(AckTag, MsgId, AM). diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index bea4758c..3ad8eb77 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -727,8 +727,8 @@ process_instruction({drop, Length, Dropped, AckRequired}, end, State1 = lists:foldl( fun (const, StateN = #state{backing_queue_state = BQSN}) -> - {{#basic_message{id = MsgId}, _, AckTag, _}, BQSN1} = - BQ:fetch(AckRequired, BQSN), + {{MsgId, AckTag, _Remaining}, BQSN1} = + BQ:drop(AckRequired, BQSN), maybe_store_ack( AckRequired, MsgId, AckTag, StateN #state { backing_queue_state = BQSN1 }) @@ -737,21 +737,6 @@ process_instruction({drop, Length, Dropped, AckRequired}, true -> State1; false -> update_delta(ToDrop - Dropped, State1) end}; -process_instruction({fetch, AckRequired, MsgId, Remaining}, - State = #state { backing_queue = BQ, - backing_queue_state = BQS }) -> - QLen = BQ:len(BQS), - {ok, case QLen - 1 of - Remaining -> - {{#basic_message{id = MsgId}, _IsDelivered, - AckTag, Remaining}, BQS1} = BQ:fetch(AckRequired, BQS), - maybe_store_ack(AckRequired, MsgId, AckTag, - State #state { backing_queue_state = BQS1 }); - _ when QLen =< Remaining andalso AckRequired -> - State; - _ when QLen =< Remaining -> - update_delta(-1, State) - end}; process_instruction({ack, MsgIds}, State = #state { backing_queue = BQ, backing_queue_state = BQS, diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 8a3fd9d9..367b4802 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -18,7 +18,7 @@ -export([init/3, terminate/2, delete_and_terminate/2, purge/1, publish/4, publish_delivered/4, discard/3, drain_confirmed/1, - dropwhile/3, fetch/2, ack/2, requeue/2, len/1, is_empty/1, + dropwhile/3, fetch/2, drop/2, ack/2, requeue/2, len/1, is_empty/1, depth/1, set_ram_duration_target/2, ram_duration/1, needs_timeout/1, timeout/1, handle_pre_hibernate/1, status/1, invoke/3, is_duplicate/2, multiple_routing_keys/0, fold/3]). @@ -615,6 +615,16 @@ fetch(AckRequired, State) -> {Res, a(State3)} end. +drop(AckRequired, State) -> + case queue_out(State) of + {empty, State1} -> + {empty, a(State1)}; + {{value, MsgStatus}, State1} -> + {{_Msg, _IsDelivered, AckTag, Remaining}, State2} = + internal_fetch(AckRequired, MsgStatus, State1), + {{MsgStatus#msg_status.msg_id, AckTag, Remaining}, a(State2)} + end. + ack([], State) -> {[], State}; ack(AckTags, State) -> -- cgit v1.2.1 From 9bf8bb79fbbaf85434f0383462c0b5d816d1a6a3 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 20 Nov 2012 15:12:06 +0000 Subject: remove unused vqstate field --- src/rabbit_variable_queue.erl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 8a3fd9d9..88270722 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -255,7 +255,6 @@ q4, next_seq_id, pending_ack, - pending_ack_index, ram_ack_index, index_state, msg_store_clients, -- cgit v1.2.1 -- cgit v1.2.1 From 7f1a4cd62f261734e84a12631ac47a5d1d56f0fd Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 20 Nov 2012 15:54:54 +0000 Subject: ack unroutable dead-lettered messages straight away thus plugging a leak --- src/rabbit_amqqueue_process.erl | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index f87f5777..d8b20335 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1274,18 +1274,24 @@ handle_cast({set_maximum_since_use, Age}, State) -> handle_cast({dead_letter, Msgs, Reason}, State = #q{dlx = XName}) -> case rabbit_exchange:lookup(XName) of {ok, X} -> - noreply(lists:foldl( - fun({Msg, AckTag}, State1 = #q{publish_seqno = SeqNo, - unconfirmed = UC, - queue_monitors = QMon}) -> - QPids = dead_letter_publish(Msg, Reason, X, - State1), - UC1 = dtree:insert(SeqNo, QPids, AckTag, UC), - QMons = pmon:monitor_all(QPids, QMon), - State1#q{queue_monitors = QMons, - publish_seqno = SeqNo + 1, - unconfirmed = UC1} - end, State, Msgs)); + {AckImmediately, State2} = + lists:foldl( + fun({Msg, AckTag}, + {Acks, State1 = #q{publish_seqno = SeqNo, + unconfirmed = UC, + queue_monitors = QMons}}) -> + case dead_letter_publish(Msg, Reason, X, State1) of + [] -> {[AckTag | Acks], State1}; + QPids -> UC1 = dtree:insert( + SeqNo, QPids, AckTag, UC), + QMons1 = pmon:monitor_all(QPids, QMons), + {Acks, + State1#q{publish_seqno = SeqNo + 1, + unconfirmed = UC1, + queue_monitors = QMons1}} + end + end, {[], State}, Msgs), + cleanup_after_confirm(AckImmediately, State2); {error, not_found} -> cleanup_after_confirm([AckTag || {_, AckTag} <- Msgs], State) end; -- cgit v1.2.1 From 28b72d7e64d42a8ddd499e03ad884713f62e2c5c Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 20 Nov 2012 16:12:06 +0000 Subject: assertion --- src/rabbit_variable_queue.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 8a3fd9d9..ca362901 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -837,6 +837,7 @@ a(State = #vqstate { q1 = Q1, q2 = Q2, delta = Delta, q3 = Q3, q4 = Q4, true = Len >= 0, true = PersistentCount >= 0, true = RamMsgCount >= 0, + true = RamMsgCount =< Len, State. -- cgit v1.2.1 From a7cd61e79c5ab447f1c02530096610f0021ceef6 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 20 Nov 2012 16:54:53 +0000 Subject: add test of vq:drop --- src/rabbit_tests.erl | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 096f9490..444c7375 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -2300,6 +2300,7 @@ test_variable_queue() -> fun test_variable_queue_partial_segments_delta_thing/1, fun test_variable_queue_all_the_bits_not_covered_elsewhere1/1, fun test_variable_queue_all_the_bits_not_covered_elsewhere2/1, + fun test_drop/1, fun test_dropwhile/1, fun test_dropwhile_varying_ram_duration/1, fun test_variable_queue_ack_limiting/1, @@ -2361,6 +2362,20 @@ test_variable_queue_ack_limiting(VQ0) -> VQ6. +test_drop(VQ0) -> + %% start by sending a messages + VQ1 = variable_queue_publish(false, 1, VQ0), + %% drop message with AckRequired = true + {{MsgId, AckTag, 0}, VQ2} = rabbit_variable_queue:drop(true, VQ1), + true = AckTag =/= undefinded, + %% drop again -> empty + {empty, VQ3} = rabbit_variable_queue:drop(false, VQ2), + %% requeue + {[MsgId], VQ4} = rabbit_variable_queue:requeue([AckTag], VQ3), + %% drop message with AckRequired = false + {{MsgId, undefined, 0}, VQ5} = rabbit_variable_queue:drop(false, VQ4), + VQ5. + test_dropwhile(VQ0) -> Count = 10, -- cgit v1.2.1 From d4e2ab21fb3ea9c5003e051c092aa0df0e451883 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 20 Nov 2012 17:51:33 +0000 Subject: some quick check tests all work ok, but then again that's also the case when I completely break vq. --- src/rabbit_backing_queue_qc.erl | 70 ++++++++++++++++++++++++++++------------- 1 file changed, 48 insertions(+), 22 deletions(-) diff --git a/src/rabbit_backing_queue_qc.erl b/src/rabbit_backing_queue_qc.erl index b37fbb29..df242062 100644 --- a/src/rabbit_backing_queue_qc.erl +++ b/src/rabbit_backing_queue_qc.erl @@ -85,17 +85,19 @@ backing_queue_test(Cmds) -> %% Commands -%% Command frequencies are tuned so that queues are normally reasonably -%% short, but they may sometimes exceed ?QUEUE_MAXLEN. Publish-multiple -%% and purging cause extreme queue lengths, so these have lower probabilities. -%% Fetches are sufficiently frequent so that commands that need acktags -%% get decent coverage. +%% Command frequencies are tuned so that queues are normally +%% reasonably short, but they may sometimes exceed +%% ?QUEUE_MAXLEN. Publish-multiple and purging cause extreme queue +%% lengths, so these have lower probabilities. Fetches/drops are +%% sufficiently frequent so that commands that need acktags get decent +%% coverage. command(S) -> frequency([{10, qc_publish(S)}, {1, qc_publish_delivered(S)}, {1, qc_publish_multiple(S)}, %% very slow - {15, qc_fetch(S)}, %% needed for ack and requeue + {9, qc_fetch(S)}, %% needed for ack and requeue + {6, qc_drop(S)}, %% {15, qc_ack(S)}, {15, qc_requeue(S)}, {3, qc_set_ram_duration_target(S)}, @@ -124,6 +126,9 @@ qc_publish_delivered(#state{bqstate = BQ}) -> qc_fetch(#state{bqstate = BQ}) -> {call, ?BQMOD, fetch, [boolean(), BQ]}. +qc_drop(#state{bqstate = BQ}) -> + {call, ?BQMOD, drop, [boolean(), BQ]}. + qc_ack(#state{bqstate = BQ, acks = Acks}) -> {call, ?BQMOD, ack, [rand_choice(proplists:get_keys(Acks)), BQ]}. @@ -217,22 +222,10 @@ next_state(S, Res, }; next_state(S, Res, {call, ?BQMOD, fetch, [AckReq, _BQ]}) -> - #state{len = Len, messages = Messages, acks = Acks} = S, - ResultInfo = {call, erlang, element, [1, Res]}, - BQ1 = {call, erlang, element, [2, Res]}, - AckTag = {call, erlang, element, [3, ResultInfo]}, - S1 = S#state{bqstate = BQ1}, - case gb_trees:is_empty(Messages) of - true -> S1; - false -> {SeqId, MsgProp_Msg, M2} = gb_trees:take_smallest(Messages), - S2 = S1#state{len = Len - 1, messages = M2}, - case AckReq of - true -> - S2#state{acks = [{AckTag, {SeqId, MsgProp_Msg}}|Acks]}; - false -> - S2 - end - end; + next_state_fetch_and_drop(S, Res, AckReq, 3); + +next_state(S, Res, {call, ?BQMOD, drop, [AckReq, _BQ]}) -> + next_state_fetch_and_drop(S, Res, AckReq, 2); next_state(S, Res, {call, ?BQMOD, ack, [AcksArg, _BQ]}) -> #state{acks = AcksState} = S, @@ -295,6 +288,21 @@ postcondition(S, {call, ?BQMOD, fetch, _Args}, Res) -> Len =:= 0 end; +postcondition(S, {call, ?BQMOD, drop, _Args}, Res) -> + #state{messages = Messages, len = Len, acks = Acks, confirms = Confrms} = S, + case Res of + {{MsgIdFetched, AckTag, RemainingLen}, _BQ} -> + {_SeqId, {_MsgProps, Msg}} = gb_trees:smallest(Messages), + MsgId = {call, erlang, element, + [?RECORD_INDEX(id, basic_message), Msg]}, + MsgIdFetched =:= MsgId andalso + not proplists:is_defined(AckTag, Acks) andalso + not gb_sets:is_element(AckTag, Confrms) andalso + RemainingLen =:= Len - 1; + {empty, _BQ} -> + Len =:= 0 + end; + postcondition(S, {call, ?BQMOD, publish_delivered, _Args}, {AckTag, _BQ}) -> #state{acks = Acks, confirms = Confrms} = S, not proplists:is_defined(AckTag, Acks) andalso @@ -388,6 +396,24 @@ drop_messages(Messages) -> end end. +next_state_fetch_and_drop(S, Res, AckReq, AckTagIdx) -> + #state{len = Len, messages = Messages, acks = Acks} = S, + ResultInfo = {call, erlang, element, [1, Res]}, + BQ1 = {call, erlang, element, [2, Res]}, + AckTag = {call, erlang, element, [AckTagIdx, ResultInfo]}, + S1 = S#state{bqstate = BQ1}, + case gb_trees:is_empty(Messages) of + true -> S1; + false -> {SeqId, MsgProp_Msg, M2} = gb_trees:take_smallest(Messages), + S2 = S1#state{len = Len - 1, messages = M2}, + case AckReq of + true -> + S2#state{acks = [{AckTag, {SeqId, MsgProp_Msg}}|Acks]}; + false -> + S2 + end + end. + -else. -export([prop_disabled/0]). -- cgit v1.2.1 From bae4a5032ec7897be858647f79c4349be5b8c363 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Wed, 21 Nov 2012 12:25:05 +0000 Subject: BQ quickcheck postcondition for drop --- src/rabbit_backing_queue_qc.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_backing_queue_qc.erl b/src/rabbit_backing_queue_qc.erl index df242062..3168ca5c 100644 --- a/src/rabbit_backing_queue_qc.erl +++ b/src/rabbit_backing_queue_qc.erl @@ -293,8 +293,8 @@ postcondition(S, {call, ?BQMOD, drop, _Args}, Res) -> case Res of {{MsgIdFetched, AckTag, RemainingLen}, _BQ} -> {_SeqId, {_MsgProps, Msg}} = gb_trees:smallest(Messages), - MsgId = {call, erlang, element, - [?RECORD_INDEX(id, basic_message), Msg]}, + MsgId = eval({call, erlang, element, + [?RECORD_INDEX(id, basic_message), Msg]}), MsgIdFetched =:= MsgId andalso not proplists:is_defined(AckTag, Acks) andalso not gb_sets:is_element(AckTag, Confrms) andalso -- cgit v1.2.1 From 843c5b04b925e5a809ac908598ca0931b5743df1 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 21 Nov 2012 13:02:16 +0000 Subject: Make start/stop mirroring into a cast not a call. --- src/rabbit_amqqueue.erl | 4 ++-- src/rabbit_amqqueue_process.erl | 34 +++++++++++++++++----------------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 8ce1160c..c48aa6dd 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -588,8 +588,8 @@ set_ram_duration_target(QPid, Duration) -> set_maximum_since_use(QPid, Age) -> gen_server2:cast(QPid, {set_maximum_since_use, Age}). -start_mirroring(QPid) -> ok = delegate_call(QPid, start_mirroring). -stop_mirroring(QPid) -> ok = delegate_call(QPid, stop_mirroring). +start_mirroring(QPid) -> ok = delegate_cast(QPid, start_mirroring). +stop_mirroring(QPid) -> ok = delegate_cast(QPid, stop_mirroring). on_node_down(Node) -> rabbit_misc:execute_mnesia_tx_with_tail( diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index f87f5777..6af43193 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1150,23 +1150,6 @@ handle_call({requeue, AckTags, ChPid}, From, State) -> gen_server2:reply(From, ok), noreply(requeue(AckTags, ChPid, State)); -handle_call(start_mirroring, _From, State = #q{backing_queue = BQ, - backing_queue_state = BQS}) -> - %% lookup again to get policy for init_with_existing_bq - {ok, Q} = rabbit_amqqueue:lookup(qname(State)), - true = BQ =/= rabbit_mirror_queue_master, %% assertion - BQ1 = rabbit_mirror_queue_master, - BQS1 = BQ1:init_with_existing_bq(Q, BQ, BQS), - reply(ok, State#q{backing_queue = BQ1, - backing_queue_state = BQS1}); - -handle_call(stop_mirroring, _From, State = #q{backing_queue = BQ, - backing_queue_state = BQS}) -> - BQ = rabbit_mirror_queue_master, %% assertion - {BQ1, BQS1} = BQ:stop_mirroring(BQS), - reply(ok, State#q{backing_queue = BQ1, - backing_queue_state = BQS1}); - handle_call(force_event_refresh, _From, State = #q{exclusive_consumer = Exclusive}) -> rabbit_event:notify(queue_created, infos(?CREATION_EVENT_KEYS, State)), @@ -1290,6 +1273,23 @@ handle_cast({dead_letter, Msgs, Reason}, State = #q{dlx = XName}) -> cleanup_after_confirm([AckTag || {_, AckTag} <- Msgs], State) end; +handle_cast(start_mirroring, State = #q{backing_queue = BQ, + backing_queue_state = BQS}) -> + %% lookup again to get policy for init_with_existing_bq + {ok, Q} = rabbit_amqqueue:lookup(qname(State)), + true = BQ =/= rabbit_mirror_queue_master, %% assertion + BQ1 = rabbit_mirror_queue_master, + BQS1 = BQ1:init_with_existing_bq(Q, BQ, BQS), + noreply(State#q{backing_queue = BQ1, + backing_queue_state = BQS1}); + +handle_cast(stop_mirroring, State = #q{backing_queue = BQ, + backing_queue_state = BQS}) -> + BQ = rabbit_mirror_queue_master, %% assertion + {BQ1, BQS1} = BQ:stop_mirroring(BQS), + noreply(State#q{backing_queue = BQ1, + backing_queue_state = BQS1}); + handle_cast(wake_up, State) -> noreply(State). -- cgit v1.2.1 From f2b6a7d5bfc5376d4df491a36fa84ac8f56c963d Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 21 Nov 2012 14:09:43 +0000 Subject: Don't try to assert result of {add,drop}_mirror. --- src/rabbit_mirror_queue_misc.erl | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 2b3bd027..2075f878 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -133,11 +133,11 @@ on_node_up() -> end end, [], rabbit_queue) end), - [{ok, _} = add_mirror(QName, node()) || QName <- QNames], + [add_mirror(QName, node()) || QName <- QNames], ok. drop_mirrors(QName, Nodes) -> - [{ok, _} = drop_mirror(QName, Node) || Node <- Nodes], + [drop_mirror(QName, Node) || Node <- Nodes], ok. drop_mirror(QName, MirrorNode) -> @@ -159,7 +159,7 @@ drop_mirror(QName, MirrorNode) -> end). add_mirrors(QName, Nodes) -> - [{ok, _} = add_mirror(QName, Node) || Node <- Nodes], + [add_mirror(QName, Node) || Node <- Nodes], ok. add_mirror(QName, MirrorNode) -> @@ -183,14 +183,6 @@ start_child(Name, MirrorNode, Q) -> fun () -> rabbit_mirror_queue_slave_sup:start_child(MirrorNode, [Q]) end) of - {ok, undefined} -> - %% this means the mirror process was - %% already running on the given node. - {ok, already_mirrored}; - {ok, down} -> - %% Node went down between us deciding to start a mirror - %% and actually starting it. Which is fine. - {ok, node_down}; {ok, SPid} -> rabbit_log:info("Adding mirror of ~s on node ~p: ~p~n", [rabbit_misc:rs(Name), MirrorNode, SPid]), -- cgit v1.2.1 From b7c18bef2f95c242865f21a43d912a02d0a51ecd Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 21 Nov 2012 14:21:54 +0000 Subject: Useful guard. --- src/rabbit_mirror_queue_misc.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 2075f878..58f20476 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -183,7 +183,7 @@ start_child(Name, MirrorNode, Q) -> fun () -> rabbit_mirror_queue_slave_sup:start_child(MirrorNode, [Q]) end) of - {ok, SPid} -> + {ok, SPid} when is_pid(SPid) -> rabbit_log:info("Adding mirror of ~s on node ~p: ~p~n", [rabbit_misc:rs(Name), MirrorNode, SPid]), {ok, started}; -- cgit v1.2.1 From 4e4ca168d5a40c091a5bd52ef4098a2094b83994 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Wed, 21 Nov 2012 14:27:51 +0000 Subject: branch from stable, expire messages eagerly --- src/rabbit_amqqueue_process.erl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index f87f5777..68bd1c6c 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -485,9 +485,11 @@ deliver_msg_to_consumer(DeliverFun, {Stop, State1}. deliver_from_queue_deliver(AckRequired, State) -> - {{Message, IsDelivered, AckTag, Remaining}, State1} = + {{Message, IsDelivered, AckTag, _Remaining}, State1} = fetch(AckRequired, State), - {{Message, IsDelivered, AckTag}, 0 == Remaining, State1}. + State2 = #q{backing_queue = BQ, backing_queue_state = BQS} = + drop_expired_messages(State1), + {{Message, IsDelivered, AckTag}, BQ:is_empty(BQS), State2}. confirm_messages([], State) -> State; -- cgit v1.2.1 From c0524468b1feb9ff7348c8c73d480c2bec875112 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 21 Nov 2012 14:52:20 +0000 Subject: Carry on if an individual notification fails. --- src/rabbit_policy.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_policy.erl b/src/rabbit_policy.erl index 2717cc92..2c997f16 100644 --- a/src/rabbit_policy.erl +++ b/src/rabbit_policy.erl @@ -166,8 +166,8 @@ update_policies(VHost) -> [update_queue(Q, Policies) || Q <- rabbit_amqqueue:list(VHost)]} end), - [notify(X) || X <- Xs], - [notify(Q) || Q <- Qs], + [catch notify(X) || X <- Xs], + [catch notify(Q) || Q <- Qs], ok. update_exchange(X = #exchange{name = XName, policy = OldPolicy}, Policies) -> -- cgit v1.2.1 From 5d308a066213334a009ae2edbb980e974279c9bd Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 21 Nov 2012 15:19:00 +0000 Subject: simplify & optimise rabbit_exchange:callback/4 --- src/rabbit_exchange.erl | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index a205b23d..f209b3ca 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -39,8 +39,7 @@ -spec(recover/0 :: () -> [name()]). -spec(callback/4:: (rabbit_types:exchange(), fun_name(), - fun((boolean()) -> non_neg_integer()) | atom(), - [any()]) -> 'ok'). + fun((boolean()) -> non_neg_integer()) | atom(), [any()]) -> 'ok'). -spec(policy_changed/2 :: (rabbit_types:exchange(), rabbit_types:exchange()) -> 'ok'). -spec(declare/6 :: @@ -114,14 +113,11 @@ recover() -> [XName || #exchange{name = XName} <- Xs]. callback(X = #exchange{type = XType}, Fun, Serial0, Args) -> - Serial = fun (Bool) -> - case Serial0 of - _ when is_atom(Serial0) -> Serial0; - _ -> Serial0(Bool) - end + Serial = if is_function(Serial0) -> Serial0; + is_atom(Serial0) -> fun (_Bool) -> Serial0 end end, - [ok = apply(M, Fun, [Serial(M:serialise_events(X)) | Args]) - || M <- decorators()], + [ok = apply(M, Fun, [Serial(M:serialise_events(X)) | Args]) || + M <- decorators()], Module = type_to_module(XType), apply(Module, Fun, [Serial(Module:serialise_events()) | Args]). -- cgit v1.2.1 From 45c6dc9aba6266ac2c39f86ca45bf07b44e50805 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 21 Nov 2012 15:47:05 +0000 Subject: refactor: simplify rabbit_exchange:serialise_events --- src/rabbit_exchange.erl | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index f209b3ca..e72cbafe 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -124,12 +124,8 @@ callback(X = #exchange{type = XType}, Fun, Serial0, Args) -> policy_changed(X1, X2) -> callback(X1, policy_changed, none, [X1, X2]). serialise_events(X = #exchange{type = Type}) -> - case [Serialise || M <- decorators(), - Serialise <- [M:serialise_events(X)], - Serialise == true] of - [] -> (type_to_module(Type)):serialise_events(); - _ -> true - end. + lists:any(fun (M) -> M:serialise_events(X) end, decorators()) + orelse (type_to_module(Type)):serialise_events(). serial(#exchange{name = XName} = X) -> Serial = case serialise_events(X) of -- cgit v1.2.1 From 96ed5762a614081576249c891c4a5b3dfd0719cc Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Wed, 21 Nov 2012 17:53:18 +0000 Subject: Minimal backing queue fold --- src/rabbit_amqqueue_process.erl | 11 ++++-- src/rabbit_backing_queue.erl | 7 ++-- src/rabbit_mirror_queue_master.erl | 15 +++++--- src/rabbit_variable_queue.erl | 72 +++++++++++++++++++++++++++++++------- 4 files changed, 83 insertions(+), 22 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index abdbd24b..ddffd8be 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1178,7 +1178,12 @@ handle_call(force_event_refresh, _From, {Ch, CTag} -> [{Ch, CTag, AckRequired}] = consumers(State), emit_consumer_created(Ch, CTag, true, AckRequired) end, - reply(ok, State). + reply(ok, State); + +handle_call({fold, Fun, Acc}, _From, State = #q{backing_queue = BQ, + backing_queue_state = BQS}) -> + {Acc1, BQS1} = BQ:fold(Fun, Acc, BQS), + reply(Acc1, State#q{backing_queue_state = BQS1}). handle_cast({confirm, MsgSeqNos, QPid}, State = #q{unconfirmed = UC}) -> {MsgSeqNoAckTags, UC1} = dtree:take(MsgSeqNos, QPid, UC), @@ -1224,8 +1229,8 @@ handle_cast({reject, AckTags, false, ChPid}, State) -> ChPid, AckTags, State, fun (State1 = #q{backing_queue = BQ, backing_queue_state = BQS}) -> - BQS1 = BQ:fold(fun(M, A) -> DLXFun([{M, A}]) end, - BQS, AckTags), + BQS1 = BQ:foreach_ack(fun(M, A) -> DLXFun([{M, A}]) end, + BQS, AckTags), State1#q{backing_queue_state = BQS1} end)); diff --git a/src/rabbit_backing_queue.erl b/src/rabbit_backing_queue.erl index af660c60..3f593e4a 100644 --- a/src/rabbit_backing_queue.erl +++ b/src/rabbit_backing_queue.erl @@ -145,12 +145,15 @@ %% Acktags supplied are for messages which should be processed. The %% provided callback function is called with each message. --callback fold(msg_fun(), state(), [ack()]) -> state(). +-callback foreach_ack(msg_fun(), state(), [ack()]) -> state(). %% Reinsert messages into the queue which have already been delivered %% and were pending acknowledgement. -callback requeue([ack()], state()) -> {msg_ids(), state()}. +-callback fold(fun((rabbit_types:basic_message(), any()) -> any()), + any(), state()) -> {any(), state()}. + %% How long is my queue? -callback len(state()) -> non_neg_integer(). @@ -212,7 +215,7 @@ behaviour_info(callbacks) -> [{start, 1}, {stop, 0}, {init, 3}, {terminate, 2}, {delete_and_terminate, 2}, {purge, 1}, {publish, 4}, {publish_delivered, 4}, {discard, 3}, {drain_confirmed, 1}, {dropwhile, 3}, - {fetch, 2}, {ack, 2}, {fold, 3}, {requeue, 2}, {len, 1}, + {fetch, 2}, {ack, 2}, {foreach_ack, 3}, {requeue, 2}, {fold, 3}, {len, 1}, {is_empty, 1}, {depth, 1}, {set_ram_duration_target, 2}, {ram_duration, 1}, {needs_timeout, 1}, {timeout, 1}, {handle_pre_hibernate, 1}, {status, 1}, {invoke, 3}, {is_duplicate, 2}] ; diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index df733546..53d1a173 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -18,10 +18,10 @@ -export([init/3, terminate/2, delete_and_terminate/2, purge/1, publish/4, publish_delivered/4, discard/3, fetch/2, ack/2, - requeue/2, len/1, is_empty/1, depth/1, drain_confirmed/1, + requeue/2, fold/3, len/1, is_empty/1, depth/1, drain_confirmed/1, dropwhile/3, set_ram_duration_target/2, ram_duration/1, needs_timeout/1, timeout/1, handle_pre_hibernate/1, - status/1, invoke/3, is_duplicate/2, fold/3]). + status/1, invoke/3, is_duplicate/2, foreach_ack/3]). -export([start/1, stop/0]). @@ -301,9 +301,9 @@ ack(AckTags, State = #state { gm = GM, {MsgIds, State #state { backing_queue_state = BQS1, ack_msg_id = AM1 }}. -fold(MsgFun, State = #state { backing_queue = BQ, - backing_queue_state = BQS }, AckTags) -> - State #state { backing_queue_state = BQ:fold(MsgFun, BQS, AckTags) }. +foreach_ack(MsgFun, State = #state { backing_queue = BQ, + backing_queue_state = BQS }, AckTags) -> + State #state { backing_queue_state = BQ:foreach_ack(MsgFun, BQS, AckTags) }. requeue(AckTags, State = #state { gm = GM, backing_queue = BQ, @@ -312,6 +312,11 @@ requeue(AckTags, State = #state { gm = GM, ok = gm:broadcast(GM, {requeue, MsgIds}), {MsgIds, State #state { backing_queue_state = BQS1 }}. +fold(Fun, Acc, State = #state { backing_queue = BQ, + backing_queue_state = BQS }) -> + {Result, BQS1} = BQ:fold(Fun, Acc, BQS), + {Result, State #state { backing_queue_state = BQS1 }}. + len(#state { backing_queue = BQ, backing_queue_state = BQS }) -> BQ:len(BQS). diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 8a3fd9d9..5a5547ae 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -18,10 +18,10 @@ -export([init/3, terminate/2, delete_and_terminate/2, purge/1, publish/4, publish_delivered/4, discard/3, drain_confirmed/1, - dropwhile/3, fetch/2, ack/2, requeue/2, len/1, is_empty/1, + dropwhile/3, fetch/2, ack/2, requeue/2, fold/3, len/1, is_empty/1, depth/1, set_ram_duration_target/2, ram_duration/1, needs_timeout/1, timeout/1, handle_pre_hibernate/1, status/1, invoke/3, - is_duplicate/2, multiple_routing_keys/0, fold/3]). + is_duplicate/2, multiple_routing_keys/0, foreach_ack/3]). -export([start/1, stop/0]). @@ -591,7 +591,7 @@ dropwhile(Pred, AckRequired, State, Msgs) -> {{value, MsgStatus = #msg_status { msg_props = MsgProps }}, State1} -> case {Pred(MsgProps), AckRequired} of {true, true} -> - {MsgStatus1, State2} = read_msg(MsgStatus, State1), + {MsgStatus1, State2} = read_msg(MsgStatus, State1, true), {{Msg, _, AckTag, _}, State3} = internal_fetch(true, MsgStatus1, State2), dropwhile(Pred, AckRequired, State3, [{Msg, AckTag} | Msgs]); @@ -610,7 +610,7 @@ fetch(AckRequired, State) -> {{value, MsgStatus}, State1} -> %% it is possible that the message wasn't read from disk %% at this point, so read it in. - {MsgStatus1, State2} = read_msg(MsgStatus, State1), + {MsgStatus1, State2} = read_msg(MsgStatus, State1, true), {Res, State3} = internal_fetch(AckRequired, MsgStatus1, State2), {Res, a(State3)} end. @@ -638,13 +638,13 @@ ack(AckTags, State) -> persistent_count = PCount1, ack_out_counter = AckOutCount + length(AckTags) })}. -fold(undefined, State, _AckTags) -> +foreach_ack(undefined, State, _AckTags) -> State; -fold(MsgFun, State = #vqstate{pending_ack = PA}, AckTags) -> +foreach_ack(MsgFun, State = #vqstate{pending_ack = PA}, AckTags) -> lists:foldl( fun(SeqId, State1) -> {MsgStatus, State2} = - read_msg(gb_trees:get(SeqId, PA), State1), + read_msg(gb_trees:get(SeqId, PA), State1, true), MsgFun(MsgStatus#msg_status.msg, SeqId), State2 end, State, AckTags). @@ -670,6 +670,53 @@ requeue(AckTags, #vqstate { delta = Delta, in_counter = InCounter + MsgCount, len = Len + MsgCount }))}. +fold(Fun, Acc, #vqstate { q1 = Q1, + q2 = Q2, + delta = Delta, + q3 = Q3, + q4 = Q4} = State) -> + QFun = fun(M, {A, S}) -> + {#msg_status{msg = Msg}, State1} = read_msg(M, S, false), + A1 = Fun(Msg, A), + {A1, State1} + end, + {Acc1, State1} = ?QUEUE:foldl(QFun, {Acc, State}, Q4), + {Acc2, State2} = ?QUEUE:foldl(QFun, {Acc1, State1}, Q3), + {Acc3, State3} = delta_fold (Fun, Acc2, Delta, State2), + {Acc4, State4} = ?QUEUE:foldl(QFun, {Acc3, State3}, Q2), + ?QUEUE:foldl(QFun, {Acc4, State4}, Q1). + +delta_fold(_Fun, Acc, ?BLANK_DELTA_PATTERN(X), State) -> + {Acc, State}; +delta_fold(Fun, Acc, #delta { start_seq_id = DeltaSeqId, + end_seq_id = DeltaSeqIdEnd}, State) -> + {List, State1 = #vqstate { msg_store_clients = MSCState }} = + delta_index(DeltaSeqId, DeltaSeqIdEnd, State), + {Result, MSCState3} = + lists:foldl(fun ({MsgId, _SeqId, _MsgProps, IsPersistent, _IsDelivered}, + {Acc1, MSCState1}) -> + {{ok, Msg = #basic_message {}}, MSCState2} = + msg_store_read(MSCState1, IsPersistent, MsgId), + {Fun(Msg, Acc1), MSCState2} + end, {Acc, MSCState}, List), + {Result, State1 #vqstate { msg_store_clients = MSCState3}}. + +delta_index(DeltaSeqId, DeltaSeqIdEnd, State) -> + delta_index(DeltaSeqId, DeltaSeqIdEnd, State, []). + +delta_index(DeltaSeqIdDone, DeltaSeqIdEnd, State, List) + when DeltaSeqIdDone == DeltaSeqIdEnd -> + {List, State}; +delta_index(DeltaSeqIdDone, DeltaSeqIdEnd, + #vqstate { index_state = IndexState } = State, List) -> + DeltaSeqId1 = lists:min( + [rabbit_queue_index:next_segment_boundary(DeltaSeqIdDone), + DeltaSeqIdEnd]), + {List1, IndexState1} = + rabbit_queue_index:read(DeltaSeqIdDone, DeltaSeqId1, IndexState), + delta_index(DeltaSeqId1, DeltaSeqIdEnd, + State #vqstate { index_state = IndexState1 }, List ++ List1). + len(#vqstate { len = Len }) -> Len. is_empty(State) -> 0 == len(State). @@ -1045,7 +1092,7 @@ in_r(MsgStatus = #msg_status { msg = undefined }, case ?QUEUE:is_empty(Q4) of true -> State #vqstate { q3 = ?QUEUE:in_r(MsgStatus, Q3) }; false -> {MsgStatus1, State1 = #vqstate { q4 = Q4a }} = - read_msg(MsgStatus, State), + read_msg(MsgStatus, State, true), State1 #vqstate { q4 = ?QUEUE:in_r(MsgStatus1, Q4a) } end; in_r(MsgStatus, State = #vqstate { q4 = Q4 }) -> @@ -1066,13 +1113,14 @@ read_msg(MsgStatus = #msg_status { msg = undefined, msg_id = MsgId, is_persistent = IsPersistent }, State = #vqstate { ram_msg_count = RamMsgCount, - msg_store_clients = MSCState}) -> + msg_store_clients = MSCState}, + UpdateRamCount) -> {{ok, Msg = #basic_message {}}, MSCState1} = msg_store_read(MSCState, IsPersistent, MsgId), {MsgStatus #msg_status { msg = Msg }, - State #vqstate { ram_msg_count = RamMsgCount + 1, + State #vqstate { ram_msg_count = RamMsgCount + one_if(UpdateRamCount), msg_store_clients = MSCState1 }}; -read_msg(MsgStatus, State) -> +read_msg(MsgStatus, State, _UpdateRamCount) -> {MsgStatus, State}. internal_fetch(AckRequired, MsgStatus = #msg_status { @@ -1348,7 +1396,7 @@ msg_indices_written_to_disk(Callback, MsgIdSet) -> %%---------------------------------------------------------------------------- publish_alpha(#msg_status { msg = undefined } = MsgStatus, State) -> - read_msg(MsgStatus, State); + read_msg(MsgStatus, State, true); publish_alpha(MsgStatus, #vqstate {ram_msg_count = RamMsgCount } = State) -> {MsgStatus, State #vqstate { ram_msg_count = RamMsgCount + 1 }}. -- cgit v1.2.1 From 59b7fbd0da63c268d7d205ae1e4e5121d21ca871 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 21 Nov 2012 23:46:19 +0000 Subject: assert after fold --- src/rabbit_variable_queue.erl | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index ca362901..b96ce3cf 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -641,13 +641,12 @@ ack(AckTags, State) -> fold(undefined, State, _AckTags) -> State; fold(MsgFun, State = #vqstate{pending_ack = PA}, AckTags) -> - lists:foldl( - fun(SeqId, State1) -> - {MsgStatus, State2} = - read_msg(gb_trees:get(SeqId, PA), State1), - MsgFun(MsgStatus#msg_status.msg, SeqId), - State2 - end, State, AckTags). + a(lists:foldl(fun(SeqId, State1) -> + {MsgStatus, State2} = + read_msg(gb_trees:get(SeqId, PA), State1), + MsgFun(MsgStatus#msg_status.msg, SeqId), + State2 + end, State, AckTags)). requeue(AckTags, #vqstate { delta = Delta, q3 = Q3, -- cgit v1.2.1 From 8e321a49d9824af496dfc0689a57d21a0965d695 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 21 Nov 2012 23:51:51 +0000 Subject: add test for invoking bq:fold when messages are on disk --- src/rabbit_tests.erl | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 096f9490..983abf29 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -2300,6 +2300,7 @@ test_variable_queue() -> fun test_variable_queue_partial_segments_delta_thing/1, fun test_variable_queue_all_the_bits_not_covered_elsewhere1/1, fun test_variable_queue_all_the_bits_not_covered_elsewhere2/1, + fun test_variable_queue_fold_msg_on_disk/1, fun test_dropwhile/1, fun test_dropwhile_varying_ram_duration/1, fun test_variable_queue_ack_limiting/1, @@ -2515,6 +2516,12 @@ test_variable_queue_all_the_bits_not_covered_elsewhere2(VQ0) -> {empty, VQ8} = rabbit_variable_queue:fetch(false, VQ7), VQ8. +test_variable_queue_fold_msg_on_disk(VQ0) -> + VQ1 = variable_queue_publish(true, 1, VQ0), + {VQ2, AckTags} = variable_queue_fetch(1, true, false, 1, VQ1), + VQ3 = rabbit_variable_queue:fold(fun (_M, _A) -> ok end, VQ2, AckTags), + VQ3. + test_queue_recover() -> Count = 2 * rabbit_queue_index:next_segment_boundary(0), {new, #amqqueue { pid = QPid, name = QName } = Q} = -- cgit v1.2.1 From 0565c4b1c518d55e6ece9c9bb03662bd54bfdef5 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 22 Nov 2012 00:20:28 +0000 Subject: leave the ram_msg_count unchanged in BQ:fold since we are not moving messages *into the queue* --- src/rabbit_variable_queue.erl | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index b96ce3cf..6dc65bab 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -643,7 +643,7 @@ fold(undefined, State, _AckTags) -> fold(MsgFun, State = #vqstate{pending_ack = PA}, AckTags) -> a(lists:foldl(fun(SeqId, State1) -> {MsgStatus, State2} = - read_msg(gb_trees:get(SeqId, PA), State1), + read_msg(gb_trees:get(SeqId, PA), false, State1), MsgFun(MsgStatus#msg_status.msg, SeqId), State2 end, State, AckTags)). @@ -1062,17 +1062,19 @@ queue_out(State = #vqstate { q4 = Q4 }) -> {{value, MsgStatus}, State #vqstate { q4 = Q4a }} end. +read_msg(MsgStatus, State) -> read_msg(MsgStatus, true, State). + read_msg(MsgStatus = #msg_status { msg = undefined, msg_id = MsgId, is_persistent = IsPersistent }, - State = #vqstate { ram_msg_count = RamMsgCount, - msg_store_clients = MSCState}) -> + CountDiskToRam, State = #vqstate { ram_msg_count = RamMsgCount, + msg_store_clients = MSCState}) -> {{ok, Msg = #basic_message {}}, MSCState1} = msg_store_read(MSCState, IsPersistent, MsgId), {MsgStatus #msg_status { msg = Msg }, - State #vqstate { ram_msg_count = RamMsgCount + 1, + State #vqstate { ram_msg_count = RamMsgCount + one_if(CountDiskToRam), msg_store_clients = MSCState1 }}; -read_msg(MsgStatus, State) -> +read_msg(MsgStatus, _CountDiskToRam, State) -> {MsgStatus, State}. internal_fetch(AckRequired, MsgStatus = #msg_status { -- cgit v1.2.1 From fbf5b8d0b9795d43e433c745ce414fd2156231ef Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 22 Nov 2012 13:10:24 +0000 Subject: Fix a race where we decide on the BQ before writing the #amqqueue{} to Mnesia, then the policy changes and we are therefore not notified. We now only make the initial decision after we can be assured we will be informed of subsequent changes. --- src/rabbit_amqqueue_process.erl | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 6af43193..6f8c9305 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -122,11 +122,10 @@ info_keys() -> ?INFO_KEYS. init(Q) -> process_flag(trap_exit, true), - State = #q{q = Q#amqqueue{pid = self()}, exclusive_consumer = none, has_had_consumers = false, - backing_queue = backing_queue_module(Q), + backing_queue = undefined, backing_queue_state = undefined, active_consumers = queue:new(), expires = undefined, @@ -193,7 +192,7 @@ code_change(_OldVsn, State, _Extra) -> %%---------------------------------------------------------------------------- declare(Recover, From, State = #q{q = Q, - backing_queue = BQ, + backing_queue = undefined, backing_queue_state = undefined}) -> case rabbit_amqqueue:internal_declare(Q, Recover =/= new) of #amqqueue{} = Q1 -> @@ -205,9 +204,11 @@ declare(Recover, From, State = #q{q = Q, ok = rabbit_memory_monitor:register( self(), {rabbit_amqqueue, set_ram_duration_target, [self()]}), + BQ = backing_queue_module(Q1), BQS = bq_init(BQ, Q, Recover), recovery_barrier(Recover), - State1 = process_args(State#q{backing_queue_state = BQS}), + State1 = process_args(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, -- cgit v1.2.1 From 26542563459bfd6ca5a3f19e9a04d28f365ea0af Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 22 Nov 2012 13:32:14 +0000 Subject: bq api tweak: don't include remaining message count in fetch/drop result --- src/rabbit_amqqueue_process.erl | 11 +++++------ src/rabbit_backing_queue.erl | 8 ++------ src/rabbit_mirror_queue_master.erl | 31 ++++++++++++++----------------- src/rabbit_mirror_queue_slave.erl | 3 +-- src/rabbit_tests.erl | 23 ++++++++++++++--------- src/rabbit_variable_queue.erl | 13 ++++++------- 6 files changed, 42 insertions(+), 47 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index dc258fa6..fe3ed88d 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -485,11 +485,10 @@ deliver_msg_to_consumer(DeliverFun, {Stop, State1}. deliver_from_queue_deliver(AckRequired, State) -> - {{Message, IsDelivered, AckTag, _Remaining}, State1} = - fetch(AckRequired, State), + {Result, State1} = fetch(AckRequired, State), State2 = #q{backing_queue = BQ, backing_queue_state = BQS} = drop_expired_messages(State1), - {{Message, IsDelivered, AckTag}, BQ:is_empty(BQS), State2}. + {Result, BQ:is_empty(BQS), State2}. confirm_messages([], State) -> State; @@ -1061,8 +1060,8 @@ handle_call({basic_get, ChPid, NoAck}, _From, case fetch(AckRequired, drop_expired_messages(State1)) of {empty, State2} -> reply(empty, State2); - {{Message, IsDelivered, AckTag, Remaining}, State2} -> - State3 = + {{Message, IsDelivered, AckTag}, State2} -> + State3 = #q{backing_queue = BQ, backing_queue_state = BQS} = case AckRequired of true -> C = #cr{acktags = ChAckTags} = ch_record(ChPid), ChAckTags1 = sets:add_element(AckTag, ChAckTags), @@ -1071,7 +1070,7 @@ handle_call({basic_get, ChPid, NoAck}, _From, false -> State2 end, Msg = {QName, self(), AckTag, IsDelivered, Message}, - reply({ok, Remaining, Msg}, State3) + reply({ok, BQ:len(BQS), Msg}, State3) end; handle_call({basic_consume, NoAck, ChPid, Limiter, diff --git a/src/rabbit_backing_queue.erl b/src/rabbit_backing_queue.erl index 00de3e17..871becc5 100644 --- a/src/rabbit_backing_queue.erl +++ b/src/rabbit_backing_queue.erl @@ -26,13 +26,9 @@ -type(msg_ids() :: [rabbit_types:msg_id()]). -type(fetch_result(Ack) :: - ('empty' | - %% Message, IsDelivered, AckTag, Remaining_Len - {rabbit_types:basic_message(), boolean(), Ack, non_neg_integer()})). + ('empty' | {rabbit_types:basic_message(), boolean(), Ack})). -type(drop_result(Ack) :: - ('empty' | - %% MessageId, AckTag, Remaining_Len - {rabbit_types:msg_id(), Ack, non_neg_integer()})). + ('empty' | {rabbit_types:msg_id(), Ack})). -type(attempt_recovery() :: boolean()). -type(purged_msg_count() :: non_neg_integer()). -type(async_callback() :: diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index 961636b1..ac2048b7 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -268,8 +268,7 @@ drain_confirmed(State = #state { backing_queue = BQ, seen_status = SS1, confirmed = [] }}. -fetch(AckRequired, State = #state { gm = GM, - backing_queue = BQ, +fetch(AckRequired, State = #state { backing_queue = BQ, backing_queue_state = BQS, set_delivered = SetDelivered }) -> {Result, BQS1} = BQ:fetch(AckRequired, BQS), @@ -277,25 +276,19 @@ fetch(AckRequired, State = #state { gm = GM, case Result of empty -> {Result, State1}; - {Message, IsDelivered, AckTag, Remaining} -> - ok = gm:broadcast(GM, {drop, Remaining, 1, AckRequired}), - IsDelivered1 = IsDelivered orelse SetDelivered > 0, - {{Message, IsDelivered1, AckTag, Remaining}, + {Message, IsDelivered, AckTag} -> + {{Message, IsDelivered orelse SetDelivered > 0, AckTag}, drop(Message#basic_message.id, AckTag, State1)} end. -drop(AckRequired, State = #state { gm = GM, - backing_queue = BQ, +drop(AckRequired, State = #state { backing_queue = BQ, backing_queue_state = BQS }) -> {Result, BQS1} = BQ:drop(AckRequired, BQS), State1 = State #state { backing_queue_state = BQS1 }, - case Result of - empty -> - {Result, State1}; - {MsgId, AckTag, Remaining} -> - ok = gm:broadcast(GM, {drop, Remaining, 1, AckRequired}), - {Result, drop(MsgId, AckTag, State1)} - end. + {Result, case Result of + empty -> State1; + {MsgId, AckTag} -> drop(MsgId, AckTag, State1) + end}. ack(AckTags, State = #state { gm = GM, backing_queue = BQ, @@ -453,8 +446,12 @@ depth_fun() -> %% Helpers %% --------------------------------------------------------------------------- -drop(MsgId, AckTag, State = #state { set_delivered = SetDelivered, - ack_msg_id = AM }) -> +drop(MsgId, AckTag, State = #state { set_delivered = SetDelivered, + ack_msg_id = AM, + gm = GM, + backing_queue = BQ, + backing_queue_state = BQS }) -> + ok = gm:broadcast(GM, {drop, BQ:len(BQS), 1, AckTag =/= undefined}), State #state { set_delivered = lists:max([0, SetDelivered - 1]), ack_msg_id = maybe_store_acktag(AckTag, MsgId, AM) }. diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 3ad8eb77..cb7a2135 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -727,8 +727,7 @@ process_instruction({drop, Length, Dropped, AckRequired}, end, State1 = lists:foldl( fun (const, StateN = #state{backing_queue_state = BQSN}) -> - {{MsgId, AckTag, _Remaining}, BQSN1} = - BQ:drop(AckRequired, BQSN), + {{MsgId, AckTag}, BQSN1} = BQ:drop(AckRequired, BQSN), maybe_store_ack( AckRequired, MsgId, AckTag, StateN #state { backing_queue_state = BQSN1 }) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 444c7375..408bacd8 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -2233,8 +2233,9 @@ variable_queue_fetch(Count, IsPersistent, IsDelivered, Len, VQ) -> lists:foldl(fun (N, {VQN, AckTagsAcc}) -> Rem = Len - N, {{#basic_message { is_persistent = IsPersistent }, - IsDelivered, AckTagN, Rem}, VQM} = + IsDelivered, AckTagN}, VQM} = rabbit_variable_queue:fetch(true, VQN), + Rem = rabbit_variable_queue:len(VQM), {VQM, [AckTagN | AckTagsAcc]} end, {VQ, []}, lists:seq(1, Count)). @@ -2326,7 +2327,7 @@ test_variable_queue_requeue(VQ0) -> VQM end, VQ4, Subset), VQ6 = lists:foldl(fun (AckTag, VQa) -> - {{#basic_message{}, true, AckTag, _}, VQb} = + {{#basic_message{}, true, AckTag}, VQb} = rabbit_variable_queue:fetch(true, VQa), VQb end, VQ5, lists:reverse(Acks)), @@ -2366,14 +2367,16 @@ test_drop(VQ0) -> %% start by sending a messages VQ1 = variable_queue_publish(false, 1, VQ0), %% drop message with AckRequired = true - {{MsgId, AckTag, 0}, VQ2} = rabbit_variable_queue:drop(true, VQ1), + {{MsgId, AckTag}, VQ2} = rabbit_variable_queue:drop(true, VQ1), + true = rabbit_variable_queue:is_empty(VQ2), true = AckTag =/= undefinded, %% drop again -> empty {empty, VQ3} = rabbit_variable_queue:drop(false, VQ2), %% requeue {[MsgId], VQ4} = rabbit_variable_queue:requeue([AckTag], VQ3), %% drop message with AckRequired = false - {{MsgId, undefined, 0}, VQ5} = rabbit_variable_queue:drop(false, VQ4), + {{MsgId, undefined}, VQ5} = rabbit_variable_queue:drop(false, VQ4), + true = rabbit_variable_queue:is_empty(VQ5), VQ5. test_dropwhile(VQ0) -> @@ -2392,7 +2395,7 @@ test_dropwhile(VQ0) -> %% fetch five now VQ3 = lists:foldl(fun (_N, VQN) -> - {{#basic_message{}, _, _, _}, VQM} = + {{#basic_message{}, _, _}, VQM} = rabbit_variable_queue:fetch(false, VQN), VQM end, VQ2, lists:seq(6, Count)), @@ -2445,7 +2448,8 @@ publish_fetch_and_ack(0, _Len, VQ0) -> VQ0; publish_fetch_and_ack(N, Len, VQ0) -> VQ1 = variable_queue_publish(false, 1, VQ0), - {{_Msg, false, AckTag, Len}, VQ2} = rabbit_variable_queue:fetch(true, VQ1), + {{_Msg, false, AckTag}, VQ2} = rabbit_variable_queue:fetch(true, VQ1), + Len = rabbit_variable_queue:len(VQ2), {_Guids, VQ3} = rabbit_variable_queue:ack([AckTag], VQ2), publish_fetch_and_ack(N-1, Len, VQ3). @@ -2510,8 +2514,8 @@ test_variable_queue_all_the_bits_not_covered_elsewhere1(VQ0) -> Count, VQ4), _VQ6 = rabbit_variable_queue:terminate(shutdown, VQ5), VQ7 = variable_queue_init(test_amqqueue(true), true), - {{_Msg1, true, _AckTag1, Count1}, VQ8} = - rabbit_variable_queue:fetch(true, VQ7), + {{_Msg1, true, _AckTag1}, VQ8} = rabbit_variable_queue:fetch(true, VQ7), + Count1 = rabbit_variable_queue:len(VQ8), VQ9 = variable_queue_publish(false, 1, VQ8), VQ10 = rabbit_variable_queue:set_ram_duration_target(0, VQ9), {VQ11, _AckTags2} = variable_queue_fetch(Count1, true, true, Count, VQ10), @@ -2551,8 +2555,9 @@ test_queue_recover() -> rabbit_amqqueue:basic_get(Q1, self(), false), exit(QPid1, shutdown), VQ1 = variable_queue_init(Q, true), - {{_Msg1, true, _AckTag1, CountMinusOne}, VQ2} = + {{_Msg1, true, _AckTag1}, VQ2} = rabbit_variable_queue:fetch(true, VQ1), + CountMinusOne = rabbit_variable_queue:len(VQ2), _VQ3 = rabbit_variable_queue:delete_and_terminate(shutdown, VQ2), rabbit_amqqueue:internal_delete(QName, QPid1) end), diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 208eb70f..3a025ba3 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -591,8 +591,8 @@ dropwhile(Pred, AckRequired, State, Msgs) -> case {Pred(MsgProps), AckRequired} of {true, true} -> {MsgStatus1, State2} = read_msg(MsgStatus, State1), - {{Msg, _, AckTag, _}, State3} = - internal_fetch(true, MsgStatus1, State2), + {{Msg, _IsDelivered, AckTag}, State3} = + internal_fetch(true, MsgStatus1, State2), dropwhile(Pred, AckRequired, State3, [{Msg, AckTag} | Msgs]); {true, false} -> {_, State2} = internal_fetch(false, MsgStatus, State1), @@ -619,9 +619,9 @@ drop(AckRequired, State) -> {empty, State1} -> {empty, a(State1)}; {{value, MsgStatus}, State1} -> - {{_Msg, _IsDelivered, AckTag, Remaining}, State2} = + {{_Msg, _IsDelivered, AckTag}, State2} = internal_fetch(AckRequired, MsgStatus, State1), - {{MsgStatus#msg_status.msg_id, AckTag, Remaining}, a(State2)} + {{MsgStatus#msg_status.msg_id, AckTag}, a(State2)} end. ack([], State) -> @@ -1125,14 +1125,13 @@ internal_fetch(AckRequired, MsgStatus = #msg_status { end, PCount1 = PCount - one_if(IsPersistent andalso not AckRequired), - Len1 = Len - 1, RamMsgCount1 = RamMsgCount - one_if(Msg =/= undefined), - {{Msg, IsDelivered, AckTag, Len1}, + {{Msg, IsDelivered, AckTag}, State1 #vqstate { ram_msg_count = RamMsgCount1, out_counter = OutCount + 1, index_state = IndexState2, - len = Len1, + len = Len - 1, persistent_count = PCount1 }}. purge_betas_and_deltas(LensByStore, -- cgit v1.2.1 From 220488f1e8df41403f038394a941dd246ce5afad Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 22 Nov 2012 13:38:36 +0000 Subject: propagate API change --- src/rabbit_backing_queue_qc.erl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rabbit_backing_queue_qc.erl b/src/rabbit_backing_queue_qc.erl index 3168ca5c..fe014ef5 100644 --- a/src/rabbit_backing_queue_qc.erl +++ b/src/rabbit_backing_queue_qc.erl @@ -278,12 +278,12 @@ next_state(S, Res, {call, ?BQMOD, purge, _Args}) -> postcondition(S, {call, ?BQMOD, fetch, _Args}, Res) -> #state{messages = Messages, len = Len, acks = Acks, confirms = Confrms} = S, case Res of - {{MsgFetched, _IsDelivered, AckTag, RemainingLen}, _BQ} -> + {{MsgFetched, _IsDelivered, AckTag}, _BQ} -> {_SeqId, {_MsgProps, Msg}} = gb_trees:smallest(Messages), MsgFetched =:= Msg andalso not proplists:is_defined(AckTag, Acks) andalso not gb_sets:is_element(AckTag, Confrms) andalso - RemainingLen =:= Len - 1; + Len =/= 0; {empty, _BQ} -> Len =:= 0 end; @@ -291,14 +291,14 @@ postcondition(S, {call, ?BQMOD, fetch, _Args}, Res) -> postcondition(S, {call, ?BQMOD, drop, _Args}, Res) -> #state{messages = Messages, len = Len, acks = Acks, confirms = Confrms} = S, case Res of - {{MsgIdFetched, AckTag, RemainingLen}, _BQ} -> + {{MsgIdFetched, AckTag}, _BQ} -> {_SeqId, {_MsgProps, Msg}} = gb_trees:smallest(Messages), MsgId = eval({call, erlang, element, [?RECORD_INDEX(id, basic_message), Msg]}), MsgIdFetched =:= MsgId andalso not proplists:is_defined(AckTag, Acks) andalso not gb_sets:is_element(AckTag, Confrms) andalso - RemainingLen =:= Len - 1; + Len =/= 0; {empty, _BQ} -> Len =:= 0 end; -- cgit v1.2.1 From 7aa01b6f4d6e2dd828ae606dc0f2d13a2dd3980c Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Thu, 22 Nov 2012 13:54:19 +0000 Subject: Rename backing queue fold --- src/rabbit_amqqueue_process.erl | 2 +- src/rabbit_backing_queue.erl | 4 ++-- src/rabbit_mirror_queue_master.erl | 8 ++++---- src/rabbit_tests.erl | 3 ++- src/rabbit_variable_queue.erl | 6 +++--- 5 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index dc258fa6..c41f02e1 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1226,7 +1226,7 @@ handle_cast({reject, AckTags, false, ChPid}, State) -> ChPid, AckTags, State, fun (State1 = #q{backing_queue = BQ, backing_queue_state = BQS}) -> - BQS1 = BQ:fold(fun(M, A) -> DLXFun([{M, A}]) end, + BQS1 = BQ:foreach_ack(fun(M, A) -> DLXFun([{M, A}]) end, BQS, AckTags), State1#q{backing_queue_state = BQS1} end)); diff --git a/src/rabbit_backing_queue.erl b/src/rabbit_backing_queue.erl index 00de3e17..329144f9 100644 --- a/src/rabbit_backing_queue.erl +++ b/src/rabbit_backing_queue.erl @@ -153,7 +153,7 @@ %% Acktags supplied are for messages which should be processed. The %% provided callback function is called with each message. --callback fold(msg_fun(), state(), [ack()]) -> state(). +-callback foreach_ack(msg_fun(), state(), [ack()]) -> state(). %% Reinsert messages into the queue which have already been delivered %% and were pending acknowledgement. @@ -220,7 +220,7 @@ behaviour_info(callbacks) -> [{start, 1}, {stop, 0}, {init, 3}, {terminate, 2}, {delete_and_terminate, 2}, {purge, 1}, {publish, 4}, {publish_delivered, 4}, {discard, 3}, {drain_confirmed, 1}, {dropwhile, 3}, - {fetch, 2}, {ack, 2}, {fold, 3}, {requeue, 2}, {len, 1}, + {fetch, 2}, {ack, 2}, {foreach_ack, 3}, {requeue, 2}, {len, 1}, {is_empty, 1}, {depth, 1}, {set_ram_duration_target, 2}, {ram_duration, 1}, {needs_timeout, 1}, {timeout, 1}, {handle_pre_hibernate, 1}, {status, 1}, {invoke, 3}, {is_duplicate, 2}] ; diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index 961636b1..39060c09 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -22,7 +22,7 @@ requeue/2, len/1, is_empty/1, depth/1, drain_confirmed/1, dropwhile/3, set_ram_duration_target/2, ram_duration/1, needs_timeout/1, timeout/1, handle_pre_hibernate/1, - status/1, invoke/3, is_duplicate/2, fold/3]). + status/1, invoke/3, is_duplicate/2, foreach_ack/3]). -export([start/1, stop/0]). @@ -310,9 +310,9 @@ ack(AckTags, State = #state { gm = GM, {MsgIds, State #state { backing_queue_state = BQS1, ack_msg_id = AM1 }}. -fold(MsgFun, State = #state { backing_queue = BQ, - backing_queue_state = BQS }, AckTags) -> - State #state { backing_queue_state = BQ:fold(MsgFun, BQS, AckTags) }. +foreach_ack(MsgFun, State = #state { backing_queue = BQ, + backing_queue_state = BQS }, AckTags) -> + State #state { backing_queue_state = BQ:foreach_ack(MsgFun, BQS, AckTags) }. requeue(AckTags, State = #state { gm = GM, backing_queue = BQ, diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 912bd3b6..176374ce 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -2534,7 +2534,8 @@ test_variable_queue_all_the_bits_not_covered_elsewhere2(VQ0) -> test_variable_queue_fold_msg_on_disk(VQ0) -> VQ1 = variable_queue_publish(true, 1, VQ0), {VQ2, AckTags} = variable_queue_fetch(1, true, false, 1, VQ1), - VQ3 = rabbit_variable_queue:fold(fun (_M, _A) -> ok end, VQ2, AckTags), + VQ3 = rabbit_variable_queue:foreach_ack(fun (_M, _A) -> ok end, + VQ2, AckTags), VQ3. test_queue_recover() -> diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 862e74f6..7813aa7b 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -21,7 +21,7 @@ dropwhile/3, fetch/2, drop/2, ack/2, requeue/2, len/1, is_empty/1, depth/1, set_ram_duration_target/2, ram_duration/1, needs_timeout/1, timeout/1, handle_pre_hibernate/1, status/1, invoke/3, - is_duplicate/2, multiple_routing_keys/0, fold/3]). + is_duplicate/2, multiple_routing_keys/0, foreach_ack/3]). -export([start/1, stop/0]). @@ -647,9 +647,9 @@ ack(AckTags, State) -> persistent_count = PCount1, ack_out_counter = AckOutCount + length(AckTags) })}. -fold(undefined, State, _AckTags) -> +foreach_ack(undefined, State, _AckTags) -> State; -fold(MsgFun, State = #vqstate{pending_ack = PA}, AckTags) -> +foreach_ack(MsgFun, State = #vqstate{pending_ack = PA}, AckTags) -> a(lists:foldl(fun(SeqId, State1) -> {MsgStatus, State2} = read_msg(gb_trees:get(SeqId, PA), false, State1), -- cgit v1.2.1 From 946623b70b0022ef88c3e4ae87c71c319693c043 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 22 Nov 2012 15:50:08 +0000 Subject: Don't duplicate name. --- src/rabbit_amqqueue_process.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 3bad6864..1f31aa22 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -108,7 +108,7 @@ owner_pid ]). --define(INFO_KEYS, ?CREATION_EVENT_KEYS ++ ?STATISTICS_KEYS). +-define(INFO_KEYS, ?CREATION_EVENT_KEYS ++ ?STATISTICS_KEYS -- [name]). %%---------------------------------------------------------------------------- -- cgit v1.2.1 From a01cf9e7870250ccdd0129c5cbb9b881581f4fc8 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 22 Nov 2012 15:50:14 +0000 Subject: Explain --- src/rabbit_channel.erl | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 6ccc2e65..b1ef3b6b 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -1357,6 +1357,16 @@ deliver_to_queues({Delivery = #delivery{message = Message = #basic_message{ queue_monitors = QMons}) -> Qs = rabbit_amqqueue:lookup(DelQNames), {RoutingRes, DeliveredQPids} = rabbit_amqqueue:deliver_flow(Qs, Delivery), + %% The pmon:monitor_all/2 monitors all queues to which we + %% delivered. But we want to monitor even queues we didn't deliver + %% to, since we need their 'DOWN' messages to clean + %% queue_names. So we also need to monitor each QPid from + %% queues. But that only gets the masters (which is fine for + %% cleaning queue_names), so we need the union of both. + %% + %% ...and we need to add even non-delivered queues to queue_names + %% since alternative algorithms to update queue_names less + %% frequently would in fact be more expensive in the common case. {QNames1, QMons1} = lists:foldl(fun (#amqqueue{pid = QPid, name = QName}, {QNames0, QMons0}) -> -- cgit v1.2.1 From acd1ab8fafab77b4a93222cd8f6dce354fe1b7d8 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Thu, 22 Nov 2012 17:30:21 +0000 Subject: QC test for backing queue fold --- src/rabbit_backing_queue_qc.erl | 21 +++++++++++++++++++-- src/rabbit_variable_queue.erl | 6 +++--- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/rabbit_backing_queue_qc.erl b/src/rabbit_backing_queue_qc.erl index 3168ca5c..00e17d02 100644 --- a/src/rabbit_backing_queue_qc.erl +++ b/src/rabbit_backing_queue_qc.erl @@ -106,7 +106,8 @@ command(S) -> {1, qc_dropwhile(S)}, {1, qc_is_empty(S)}, {1, qc_timeout(S)}, - {1, qc_purge(S)}]). + {1, qc_purge(S)}, + {1, qc_fold(S)}]). qc_publish(#state{bqstate = BQ}) -> {call, ?BQMOD, publish, @@ -157,6 +158,9 @@ qc_timeout(#state{bqstate = BQ}) -> qc_purge(#state{bqstate = BQ}) -> {call, ?BQMOD, purge, [BQ]}. +qc_fold(#state{bqstate = BQ}) -> + {call, ?BQMOD, fold, [fun foldfun/2, foldacc(), BQ]}. + %% Preconditions %% Create long queues by only allowing publishing @@ -271,7 +275,11 @@ next_state(S, BQ, {call, ?MODULE, timeout, _Args}) -> next_state(S, Res, {call, ?BQMOD, purge, _Args}) -> BQ1 = {call, erlang, element, [2, Res]}, - S#state{bqstate = BQ1, len = 0, messages = gb_trees:empty()}. + S#state{bqstate = BQ1, len = 0, messages = gb_trees:empty()}; + +next_state(S, Res, {call, ?BQMOD, fold, _Args}) -> + BQ1 = {call, erlang, element, [2, Res]}, + S#state{bqstate = BQ1}. %% Postconditions @@ -321,6 +329,12 @@ postcondition(S, {call, ?BQMOD, drain_confirmed, _Args}, Res) -> lists:all(fun (M) -> gb_sets:is_element(M, Confirms) end, ReportedConfirmed); +postcondition(S, {call, ?BQMOD, fold, _Args}, {Res, _BQ}) -> + #state{messages = Messages} = S, + lists:foldl(fun ({_SeqId, {_MsgProps, Msg}}, Acc) -> + foldfun(Msg, Acc) + end, foldacc(), gb_trees:to_list(Messages)) =:= Res; + postcondition(#state{bqstate = BQ, len = Len}, {call, _M, _F, _A}, _Res) -> ?BQMOD:len(BQ) =:= Len. @@ -379,6 +393,9 @@ rand_choice(List, Selection, N) -> rand_choice(List -- [Picked], [Picked | Selection], N - 1). +foldfun(Msg, Acc) -> [Msg | Acc]. +foldacc() -> []. + dropfun(Props) -> Expiry = eval({call, erlang, element, [?RECORD_INDEX(expiry, message_properties), Props]}), diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 7636d5ea..f49aa085 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -18,8 +18,8 @@ -export([init/3, terminate/2, delete_and_terminate/2, purge/1, publish/4, publish_delivered/4, discard/3, drain_confirmed/1, - dropwhile/3, fetch/2, drop/2, ack/2, requeue/2, len/1, is_empty/1, - depth/1, set_ram_duration_target/2, ram_duration/1, + dropwhile/3, fetch/2, drop/2, ack/2, requeue/2, fold/3, len/1, + is_empty/1, depth/1, set_ram_duration_target/2, ram_duration/1, needs_timeout/1, timeout/1, handle_pre_hibernate/1, status/1, invoke/3, is_duplicate/2, multiple_routing_keys/0, foreach_ack/3]). @@ -684,7 +684,7 @@ fold(Fun, Acc, #vqstate { q1 = Q1, q3 = Q3, q4 = Q4} = State) -> QFun = fun(M, {A, S}) -> - {#msg_status{msg = Msg}, State1} = read_msg(M, S, false), + {#msg_status{msg = Msg}, State1} = read_msg(M, false, S), A1 = Fun(Msg, A), {A1, State1} end, -- cgit v1.2.1 From 6cddbaf0135c269e2d3be5d9b7c956cd2fcd60f4 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Fri, 23 Nov 2012 12:28:48 +0000 Subject: Refactor backing queue delta fold --- src/rabbit_variable_queue.erl | 71 +++++++++++++++++++------------------------ 1 file changed, 32 insertions(+), 39 deletions(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index f49aa085..10ada2dd 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -680,51 +680,22 @@ requeue(AckTags, #vqstate { delta = Delta, fold(Fun, Acc, #vqstate { q1 = Q1, q2 = Q2, - delta = Delta, + delta = #delta { start_seq_id = DeltaSeqId, + end_seq_id = DeltaSeqIdEnd }, q3 = Q3, - q4 = Q4} = State) -> - QFun = fun(M, {A, S}) -> - {#msg_status{msg = Msg}, State1} = read_msg(M, false, S), - A1 = Fun(Msg, A), - {A1, State1} + q4 = Q4 } = State) -> + QFun = fun(MsgStatus, {Acc0, State0}) -> + {#msg_status { msg = Msg }, State1 } = + read_msg(MsgStatus, false, State0), + Acc1 = Fun(Msg, Acc0), + {Acc1, State1} end, {Acc1, State1} = ?QUEUE:foldl(QFun, {Acc, State}, Q4), {Acc2, State2} = ?QUEUE:foldl(QFun, {Acc1, State1}, Q3), - {Acc3, State3} = delta_fold (Fun, Acc2, Delta, State2), + {Acc3, State3} = delta_fold (Fun, Acc2, DeltaSeqId, DeltaSeqIdEnd, State2), {Acc4, State4} = ?QUEUE:foldl(QFun, {Acc3, State3}, Q2), ?QUEUE:foldl(QFun, {Acc4, State4}, Q1). -delta_fold(_Fun, Acc, ?BLANK_DELTA_PATTERN(X), State) -> - {Acc, State}; -delta_fold(Fun, Acc, #delta { start_seq_id = DeltaSeqId, - end_seq_id = DeltaSeqIdEnd}, State) -> - {List, State1 = #vqstate { msg_store_clients = MSCState }} = - delta_index(DeltaSeqId, DeltaSeqIdEnd, State), - {Result, MSCState3} = - lists:foldl(fun ({MsgId, _SeqId, _MsgProps, IsPersistent, _IsDelivered}, - {Acc1, MSCState1}) -> - {{ok, Msg = #basic_message {}}, MSCState2} = - msg_store_read(MSCState1, IsPersistent, MsgId), - {Fun(Msg, Acc1), MSCState2} - end, {Acc, MSCState}, List), - {Result, State1 #vqstate { msg_store_clients = MSCState3}}. - -delta_index(DeltaSeqId, DeltaSeqIdEnd, State) -> - delta_index(DeltaSeqId, DeltaSeqIdEnd, State, []). - -delta_index(DeltaSeqIdDone, DeltaSeqIdEnd, State, List) - when DeltaSeqIdDone == DeltaSeqIdEnd -> - {List, State}; -delta_index(DeltaSeqIdDone, DeltaSeqIdEnd, - #vqstate { index_state = IndexState } = State, List) -> - DeltaSeqId1 = lists:min( - [rabbit_queue_index:next_segment_boundary(DeltaSeqIdDone), - DeltaSeqIdEnd]), - {List1, IndexState1} = - rabbit_queue_index:read(DeltaSeqIdDone, DeltaSeqId1, IndexState), - delta_index(DeltaSeqId1, DeltaSeqIdEnd, - State #vqstate { index_state = IndexState1 }, List ++ List1). - len(#vqstate { len = Len }) -> Len. is_empty(State) -> 0 == len(State). @@ -1402,7 +1373,7 @@ msg_indices_written_to_disk(Callback, MsgIdSet) -> end). %%---------------------------------------------------------------------------- -%% Internal plumbing for requeue +%% Internal plumbing for requeue and fold %%---------------------------------------------------------------------------- publish_alpha(#msg_status { msg = undefined } = MsgStatus, State) -> @@ -1471,6 +1442,28 @@ beta_limit(Q) -> delta_limit(?BLANK_DELTA_PATTERN(_X)) -> undefined; delta_limit(#delta { start_seq_id = StartSeqId }) -> StartSeqId. +delta_fold(_Fun, Acc, DeltaSeqId, DeltaSeqIdEnd, State) + when DeltaSeqId == DeltaSeqIdEnd -> + {Acc, State}; +delta_fold(Fun, Acc, DeltaSeqId, DeltaSeqIdEnd, + #vqstate { index_state = IndexState, + msg_store_clients = MSCState } = State) -> + DeltaSeqId1 = lists:min( + [rabbit_queue_index:next_segment_boundary(DeltaSeqId), + DeltaSeqIdEnd]), + {List, IndexState1} = rabbit_queue_index:read(DeltaSeqId, DeltaSeqId1, + IndexState), + {Acc1, MSCState1} = + lists:foldl(fun ({MsgId, _SeqId, _MsgProps, IsPersistent, + _IsDelivered}, {Acc0, MSCState0}) -> + {{ok, Msg = #basic_message {}}, MSCState1} = + msg_store_read(MSCState0, IsPersistent, MsgId), + {Fun(Msg, Acc0), MSCState1} + end, {Acc, MSCState}, List), + delta_fold(Fun, Acc1, DeltaSeqId1, DeltaSeqIdEnd, + State #vqstate { index_state = IndexState1, + msg_store_clients = MSCState1 }). + %%---------------------------------------------------------------------------- %% Phase changes %%---------------------------------------------------------------------------- -- cgit v1.2.1 From 75cec9b38c1debbeb4cf5ac37c0c4b781d620485 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Fri, 23 Nov 2012 13:22:12 +0000 Subject: Backing queue fold tests --- src/rabbit_tests.erl | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 176374ce..e9d923ac 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -2217,6 +2217,10 @@ variable_queue_publish(IsPersistent, Count, VQ) -> variable_queue_publish(IsPersistent, Count, fun (_N, P) -> P end, VQ). variable_queue_publish(IsPersistent, Count, PropFun, VQ) -> + variable_queue_publish(IsPersistent, Count, PropFun, + fun (_N) -> <<>> end, VQ). + +variable_queue_publish(IsPersistent, Count, PropFun, PayloadFun, VQ) -> lists:foldl( fun (N, VQN) -> rabbit_variable_queue:publish( @@ -2225,7 +2229,8 @@ variable_queue_publish(IsPersistent, Count, PropFun, VQ) -> <<>>, #'P_basic'{delivery_mode = case IsPersistent of true -> 2; false -> 1 - end}, <<>>), + end}, + PayloadFun(N)), PropFun(N, #message_properties{}), self(), VQN) end, VQ, lists:seq(1, Count)). @@ -2305,9 +2310,22 @@ test_variable_queue() -> fun test_dropwhile/1, fun test_dropwhile_varying_ram_duration/1, fun test_variable_queue_ack_limiting/1, - fun test_variable_queue_requeue/1]], + fun test_variable_queue_requeue/1, + fun test_variable_queue_fold/1]], passed. +test_variable_queue_fold(VQ0) -> + Count = rabbit_queue_index:next_segment_boundary(0) * 2 + 1, + VQ1 = rabbit_variable_queue:set_ram_duration_target(0, VQ0), + VQ2 = variable_queue_publish( + true, Count, fun (_, P) -> P end, fun erlang:term_to_binary/1, VQ1), + {Acc, VQ3} = rabbit_variable_queue:fold(fun (M, A) -> [M | A] end, [], VQ2), + true = [term_to_binary(N) || N <- lists:seq(Count, 1, -1)] == + [list_to_binary(lists:reverse(P)) || + #basic_message{ content = #content{ payload_fragments_rev = P}} <- + Acc], + VQ3. + test_variable_queue_requeue(VQ0) -> Interval = 50, Count = rabbit_queue_index:next_segment_boundary(0) + 2 * Interval, -- cgit v1.2.1 From 9e552c8457e7188631802d7d226078eda8adc085 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 23 Nov 2012 13:31:21 +0000 Subject: Wake up all queues after policy change. --- src/rabbit_amqqueue.erl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 8ce1160c..5aec3bee 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -284,7 +284,11 @@ store_queue(Q = #amqqueue{durable = false}) -> ok = mnesia:write(rabbit_queue, Q, write), ok. -policy_changed(Q1, Q2) -> rabbit_mirror_queue_misc:update_mirrors(Q1, Q2). +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). start_queue_process(Node, Q) -> {ok, Pid} = rabbit_amqqueue_sup:start_child(Node, [Q]), -- cgit v1.2.1 From 0e455cb23ccc8045361b9769f5fbf378254b4013 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Fri, 23 Nov 2012 13:50:37 +0000 Subject: Propagate API change --- src/rabbit_amqqueue_process.erl | 5 +++++ src/rabbit_backing_queue.erl | 7 ++++++- src/rabbit_mirror_queue_master.erl | 7 ++++++- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index b3c44a50..743d72ef 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1154,6 +1154,11 @@ handle_call({requeue, AckTags, ChPid}, From, State) -> gen_server2:reply(From, ok), noreply(requeue(AckTags, ChPid, State)); +handle_call({fold, Fun, Acc}, _From, State = #q{backing_queue = BQ, + backing_queue_state = BQS}) -> + {Reply, BQS1} = BQ:fold(Fun, Acc, BQS), + reply(Reply, State #q{backing_queue_state = BQS1}); + handle_call(start_mirroring, _From, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> %% lookup again to get policy for init_with_existing_bq diff --git a/src/rabbit_backing_queue.erl b/src/rabbit_backing_queue.erl index 329144f9..f0d0b46c 100644 --- a/src/rabbit_backing_queue.erl +++ b/src/rabbit_backing_queue.erl @@ -159,6 +159,11 @@ %% and were pending acknowledgement. -callback requeue([ack()], state()) -> {msg_ids(), state()}. +%% Fold over all the messages in a queue and return the accumulated +%% results, leaving the queue undisturbed. +-callback fold(fun((rabbit_types:basic_message(), any()) -> any()), + any(), state()) -> {any(), state()}. + %% How long is my queue? -callback len(state()) -> non_neg_integer(). @@ -220,7 +225,7 @@ behaviour_info(callbacks) -> [{start, 1}, {stop, 0}, {init, 3}, {terminate, 2}, {delete_and_terminate, 2}, {purge, 1}, {publish, 4}, {publish_delivered, 4}, {discard, 3}, {drain_confirmed, 1}, {dropwhile, 3}, - {fetch, 2}, {ack, 2}, {foreach_ack, 3}, {requeue, 2}, {len, 1}, + {fetch, 2}, {ack, 2}, {foreach_ack, 3}, {requeue, 2}, {fold, 3}, {len, 1}, {is_empty, 1}, {depth, 1}, {set_ram_duration_target, 2}, {ram_duration, 1}, {needs_timeout, 1}, {timeout, 1}, {handle_pre_hibernate, 1}, {status, 1}, {invoke, 3}, {is_duplicate, 2}] ; diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index 39060c09..15aeea01 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -19,7 +19,7 @@ -export([init/3, terminate/2, delete_and_terminate/2, purge/1, publish/4, publish_delivered/4, discard/3, fetch/2, drop/2, ack/2, - requeue/2, len/1, is_empty/1, depth/1, drain_confirmed/1, + requeue/2, fold/3, len/1, is_empty/1, depth/1, drain_confirmed/1, dropwhile/3, set_ram_duration_target/2, ram_duration/1, needs_timeout/1, timeout/1, handle_pre_hibernate/1, status/1, invoke/3, is_duplicate/2, foreach_ack/3]). @@ -321,6 +321,11 @@ requeue(AckTags, State = #state { gm = GM, ok = gm:broadcast(GM, {requeue, MsgIds}), {MsgIds, State #state { backing_queue_state = BQS1 }}. +fold(Fun, Acc, State = #state { backing_queue = BQ, + backing_queue_state = BQS }) -> + {Result, BQS1} = BQ:fold(Fun, Acc, BQS), + {Result, State #state { backing_queue_state = BQS1 }}. + len(#state { backing_queue = BQ, backing_queue_state = BQS }) -> BQ:len(BQS). -- cgit v1.2.1 From e6b612d8265e81363cee4152c54aa4b0354380af Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Fri, 23 Nov 2012 14:25:56 +0000 Subject: Better type signature ...and rollback amqqueue_process changes --- src/rabbit_amqqueue_process.erl | 5 ----- src/rabbit_backing_queue.erl | 5 +++-- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 743d72ef..b3c44a50 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1154,11 +1154,6 @@ handle_call({requeue, AckTags, ChPid}, From, State) -> gen_server2:reply(From, ok), noreply(requeue(AckTags, ChPid, State)); -handle_call({fold, Fun, Acc}, _From, State = #q{backing_queue = BQ, - backing_queue_state = BQS}) -> - {Reply, BQS1} = BQ:fold(Fun, Acc, BQS), - reply(Reply, State #q{backing_queue_state = BQS1}); - handle_call(start_mirroring, _From, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> %% lookup again to get policy for init_with_existing_bq diff --git a/src/rabbit_backing_queue.erl b/src/rabbit_backing_queue.erl index f0d0b46c..e9c014be 100644 --- a/src/rabbit_backing_queue.erl +++ b/src/rabbit_backing_queue.erl @@ -23,6 +23,7 @@ %% We can't specify a per-queue ack/state with callback signatures -type(ack() :: any()). -type(state() :: any()). +-type(acc() :: any()). -type(msg_ids() :: [rabbit_types:msg_id()]). -type(fetch_result(Ack) :: @@ -161,8 +162,8 @@ %% Fold over all the messages in a queue and return the accumulated %% results, leaving the queue undisturbed. --callback fold(fun((rabbit_types:basic_message(), any()) -> any()), - any(), state()) -> {any(), state()}. +-callback fold(fun((rabbit_types:basic_message(), acc()) -> acc()), + acc(), state()) -> {acc(), state()}. %% How long is my queue? -callback len(state()) -> non_neg_integer(). -- cgit v1.2.1 From 107edf1a80b9e43d960bff62310b1ecee5157499 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 23 Nov 2012 14:43:23 +0000 Subject: refactor: vq:ram_ack_index doesn't need to be a gb_tree a gb_set suffices --- src/rabbit_variable_queue.erl | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 7813aa7b..298c68b6 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -348,7 +348,7 @@ q4 :: ?QUEUE:?QUEUE(), next_seq_id :: seq_id(), pending_ack :: gb_tree(), - ram_ack_index :: gb_tree(), + ram_ack_index :: gb_set(), index_state :: any(), msg_store_clients :: 'undefined' | {{any(), binary()}, {any(), binary()}}, @@ -731,7 +731,7 @@ ram_duration(State = #vqstate { {AvgAckIngressRate, AckIngress1} = update_rate(Now, AckTimestamp, AckInCount, AckIngress), - RamAckCount = gb_trees:size(RamAckIndex), + RamAckCount = gb_sets:size(RamAckIndex), Duration = %% msgs+acks / (msgs+acks/sec) == sec case (AvgEgressRate == 0 andalso AvgIngressRate == 0 andalso @@ -810,7 +810,7 @@ status(#vqstate { {pending_acks , gb_trees:size(PA)}, {target_ram_count , TargetRamCount}, {ram_msg_count , RamMsgCount}, - {ram_ack_count , gb_trees:size(RAI)}, + {ram_ack_count , gb_sets:size(RAI)}, {next_seq_id , NextSeqId}, {persistent_count , PersistentCount}, {avg_ingress_rate , AvgIngressRate}, @@ -1015,7 +1015,7 @@ init(IsDurable, IndexState, DeltaCount, Terms, AsyncCallback, q4 = ?QUEUE:new(), next_seq_id = NextSeqId, pending_ack = gb_trees:empty(), - ram_ack_index = gb_trees:empty(), + ram_ack_index = gb_sets:empty(), index_state = IndexState1, msg_store_clients = {PersistentClient, TransientClient}, durable = IsDurable, @@ -1233,7 +1233,6 @@ maybe_write_to_disk(ForceMsg, ForceIndex, MsgStatus, %%---------------------------------------------------------------------------- record_pending_ack(#msg_status { seq_id = SeqId, - msg_id = MsgId, msg_on_disk = MsgOnDisk } = MsgStatus, State = #vqstate { pending_ack = PA, ram_ack_index = RAI, @@ -1241,7 +1240,7 @@ record_pending_ack(#msg_status { seq_id = SeqId, {AckEntry, RAI1} = case MsgOnDisk of true -> {m(trim_msg_status(MsgStatus)), RAI}; - false -> {MsgStatus, gb_trees:insert(SeqId, MsgId, RAI)} + false -> {MsgStatus, gb_sets:insert(SeqId, RAI)} end, State #vqstate { pending_ack = gb_trees:insert(SeqId, AckEntry, PA), ram_ack_index = RAI1, @@ -1251,7 +1250,7 @@ remove_pending_ack(SeqId, State = #vqstate { pending_ack = PA, ram_ack_index = RAI }) -> {gb_trees:get(SeqId, PA), State #vqstate { pending_ack = gb_trees:delete(SeqId, PA), - ram_ack_index = gb_trees:delete_any(SeqId, RAI) }}. + ram_ack_index = gb_sets:delete_any(SeqId, RAI) }}. purge_pending_ack(KeepPersistent, State = #vqstate { pending_ack = PA, @@ -1262,7 +1261,7 @@ purge_pending_ack(KeepPersistent, accumulate_ack(MsgStatus, Acc) end, accumulate_ack_init(), PA), State1 = State #vqstate { pending_ack = gb_trees:empty(), - ram_ack_index = gb_trees:empty() }, + ram_ack_index = gb_sets:empty() }, case KeepPersistent of true -> case orddict:find(false, MsgIdsByStore) of error -> State1; @@ -1462,7 +1461,7 @@ reduce_memory_use(AlphaBetaFun, BetaDeltaFun, AckFun, }) -> {Reduce, State1 = #vqstate { q2 = Q2, q3 = Q3 }} = - case chunk_size(RamMsgCount + gb_trees:size(RamAckIndex), + case chunk_size(RamMsgCount + gb_sets:size(RamAckIndex), TargetRamCount) of 0 -> {false, State}; %% Reduce memory of pending acks and alphas. The order is @@ -1490,12 +1489,12 @@ limit_ram_acks(0, State) -> {0, State}; limit_ram_acks(Quota, State = #vqstate { pending_ack = PA, ram_ack_index = RAI }) -> - case gb_trees:is_empty(RAI) of + case gb_sets:is_empty(RAI) of true -> {Quota, State}; false -> - {SeqId, MsgId, RAI1} = gb_trees:take_largest(RAI), - MsgStatus = #msg_status { msg_id = MsgId, is_persistent = false} = + {SeqId, RAI1} = gb_sets:take_largest(RAI), + MsgStatus = #msg_status { is_persistent = false} = gb_trees:get(SeqId, PA), {MsgStatus1, State1} = maybe_write_to_disk(true, false, MsgStatus, State), -- cgit v1.2.1 From b872b881f803417ed32e3b31a199a237998eceff Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 23 Nov 2012 14:59:47 +0000 Subject: don't evict messages from RAM when inserting them into pending_ack --- src/rabbit_variable_queue.erl | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 298c68b6..1aea8a3b 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -1232,17 +1232,15 @@ maybe_write_to_disk(ForceMsg, ForceIndex, MsgStatus, %% Internal gubbins for acks %%---------------------------------------------------------------------------- -record_pending_ack(#msg_status { seq_id = SeqId, - msg_on_disk = MsgOnDisk } = MsgStatus, +record_pending_ack(#msg_status { seq_id = SeqId, msg = Msg } = MsgStatus, State = #vqstate { pending_ack = PA, ram_ack_index = RAI, ack_in_counter = AckInCount}) -> - {AckEntry, RAI1} = - case MsgOnDisk of - true -> {m(trim_msg_status(MsgStatus)), RAI}; - false -> {MsgStatus, gb_sets:insert(SeqId, RAI)} - end, - State #vqstate { pending_ack = gb_trees:insert(SeqId, AckEntry, PA), + RAI1 = case Msg of + undefined -> RAI; + _ -> gb_sets:insert(SeqId, RAI) + end, + State #vqstate { pending_ack = gb_trees:insert(SeqId, MsgStatus, PA), ram_ack_index = RAI1, ack_in_counter = AckInCount + 1}. @@ -1494,8 +1492,7 @@ limit_ram_acks(Quota, State = #vqstate { pending_ack = PA, {Quota, State}; false -> {SeqId, RAI1} = gb_sets:take_largest(RAI), - MsgStatus = #msg_status { is_persistent = false} = - gb_trees:get(SeqId, PA), + MsgStatus = gb_trees:get(SeqId, PA), {MsgStatus1, State1} = maybe_write_to_disk(true, false, MsgStatus, State), PA1 = gb_trees:update(SeqId, m(trim_msg_status(MsgStatus1)), PA), -- cgit v1.2.1 From ba1fadc5a1c9f4d8cb1e537bfed1365d1e6bbf37 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 23 Nov 2012 15:09:28 +0000 Subject: more precise signature --- src/rabbit_backing_queue.erl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/rabbit_backing_queue.erl b/src/rabbit_backing_queue.erl index e9c014be..2fc10bb2 100644 --- a/src/rabbit_backing_queue.erl +++ b/src/rabbit_backing_queue.erl @@ -23,7 +23,6 @@ %% We can't specify a per-queue ack/state with callback signatures -type(ack() :: any()). -type(state() :: any()). --type(acc() :: any()). -type(msg_ids() :: [rabbit_types:msg_id()]). -type(fetch_result(Ack) :: @@ -162,8 +161,8 @@ %% Fold over all the messages in a queue and return the accumulated %% results, leaving the queue undisturbed. --callback fold(fun((rabbit_types:basic_message(), acc()) -> acc()), - acc(), state()) -> {acc(), state()}. +-callback fold(fun((rabbit_types:basic_message(), A) -> A), A, state()) + -> {A, state()}. %% How long is my queue? -callback len(state()) -> non_neg_integer(). -- cgit v1.2.1 From 019d70472eae0441a30926e115130bc574f7c406 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 23 Nov 2012 15:28:45 +0000 Subject: cosmetic --- src/rabbit_variable_queue.erl | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 10ada2dd..6aab6bf0 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -687,14 +687,14 @@ fold(Fun, Acc, #vqstate { q1 = Q1, QFun = fun(MsgStatus, {Acc0, State0}) -> {#msg_status { msg = Msg }, State1 } = read_msg(MsgStatus, false, State0), - Acc1 = Fun(Msg, Acc0), - {Acc1, State1} + {Fun(Msg, Acc0), State1} end, {Acc1, State1} = ?QUEUE:foldl(QFun, {Acc, State}, Q4), {Acc2, State2} = ?QUEUE:foldl(QFun, {Acc1, State1}, Q3), - {Acc3, State3} = delta_fold (Fun, Acc2, DeltaSeqId, DeltaSeqIdEnd, State2), + {Acc3, State3} = delta_fold(Fun, Acc2, DeltaSeqId, DeltaSeqIdEnd, State2), {Acc4, State4} = ?QUEUE:foldl(QFun, {Acc3, State3}, Q2), - ?QUEUE:foldl(QFun, {Acc4, State4}, Q1). + {Acc5, State5} = ?QUEUE:foldl(QFun, {Acc4, State4}, Q1), + {Acc5, State5}. len(#vqstate { len = Len }) -> Len. @@ -1442,8 +1442,7 @@ beta_limit(Q) -> delta_limit(?BLANK_DELTA_PATTERN(_X)) -> undefined; delta_limit(#delta { start_seq_id = StartSeqId }) -> StartSeqId. -delta_fold(_Fun, Acc, DeltaSeqId, DeltaSeqIdEnd, State) - when DeltaSeqId == DeltaSeqIdEnd -> +delta_fold(_Fun, Acc, DeltaSeqIdEnd, DeltaSeqIdEnd, State) -> {Acc, State}; delta_fold(Fun, Acc, DeltaSeqId, DeltaSeqIdEnd, #vqstate { index_state = IndexState, -- cgit v1.2.1 From c0064525834826fa355f47fa42a72e269bf6e4f3 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 23 Nov 2012 16:00:41 +0000 Subject: Eager mirror synchronisation. --- src/rabbit_amqqueue.erl | 8 +++++++- src/rabbit_amqqueue_process.erl | 14 ++++++++++++++ src/rabbit_mirror_queue_master.erl | 29 ++++++++++++++++++++++++++++- src/rabbit_mirror_queue_slave.erl | 32 ++++++++++++++++++++++++++++++++ 4 files changed, 81 insertions(+), 2 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 8ce1160c..4d957789 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -31,7 +31,7 @@ -export([notify_down_all/2, limit_all/3]). -export([on_node_down/1]). -export([update/2, store_queue/1, policy_changed/2]). --export([start_mirroring/1, stop_mirroring/1]). +-export([start_mirroring/1, stop_mirroring/1, sync_mirrors/1]). %% internal -export([internal_declare/2, internal_delete/2, run_backing_queue/3, @@ -591,6 +591,12 @@ set_maximum_since_use(QPid, Age) -> start_mirroring(QPid) -> ok = delegate_call(QPid, start_mirroring). stop_mirroring(QPid) -> ok = delegate_call(QPid, stop_mirroring). +sync_mirrors(Name) -> + case lookup(Name) of + {ok, #amqqueue{pid = QPid}} -> delegate_cast(QPid, sync_mirrors); + _ -> ok + end. + on_node_down(Node) -> rabbit_misc:execute_mnesia_tx_with_tail( fun () -> QsDels = diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index b3c44a50..68ae4816 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1300,6 +1300,20 @@ handle_cast({dead_letter, Msgs, Reason}, State = #q{dlx = XName}) -> cleanup_after_confirm([AckTag || {_, AckTag} <- Msgs], State) end; +handle_cast(sync_mirrors, + State = #q{q = #amqqueue{name = Name}, + backing_queue = BQ, + backing_queue_state = BQS}) -> + case BQ of + rabbit_mirror_queue_master -> + {ok, #amqqueue{slave_pids = SPids, sync_slave_pids = SSPids}} = + rabbit_amqqueue:lookup(Name), + rabbit_mirror_queue_master:sync_mirrors(SPids -- SSPids, BQS); + _ -> + ok + end, + noreply(State); + handle_cast(wake_up, State) -> noreply(State). diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index 39060c09..bc2d21ac 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -28,7 +28,7 @@ -export([promote_backing_queue_state/7, sender_death_fun/0, depth_fun/0]). --export([init_with_existing_bq/3, stop_mirroring/1]). +-export([init_with_existing_bq/3, stop_mirroring/1, sync_mirrors/2]). -behaviour(rabbit_backing_queue). @@ -127,6 +127,33 @@ stop_mirroring(State = #state { coordinator = CPid, stop_all_slaves(shutdown, State), {BQ, BQS}. +sync_mirrors(SPids, #state { backing_queue = BQ, + backing_queue_state = BQS }) -> + Ref = make_ref(), + SPidsMRefs = [begin + SPid ! {sync_start, Ref, self()}, + MRef = erlang:monitor(process, SPid), + {SPid, MRef} + end || SPid <- SPids], + %% We wait for a reply from the slaves so that we know they are in + %% a receive block and will thus receive messages we send to them + %% *without* those messages ending up in their gen_server2 pqueue. + SPids1 = [SPid1 || {SPid, MRef} <- SPidsMRefs, + SPid1 <- [receive + {'DOWN', MRef, process, SPid, _Reason} -> + dead; + {sync_ready, Ref, SPid} -> + SPid + end], + SPid1 =/= dead], + [erlang:demonitor(MRef) || {_, MRef} <- SPidsMRefs], + BQ:fold(fun (M = #basic_message{}, none) -> + [SPid ! {sync_message, Ref, M} || SPid <- SPids1], + none + end, none, BQS), + [SPid ! {sync_complete, Ref} || SPid <- SPids1], + ok. + terminate({shutdown, dropped} = Reason, State = #state { backing_queue = BQ, backing_queue_state = BQS }) -> diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 3ad8eb77..cd2b205c 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -264,6 +264,14 @@ handle_info({bump_credit, Msg}, State) -> credit_flow:handle_bump_msg(Msg), noreply(State); +handle_info({sync_start, Ref, MPid}, + State = #state { backing_queue = BQ, + backing_queue_state = BQS }) -> + MRef = erlang:monitor(process, MPid), + MPid ! {sync_ready, Ref, self()}, + {_MsgCount, BQS1} = BQ:purge(BQS), + noreply(sync_loop(Ref, MRef, State#state{backing_queue_state = BQS1})); + handle_info(Msg, State) -> {stop, {unexpected_info, Msg}, State}. @@ -830,3 +838,27 @@ record_synchronised(#amqqueue { name = QName }) -> ok end end). + +sync_loop(Ref, MRef, State = #state{backing_queue = BQ, + backing_queue_state = BQS}) -> + receive + {'DOWN', MRef, process, _MPid, _Reason} -> + %% If the master dies half way we are not in the usual + %% half-synced state (with messages nearer the tail of the + %% queue; instead we have ones nearer the head. If we then + %% sync with a newly promoted master, or even just receive + %% messages from it, we have a hole in the middle. So the + %% only thing to do here is purge.) + State#state{backing_queue_state = BQ:purge(BQS)}; + {sync_complete, Ref} -> + erlang:demonitor(MRef), + set_delta(0, State); + {sync_message, Ref, M} -> + %% TODO expiry / delivered need fixing + Props = #message_properties{expiry = undefined, + needs_confirming = false, + delivered = false}, + BQS1 = BQ:publish(M, Props, none, BQS), + sync_loop(Ref, MRef, State#state{backing_queue_state = BQS1}) + end. + -- cgit v1.2.1 From 38dad4b998445675d1f91d2cf0d79303ac2ce9e0 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Fri, 23 Nov 2012 16:28:26 +0000 Subject: Add message properties to backing queue fold --- src/rabbit_backing_queue.erl | 5 +++-- src/rabbit_backing_queue_qc.erl | 4 ++-- src/rabbit_tests.erl | 4 +++- src/rabbit_variable_queue.erl | 8 ++++---- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/rabbit_backing_queue.erl b/src/rabbit_backing_queue.erl index 2fc10bb2..24dd36e1 100644 --- a/src/rabbit_backing_queue.erl +++ b/src/rabbit_backing_queue.erl @@ -161,8 +161,9 @@ %% Fold over all the messages in a queue and return the accumulated %% results, leaving the queue undisturbed. --callback fold(fun((rabbit_types:basic_message(), A) -> A), A, state()) - -> {A, state()}. +-callback fold(fun(({rabbit_types:basic_message(), + rabbit_types:message_properties()}, A) -> A), + A, state()) -> {A, state()}. %% How long is my queue? -callback len(state()) -> non_neg_integer(). diff --git a/src/rabbit_backing_queue_qc.erl b/src/rabbit_backing_queue_qc.erl index 00e17d02..a6d9b59a 100644 --- a/src/rabbit_backing_queue_qc.erl +++ b/src/rabbit_backing_queue_qc.erl @@ -331,8 +331,8 @@ postcondition(S, {call, ?BQMOD, drain_confirmed, _Args}, Res) -> postcondition(S, {call, ?BQMOD, fold, _Args}, {Res, _BQ}) -> #state{messages = Messages} = S, - lists:foldl(fun ({_SeqId, {_MsgProps, Msg}}, Acc) -> - foldfun(Msg, Acc) + lists:foldl(fun ({_SeqId, {MsgProps, Msg}}, Acc) -> + foldfun({Msg, MsgProps}, Acc) end, foldacc(), gb_trees:to_list(Messages)) =:= Res; postcondition(#state{bqstate = BQ, len = Len}, {call, _M, _F, _A}, _Res) -> diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index e9d923ac..dffca79d 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -2319,7 +2319,9 @@ test_variable_queue_fold(VQ0) -> VQ1 = rabbit_variable_queue:set_ram_duration_target(0, VQ0), VQ2 = variable_queue_publish( true, Count, fun (_, P) -> P end, fun erlang:term_to_binary/1, VQ1), - {Acc, VQ3} = rabbit_variable_queue:fold(fun (M, A) -> [M | A] end, [], VQ2), + {Acc, VQ3} = rabbit_variable_queue:fold(fun ({M, _}, A) -> + [M | A] + end, [], VQ2), true = [term_to_binary(N) || N <- lists:seq(Count, 1, -1)] == [list_to_binary(lists:reverse(P)) || #basic_message{ content = #content{ payload_fragments_rev = P}} <- diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 6aab6bf0..644ba182 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -685,9 +685,9 @@ fold(Fun, Acc, #vqstate { q1 = Q1, q3 = Q3, q4 = Q4 } = State) -> QFun = fun(MsgStatus, {Acc0, State0}) -> - {#msg_status { msg = Msg }, State1 } = + {#msg_status { msg = Msg, msg_props = MsgProps }, State1 } = read_msg(MsgStatus, false, State0), - {Fun(Msg, Acc0), State1} + {Fun({Msg, MsgProps}, Acc0), State1} end, {Acc1, State1} = ?QUEUE:foldl(QFun, {Acc, State}, Q4), {Acc2, State2} = ?QUEUE:foldl(QFun, {Acc1, State1}, Q3), @@ -1453,11 +1453,11 @@ delta_fold(Fun, Acc, DeltaSeqId, DeltaSeqIdEnd, {List, IndexState1} = rabbit_queue_index:read(DeltaSeqId, DeltaSeqId1, IndexState), {Acc1, MSCState1} = - lists:foldl(fun ({MsgId, _SeqId, _MsgProps, IsPersistent, + lists:foldl(fun ({MsgId, _SeqId, MsgProps, IsPersistent, _IsDelivered}, {Acc0, MSCState0}) -> {{ok, Msg = #basic_message {}}, MSCState1} = msg_store_read(MSCState0, IsPersistent, MsgId), - {Fun(Msg, Acc0), MSCState1} + {Fun({Msg, MsgProps}, Acc0), MSCState1} end, {Acc, MSCState}, List), delta_fold(Fun, Acc1, DeltaSeqId1, DeltaSeqIdEnd, State #vqstate { index_state = IndexState1, -- cgit v1.2.1 From 9746f148c5641137438ac79b31b7381e005343e2 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 23 Nov 2012 16:33:30 +0000 Subject: Cosmetic / clearer. --- src/rabbit_mirror_queue_slave.erl | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index cd2b205c..ae069898 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -840,7 +840,7 @@ record_synchronised(#amqqueue { name = QName }) -> end). sync_loop(Ref, MRef, State = #state{backing_queue = BQ, - backing_queue_state = BQS}) -> + backing_queue_state = BQS}) -> receive {'DOWN', MRef, process, _MPid, _Reason} -> %% If the master dies half way we are not in the usual @@ -854,11 +854,10 @@ sync_loop(Ref, MRef, State = #state{backing_queue = BQ, erlang:demonitor(MRef), set_delta(0, State); {sync_message, Ref, M} -> - %% TODO expiry / delivered need fixing + %% TODO expiry needs fixing Props = #message_properties{expiry = undefined, needs_confirming = false, - delivered = false}, + delivered = true}, BQS1 = BQ:publish(M, Props, none, BQS), sync_loop(Ref, MRef, State#state{backing_queue_state = BQS1}) end. - -- cgit v1.2.1 From 4f02718feda2495c6ea02ea93ce4de896b9dc102 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 23 Nov 2012 16:52:59 +0000 Subject: Log progress, and an important optimisation. --- src/rabbit_amqqueue_process.erl | 2 +- src/rabbit_mirror_queue_master.erl | 29 ++++++++++++++++++++++------- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 68ae4816..07f4c3b1 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1308,7 +1308,7 @@ handle_cast(sync_mirrors, rabbit_mirror_queue_master -> {ok, #amqqueue{slave_pids = SPids, sync_slave_pids = SSPids}} = rabbit_amqqueue:lookup(Name), - rabbit_mirror_queue_master:sync_mirrors(SPids -- SSPids, BQS); + rabbit_mirror_queue_master:sync_mirrors(SPids -- SSPids, Name, BQS); _ -> ok end, diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index bc2d21ac..05075d0f 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -28,7 +28,7 @@ -export([promote_backing_queue_state/7, sender_death_fun/0, depth_fun/0]). --export([init_with_existing_bq/3, stop_mirroring/1, sync_mirrors/2]). +-export([init_with_existing_bq/3, stop_mirroring/1, sync_mirrors/3]). -behaviour(rabbit_backing_queue). @@ -127,8 +127,14 @@ stop_mirroring(State = #state { coordinator = CPid, stop_all_slaves(shutdown, State), {BQ, BQS}. -sync_mirrors(SPids, #state { backing_queue = BQ, - backing_queue_state = BQS }) -> +sync_mirrors([], Name, _State) -> + rabbit_log:info("Synchronising ~s: nothing to do~n", + [rabbit_misc:rs(Name)]), + ok; +sync_mirrors(SPids, Name, #state { backing_queue = BQ, + backing_queue_state = BQS }) -> + rabbit_log:info("Synchronising ~s with slaves ~p~n", + [rabbit_misc:rs(Name), SPids]), Ref = make_ref(), SPidsMRefs = [begin SPid ! {sync_start, Ref, self()}, @@ -147,11 +153,20 @@ sync_mirrors(SPids, #state { backing_queue = BQ, end], SPid1 =/= dead], [erlang:demonitor(MRef) || {_, MRef} <- SPidsMRefs], - BQ:fold(fun (M = #basic_message{}, none) -> - [SPid ! {sync_message, Ref, M} || SPid <- SPids1], - none - end, none, BQS), + {Total, _BQS} = + BQ:fold(fun (M = #basic_message{}, I) -> + [SPid ! {sync_message, Ref, M} || SPid <- SPids1], + case I rem 1000 of + 0 -> rabbit_log:info( + "Synchronising ~s: ~p messages~n", + [rabbit_misc:rs(Name), I]); + _ -> ok + end, + I + 1 + end, 0, BQS), [SPid ! {sync_complete, Ref} || SPid <- SPids1], + rabbit_log:info("Synchronising ~s: ~p messages; complete~n", + [rabbit_misc:rs(Name), Total]), ok. terminate({shutdown, dropped} = Reason, -- cgit v1.2.1 From e2fb9a87298b9131d24795cf04ce164eb240d6ef Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 23 Nov 2012 17:19:03 +0000 Subject: Flow control for the sync process. --- src/rabbit_mirror_queue_master.erl | 15 ++++++++++++++- src/rabbit_mirror_queue_slave.erl | 13 +++++++++---- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index 05075d0f..dadaef1d 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -155,7 +155,11 @@ sync_mirrors(SPids, Name, #state { backing_queue = BQ, [erlang:demonitor(MRef) || {_, MRef} <- SPidsMRefs], {Total, _BQS} = BQ:fold(fun (M = #basic_message{}, I) -> - [SPid ! {sync_message, Ref, M} || SPid <- SPids1], + wait_for_credit(), + [begin + credit_flow:send(SPid, ?CREDIT_DISC_BOUND), + SPid ! {sync_message, Ref, M} + end || SPid <- SPids1], case I rem 1000 of 0 -> rabbit_log:info( "Synchronising ~s: ~p messages~n", @@ -169,6 +173,15 @@ sync_mirrors(SPids, Name, #state { backing_queue = BQ, [rabbit_misc:rs(Name), Total]), ok. +wait_for_credit() -> + case credit_flow:blocked() of + true -> receive + {bump_credit, Msg} -> credit_flow:handle_bump_msg(Msg), + wait_for_credit() + end; + false -> ok + end. + terminate({shutdown, dropped} = Reason, State = #state { backing_queue = BQ, backing_queue_state = BQS }) -> diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index ae069898..c1d2e8e4 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -270,7 +270,8 @@ handle_info({sync_start, Ref, MPid}, MRef = erlang:monitor(process, MPid), MPid ! {sync_ready, Ref, self()}, {_MsgCount, BQS1} = BQ:purge(BQS), - noreply(sync_loop(Ref, MRef, State#state{backing_queue_state = BQS1})); + noreply( + sync_loop(Ref, MRef, MPid, State#state{backing_queue_state = BQS1})); handle_info(Msg, State) -> {stop, {unexpected_info, Msg}, State}. @@ -839,10 +840,10 @@ record_synchronised(#amqqueue { name = QName }) -> end end). -sync_loop(Ref, MRef, State = #state{backing_queue = BQ, +sync_loop(Ref, MRef, MPid, State = #state{backing_queue = BQ, backing_queue_state = BQS}) -> receive - {'DOWN', MRef, process, _MPid, _Reason} -> + {'DOWN', MRef, process, MPid, _Reason} -> %% If the master dies half way we are not in the usual %% half-synced state (with messages nearer the tail of the %% queue; instead we have ones nearer the head. If we then @@ -850,14 +851,18 @@ sync_loop(Ref, MRef, State = #state{backing_queue = BQ, %% messages from it, we have a hole in the middle. So the %% only thing to do here is purge.) State#state{backing_queue_state = BQ:purge(BQS)}; + {bump_credit, Msg} -> + credit_flow:handle_bump_msg(Msg), + sync_loop(Ref, MRef, MPid, State); {sync_complete, Ref} -> erlang:demonitor(MRef), set_delta(0, State); {sync_message, Ref, M} -> + credit_flow:ack(MPid, ?CREDIT_DISC_BOUND), %% TODO expiry needs fixing Props = #message_properties{expiry = undefined, needs_confirming = false, delivered = true}, BQS1 = BQ:publish(M, Props, none, BQS), - sync_loop(Ref, MRef, State#state{backing_queue_state = BQS1}) + sync_loop(Ref, MRef, MPid, State#state{backing_queue_state = BQS1}) end. -- cgit v1.2.1 From c08f72e5ca8043eb1199acd09fbf229551e516e2 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 23 Nov 2012 17:28:35 +0000 Subject: Oops --- src/rabbit_mirror_queue_slave.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index c1d2e8e4..a8615cee 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -850,7 +850,8 @@ sync_loop(Ref, MRef, MPid, State = #state{backing_queue = BQ, %% sync with a newly promoted master, or even just receive %% messages from it, we have a hole in the middle. So the %% only thing to do here is purge.) - State#state{backing_queue_state = BQ:purge(BQS)}; + {_MsgCount, BQS1} = BQ:purge(BQS), + State#state{backing_queue_state = BQS1}; {bump_credit, Msg} -> credit_flow:handle_bump_msg(Msg), sync_loop(Ref, MRef, MPid, State); -- cgit v1.2.1 From fdd2a4bd4c3f84ff80d6450fd23d50f29be24acc Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 23 Nov 2012 17:44:01 +0000 Subject: Fix expiry --- src/rabbit_mirror_queue_master.erl | 4 ++-- src/rabbit_mirror_queue_slave.erl | 10 ++++------ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index a9f5e5ac..16642604 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -154,11 +154,11 @@ sync_mirrors(SPids, Name, #state { backing_queue = BQ, SPid1 =/= dead], [erlang:demonitor(MRef) || {_, MRef} <- SPidsMRefs], {Total, _BQS} = - BQ:fold(fun (M = #basic_message{}, I) -> + BQ:fold(fun ({Msg, MsgProps}, I) -> wait_for_credit(), [begin credit_flow:send(SPid, ?CREDIT_DISC_BOUND), - SPid ! {sync_message, Ref, M} + SPid ! {sync_message, Ref, Msg, MsgProps} end || SPid <- SPids1], case I rem 1000 of 0 -> rabbit_log:info( diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index a8615cee..d408c56e 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -858,12 +858,10 @@ sync_loop(Ref, MRef, MPid, State = #state{backing_queue = BQ, {sync_complete, Ref} -> erlang:demonitor(MRef), set_delta(0, State); - {sync_message, Ref, M} -> + {sync_message, Ref, Msg, Props0} -> credit_flow:ack(MPid, ?CREDIT_DISC_BOUND), - %% TODO expiry needs fixing - Props = #message_properties{expiry = undefined, - needs_confirming = false, - delivered = true}, - BQS1 = BQ:publish(M, Props, none, BQS), + Props = Props0#message_properties{needs_confirming = false, + delivered = true}, + BQS1 = BQ:publish(Msg, Props, none, BQS), sync_loop(Ref, MRef, MPid, State#state{backing_queue_state = BQS1}) end. -- cgit v1.2.1 From dfb04276e78cc67bffa869effe34c77d82cf039c Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 24 Nov 2012 10:14:09 +0000 Subject: 'pid' is a valid INFO_KEY and some code, namely the clustering tests, rely on that --- src/rabbit_amqqueue_process.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 476f806d..5ddafba8 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -108,7 +108,7 @@ owner_pid ]). --define(INFO_KEYS, ?CREATION_EVENT_KEYS ++ ?STATISTICS_KEYS -- [name]). +-define(INFO_KEYS, [pid | ?CREATION_EVENT_KEYS ++ ?STATISTICS_KEYS -- [name]]). %%---------------------------------------------------------------------------- -- cgit v1.2.1 From ab9ab903487244bcbcb50b982f1c44cfcfbe20f0 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 26 Nov 2012 10:44:36 +0000 Subject: mark all messages enqueued in the slave as 'delivered' which is a much better than the set_delivered logic, which we can now get rid of. In doing so it also becomes clear that having the 'delivered' flag in the #message_properties is less than ideal. It is mutable and we never bothered updating vq s.t. it sets the flag correctly. So lets get rid of it and add a parameter to bq:publish instead --- include/rabbit.hrl | 3 +-- src/rabbit_amqqueue_process.erl | 15 +++++++-------- src/rabbit_backing_queue.erl | 6 +++--- src/rabbit_mirror_queue_master.erl | 39 +++++++++++++------------------------- src/rabbit_mirror_queue_slave.erl | 2 +- src/rabbit_tests.erl | 2 +- src/rabbit_variable_queue.erl | 30 ++++++++++++++--------------- 7 files changed, 40 insertions(+), 57 deletions(-) diff --git a/include/rabbit.hrl b/include/rabbit.hrl index b2832b45..0ccb80bf 100644 --- a/include/rabbit.hrl +++ b/include/rabbit.hrl @@ -78,8 +78,7 @@ -record(event, {type, props, timestamp}). --record(message_properties, {expiry, needs_confirming = false, - delivered = false}). +-record(message_properties, {expiry, needs_confirming = false}). -record(plugin, {name, %% atom() version, %% string() diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 5ddafba8..bfc0f418 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -540,8 +540,8 @@ run_message_queue(State) -> State2. attempt_delivery(Delivery = #delivery{sender = SenderPid, message = Message}, - Props = #message_properties{delivered = Delivered}, - State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> + Props, Delivered, State = #q{backing_queue = BQ, + backing_queue_state = BQS}) -> case BQ:is_duplicate(Message, BQS) of {false, BQS1} -> deliver_msgs_to_consumers( @@ -563,15 +563,15 @@ attempt_delivery(Delivery = #delivery{sender = SenderPid, message = Message}, deliver_or_enqueue(Delivery = #delivery{message = Message, sender = SenderPid}, Delivered, State) -> {Confirm, State1} = send_or_record_confirm(Delivery, State), - Props = message_properties(Message, Confirm, Delivered, State), - case attempt_delivery(Delivery, Props, State1) of + Props = message_properties(Message, Confirm, State), + case attempt_delivery(Delivery, Props, Delivered, State1) of {true, State2} -> State2; %% The next one is an optimisation {false, State2 = #q{ttl = 0, dlx = undefined}} -> discard(Delivery, State2); {false, State2 = #q{backing_queue = BQ, backing_queue_state = BQS}} -> - BQS1 = BQ:publish(Message, Props, SenderPid, BQS), + BQS1 = BQ:publish(Message, Props, Delivered, SenderPid, BQS), ensure_ttl_timer(Props#message_properties.expiry, State2#q{backing_queue_state = BQS1}) end. @@ -704,10 +704,9 @@ subtract_acks(ChPid, AckTags, State, Fun) -> Fun(State) end. -message_properties(Message, Confirm, Delivered, #q{ttl = TTL}) -> +message_properties(Message, Confirm, #q{ttl = TTL}) -> #message_properties{expiry = calculate_msg_expiry(Message, TTL), - needs_confirming = Confirm == eventually, - delivered = Delivered}. + needs_confirming = Confirm == eventually}. calculate_msg_expiry(#basic_message{content = Content}, TTL) -> #content{properties = Props} = diff --git a/src/rabbit_backing_queue.erl b/src/rabbit_backing_queue.erl index 9e99ca5e..26c63b08 100644 --- a/src/rabbit_backing_queue.erl +++ b/src/rabbit_backing_queue.erl @@ -78,8 +78,8 @@ %% Publish a message. -callback publish(rabbit_types:basic_message(), - rabbit_types:message_properties(), pid(), state()) -> - state(). + rabbit_types:message_properties(), boolean(), pid(), + state()) -> state(). %% Called for messages which have already been passed straight %% out to a client. The queue will be empty for these calls @@ -219,7 +219,7 @@ behaviour_info(callbacks) -> [{start, 1}, {stop, 0}, {init, 3}, {terminate, 2}, - {delete_and_terminate, 2}, {purge, 1}, {publish, 4}, + {delete_and_terminate, 2}, {purge, 1}, {publish, 5}, {publish_delivered, 4}, {discard, 3}, {drain_confirmed, 1}, {dropwhile, 3}, {fetch, 2}, {ack, 2}, {foreach_ack, 3}, {requeue, 2}, {fold, 3}, {len, 1}, {is_empty, 1}, {depth, 1}, {set_ram_duration_target, 2}, diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index 8fcd1893..c8a361b1 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -17,7 +17,7 @@ -module(rabbit_mirror_queue_master). -export([init/3, terminate/2, delete_and_terminate/2, - purge/1, publish/4, publish_delivered/4, + purge/1, publish/5, publish_delivered/4, discard/3, fetch/2, drop/2, ack/2, requeue/2, fold/3, len/1, is_empty/1, depth/1, drain_confirmed/1, dropwhile/3, set_ram_duration_target/2, ram_duration/1, @@ -38,7 +38,6 @@ coordinator, backing_queue, backing_queue_state, - set_delivered, seen_status, confirmed, ack_msg_id, @@ -55,7 +54,6 @@ coordinator :: pid(), backing_queue :: atom(), backing_queue_state :: any(), - set_delivered :: non_neg_integer(), seen_status :: dict(), confirmed :: [rabbit_guid:guid()], ack_msg_id :: dict(), @@ -114,7 +112,6 @@ init_with_existing_bq(Q = #amqqueue{name = QName}, BQ, BQS) -> coordinator = CPid, backing_queue = BQ, backing_queue_state = BQS, - set_delivered = 0, seen_status = dict:new(), confirmed = [], ack_msg_id = dict:new(), @@ -136,8 +133,8 @@ terminate({shutdown, dropped} = Reason, %% in without this node being restarted. Thus we must do the full %% blown delete_and_terminate now, but only locally: we do not %% broadcast delete_and_terminate. - State #state { backing_queue_state = BQ:delete_and_terminate(Reason, BQS), - set_delivered = 0 }; + State#state{backing_queue_state = BQ:delete_and_terminate(Reason, BQS)}; + terminate(Reason, State = #state { backing_queue = BQ, backing_queue_state = BQS }) -> %% Backing queue termination. The queue is going down but @@ -148,8 +145,7 @@ terminate(Reason, delete_and_terminate(Reason, State = #state { backing_queue = BQ, backing_queue_state = BQS }) -> stop_all_slaves(Reason, State), - State #state { backing_queue_state = BQ:delete_and_terminate(Reason, BQS), - set_delivered = 0 }. + State#state{backing_queue_state = BQ:delete_and_terminate(Reason, BQS)}. stop_all_slaves(Reason, #state{gm = GM}) -> Info = gm:info(GM), @@ -175,17 +171,16 @@ purge(State = #state { gm = GM, backing_queue_state = BQS }) -> ok = gm:broadcast(GM, {drop, 0, BQ:len(BQS), false}), {Count, BQS1} = BQ:purge(BQS), - {Count, State #state { backing_queue_state = BQS1, - set_delivered = 0 }}. + {Count, State #state { backing_queue_state = BQS1 }}. -publish(Msg = #basic_message { id = MsgId }, MsgProps, ChPid, +publish(Msg = #basic_message { id = MsgId }, MsgProps, IsDelivered, ChPid, State = #state { gm = GM, seen_status = SS, backing_queue = BQ, backing_queue_state = BQS }) -> false = dict:is_key(MsgId, SS), %% ASSERTION ok = gm:broadcast(GM, {publish, ChPid, MsgProps, Msg}), - BQS1 = BQ:publish(Msg, MsgProps, ChPid, BQS), + BQS1 = BQ:publish(Msg, MsgProps, IsDelivered, ChPid, BQS), ensure_monitoring(ChPid, State #state { backing_queue_state = BQS1 }). publish_delivered(Msg = #basic_message { id = MsgId }, MsgProps, @@ -224,7 +219,6 @@ discard(MsgId, ChPid, State = #state { gm = GM, dropwhile(Pred, AckRequired, State = #state{gm = GM, backing_queue = BQ, - set_delivered = SetDelivered, backing_queue_state = BQS }) -> Len = BQ:len(BQS), {Next, Msgs, BQS1} = BQ:dropwhile(Pred, AckRequired, BQS), @@ -234,9 +228,7 @@ dropwhile(Pred, AckRequired, 0 -> ok; _ -> ok = gm:broadcast(GM, {drop, Len1, Dropped, AckRequired}) end, - SetDelivered1 = lists:max([0, SetDelivered - Dropped]), - {Next, Msgs, State #state { backing_queue_state = BQS1, - set_delivered = SetDelivered1 } }. + {Next, Msgs, State #state { backing_queue_state = BQS1 } }. drain_confirmed(State = #state { backing_queue = BQ, backing_queue_state = BQS, @@ -269,16 +261,14 @@ drain_confirmed(State = #state { backing_queue = BQ, confirmed = [] }}. fetch(AckRequired, State = #state { backing_queue = BQ, - backing_queue_state = BQS, - set_delivered = SetDelivered }) -> + backing_queue_state = BQS }) -> {Result, BQS1} = BQ:fetch(AckRequired, BQS), State1 = State #state { backing_queue_state = BQS1 }, case Result of empty -> {Result, State1}; - {Message, IsDelivered, AckTag} -> - {{Message, IsDelivered orelse SetDelivered > 0, AckTag}, - drop(Message#basic_message.id, AckTag, State1)} + {#basic_message{id = MsgId}, _IsDelivered, AckTag} -> + {Result, drop(MsgId, AckTag, State1)} end. drop(AckRequired, State = #state { backing_queue = BQ, @@ -416,7 +406,6 @@ promote_backing_queue_state(CPid, BQ, BQS, GM, AckTags, SeenStatus, KS) -> coordinator = CPid, backing_queue = BQ, backing_queue_state = BQS1, - set_delivered = Len, seen_status = SeenStatus, confirmed = [], ack_msg_id = dict:new(), @@ -451,14 +440,12 @@ depth_fun() -> %% Helpers %% --------------------------------------------------------------------------- -drop(MsgId, AckTag, State = #state { set_delivered = SetDelivered, - ack_msg_id = AM, +drop(MsgId, AckTag, State = #state { ack_msg_id = AM, gm = GM, backing_queue = BQ, backing_queue_state = BQS }) -> ok = gm:broadcast(GM, {drop, BQ:len(BQS), 1, AckTag =/= undefined}), - State #state { set_delivered = lists:max([0, SetDelivered - 1]), - ack_msg_id = maybe_store_acktag(AckTag, MsgId, AM) }. + State #state { ack_msg_id = maybe_store_acktag(AckTag, MsgId, AM) }. maybe_store_acktag(undefined, _MsgId, AM) -> AM; maybe_store_acktag(AckTag, MsgId, AM) -> dict:store(AckTag, MsgId, AM). diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index cb7a2135..752dac89 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -703,7 +703,7 @@ process_instruction({publish, ChPid, MsgProps, Msg = #basic_message { id = MsgId }}, State) -> State1 = #state { backing_queue = BQ, backing_queue_state = BQS } = publish_or_discard(published, ChPid, MsgId, State), - BQS1 = BQ:publish(Msg, MsgProps, ChPid, BQS), + BQS1 = BQ:publish(Msg, MsgProps, true, ChPid, BQS), {ok, State1 #state { backing_queue_state = BQS1 }}; process_instruction({publish_delivered, ChPid, MsgProps, Msg = #basic_message { id = MsgId }}, State) -> diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 81180ebe..4a989424 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -2230,7 +2230,7 @@ variable_queue_publish(IsPersistent, Count, PropFun, PayloadFun, VQ) -> false -> 1 end}, PayloadFun(N)), - PropFun(N, #message_properties{}), self(), VQN) + PropFun(N, #message_properties{}), false, self(), VQN) end, VQ, lists:seq(1, Count)). variable_queue_fetch(Count, IsPersistent, IsDelivered, Len, VQ) -> diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index e2566e10..be340cdd 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -17,7 +17,7 @@ -module(rabbit_variable_queue). -export([init/3, terminate/2, delete_and_terminate/2, purge/1, - publish/4, publish_delivered/4, discard/3, drain_confirmed/1, + publish/5, publish_delivered/4, discard/3, drain_confirmed/1, dropwhile/3, fetch/2, drop/2, ack/2, requeue/2, fold/3, len/1, is_empty/1, depth/1, set_ram_duration_target/2, ram_duration/1, needs_timeout/1, timeout/1, handle_pre_hibernate/1, status/1, invoke/3, @@ -520,16 +520,16 @@ purge(State = #vqstate { q4 = Q4, publish(Msg = #basic_message { is_persistent = IsPersistent, id = MsgId }, MsgProps = #message_properties { needs_confirming = NeedsConfirming }, - _ChPid, State = #vqstate { q1 = Q1, q3 = Q3, q4 = Q4, - next_seq_id = SeqId, - len = Len, - in_counter = InCount, - persistent_count = PCount, - durable = IsDurable, - ram_msg_count = RamMsgCount, - unconfirmed = UC }) -> + IsDelivered, _ChPid, State = #vqstate { q1 = Q1, q3 = Q3, q4 = Q4, + next_seq_id = SeqId, + len = Len, + in_counter = InCount, + persistent_count = PCount, + durable = IsDurable, + ram_msg_count = RamMsgCount, + unconfirmed = UC }) -> IsPersistent1 = IsDurable andalso IsPersistent, - MsgStatus = msg_status(IsPersistent1, SeqId, Msg, MsgProps), + MsgStatus = msg_status(IsPersistent1, IsDelivered, SeqId, Msg, MsgProps), {MsgStatus1, State1} = maybe_write_to_disk(false, false, MsgStatus, State), State2 = case ?QUEUE:is_empty(Q3) of false -> State1 #vqstate { q1 = ?QUEUE:in(m(MsgStatus1), Q1) }; @@ -556,8 +556,7 @@ publish_delivered(Msg = #basic_message { is_persistent = IsPersistent, durable = IsDurable, unconfirmed = UC }) -> IsPersistent1 = IsDurable andalso IsPersistent, - MsgStatus = (msg_status(IsPersistent1, SeqId, Msg, MsgProps)) - #msg_status { is_delivered = true }, + MsgStatus = msg_status(IsPersistent1, true, SeqId, Msg, MsgProps), {MsgStatus1, State1} = maybe_write_to_disk(false, false, MsgStatus, State), State2 = record_pending_ack(m(MsgStatus1), State1), PCount1 = PCount + one_if(IsPersistent1), @@ -891,11 +890,10 @@ gb_sets_maybe_insert(false, _Val, Set) -> Set; %% when requeueing, we re-add a msg_id to the unconfirmed set gb_sets_maybe_insert(true, Val, Set) -> gb_sets:add(Val, Set). -msg_status(IsPersistent, SeqId, Msg = #basic_message { id = MsgId }, - MsgProps = #message_properties { delivered = Delivered }) -> - %% TODO would it make sense to remove #msg_status.is_delivered? +msg_status(IsPersistent, IsDelivered, SeqId, + Msg = #basic_message { id = MsgId }, MsgProps) -> #msg_status { seq_id = SeqId, msg_id = MsgId, msg = Msg, - is_persistent = IsPersistent, is_delivered = Delivered, + is_persistent = IsPersistent, is_delivered = IsDelivered, msg_on_disk = false, index_on_disk = false, msg_props = MsgProps }. -- cgit v1.2.1 From 33394fa90b5b5f5596b8dc0e34b58879230b6ee5 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Mon, 26 Nov 2012 11:36:57 +0000 Subject: Untuple fold function --- src/rabbit_backing_queue.erl | 4 ++-- src/rabbit_backing_queue_qc.erl | 2 +- src/rabbit_tests.erl | 2 +- src/rabbit_variable_queue.erl | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/rabbit_backing_queue.erl b/src/rabbit_backing_queue.erl index 24dd36e1..ffa716b6 100644 --- a/src/rabbit_backing_queue.erl +++ b/src/rabbit_backing_queue.erl @@ -161,8 +161,8 @@ %% Fold over all the messages in a queue and return the accumulated %% results, leaving the queue undisturbed. --callback fold(fun(({rabbit_types:basic_message(), - rabbit_types:message_properties()}, A) -> A), +-callback fold(fun((rabbit_types:basic_message(), + rabbit_types:message_properties(), A) -> A), A, state()) -> {A, state()}. %% How long is my queue? diff --git a/src/rabbit_backing_queue_qc.erl b/src/rabbit_backing_queue_qc.erl index a6d9b59a..fb8b82ea 100644 --- a/src/rabbit_backing_queue_qc.erl +++ b/src/rabbit_backing_queue_qc.erl @@ -332,7 +332,7 @@ postcondition(S, {call, ?BQMOD, drain_confirmed, _Args}, Res) -> postcondition(S, {call, ?BQMOD, fold, _Args}, {Res, _BQ}) -> #state{messages = Messages} = S, lists:foldl(fun ({_SeqId, {MsgProps, Msg}}, Acc) -> - foldfun({Msg, MsgProps}, Acc) + foldfun(Msg, MsgProps, Acc) end, foldacc(), gb_trees:to_list(Messages)) =:= Res; postcondition(#state{bqstate = BQ, len = Len}, {call, _M, _F, _A}, _Res) -> diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index dffca79d..c4bd1836 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -2319,7 +2319,7 @@ test_variable_queue_fold(VQ0) -> VQ1 = rabbit_variable_queue:set_ram_duration_target(0, VQ0), VQ2 = variable_queue_publish( true, Count, fun (_, P) -> P end, fun erlang:term_to_binary/1, VQ1), - {Acc, VQ3} = rabbit_variable_queue:fold(fun ({M, _}, A) -> + {Acc, VQ3} = rabbit_variable_queue:fold(fun (M, _, A) -> [M | A] end, [], VQ2), true = [term_to_binary(N) || N <- lists:seq(Count, 1, -1)] == diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 644ba182..b826413a 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -687,7 +687,7 @@ fold(Fun, Acc, #vqstate { q1 = Q1, QFun = fun(MsgStatus, {Acc0, State0}) -> {#msg_status { msg = Msg, msg_props = MsgProps }, State1 } = read_msg(MsgStatus, false, State0), - {Fun({Msg, MsgProps}, Acc0), State1} + {Fun(Msg, MsgProps, Acc0), State1} end, {Acc1, State1} = ?QUEUE:foldl(QFun, {Acc, State}, Q4), {Acc2, State2} = ?QUEUE:foldl(QFun, {Acc1, State1}, Q3), @@ -1457,7 +1457,7 @@ delta_fold(Fun, Acc, DeltaSeqId, DeltaSeqIdEnd, _IsDelivered}, {Acc0, MSCState0}) -> {{ok, Msg = #basic_message {}}, MSCState1} = msg_store_read(MSCState0, IsPersistent, MsgId), - {Fun({Msg, MsgProps}, Acc0), MSCState1} + {Fun(Msg, MsgProps, Acc0), MSCState1} end, {Acc, MSCState}, List), delta_fold(Fun, Acc1, DeltaSeqId1, DeltaSeqIdEnd, State #vqstate { index_state = IndexState1, -- cgit v1.2.1 From 02d367c7f7d1bcdfb492f5e57cfc9b11a99568a0 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 26 Nov 2012 12:02:53 +0000 Subject: Add spec, fix API, refuse to run if there are pending acks, return status, don't throw away state. --- src/rabbit_amqqueue.erl | 8 +++----- src/rabbit_amqqueue_process.erl | 32 ++++++++++++++++++-------------- src/rabbit_mirror_queue_master.erl | 12 ++++++------ src/rabbit_mirror_queue_slave.erl | 1 + 4 files changed, 28 insertions(+), 25 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 4d957789..ad81ba03 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -173,6 +173,8 @@ (rabbit_types:amqqueue(), rabbit_types:amqqueue()) -> 'ok'). -spec(start_mirroring/1 :: (pid()) -> 'ok'). -spec(stop_mirroring/1 :: (pid()) -> 'ok'). +-spec(sync_mirrors/1 :: (rabbit_types:amqqueue()) -> + 'ok' | error('queue_has_pending_acks') | error('queue_not_mirrored')). -endif. @@ -591,11 +593,7 @@ set_maximum_since_use(QPid, Age) -> start_mirroring(QPid) -> ok = delegate_call(QPid, start_mirroring). stop_mirroring(QPid) -> ok = delegate_call(QPid, stop_mirroring). -sync_mirrors(Name) -> - case lookup(Name) of - {ok, #amqqueue{pid = QPid}} -> delegate_cast(QPid, sync_mirrors); - _ -> ok - end. +sync_mirrors(#amqqueue{pid = QPid}) -> delegate_call(QPid, sync_mirrors). on_node_down(Node) -> rabbit_misc:execute_mnesia_tx_with_tail( diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 07f4c3b1..19872d8d 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1171,6 +1171,24 @@ handle_call(stop_mirroring, _From, State = #q{backing_queue = BQ, reply(ok, State#q{backing_queue = BQ1, backing_queue_state = BQS1}); +handle_call(sync_mirrors, From, + State = #q{q = #amqqueue{name = Name}, + backing_queue = rabbit_mirror_queue_master = BQ, + backing_queue_state = BQS}) -> + case BQ:depth(BQS) - BQ:len(BQS) of + 0 -> + {ok, #amqqueue{slave_pids = SPids, sync_slave_pids = SSPids}} = + rabbit_amqqueue:lookup(Name), + gen_server2:reply(From, ok), + noreply(rabbit_mirror_queue_master:sync_mirrors( + SPids -- SSPids, Name, BQS)); + _ -> + reply({error, queue_has_pending_acks}, State) + end; + +handle_call(sync_mirrors, _From, State) -> + reply({error, queue_not_mirrored}, State); + handle_call(force_event_refresh, _From, State = #q{exclusive_consumer = Exclusive}) -> rabbit_event:notify(queue_created, infos(?CREATION_EVENT_KEYS, State)), @@ -1300,20 +1318,6 @@ handle_cast({dead_letter, Msgs, Reason}, State = #q{dlx = XName}) -> cleanup_after_confirm([AckTag || {_, AckTag} <- Msgs], State) end; -handle_cast(sync_mirrors, - State = #q{q = #amqqueue{name = Name}, - backing_queue = BQ, - backing_queue_state = BQS}) -> - case BQ of - rabbit_mirror_queue_master -> - {ok, #amqqueue{slave_pids = SPids, sync_slave_pids = SSPids}} = - rabbit_amqqueue:lookup(Name), - rabbit_mirror_queue_master:sync_mirrors(SPids -- SSPids, Name, BQS); - _ -> - ok - end, - noreply(State); - handle_cast(wake_up, State) -> noreply(State). diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index 16642604..ea9e04fe 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -127,12 +127,12 @@ stop_mirroring(State = #state { coordinator = CPid, stop_all_slaves(shutdown, State), {BQ, BQS}. -sync_mirrors([], Name, _State) -> +sync_mirrors([], Name, State) -> rabbit_log:info("Synchronising ~s: nothing to do~n", [rabbit_misc:rs(Name)]), - ok; -sync_mirrors(SPids, Name, #state { backing_queue = BQ, - backing_queue_state = BQS }) -> + State; +sync_mirrors(SPids, Name, State = #state { backing_queue = BQ, + backing_queue_state = BQS }) -> rabbit_log:info("Synchronising ~s with slaves ~p~n", [rabbit_misc:rs(Name), SPids]), Ref = make_ref(), @@ -153,7 +153,7 @@ sync_mirrors(SPids, Name, #state { backing_queue = BQ, end], SPid1 =/= dead], [erlang:demonitor(MRef) || {_, MRef} <- SPidsMRefs], - {Total, _BQS} = + {Total, BQS1} = BQ:fold(fun ({Msg, MsgProps}, I) -> wait_for_credit(), [begin @@ -171,7 +171,7 @@ sync_mirrors(SPids, Name, #state { backing_queue = BQ, [SPid ! {sync_complete, Ref} || SPid <- SPids1], rabbit_log:info("Synchronising ~s: ~p messages; complete~n", [rabbit_misc:rs(Name), Total]), - ok. + State#state{backing_queue_state = BQS1}. wait_for_credit() -> case credit_flow:blocked() of diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index d408c56e..f4130e02 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -857,6 +857,7 @@ sync_loop(Ref, MRef, MPid, State = #state{backing_queue = BQ, sync_loop(Ref, MRef, MPid, State); {sync_complete, Ref} -> erlang:demonitor(MRef), + %% We can only sync when there are no pending acks set_delta(0, State); {sync_message, Ref, Msg, Props0} -> credit_flow:ack(MPid, ?CREDIT_DISC_BOUND), -- cgit v1.2.1 From ead36b74b15e2eefce4b66579e524c5bf5ed15b5 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 26 Nov 2012 12:04:27 +0000 Subject: recover for accidental merge with default --- README | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README b/README index 90e99e62..67e3a66a 100644 --- a/README +++ b/README @@ -1 +1 @@ -Please see http://www.rabbitmq.com/build-server.html for build instructions. \ No newline at end of file +Please see http://www.rabbitmq.com/build-server.html for build instructions. -- cgit v1.2.1 From 37eabc35f816bf08e3bc9372e5a1528ec099661d Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 26 Nov 2012 12:06:20 +0000 Subject: Cosmetic --- src/rabbit_amqqueue_process.erl | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 19872d8d..7b167a9a 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1176,14 +1176,12 @@ handle_call(sync_mirrors, From, backing_queue = rabbit_mirror_queue_master = BQ, backing_queue_state = BQS}) -> case BQ:depth(BQS) - BQ:len(BQS) of - 0 -> - {ok, #amqqueue{slave_pids = SPids, sync_slave_pids = SSPids}} = - rabbit_amqqueue:lookup(Name), - gen_server2:reply(From, ok), - noreply(rabbit_mirror_queue_master:sync_mirrors( - SPids -- SSPids, Name, BQS)); - _ -> - reply({error, queue_has_pending_acks}, State) + 0 -> {ok, #amqqueue{slave_pids = SPids, sync_slave_pids = SSPids}} = + rabbit_amqqueue:lookup(Name), + gen_server2:reply(From, ok), + noreply(rabbit_mirror_queue_master:sync_mirrors( + SPids -- SSPids, Name, BQS)); + _ -> reply({error, queue_has_pending_acks}, State) end; handle_call(sync_mirrors, _From, State) -> -- cgit v1.2.1 From c568567524756c0d156599e8f9cbf33dcbcce78e Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 26 Nov 2012 12:08:21 +0000 Subject: cosmetic --- src/rabbit_tests.erl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index c4bd1836..d5c096a1 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -2319,9 +2319,8 @@ test_variable_queue_fold(VQ0) -> VQ1 = rabbit_variable_queue:set_ram_duration_target(0, VQ0), VQ2 = variable_queue_publish( true, Count, fun (_, P) -> P end, fun erlang:term_to_binary/1, VQ1), - {Acc, VQ3} = rabbit_variable_queue:fold(fun (M, _, A) -> - [M | A] - end, [], VQ2), + {Acc, VQ3} = rabbit_variable_queue:fold( + fun (M, _, A) -> [M | A] end, [], VQ2), true = [term_to_binary(N) || N <- lists:seq(Count, 1, -1)] == [list_to_binary(lists:reverse(P)) || #basic_message{ content = #content{ payload_fragments_rev = P}} <- -- 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(-) 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 e0fcf0d437ed5f8b71e555ece79d34ed4797e23c Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 26 Nov 2012 14:03:09 +0000 Subject: Fix bugs in previous commit, and be rather more thorough about monitoring and cleaning up credit flow. --- src/rabbit_amqqueue.erl | 3 +- src/rabbit_amqqueue_process.erl | 5 +-- src/rabbit_mirror_queue_master.erl | 65 +++++++++++++++++++++++++------------- src/rabbit_mirror_queue_slave.erl | 3 ++ 4 files changed, 51 insertions(+), 25 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index ad81ba03..e5f1cb90 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -174,7 +174,8 @@ -spec(start_mirroring/1 :: (pid()) -> 'ok'). -spec(stop_mirroring/1 :: (pid()) -> 'ok'). -spec(sync_mirrors/1 :: (rabbit_types:amqqueue()) -> - 'ok' | error('queue_has_pending_acks') | error('queue_not_mirrored')). + 'ok' | rabbit_types:error('queue_has_pending_acks') + | rabbit_types:error('queue_not_mirrored')). -endif. diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 7b167a9a..8c2fafa6 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1179,8 +1179,9 @@ handle_call(sync_mirrors, From, 0 -> {ok, #amqqueue{slave_pids = SPids, sync_slave_pids = SSPids}} = rabbit_amqqueue:lookup(Name), gen_server2:reply(From, ok), - noreply(rabbit_mirror_queue_master:sync_mirrors( - SPids -- SSPids, Name, BQS)); + noreply(State#q{backing_queue_state = + rabbit_mirror_queue_master:sync_mirrors( + SPids -- SSPids, Name, BQS)}); _ -> reply({error, queue_has_pending_acks}, State) end; diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index ea9e04fe..542d724a 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -144,44 +144,65 @@ sync_mirrors(SPids, Name, State = #state { backing_queue = BQ, %% We wait for a reply from the slaves so that we know they are in %% a receive block and will thus receive messages we send to them %% *without* those messages ending up in their gen_server2 pqueue. - SPids1 = [SPid1 || {SPid, MRef} <- SPidsMRefs, - SPid1 <- [receive - {'DOWN', MRef, process, SPid, _Reason} -> - dead; - {sync_ready, Ref, SPid} -> - SPid - end], - SPid1 =/= dead], - [erlang:demonitor(MRef) || {_, MRef} <- SPidsMRefs], - {Total, BQS1} = - BQ:fold(fun ({Msg, MsgProps}, I) -> - wait_for_credit(), + SPidsMRefs1 = sync_foreach(SPidsMRefs, Ref, fun sync_receive_ready/3), + {{Total, SPidsMRefs2}, BQS1} = + BQ:fold(fun ({Msg, MsgProps}, {I, SPMR}) -> + SPMR1 = wait_for_credit(SPMR, Ref), [begin credit_flow:send(SPid, ?CREDIT_DISC_BOUND), SPid ! {sync_message, Ref, Msg, MsgProps} - end || SPid <- SPids1], + end || {SPid, _} <- SPMR1], case I rem 1000 of 0 -> rabbit_log:info( "Synchronising ~s: ~p messages~n", [rabbit_misc:rs(Name), I]); _ -> ok end, - I + 1 - end, 0, BQS), - [SPid ! {sync_complete, Ref} || SPid <- SPids1], + {I + 1, SPMR1} + end, {0, SPidsMRefs1}, BQS), + sync_foreach(SPidsMRefs2, Ref, fun sync_receive_complete/3), rabbit_log:info("Synchronising ~s: ~p messages; complete~n", [rabbit_misc:rs(Name), Total]), State#state{backing_queue_state = BQS1}. -wait_for_credit() -> +wait_for_credit(SPidsMRefs, Ref) -> case credit_flow:blocked() of - true -> receive - {bump_credit, Msg} -> credit_flow:handle_bump_msg(Msg), - wait_for_credit() - end; - false -> ok + true -> wait_for_credit(sync_foreach(SPidsMRefs, Ref, + fun sync_receive_credit/3), Ref); + false -> SPidsMRefs end. +sync_foreach(SPidsMRefs, Ref, Fun) -> + [{SPid, MRef} || {SPid, MRef} <- SPidsMRefs, + SPid1 <- [Fun(SPid, MRef, Ref)], + SPid1 =/= dead]. + +sync_receive_ready(SPid, MRef, Ref) -> + receive + {sync_ready, Ref, SPid} -> SPid; + {'DOWN', MRef, _, SPid, _} -> dead + end. + +sync_receive_credit(SPid, MRef, Ref) -> + receive + {bump_credit, {SPid, _} = Msg} -> credit_flow:handle_bump_msg(Msg), + sync_receive_credit(SPid, MRef, Ref); + {'DOWN', MRef, _, SPid, _} -> credit_flow:peer_down(SPid), + dead + after 0 -> + SPid + end. + +sync_receive_complete(SPid, MRef, Ref) -> + SPid ! {sync_complete, Ref}, + receive + {sync_complete_ok, Ref, SPid} -> ok; + {'DOWN', MRef, _, SPid, _} -> ok + end, + erlang:demonitor(MRef, [flush]), + credit_flow:peer_down(SPid). + + terminate({shutdown, dropped} = Reason, State = #state { backing_queue = BQ, backing_queue_state = BQS }) -> diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index f4130e02..311c6ca6 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -851,12 +851,15 @@ sync_loop(Ref, MRef, MPid, State = #state{backing_queue = BQ, %% messages from it, we have a hole in the middle. So the %% only thing to do here is purge.) {_MsgCount, BQS1} = BQ:purge(BQS), + credit_flow:peer_down(MPid), State#state{backing_queue_state = BQS1}; {bump_credit, Msg} -> credit_flow:handle_bump_msg(Msg), sync_loop(Ref, MRef, MPid, State); {sync_complete, Ref} -> + MPid ! {sync_complete_ok, Ref, self()}, erlang:demonitor(MRef), + credit_flow:peer_down(MPid), %% We can only sync when there are no pending acks set_delta(0, State); {sync_message, Ref, Msg, Props0} -> -- cgit v1.2.1 From 16bf93483b233c9689ef9163a4826d19584ccd22 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 26 Nov 2012 14:13:54 +0000 Subject: Send the sync_start over GM to flush out any other messages that we might have sent that way already. --- src/rabbit_mirror_queue_master.erl | 7 +++++-- src/rabbit_mirror_queue_slave.erl | 23 ++++++++++++++--------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index 542d724a..9527ff30 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -131,13 +131,16 @@ sync_mirrors([], Name, State) -> rabbit_log:info("Synchronising ~s: nothing to do~n", [rabbit_misc:rs(Name)]), State; -sync_mirrors(SPids, Name, State = #state { backing_queue = BQ, +sync_mirrors(SPids, Name, State = #state { gm = GM, + backing_queue = BQ, backing_queue_state = BQS }) -> rabbit_log:info("Synchronising ~s with slaves ~p~n", [rabbit_misc:rs(Name), SPids]), Ref = make_ref(), + %% We send the start over GM to flush out any other messages that + %% we might have sent that way already. + gm:broadcast(GM, {sync_start, Ref, self(), SPids}), SPidsMRefs = [begin - SPid ! {sync_start, Ref, self()}, MRef = erlang:monitor(process, SPid), {SPid, MRef} end || SPid <- SPids], diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 311c6ca6..e7f26e6b 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -222,6 +222,15 @@ handle_cast({deliver, Delivery = #delivery{sender = Sender}, true, Flow}, end, noreply(maybe_enqueue_message(Delivery, State)); +handle_cast({sync_start, Ref, MPid}, + State = #state { backing_queue = BQ, + backing_queue_state = BQS }) -> + MRef = erlang:monitor(process, MPid), + MPid ! {sync_ready, Ref, self()}, + {_MsgCount, BQS1} = BQ:purge(BQS), + noreply( + sync_loop(Ref, MRef, MPid, State#state{backing_queue_state = BQS1})); + handle_cast({set_maximum_since_use, Age}, State) -> ok = file_handle_cache:set_maximum_since_use(Age), noreply(State); @@ -264,15 +273,6 @@ handle_info({bump_credit, Msg}, State) -> credit_flow:handle_bump_msg(Msg), noreply(State); -handle_info({sync_start, Ref, MPid}, - State = #state { backing_queue = BQ, - backing_queue_state = BQS }) -> - MRef = erlang:monitor(process, MPid), - MPid ! {sync_ready, Ref, self()}, - {_MsgCount, BQS1} = BQ:purge(BQS), - noreply( - sync_loop(Ref, MRef, MPid, State#state{backing_queue_state = BQS1})); - handle_info(Msg, State) -> {stop, {unexpected_info, Msg}, State}. @@ -367,6 +367,11 @@ handle_msg([_SPid], _From, process_death) -> handle_msg([CPid], _From, {delete_and_terminate, _Reason} = Msg) -> ok = gen_server2:cast(CPid, {gm, Msg}), {stop, {shutdown, ring_shutdown}}; +handle_msg([SPid], _From, {sync_start, Ref, MPid, SPids}) -> + case lists:member(SPid, SPids) of + true -> ok = gen_server2:cast(SPid, {sync_start, Ref, MPid}); + false -> ok + end; handle_msg([SPid], _From, Msg) -> ok = gen_server2:cast(SPid, {gm, Msg}). -- cgit v1.2.1 From 01ed88848239bae9c2f281f6c8dfcd14d93b866b Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 26 Nov 2012 14:23:33 +0000 Subject: Improve progfess logging. --- src/rabbit_mirror_queue_master.erl | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index 9527ff30..cae46706 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -45,6 +45,8 @@ known_senders }). +-define(SYNC_PROGRESS_INTERVAL, 1000000). + -ifdef(use_specs). -export_type([death_fun/0, depth_fun/0]). @@ -134,8 +136,8 @@ sync_mirrors([], Name, State) -> sync_mirrors(SPids, Name, State = #state { gm = GM, backing_queue = BQ, backing_queue_state = BQS }) -> - rabbit_log:info("Synchronising ~s with slaves ~p~n", - [rabbit_misc:rs(Name), SPids]), + rabbit_log:info("Synchronising ~s with slaves ~p: ~p messages to do~n", + [rabbit_misc:rs(Name), SPids, BQ:len(BQS)]), Ref = make_ref(), %% We send the start over GM to flush out any other messages that %% we might have sent that way already. @@ -148,24 +150,26 @@ sync_mirrors(SPids, Name, State = #state { gm = GM, %% a receive block and will thus receive messages we send to them %% *without* those messages ending up in their gen_server2 pqueue. SPidsMRefs1 = sync_foreach(SPidsMRefs, Ref, fun sync_receive_ready/3), - {{Total, SPidsMRefs2}, BQS1} = - BQ:fold(fun ({Msg, MsgProps}, {I, SPMR}) -> + {{_, SPidsMRefs2, _}, BQS1} = + BQ:fold(fun ({Msg, MsgProps}, {I, SPMR, Last}) -> SPMR1 = wait_for_credit(SPMR, Ref), [begin credit_flow:send(SPid, ?CREDIT_DISC_BOUND), SPid ! {sync_message, Ref, Msg, MsgProps} end || {SPid, _} <- SPMR1], - case I rem 1000 of - 0 -> rabbit_log:info( - "Synchronising ~s: ~p messages~n", - [rabbit_misc:rs(Name), I]); - _ -> ok - end, - {I + 1, SPMR1} - end, {0, SPidsMRefs1}, BQS), + {I + 1, SPMR1, + case timer:now_diff(erlang:now(), Last) > + ?SYNC_PROGRESS_INTERVAL of + true -> rabbit_log:info( + "Synchronising ~s: ~p messages~n", + [rabbit_misc:rs(Name), I]), + erlang:now(); + false -> Last + end} + end, {0, SPidsMRefs1, erlang:now()}, BQS), sync_foreach(SPidsMRefs2, Ref, fun sync_receive_complete/3), - rabbit_log:info("Synchronising ~s: ~p messages; complete~n", - [rabbit_misc:rs(Name), Total]), + rabbit_log:info("Synchronising ~s: complete~n", + [rabbit_misc:rs(Name)]), State#state{backing_queue_state = BQS1}. wait_for_credit(SPidsMRefs, Ref) -> -- cgit v1.2.1 From ed976b130333fc65d81ab1bddb5711e5afdca3b9 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 26 Nov 2012 15:02:53 +0000 Subject: React to memory monitor and FHC messages. --- src/rabbit_mirror_queue_slave.erl | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 5ea14698..965ea090 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -241,15 +241,8 @@ handle_cast({set_ram_duration_target, Duration}, BQS1 = BQ:set_ram_duration_target(Duration, BQS), noreply(State #state { backing_queue_state = BQS1 }). -handle_info(update_ram_duration, - State = #state { backing_queue = BQ, - backing_queue_state = BQS }) -> - {RamDuration, BQS1} = BQ:ram_duration(BQS), - DesiredDuration = - rabbit_memory_monitor:report_ram_duration(self(), RamDuration), - BQS2 = BQ:set_ram_duration_target(DesiredDuration, BQS1), - noreply(State #state { rate_timer_ref = just_measured, - backing_queue_state = BQS2 }); +handle_info(update_ram_duration, State) -> + noreply(update_ram_duration(State)); handle_info(sync_timeout, State) -> noreply(backing_queue_timeout( @@ -830,6 +823,15 @@ update_delta( DeltaChange, State = #state { depth_delta = Delta }) -> true = DeltaChange =< 0, %% assertion: we cannot become 'less' sync'ed set_delta(Delta + DeltaChange, State #state { depth_delta = undefined }). +update_ram_duration(State = #state { backing_queue = BQ, + backing_queue_state = BQS }) -> + {RamDuration, BQS1} = BQ:ram_duration(BQS), + DesiredDuration = + rabbit_memory_monitor:report_ram_duration(self(), RamDuration), + BQS2 = BQ:set_ram_duration_target(DesiredDuration, BQS1), + State #state { rate_timer_ref = just_measured, + backing_queue_state = BQS2 }. + record_synchronised(#amqqueue { name = QName }) -> Self = self(), rabbit_misc:execute_mnesia_transaction( @@ -845,7 +847,7 @@ record_synchronised(#amqqueue { name = QName }) -> end). sync_loop(Ref, MRef, MPid, State = #state{backing_queue = BQ, - backing_queue_state = BQS}) -> + backing_queue_state = BQS}) -> receive {'DOWN', MRef, process, MPid, _Reason} -> %% If the master dies half way we are not in the usual @@ -866,6 +868,15 @@ sync_loop(Ref, MRef, MPid, State = #state{backing_queue = BQ, credit_flow:peer_down(MPid), %% We can only sync when there are no pending acks set_delta(0, State); + {'$gen_cast', {set_maximum_since_use, Age}} -> + ok = file_handle_cache:set_maximum_since_use(Age), + sync_loop(Ref, MRef, MPid, State); + {'$gen_cast', {set_ram_duration_target, Duration}} -> + BQS1 = BQ:set_ram_duration_target(Duration, BQS), + sync_loop(Ref, MRef, MPid, + State#state{backing_queue_state = BQS1}); + update_ram_duration -> + sync_loop(Ref, MRef, MPid, update_ram_duration(State)); {sync_message, Ref, Msg, Props0} -> credit_flow:ack(MPid, ?CREDIT_DISC_BOUND), Props = Props0#message_properties{needs_confirming = false, -- cgit v1.2.1 From e9ae5b7fabd7070f542b50942927c987c019341e Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 26 Nov 2012 15:36:10 +0000 Subject: remove cruft --- src/rabbit_mirror_queue_slave.erl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index cb7a2135..982768e8 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -318,7 +318,6 @@ prioritise_cast(Msg, _State) -> {set_maximum_since_use, _Age} -> 8; {run_backing_queue, _Mod, _Fun} -> 6; {gm, _Msg} -> 5; - {post_commit, _Txn, _AckTags} -> 4; _ -> 0 end. -- cgit v1.2.1 From d7a5c44d559c54d17ebee808523e4c474a4675cd Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 26 Nov 2012 15:58:02 +0000 Subject: Don't hang on shutdown --- src/rabbit_amqqueue_process.erl | 11 ++++++++--- src/rabbit_mirror_queue_master.erl | 6 ++++++ src/rabbit_mirror_queue_slave.erl | 11 ++++++----- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 60857e7e..f7bb4453 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1162,9 +1162,14 @@ handle_call(sync_mirrors, From, 0 -> {ok, #amqqueue{slave_pids = SPids, sync_slave_pids = SSPids}} = rabbit_amqqueue:lookup(Name), gen_server2:reply(From, ok), - noreply(State#q{backing_queue_state = - rabbit_mirror_queue_master:sync_mirrors( - SPids -- SSPids, Name, BQS)}); + try + noreply(State#q{backing_queue_state = + rabbit_mirror_queue_master:sync_mirrors( + SPids -- SSPids, Name, BQS)}) + catch + {time_to_shutdown, Reason} -> + {stop, Reason, State} + end; _ -> reply({error, queue_has_pending_acks}, State) end; diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index 64b78fbb..a695d6f2 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -152,6 +152,12 @@ sync_mirrors(SPids, Name, State = #state { gm = GM, SPidsMRefs1 = sync_foreach(SPidsMRefs, Ref, fun sync_receive_ready/3), {{_, SPidsMRefs2, _}, BQS1} = BQ:fold(fun (Msg, MsgProps, {I, SPMR, Last}) -> + receive + {'EXIT', _Pid, Reason} -> + throw({time_to_shutdown, Reason}) + after 0 -> + ok + end, SPMR1 = wait_for_credit(SPMR, Ref), [begin credit_flow:send(SPid, ?CREDIT_DISC_BOUND), diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 965ea090..bb8def3d 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -228,8 +228,7 @@ handle_cast({sync_start, Ref, MPid}, MRef = erlang:monitor(process, MPid), MPid ! {sync_ready, Ref, self()}, {_MsgCount, BQS1} = BQ:purge(BQS), - noreply( - sync_loop(Ref, MRef, MPid, State#state{backing_queue_state = BQS1})); + sync_loop(Ref, MRef, MPid, State#state{backing_queue_state = BQS1}); handle_cast({set_maximum_since_use, Age}, State) -> ok = file_handle_cache:set_maximum_since_use(Age), @@ -858,7 +857,7 @@ sync_loop(Ref, MRef, MPid, State = #state{backing_queue = BQ, %% only thing to do here is purge.) {_MsgCount, BQS1} = BQ:purge(BQS), credit_flow:peer_down(MPid), - State#state{backing_queue_state = BQS1}; + noreply(State#state{backing_queue_state = BQS1}); {bump_credit, Msg} -> credit_flow:handle_bump_msg(Msg), sync_loop(Ref, MRef, MPid, State); @@ -867,7 +866,7 @@ sync_loop(Ref, MRef, MPid, State = #state{backing_queue = BQ, erlang:demonitor(MRef), credit_flow:peer_down(MPid), %% We can only sync when there are no pending acks - set_delta(0, State); + noreply(set_delta(0, State)); {'$gen_cast', {set_maximum_since_use, Age}} -> ok = file_handle_cache:set_maximum_since_use(Age), sync_loop(Ref, MRef, MPid, State); @@ -882,5 +881,7 @@ sync_loop(Ref, MRef, MPid, State = #state{backing_queue = BQ, Props = Props0#message_properties{needs_confirming = false, delivered = true}, BQS1 = BQ:publish(Msg, Props, none, BQS), - sync_loop(Ref, MRef, MPid, State#state{backing_queue_state = BQS1}) + sync_loop(Ref, MRef, MPid, State#state{backing_queue_state = BQS1}); + {'EXIT', _Pid, Reason} -> + {stop, Reason, State} end. -- cgit v1.2.1 From 99dbfffc3429340e04f20e340d9d527bfbfefeec Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 26 Nov 2012 18:08:47 +0000 Subject: Move the sync stuff to its own module --- src/rabbit_mirror_queue_master.erl | 73 +------------------ src/rabbit_mirror_queue_slave.erl | 63 ++++------------ src/rabbit_mirror_queue_sync.erl | 144 +++++++++++++++++++++++++++++++++++++ 3 files changed, 160 insertions(+), 120 deletions(-) create mode 100644 src/rabbit_mirror_queue_sync.erl diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index a695d6f2..cba3def7 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -45,8 +45,6 @@ known_senders }). --define(SYNC_PROGRESS_INTERVAL, 1000000). - -ifdef(use_specs). -export_type([death_fun/0, depth_fun/0]). @@ -142,80 +140,11 @@ sync_mirrors(SPids, Name, State = #state { gm = GM, %% We send the start over GM to flush out any other messages that %% we might have sent that way already. gm:broadcast(GM, {sync_start, Ref, self(), SPids}), - SPidsMRefs = [begin - MRef = erlang:monitor(process, SPid), - {SPid, MRef} - end || SPid <- SPids], - %% We wait for a reply from the slaves so that we know they are in - %% a receive block and will thus receive messages we send to them - %% *without* those messages ending up in their gen_server2 pqueue. - SPidsMRefs1 = sync_foreach(SPidsMRefs, Ref, fun sync_receive_ready/3), - {{_, SPidsMRefs2, _}, BQS1} = - BQ:fold(fun (Msg, MsgProps, {I, SPMR, Last}) -> - receive - {'EXIT', _Pid, Reason} -> - throw({time_to_shutdown, Reason}) - after 0 -> - ok - end, - SPMR1 = wait_for_credit(SPMR, Ref), - [begin - credit_flow:send(SPid, ?CREDIT_DISC_BOUND), - SPid ! {sync_message, Ref, Msg, MsgProps} - end || {SPid, _} <- SPMR1], - {I + 1, SPMR1, - case timer:now_diff(erlang:now(), Last) > - ?SYNC_PROGRESS_INTERVAL of - true -> rabbit_log:info( - "Synchronising ~s: ~p messages~n", - [rabbit_misc:rs(Name), I]), - erlang:now(); - false -> Last - end} - end, {0, SPidsMRefs1, erlang:now()}, BQS), - sync_foreach(SPidsMRefs2, Ref, fun sync_receive_complete/3), + BQS1 = rabbit_mirror_queue_sync:master(Name, Ref, SPids, BQ, BQS), rabbit_log:info("Synchronising ~s: complete~n", [rabbit_misc:rs(Name)]), State#state{backing_queue_state = BQS1}. -wait_for_credit(SPidsMRefs, Ref) -> - case credit_flow:blocked() of - true -> wait_for_credit(sync_foreach(SPidsMRefs, Ref, - fun sync_receive_credit/3), Ref); - false -> SPidsMRefs - end. - -sync_foreach(SPidsMRefs, Ref, Fun) -> - [{SPid, MRef} || {SPid, MRef} <- SPidsMRefs, - SPid1 <- [Fun(SPid, MRef, Ref)], - SPid1 =/= dead]. - -sync_receive_ready(SPid, MRef, Ref) -> - receive - {sync_ready, Ref, SPid} -> SPid; - {'DOWN', MRef, _, SPid, _} -> dead - end. - -sync_receive_credit(SPid, MRef, Ref) -> - receive - {bump_credit, {SPid, _} = Msg} -> credit_flow:handle_bump_msg(Msg), - sync_receive_credit(SPid, MRef, Ref); - {'DOWN', MRef, _, SPid, _} -> credit_flow:peer_down(SPid), - dead - after 0 -> - SPid - end. - -sync_receive_complete(SPid, MRef, Ref) -> - SPid ! {sync_complete, Ref}, - receive - {sync_complete_ok, Ref, SPid} -> ok; - {'DOWN', MRef, _, SPid, _} -> ok - end, - erlang:demonitor(MRef, [flush]), - credit_flow:peer_down(SPid). - - terminate({shutdown, dropped} = Reason, State = #state { backing_queue = BQ, backing_queue_state = BQS }) -> diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index bb8def3d..93ba882b 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -225,10 +225,16 @@ handle_cast({deliver, Delivery = #delivery{sender = Sender}, true, Flow}, handle_cast({sync_start, Ref, MPid}, State = #state { backing_queue = BQ, backing_queue_state = BQS }) -> - MRef = erlang:monitor(process, MPid), - MPid ! {sync_ready, Ref, self()}, - {_MsgCount, BQS1} = BQ:purge(BQS), - sync_loop(Ref, MRef, MPid, State#state{backing_queue_state = BQS1}); + S = fun(BQSN) -> State#state{backing_queue_state = BQSN} end, + %% [0] We can only sync when there are no pending acks + %% [1] The master died so we do not need to set_delta even though + %% we purged since we will get a depth instruction soon. + case rabbit_mirror_queue_sync:slave(Ref, MPid, BQ, BQS, + fun update_ram_duration/2) of + {ok, BQS1} -> noreply(set_delta(0, S(BQS1))); %% [0] + {failed, BQS1} -> noreply(S(BQS1)); %% [1] + {stop, R, BQS1} -> {stop, R, S(BQS1)} + end; handle_cast({set_maximum_since_use, Age}, State) -> ok = file_handle_cache:set_maximum_since_use(Age), @@ -824,12 +830,14 @@ update_delta( DeltaChange, State = #state { depth_delta = Delta }) -> update_ram_duration(State = #state { backing_queue = BQ, backing_queue_state = BQS }) -> + State#state{rate_timer_ref = just_measured, + backing_queue_state = update_ram_duration(BQ, BQS)}. + +update_ram_duration(BQ, BQS) -> {RamDuration, BQS1} = BQ:ram_duration(BQS), DesiredDuration = rabbit_memory_monitor:report_ram_duration(self(), RamDuration), - BQS2 = BQ:set_ram_duration_target(DesiredDuration, BQS1), - State #state { rate_timer_ref = just_measured, - backing_queue_state = BQS2 }. + BQ:set_ram_duration_target(DesiredDuration, BQS1). record_synchronised(#amqqueue { name = QName }) -> Self = self(), @@ -844,44 +852,3 @@ record_synchronised(#amqqueue { name = QName }) -> ok end end). - -sync_loop(Ref, MRef, MPid, State = #state{backing_queue = BQ, - backing_queue_state = BQS}) -> - receive - {'DOWN', MRef, process, MPid, _Reason} -> - %% If the master dies half way we are not in the usual - %% half-synced state (with messages nearer the tail of the - %% queue; instead we have ones nearer the head. If we then - %% sync with a newly promoted master, or even just receive - %% messages from it, we have a hole in the middle. So the - %% only thing to do here is purge.) - {_MsgCount, BQS1} = BQ:purge(BQS), - credit_flow:peer_down(MPid), - noreply(State#state{backing_queue_state = BQS1}); - {bump_credit, Msg} -> - credit_flow:handle_bump_msg(Msg), - sync_loop(Ref, MRef, MPid, State); - {sync_complete, Ref} -> - MPid ! {sync_complete_ok, Ref, self()}, - erlang:demonitor(MRef), - credit_flow:peer_down(MPid), - %% We can only sync when there are no pending acks - noreply(set_delta(0, State)); - {'$gen_cast', {set_maximum_since_use, Age}} -> - ok = file_handle_cache:set_maximum_since_use(Age), - sync_loop(Ref, MRef, MPid, State); - {'$gen_cast', {set_ram_duration_target, Duration}} -> - BQS1 = BQ:set_ram_duration_target(Duration, BQS), - sync_loop(Ref, MRef, MPid, - State#state{backing_queue_state = BQS1}); - update_ram_duration -> - sync_loop(Ref, MRef, MPid, update_ram_duration(State)); - {sync_message, Ref, Msg, Props0} -> - credit_flow:ack(MPid, ?CREDIT_DISC_BOUND), - Props = Props0#message_properties{needs_confirming = false, - delivered = true}, - BQS1 = BQ:publish(Msg, Props, none, BQS), - sync_loop(Ref, MRef, MPid, State#state{backing_queue_state = BQS1}); - {'EXIT', _Pid, Reason} -> - {stop, Reason, State} - end. diff --git a/src/rabbit_mirror_queue_sync.erl b/src/rabbit_mirror_queue_sync.erl new file mode 100644 index 00000000..1a7cdbb9 --- /dev/null +++ b/src/rabbit_mirror_queue_sync.erl @@ -0,0 +1,144 @@ +%% 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 VMware, Inc. +%% Copyright (c) 2010-2012 VMware, Inc. All rights reserved. +%% + +-module(rabbit_mirror_queue_sync). + +-include("rabbit.hrl"). + +-export([master/5, slave/5]). + +-define(SYNC_PROGRESS_INTERVAL, 1000000). + +%% --------------------------------------------------------------------------- + +master(Name, Ref, SPids, BQ, BQS) -> + SPidsMRefs = [begin + MRef = erlang:monitor(process, SPid), + {SPid, MRef} + end || SPid <- SPids], + %% We wait for a reply from the slaves so that we know they are in + %% a receive block and will thus receive messages we send to them + %% *without* those messages ending up in their gen_server2 pqueue. + SPidsMRefs1 = sync_foreach(SPidsMRefs, Ref, fun sync_receive_ready/3), + {{_, SPidsMRefs2, _}, BQS1} = + BQ:fold(fun (Msg, MsgProps, {I, SPMR, Last}) -> + receive + {'EXIT', _Pid, Reason} -> + throw({time_to_shutdown, Reason}) + after 0 -> + ok + end, + SPMR1 = wait_for_credit(SPMR, Ref), + [begin + credit_flow:send(SPid, ?CREDIT_DISC_BOUND), + SPid ! {sync_message, Ref, Msg, MsgProps} + end || {SPid, _} <- SPMR1], + {I + 1, SPMR1, + case timer:now_diff(erlang:now(), Last) > + ?SYNC_PROGRESS_INTERVAL of + true -> rabbit_log:info( + "Synchronising ~s: ~p messages~n", + [rabbit_misc:rs(Name), I]), + erlang:now(); + false -> Last + end} + end, {0, SPidsMRefs1, erlang:now()}, BQS), + sync_foreach(SPidsMRefs2, Ref, fun sync_receive_complete/3), + BQS1. + +wait_for_credit(SPidsMRefs, Ref) -> + case credit_flow:blocked() of + true -> wait_for_credit(sync_foreach(SPidsMRefs, Ref, + fun sync_receive_credit/3), Ref); + false -> SPidsMRefs + end. + +sync_foreach(SPidsMRefs, Ref, Fun) -> + [{SPid, MRef} || {SPid, MRef} <- SPidsMRefs, + SPid1 <- [Fun(SPid, MRef, Ref)], + SPid1 =/= dead]. + +sync_receive_ready(SPid, MRef, Ref) -> + receive + {sync_ready, Ref, SPid} -> SPid; + {'DOWN', MRef, _, SPid, _} -> dead + end. + +sync_receive_credit(SPid, MRef, Ref) -> + receive + {bump_credit, {SPid, _} = Msg} -> credit_flow:handle_bump_msg(Msg), + sync_receive_credit(SPid, MRef, Ref); + {'DOWN', MRef, _, SPid, _} -> credit_flow:peer_down(SPid), + dead + after 0 -> + SPid + end. + +sync_receive_complete(SPid, MRef, Ref) -> + SPid ! {sync_complete, Ref}, + receive + {sync_complete_ok, Ref, SPid} -> ok; + {'DOWN', MRef, _, SPid, _} -> ok + end, + erlang:demonitor(MRef, [flush]), + credit_flow:peer_down(SPid). + +%% --------------------------------------------------------------------------- + +slave(Ref, MPid, BQ, BQS, UpdateRamDuration) -> + MRef = erlang:monitor(process, MPid), + MPid ! {sync_ready, Ref, self()}, + {_MsgCount, BQS1} = BQ:purge(BQS), + slave_sync_loop(Ref, MRef, MPid, BQ, BQS1, UpdateRamDuration). + +slave_sync_loop(Ref, MRef, MPid, BQ, BQS, UpdateRamDuration) -> + receive + {'DOWN', MRef, process, MPid, _Reason} -> + %% If the master dies half way we are not in the usual + %% half-synced state (with messages nearer the tail of the + %% queue; instead we have ones nearer the head. If we then + %% sync with a newly promoted master, or even just receive + %% messages from it, we have a hole in the middle. So the + %% only thing to do here is purge.) + {_MsgCount, BQS1} = BQ:purge(BQS), + credit_flow:peer_down(MPid), + {failed, BQS1}; + {bump_credit, Msg} -> + credit_flow:handle_bump_msg(Msg), + slave_sync_loop(Ref, MRef, MPid, BQ, BQS, UpdateRamDuration); + {sync_complete, Ref} -> + MPid ! {sync_complete_ok, Ref, self()}, + erlang:demonitor(MRef), + credit_flow:peer_down(MPid), + {ok, BQS}; + {'$gen_cast', {set_maximum_since_use, Age}} -> + ok = file_handle_cache:set_maximum_since_use(Age), + slave_sync_loop(Ref, MRef, MPid, BQ, BQS, UpdateRamDuration); + {'$gen_cast', {set_ram_duration_target, Duration}} -> + BQS1 = BQ:set_ram_duration_target(Duration, BQS), + slave_sync_loop(Ref, MRef, MPid, BQ, BQS1, UpdateRamDuration); + update_ram_duration -> + BQS1 = UpdateRamDuration(BQ, BQS), + slave_sync_loop(Ref, MRef, MPid, BQ, BQS1, UpdateRamDuration); + {sync_message, Ref, Msg, Props0} -> + credit_flow:ack(MPid, ?CREDIT_DISC_BOUND), + Props = Props0#message_properties{needs_confirming = false, + delivered = true}, + BQS1 = BQ:publish(Msg, Props, none, BQS), + slave_sync_loop(Ref, MRef, MPid, BQ, BQS1, UpdateRamDuration); + {'EXIT', _Pid, Reason} -> + {stop, Reason, BQS} + end. -- cgit v1.2.1 From 87faa2fbc8a551838dee3a5114f0ae526a1d8642 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 27 Nov 2012 10:46:33 +0000 Subject: cosmetic --- src/rabbit.erl | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index c3a6d283..7b8348fc 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -400,13 +400,11 @@ status() -> is_running() -> is_running(node()). -is_running(Node) -> - rabbit_nodes:is_running(Node, rabbit). +is_running(Node) -> rabbit_nodes:is_running(Node, rabbit). environment() -> - lists:keysort( - 1, [P || P = {K, _} <- application:get_all_env(rabbit), - K =/= default_pass]). + lists:keysort(1, [P || P = {K, _} <- application:get_all_env(rabbit), + K =/= default_pass]). rotate_logs(BinarySuffix) -> Suffix = binary_to_list(BinarySuffix), -- 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(-) 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 1d30f6b9e685c534be827d265779a945a890ef57 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 27 Nov 2012 16:41:39 +0000 Subject: Handle update_ram_duration correctly. --- src/rabbit_mirror_queue_slave.erl | 20 ++++++++++++++------ src/rabbit_mirror_queue_sync.erl | 30 +++++++++++++++--------------- 2 files changed, 29 insertions(+), 21 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 93ba882b..06cda0b8 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -225,15 +225,17 @@ handle_cast({deliver, Delivery = #delivery{sender = Sender}, true, Flow}, handle_cast({sync_start, Ref, MPid}, State = #state { backing_queue = BQ, backing_queue_state = BQS }) -> - S = fun(BQSN) -> State#state{backing_queue_state = BQSN} end, + State1 = #state{rate_timer_ref = TRef} = ensure_rate_timer(State), + S = fun({TRefN, BQSN}) -> State1#state{rate_timer_ref = TRefN, + backing_queue_state = BQSN} end, %% [0] We can only sync when there are no pending acks %% [1] The master died so we do not need to set_delta even though %% we purged since we will get a depth instruction soon. - case rabbit_mirror_queue_sync:slave(Ref, MPid, BQ, BQS, - fun update_ram_duration/2) of - {ok, BQS1} -> noreply(set_delta(0, S(BQS1))); %% [0] - {failed, BQS1} -> noreply(S(BQS1)); %% [1] - {stop, R, BQS1} -> {stop, R, S(BQS1)} + case rabbit_mirror_queue_sync:slave(Ref, TRef, MPid, BQ, BQS, + fun update_ram_duration_sync/2) of + {ok, Res} -> noreply(set_delta(0, S(Res))); %% [0] + {failed, Res} -> noreply(S(Res)); %% [1] + {stop, Reason, Res} -> {stop, Reason, S(Res)} end; handle_cast({set_maximum_since_use, Age}, State) -> @@ -833,6 +835,12 @@ update_ram_duration(State = #state { backing_queue = BQ, State#state{rate_timer_ref = just_measured, backing_queue_state = update_ram_duration(BQ, BQS)}. +update_ram_duration_sync(BQ, BQS) -> + BQS1 = update_ram_duration(BQ, BQS), + TRef = erlang:send_after(?RAM_DURATION_UPDATE_INTERVAL, + self(), update_ram_duration), + {TRef, BQS1}. + update_ram_duration(BQ, BQS) -> {RamDuration, BQS1} = BQ:ram_duration(BQS), DesiredDuration = diff --git a/src/rabbit_mirror_queue_sync.erl b/src/rabbit_mirror_queue_sync.erl index 1a7cdbb9..cdf35eb2 100644 --- a/src/rabbit_mirror_queue_sync.erl +++ b/src/rabbit_mirror_queue_sync.erl @@ -18,7 +18,7 @@ -include("rabbit.hrl"). --export([master/5, slave/5]). +-export([master/5, slave/6]). -define(SYNC_PROGRESS_INTERVAL, 1000000). @@ -98,47 +98,47 @@ sync_receive_complete(SPid, MRef, Ref) -> %% --------------------------------------------------------------------------- -slave(Ref, MPid, BQ, BQS, UpdateRamDuration) -> +slave(Ref, TRef, MPid, BQ, BQS, UpdateRamDuration) -> MRef = erlang:monitor(process, MPid), MPid ! {sync_ready, Ref, self()}, {_MsgCount, BQS1} = BQ:purge(BQS), - slave_sync_loop(Ref, MRef, MPid, BQ, BQS1, UpdateRamDuration). + slave_sync_loop(Ref, TRef, MRef, MPid, BQ, BQS1, UpdateRamDuration). -slave_sync_loop(Ref, MRef, MPid, BQ, BQS, UpdateRamDuration) -> +slave_sync_loop(Ref, TRef, MRef, MPid, BQ, BQS, UpdateRamDur) -> receive {'DOWN', MRef, process, MPid, _Reason} -> %% If the master dies half way we are not in the usual %% half-synced state (with messages nearer the tail of the - %% queue; instead we have ones nearer the head. If we then + %% queue); instead we have ones nearer the head. If we then %% sync with a newly promoted master, or even just receive %% messages from it, we have a hole in the middle. So the - %% only thing to do here is purge.) + %% only thing to do here is purge. {_MsgCount, BQS1} = BQ:purge(BQS), credit_flow:peer_down(MPid), - {failed, BQS1}; + {failed, {TRef, BQS1}}; {bump_credit, Msg} -> credit_flow:handle_bump_msg(Msg), - slave_sync_loop(Ref, MRef, MPid, BQ, BQS, UpdateRamDuration); + slave_sync_loop(Ref, TRef, MRef, MPid, BQ, BQS, UpdateRamDur); {sync_complete, Ref} -> MPid ! {sync_complete_ok, Ref, self()}, erlang:demonitor(MRef), credit_flow:peer_down(MPid), - {ok, BQS}; + {ok, {TRef, BQS}}; {'$gen_cast', {set_maximum_since_use, Age}} -> ok = file_handle_cache:set_maximum_since_use(Age), - slave_sync_loop(Ref, MRef, MPid, BQ, BQS, UpdateRamDuration); + slave_sync_loop(Ref, TRef, MRef, MPid, BQ, BQS, UpdateRamDur); {'$gen_cast', {set_ram_duration_target, Duration}} -> BQS1 = BQ:set_ram_duration_target(Duration, BQS), - slave_sync_loop(Ref, MRef, MPid, BQ, BQS1, UpdateRamDuration); + slave_sync_loop(Ref, TRef, MRef, MPid, BQ, BQS1, UpdateRamDur); update_ram_duration -> - BQS1 = UpdateRamDuration(BQ, BQS), - slave_sync_loop(Ref, MRef, MPid, BQ, BQS1, UpdateRamDuration); + {TRef2, BQS1} = UpdateRamDur(BQ, BQS), + slave_sync_loop(Ref, TRef2, MRef, MPid, BQ, BQS1, UpdateRamDur); {sync_message, Ref, Msg, Props0} -> credit_flow:ack(MPid, ?CREDIT_DISC_BOUND), Props = Props0#message_properties{needs_confirming = false, delivered = true}, BQS1 = BQ:publish(Msg, Props, none, BQS), - slave_sync_loop(Ref, MRef, MPid, BQ, BQS1, UpdateRamDuration); + slave_sync_loop(Ref, TRef, MRef, MPid, BQ, BQS1, UpdateRamDur); {'EXIT', _Pid, Reason} -> - {stop, Reason, BQS} + {stop, Reason, {TRef, BQS}} end. -- cgit v1.2.1 From 23122e968b13fc958534f59996e3d49a9353cd4f Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 27 Nov 2012 18:20:34 +0000 Subject: simplify errors --- src/rabbit_amqqueue.erl | 3 +-- src/rabbit_amqqueue_process.erl | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index c1884118..4bdab0bc 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -174,8 +174,7 @@ -spec(start_mirroring/1 :: (pid()) -> 'ok'). -spec(stop_mirroring/1 :: (pid()) -> 'ok'). -spec(sync_mirrors/1 :: (rabbit_types:amqqueue()) -> - 'ok' | rabbit_types:error('queue_has_pending_acks') - | rabbit_types:error('queue_not_mirrored')). + 'ok' | rabbit_types:error('pending_acks' | 'not_mirrored')). -endif. diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index f7bb4453..3f9894f8 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1170,11 +1170,11 @@ handle_call(sync_mirrors, From, {time_to_shutdown, Reason} -> {stop, Reason, State} end; - _ -> reply({error, queue_has_pending_acks}, State) + _ -> reply({error, pending_acks}, State) end; handle_call(sync_mirrors, _From, State) -> - reply({error, queue_not_mirrored}, State); + reply({error, not_mirrored}, State); handle_call(force_event_refresh, _From, State = #q{exclusive_consumer = Exclusive}) -> -- cgit v1.2.1 From 9e8f4fc04ed36a6d7243cf050ce590e14375e15e Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 27 Nov 2012 18:22:45 +0000 Subject: fix bug --- src/rabbit_backing_queue_qc.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_backing_queue_qc.erl b/src/rabbit_backing_queue_qc.erl index fb8b82ea..764911b9 100644 --- a/src/rabbit_backing_queue_qc.erl +++ b/src/rabbit_backing_queue_qc.erl @@ -159,7 +159,7 @@ qc_purge(#state{bqstate = BQ}) -> {call, ?BQMOD, purge, [BQ]}. qc_fold(#state{bqstate = BQ}) -> - {call, ?BQMOD, fold, [fun foldfun/2, foldacc(), BQ]}. + {call, ?BQMOD, fold, [fun foldfun/3, foldacc(), BQ]}. %% Preconditions @@ -393,7 +393,7 @@ rand_choice(List, Selection, N) -> rand_choice(List -- [Picked], [Picked | Selection], N - 1). -foldfun(Msg, Acc) -> [Msg | Acc]. +foldfun(Msg, _MsgProps, Acc) -> [Msg | Acc]. foldacc() -> []. dropfun(Props) -> -- cgit v1.2.1 From cc25f470115cfd68e8f474ee9252d1f3eb30ae88 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 28 Nov 2012 11:45:33 +0000 Subject: Various QA cleanups --- src/rabbit_amqqueue_process.erl | 12 +++------ src/rabbit_mirror_queue_master.erl | 53 ++++++++++++++++++++++---------------- src/rabbit_mirror_queue_slave.erl | 29 ++++++++++----------- src/rabbit_mirror_queue_sync.erl | 29 ++++++++++----------- 4 files changed, 62 insertions(+), 61 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 70dc8aee..10efc798 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1154,17 +1154,13 @@ handle_call({requeue, AckTags, ChPid}, From, State) -> noreply(requeue(AckTags, ChPid, State)); handle_call(sync_mirrors, From, - State = #q{q = #amqqueue{name = Name}, - backing_queue = rabbit_mirror_queue_master = BQ, + State = #q{backing_queue = rabbit_mirror_queue_master = BQ, backing_queue_state = BQS}) -> case BQ:depth(BQS) - BQ:len(BQS) of - 0 -> {ok, #amqqueue{slave_pids = SPids, sync_slave_pids = SSPids}} = - rabbit_amqqueue:lookup(Name), - gen_server2:reply(From, ok), + 0 -> gen_server2:reply(From, ok), try - noreply(State#q{backing_queue_state = - rabbit_mirror_queue_master:sync_mirrors( - SPids -- SSPids, Name, BQS)}) + BQS1 = rabbit_mirror_queue_master:sync_mirrors(BQS), + noreply(State#q{backing_queue_state = BQS1}) catch {time_to_shutdown, Reason} -> {stop, Reason, State} diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index e19c1a09..545f2219 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -26,15 +26,16 @@ -export([start/1, stop/0]). --export([promote_backing_queue_state/7, sender_death_fun/0, depth_fun/0]). +-export([promote_backing_queue_state/8, sender_death_fun/0, depth_fun/0]). --export([init_with_existing_bq/3, stop_mirroring/1, sync_mirrors/3]). +-export([init_with_existing_bq/3, stop_mirroring/1, sync_mirrors/1]). -behaviour(rabbit_backing_queue). -include("rabbit.hrl"). --record(state, { gm, +-record(state, { name, + gm, coordinator, backing_queue, backing_queue_state, @@ -50,7 +51,8 @@ -type(death_fun() :: fun ((pid()) -> 'ok')). -type(depth_fun() :: fun (() -> 'ok')). --type(master_state() :: #state { gm :: pid(), +-type(master_state() :: #state { name :: rabbit_amqqueue:name(), + gm :: pid(), coordinator :: pid(), backing_queue :: atom(), backing_queue_state :: any(), @@ -60,9 +62,9 @@ known_senders :: set() }). --spec(promote_backing_queue_state/7 :: - (pid(), atom(), any(), pid(), [any()], dict(), [pid()]) -> - master_state()). +-spec(promote_backing_queue_state/8 :: + (rabbit_amqqueue:name(), pid(), atom(), any(), pid(), [any()], dict(), + [pid()]) -> master_state()). -spec(sender_death_fun/0 :: () -> death_fun()). -spec(depth_fun/0 :: () -> depth_fun()). -spec(init_with_existing_bq/3 :: (rabbit_types:amqqueue(), atom(), any()) -> @@ -108,7 +110,8 @@ init_with_existing_bq(Q = #amqqueue{name = QName}, BQ, BQS) -> end), {_MNode, SNodes} = rabbit_mirror_queue_misc:suggested_queue_nodes(Q), rabbit_mirror_queue_misc:add_mirrors(QName, SNodes), - #state { gm = GM, + #state { name = QName, + gm = GM, coordinator = CPid, backing_queue = BQ, backing_queue_state = BQS, @@ -124,13 +127,19 @@ stop_mirroring(State = #state { coordinator = CPid, stop_all_slaves(shutdown, State), {BQ, BQS}. -sync_mirrors([], Name, State) -> +sync_mirrors(State = #state{name = Name}) -> + {ok, #amqqueue{slave_pids = SPids, sync_slave_pids = SSPids}} = + rabbit_amqqueue:lookup(Name), + sync_mirrors(SPids -- SSPids, State). + +sync_mirrors([], State = #state{name = Name}) -> rabbit_log:info("Synchronising ~s: nothing to do~n", [rabbit_misc:rs(Name)]), State; -sync_mirrors(SPids, Name, State = #state { gm = GM, - backing_queue = BQ, - backing_queue_state = BQS }) -> +sync_mirrors(SPids, State = #state { name = Name, + gm = GM, + backing_queue = BQ, + backing_queue_state = BQS }) -> rabbit_log:info("Synchronising ~s with slaves ~p: ~p messages to do~n", [rabbit_misc:rs(Name), SPids, BQ:len(BQS)]), Ref = make_ref(), @@ -165,24 +174,23 @@ delete_and_terminate(Reason, State = #state { backing_queue = BQ, stop_all_slaves(Reason, State), State#state{backing_queue_state = BQ:delete_and_terminate(Reason, BQS)}. -stop_all_slaves(Reason, #state{gm = GM}) -> - Info = gm:info(GM), - Slaves = [Pid || Pid <- proplists:get_value(group_members, Info), - node(Pid) =/= node()], +stop_all_slaves(Reason, #state{name = Name, + gm = GM}) -> + {ok, #amqqueue{slave_pids = SPids}} = rabbit_amqqueue:lookup(Name), + Slaves = [Pid || Pid <- SPids], MRefs = [erlang:monitor(process, S) || S <- Slaves], ok = gm:broadcast(GM, {delete_and_terminate, Reason}), [receive {'DOWN', MRef, process, _Pid, _Info} -> ok end || MRef <- MRefs], %% Normally when we remove a slave another slave or master will %% notice and update Mnesia. But we just removed them all, and %% have stopped listening ourselves. So manually clean up. - QName = proplists:get_value(group_name, Info), rabbit_misc:execute_mnesia_transaction( fun () -> - [Q] = mnesia:read({rabbit_queue, QName}), + [Q] = mnesia:read({rabbit_queue, Name}), rabbit_mirror_queue_misc:store_updated_slaves( Q #amqqueue { gm_pids = [], slave_pids = [] }) end), - ok = gm:forget_group(QName). + ok = gm:forget_group(Name). purge(State = #state { gm = GM, backing_queue = BQ, @@ -414,17 +422,18 @@ is_duplicate(Message = #basic_message { id = MsgId }, %% Other exported functions %% --------------------------------------------------------------------------- -promote_backing_queue_state(CPid, BQ, BQS, GM, AckTags, SeenStatus, KS) -> +promote_backing_queue_state(QName, CPid, BQ, BQS, GM, AckTags, Seen, KS) -> {_MsgIds, BQS1} = BQ:requeue(AckTags, BQS), Len = BQ:len(BQS1), Depth = BQ:depth(BQS1), true = Len == Depth, %% ASSERTION: everything must have been requeued ok = gm:broadcast(GM, {depth, Depth}), - #state { gm = GM, + #state { name = QName, + gm = GM, coordinator = CPid, backing_queue = BQ, backing_queue_state = BQS1, - seen_status = SeenStatus, + seen_status = Seen, confirmed = [], ack_msg_id = dict:new(), known_senders = sets:from_list(KS) }. diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 320c07a6..1ba6774a 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -231,8 +231,14 @@ handle_cast({sync_start, Ref, MPid}, %% [0] We can only sync when there are no pending acks %% [1] The master died so we do not need to set_delta even though %% we purged since we will get a depth instruction soon. - case rabbit_mirror_queue_sync:slave(Ref, TRef, MPid, BQ, BQS, - fun update_ram_duration_sync/2) of + case rabbit_mirror_queue_sync:slave( + Ref, TRef, MPid, BQ, BQS, + fun (BQN, BQSN) -> + BQSN1 = update_ram_duration(BQN, BQSN), + TRef = erlang:send_after(?RAM_DURATION_UPDATE_INTERVAL, + self(), update_ram_duration), + {TRef, BQSN1} + end) of {ok, Res} -> noreply(set_delta(0, S(Res))); %% [0] {failed, Res} -> noreply(S(Res)); %% [1] {stop, Reason, Res} -> {stop, Reason, S(Res)} @@ -248,8 +254,10 @@ handle_cast({set_ram_duration_target, Duration}, BQS1 = BQ:set_ram_duration_target(Duration, BQS), noreply(State #state { backing_queue_state = BQS1 }). -handle_info(update_ram_duration, State) -> - noreply(update_ram_duration(State)); +handle_info(update_ram_duration, State = #state{backing_queue = BQ, + backing_queue_state = BQS}) -> + noreply(State#state{rate_timer_ref = just_measured, + backing_queue_state = update_ram_duration(BQ, BQS)}); handle_info(sync_timeout, State) -> noreply(backing_queue_timeout( @@ -542,7 +550,7 @@ promote_me(From, #state { q = Q = #amqqueue { name = QName }, AckTags = [AckTag || {_MsgId, AckTag} <- dict:to_list(MA)], MasterState = rabbit_mirror_queue_master:promote_backing_queue_state( - CPid, BQ, BQS, GM, AckTags, SS, MPids), + QName, CPid, BQ, BQS, GM, AckTags, SS, MPids), MTC = dict:fold(fun (MsgId, {published, ChPid, MsgSeqNo}, MTC0) -> gb_trees:insert(MsgId, {ChPid, MsgSeqNo}, MTC0); @@ -829,17 +837,6 @@ update_delta( DeltaChange, State = #state { depth_delta = Delta }) -> true = DeltaChange =< 0, %% assertion: we cannot become 'less' sync'ed set_delta(Delta + DeltaChange, State #state { depth_delta = undefined }). -update_ram_duration(State = #state { backing_queue = BQ, - backing_queue_state = BQS }) -> - State#state{rate_timer_ref = just_measured, - backing_queue_state = update_ram_duration(BQ, BQS)}. - -update_ram_duration_sync(BQ, BQS) -> - BQS1 = update_ram_duration(BQ, BQS), - TRef = erlang:send_after(?RAM_DURATION_UPDATE_INTERVAL, - self(), update_ram_duration), - {TRef, BQS1}. - update_ram_duration(BQ, BQS) -> {RamDuration, BQS1} = BQ:ram_duration(BQS), DesiredDuration = diff --git a/src/rabbit_mirror_queue_sync.erl b/src/rabbit_mirror_queue_sync.erl index e1216120..36e9f1eb 100644 --- a/src/rabbit_mirror_queue_sync.erl +++ b/src/rabbit_mirror_queue_sync.erl @@ -32,7 +32,7 @@ master(Name, Ref, SPids, BQ, BQS) -> %% We wait for a reply from the slaves so that we know they are in %% a receive block and will thus receive messages we send to them %% *without* those messages ending up in their gen_server2 pqueue. - SPidsMRefs1 = sync_foreach(SPidsMRefs, Ref, fun sync_receive_ready/3), + SPidsMRefs1 = foreach_slave(SPidsMRefs, Ref, fun sync_receive_ready/3), {{_, SPidsMRefs2, _}, BQS1} = BQ:fold(fun (Msg, MsgProps, {I, SPMR, Last}) -> receive @@ -56,20 +56,19 @@ master(Name, Ref, SPids, BQ, BQS) -> false -> Last end} end, {0, SPidsMRefs1, erlang:now()}, BQS), - sync_foreach(SPidsMRefs2, Ref, fun sync_receive_complete/3), + foreach_slave(SPidsMRefs2, Ref, fun sync_receive_complete/3), BQS1. wait_for_credit(SPidsMRefs, Ref) -> case credit_flow:blocked() of - true -> wait_for_credit(sync_foreach(SPidsMRefs, Ref, - fun sync_receive_credit/3), Ref); + true -> wait_for_credit(foreach_slave(SPidsMRefs, Ref, + fun sync_receive_credit/3), Ref); false -> SPidsMRefs end. -sync_foreach(SPidsMRefs, Ref, Fun) -> +foreach_slave(SPidsMRefs, Ref, Fun) -> [{SPid, MRef} || {SPid, MRef} <- SPidsMRefs, - SPid1 <- [Fun(SPid, MRef, Ref)], - SPid1 =/= dead]. + Fun(SPid, MRef, Ref) =/= dead]. sync_receive_ready(SPid, MRef, Ref) -> receive @@ -102,9 +101,9 @@ slave(Ref, TRef, MPid, BQ, BQS, UpdateRamDuration) -> MRef = erlang:monitor(process, MPid), MPid ! {sync_ready, Ref, self()}, {_MsgCount, BQS1} = BQ:purge(BQS), - slave_sync_loop(Ref, TRef, MRef, MPid, BQ, BQS1, UpdateRamDuration). + slave_sync_loop({Ref, MRef, MPid, BQ, UpdateRamDuration}, TRef, BQS1). -slave_sync_loop(Ref, TRef, MRef, MPid, BQ, BQS, UpdateRamDur) -> +slave_sync_loop(Args = {Ref, MRef, MPid, BQ, UpdateRamDur}, TRef, BQS) -> receive {'DOWN', MRef, process, MPid, _Reason} -> %% If the master dies half way we are not in the usual @@ -118,7 +117,7 @@ slave_sync_loop(Ref, TRef, MRef, MPid, BQ, BQS, UpdateRamDur) -> {failed, {TRef, BQS1}}; {bump_credit, Msg} -> credit_flow:handle_bump_msg(Msg), - slave_sync_loop(Ref, TRef, MRef, MPid, BQ, BQS, UpdateRamDur); + slave_sync_loop(Args, TRef, BQS); {sync_complete, Ref} -> MPid ! {sync_complete_ok, Ref, self()}, erlang:demonitor(MRef), @@ -126,18 +125,18 @@ slave_sync_loop(Ref, TRef, MRef, MPid, BQ, BQS, UpdateRamDur) -> {ok, {TRef, BQS}}; {'$gen_cast', {set_maximum_since_use, Age}} -> ok = file_handle_cache:set_maximum_since_use(Age), - slave_sync_loop(Ref, TRef, MRef, MPid, BQ, BQS, UpdateRamDur); + slave_sync_loop(Args, TRef, BQS); {'$gen_cast', {set_ram_duration_target, Duration}} -> BQS1 = BQ:set_ram_duration_target(Duration, BQS), - slave_sync_loop(Ref, TRef, MRef, MPid, BQ, BQS1, UpdateRamDur); + slave_sync_loop(Args, TRef, BQS1); update_ram_duration -> {TRef2, BQS1} = UpdateRamDur(BQ, BQS), - slave_sync_loop(Ref, TRef2, MRef, MPid, BQ, BQS1, UpdateRamDur); + slave_sync_loop(Args, TRef2, BQS1); {sync_message, Ref, Msg, Props} -> credit_flow:ack(MPid, ?CREDIT_DISC_BOUND), Props1 = Props#message_properties{needs_confirming = false}, - BQS1 = BQ:publish(Msg, Props1, none, BQS), - slave_sync_loop(Ref, TRef, MRef, MPid, BQ, BQS1, UpdateRamDur); + BQS1 = BQ:publish(Msg, Props1, true, none, BQS), + slave_sync_loop(Args, TRef, BQS1); {'EXIT', _Pid, Reason} -> {stop, Reason, {TRef, BQS}} end. -- cgit v1.2.1 From f4786168c90dd2ffa1a4e8a1b54eae1c61e2a7eb Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 28 Nov 2012 12:09:37 +0000 Subject: Oops --- src/rabbit_mirror_queue_slave.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 1ba6774a..11490b9c 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -235,9 +235,9 @@ handle_cast({sync_start, Ref, MPid}, Ref, TRef, MPid, BQ, BQS, fun (BQN, BQSN) -> BQSN1 = update_ram_duration(BQN, BQSN), - TRef = erlang:send_after(?RAM_DURATION_UPDATE_INTERVAL, - self(), update_ram_duration), - {TRef, BQSN1} + TRefN = erlang:send_after(?RAM_DURATION_UPDATE_INTERVAL, + self(), update_ram_duration), + {TRefN, BQSN1} end) of {ok, Res} -> noreply(set_delta(0, S(Res))); %% [0] {failed, Res} -> noreply(S(Res)); %% [1] -- cgit v1.2.1 From 566125a445ab33c08d55bdd4b7f3a5bc2bc58311 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 28 Nov 2012 13:02:50 +0000 Subject: Spawn a separate process to send out messages from the master. This lets us simplify some monitor and credit_flow handling. Thus also stop sync_receive_credit from being a spinloop. --- src/rabbit_mirror_queue_master.erl | 5 ++-- src/rabbit_mirror_queue_sync.erl | 56 +++++++++++++++++++++----------------- 2 files changed, 34 insertions(+), 27 deletions(-) diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index 545f2219..ff1eccaf 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -145,8 +145,9 @@ sync_mirrors(SPids, State = #state { name = Name, Ref = make_ref(), %% We send the start over GM to flush out any other messages that %% we might have sent that way already. - gm:broadcast(GM, {sync_start, Ref, self(), SPids}), - BQS1 = rabbit_mirror_queue_sync:master(Name, Ref, SPids, BQ, BQS), + Syncer = rabbit_mirror_queue_sync:master_prepare(Name, Ref, SPids, BQ, BQS), + gm:broadcast(GM, {sync_start, Ref, Syncer, SPids}), + BQS1 = rabbit_mirror_queue_sync:master_go(Syncer, Ref), rabbit_log:info("Synchronising ~s: complete~n", [rabbit_misc:rs(Name)]), State#state{backing_queue_state = BQS1}. diff --git a/src/rabbit_mirror_queue_sync.erl b/src/rabbit_mirror_queue_sync.erl index 36e9f1eb..978d940c 100644 --- a/src/rabbit_mirror_queue_sync.erl +++ b/src/rabbit_mirror_queue_sync.erl @@ -18,13 +18,26 @@ -include("rabbit.hrl"). --export([master/5, slave/6]). +-export([master_prepare/5, master_go/2, slave/6]). -define(SYNC_PROGRESS_INTERVAL, 1000000). %% --------------------------------------------------------------------------- -master(Name, Ref, SPids, BQ, BQS) -> +master_prepare(Name, Ref, SPids, BQ, BQS) -> + MPid = self(), + spawn_link(fun () -> master(Name, Ref, MPid, SPids, BQ, BQS) end). + +master_go(Syncer, Ref) -> + Syncer ! {go, Ref}, + receive + {done, Ref, BQS1} -> BQS1 + end. + +master(Name, Ref, MPid, SPids, BQ, BQS) -> + receive + {go, Ref} -> ok + end, SPidsMRefs = [begin MRef = erlang:monitor(process, SPid), {SPid, MRef} @@ -57,7 +70,8 @@ master(Name, Ref, SPids, BQ, BQS) -> end} end, {0, SPidsMRefs1, erlang:now()}, BQS), foreach_slave(SPidsMRefs2, Ref, fun sync_receive_complete/3), - BQS1. + MPid ! {done, Ref, BQS1}, + unlink(MPid). wait_for_credit(SPidsMRefs, Ref) -> case credit_flow:blocked() of @@ -76,36 +90,28 @@ sync_receive_ready(SPid, MRef, Ref) -> {'DOWN', MRef, _, SPid, _} -> dead end. -sync_receive_credit(SPid, MRef, Ref) -> +sync_receive_credit(SPid, MRef, _Ref) -> receive {bump_credit, {SPid, _} = Msg} -> credit_flow:handle_bump_msg(Msg), - sync_receive_credit(SPid, MRef, Ref); + SPid; {'DOWN', MRef, _, SPid, _} -> credit_flow:peer_down(SPid), dead - after 0 -> - SPid end. -sync_receive_complete(SPid, MRef, Ref) -> - SPid ! {sync_complete, Ref}, - receive - {sync_complete_ok, Ref, SPid} -> ok; - {'DOWN', MRef, _, SPid, _} -> ok - end, - erlang:demonitor(MRef, [flush]), - credit_flow:peer_down(SPid). +sync_receive_complete(SPid, _MRef, Ref) -> + SPid ! {sync_complete, Ref}. %% --------------------------------------------------------------------------- -slave(Ref, TRef, MPid, BQ, BQS, UpdateRamDuration) -> - MRef = erlang:monitor(process, MPid), - MPid ! {sync_ready, Ref, self()}, +slave(Ref, TRef, Syncer, BQ, BQS, UpdateRamDuration) -> + MRef = erlang:monitor(process, Syncer), + Syncer ! {sync_ready, Ref, self()}, {_MsgCount, BQS1} = BQ:purge(BQS), - slave_sync_loop({Ref, MRef, MPid, BQ, UpdateRamDuration}, TRef, BQS1). + slave_sync_loop({Ref, MRef, Syncer, BQ, UpdateRamDuration}, TRef, BQS1). -slave_sync_loop(Args = {Ref, MRef, MPid, BQ, UpdateRamDur}, TRef, BQS) -> +slave_sync_loop(Args = {Ref, MRef, Syncer, BQ, UpdateRamDur}, TRef, BQS) -> receive - {'DOWN', MRef, process, MPid, _Reason} -> + {'DOWN', MRef, process, Syncer, _Reason} -> %% If the master dies half way we are not in the usual %% half-synced state (with messages nearer the tail of the %% queue); instead we have ones nearer the head. If we then @@ -113,15 +119,15 @@ slave_sync_loop(Args = {Ref, MRef, MPid, BQ, UpdateRamDur}, TRef, BQS) -> %% messages from it, we have a hole in the middle. So the %% only thing to do here is purge. {_MsgCount, BQS1} = BQ:purge(BQS), - credit_flow:peer_down(MPid), + credit_flow:peer_down(Syncer), {failed, {TRef, BQS1}}; {bump_credit, Msg} -> credit_flow:handle_bump_msg(Msg), slave_sync_loop(Args, TRef, BQS); {sync_complete, Ref} -> - MPid ! {sync_complete_ok, Ref, self()}, + Syncer ! {sync_complete_ok, Ref, self()}, erlang:demonitor(MRef), - credit_flow:peer_down(MPid), + credit_flow:peer_down(Syncer), {ok, {TRef, BQS}}; {'$gen_cast', {set_maximum_since_use, Age}} -> ok = file_handle_cache:set_maximum_since_use(Age), @@ -133,7 +139,7 @@ slave_sync_loop(Args = {Ref, MRef, MPid, BQ, UpdateRamDur}, TRef, BQS) -> {TRef2, BQS1} = UpdateRamDur(BQ, BQS), slave_sync_loop(Args, TRef2, BQS1); {sync_message, Ref, Msg, Props} -> - credit_flow:ack(MPid, ?CREDIT_DISC_BOUND), + credit_flow:ack(Syncer, ?CREDIT_DISC_BOUND), Props1 = Props#message_properties{needs_confirming = false}, BQS1 = BQ:publish(Msg, Props1, true, none, BQS), slave_sync_loop(Args, TRef, BQS1); -- cgit v1.2.1 From 8f3a8f6de8c86783e7f22d76d099955b7e58dc5b Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 28 Nov 2012 13:41:34 +0000 Subject: Don't expose the BQ to the syncer. --- src/rabbit_mirror_queue_master.erl | 4 +- src/rabbit_mirror_queue_sync.erl | 88 ++++++++++++++++++++++---------------- 2 files changed, 52 insertions(+), 40 deletions(-) diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index ff1eccaf..04e868bc 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -145,9 +145,9 @@ sync_mirrors(SPids, State = #state { name = Name, Ref = make_ref(), %% We send the start over GM to flush out any other messages that %% we might have sent that way already. - Syncer = rabbit_mirror_queue_sync:master_prepare(Name, Ref, SPids, BQ, BQS), + Syncer = rabbit_mirror_queue_sync:master_prepare(Ref, SPids), gm:broadcast(GM, {sync_start, Ref, Syncer, SPids}), - BQS1 = rabbit_mirror_queue_sync:master_go(Syncer, Ref), + BQS1 = rabbit_mirror_queue_sync:master_go(Syncer, Ref, Name, BQ, BQS), rabbit_log:info("Synchronising ~s: complete~n", [rabbit_misc:rs(Name)]), State#state{backing_queue_state = BQS1}. diff --git a/src/rabbit_mirror_queue_sync.erl b/src/rabbit_mirror_queue_sync.erl index 978d940c..560e0d43 100644 --- a/src/rabbit_mirror_queue_sync.erl +++ b/src/rabbit_mirror_queue_sync.erl @@ -18,26 +18,45 @@ -include("rabbit.hrl"). --export([master_prepare/5, master_go/2, slave/6]). +-export([master_prepare/2, master_go/5, slave/6]). -define(SYNC_PROGRESS_INTERVAL, 1000000). %% --------------------------------------------------------------------------- +%% Master -master_prepare(Name, Ref, SPids, BQ, BQS) -> +master_prepare(Ref, SPids) -> MPid = self(), - spawn_link(fun () -> master(Name, Ref, MPid, SPids, BQ, BQS) end). - -master_go(Syncer, Ref) -> - Syncer ! {go, Ref}, + spawn_link(fun () -> syncer(Ref, MPid, SPids) end). + +master_go(Syncer, Ref, Name, BQ, BQS) -> + SendArgs = {Syncer, Ref, Name}, + {_, BQS1} = + BQ:fold(fun (Msg, MsgProps, {I, Last}) -> + {I + 1, master_send(SendArgs, I, Last, Msg, MsgProps)} + end, {0, erlang:now()}, BQS), + Syncer ! {done, Ref}, + BQS1. + +master_send({Syncer, Ref, Name}, I, Last, Msg, MsgProps) -> + Syncer ! {msg, Ref, Msg, MsgProps}, receive - {done, Ref, BQS1} -> BQS1 + {msg_ok, Ref} -> ok; + {'EXIT', _Pid, Reason} -> throw({time_to_shutdown, Reason}) + end, + case timer:now_diff(erlang:now(), Last) > + ?SYNC_PROGRESS_INTERVAL of + true -> rabbit_log:info("Synchronising ~s: ~p messages~n", + [rabbit_misc:rs(Name), I]), + erlang:now(); + false -> Last end. -master(Name, Ref, MPid, SPids, BQ, BQS) -> - receive - {go, Ref} -> ok - end, +%% Master +%% --------------------------------------------------------------------------- +%% Syncer + +syncer(Ref, MPid, SPids) -> SPidsMRefs = [begin MRef = erlang:monitor(process, SPid), {SPid, MRef} @@ -46,33 +65,24 @@ master(Name, Ref, MPid, SPids, BQ, BQS) -> %% a receive block and will thus receive messages we send to them %% *without* those messages ending up in their gen_server2 pqueue. SPidsMRefs1 = foreach_slave(SPidsMRefs, Ref, fun sync_receive_ready/3), - {{_, SPidsMRefs2, _}, BQS1} = - BQ:fold(fun (Msg, MsgProps, {I, SPMR, Last}) -> - receive - {'EXIT', _Pid, Reason} -> - throw({time_to_shutdown, Reason}) - after 0 -> - ok - end, - SPMR1 = wait_for_credit(SPMR, Ref), - [begin - credit_flow:send(SPid, ?CREDIT_DISC_BOUND), - SPid ! {sync_message, Ref, Msg, MsgProps} - end || {SPid, _} <- SPMR1], - {I + 1, SPMR1, - case timer:now_diff(erlang:now(), Last) > - ?SYNC_PROGRESS_INTERVAL of - true -> rabbit_log:info( - "Synchronising ~s: ~p messages~n", - [rabbit_misc:rs(Name), I]), - erlang:now(); - false -> Last - end} - end, {0, SPidsMRefs1, erlang:now()}, BQS), - foreach_slave(SPidsMRefs2, Ref, fun sync_receive_complete/3), - MPid ! {done, Ref, BQS1}, + SPidsMRefs2 = syncer_loop({Ref, MPid}, SPidsMRefs1), + foreach_slave(SPidsMRefs2, Ref, fun sync_send_complete/3), unlink(MPid). +syncer_loop({Ref, MPid} = Args, SPidsMRefs) -> + receive + {msg, Ref, Msg, MsgProps} -> + MPid ! {msg_ok, Ref}, + SPidsMRefs1 = wait_for_credit(SPidsMRefs, Ref), + [begin + credit_flow:send(SPid, ?CREDIT_DISC_BOUND), + SPid ! {sync_msg, Ref, Msg, MsgProps} + end || {SPid, _} <- SPidsMRefs1], + syncer_loop(Args, SPidsMRefs1); + {done, Ref} -> + SPidsMRefs + end. + wait_for_credit(SPidsMRefs, Ref) -> case credit_flow:blocked() of true -> wait_for_credit(foreach_slave(SPidsMRefs, Ref, @@ -98,10 +108,12 @@ sync_receive_credit(SPid, MRef, _Ref) -> dead end. -sync_receive_complete(SPid, _MRef, Ref) -> +sync_send_complete(SPid, _MRef, Ref) -> SPid ! {sync_complete, Ref}. +%% Syncer %% --------------------------------------------------------------------------- +%% Slave slave(Ref, TRef, Syncer, BQ, BQS, UpdateRamDuration) -> MRef = erlang:monitor(process, Syncer), @@ -138,7 +150,7 @@ slave_sync_loop(Args = {Ref, MRef, Syncer, BQ, UpdateRamDur}, TRef, BQS) -> update_ram_duration -> {TRef2, BQS1} = UpdateRamDur(BQ, BQS), slave_sync_loop(Args, TRef2, BQS1); - {sync_message, Ref, Msg, Props} -> + {sync_msg, Ref, Msg, Props} -> credit_flow:ack(Syncer, ?CREDIT_DISC_BOUND), Props1 = Props#message_properties{needs_confirming = false}, BQS1 = BQ:publish(Msg, Props1, true, none, BQS), -- cgit v1.2.1 From d3de0a33eff8467bc584c72e095b8865ce3159a5 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 28 Nov 2012 13:56:50 +0000 Subject: Diagram. --- src/rabbit_mirror_queue_master.erl | 2 -- src/rabbit_mirror_queue_sync.erl | 26 ++++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index 04e868bc..e8734a8c 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -143,8 +143,6 @@ sync_mirrors(SPids, State = #state { name = Name, rabbit_log:info("Synchronising ~s with slaves ~p: ~p messages to do~n", [rabbit_misc:rs(Name), SPids, BQ:len(BQS)]), Ref = make_ref(), - %% We send the start over GM to flush out any other messages that - %% we might have sent that way already. Syncer = rabbit_mirror_queue_sync:master_prepare(Ref, SPids), gm:broadcast(GM, {sync_start, Ref, Syncer, SPids}), BQS1 = rabbit_mirror_queue_sync:master_go(Syncer, Ref, Name, BQ, BQS), diff --git a/src/rabbit_mirror_queue_sync.erl b/src/rabbit_mirror_queue_sync.erl index 560e0d43..f7901d9c 100644 --- a/src/rabbit_mirror_queue_sync.erl +++ b/src/rabbit_mirror_queue_sync.erl @@ -22,6 +22,32 @@ -define(SYNC_PROGRESS_INTERVAL, 1000000). +%% There are three processes around, the master, the syncer and the +%% slave(s). The syncer is an intermediary, linked to the master in +%% order to make sure we do not mess with the master's credit flow or +%% set of monitors. +%% +%% Interactions +%% ------------ +%% +%% '*' indicates repeating messages. All are standard Erlang messages +%% except sync_start which is sent over GM to flush out any other +%% messages that we might have sent that way already. (credit) is the +%% usual credit_flow bump message every so often. +%% +%% Master Syncer Slave(s) +%% sync_mirrors -> || || +%% (from channel) || -- (spawns) --> || || +%% || --------- sync_start (over GM) -------> || +%% || || <--- sync_ready ---- || +%% || ----- msg* ---> || || } +%% || <-- msg_ok* --- || || } loop +%% || || ----- sync_msg* ---> || } +%% || || <---- (credit)* ---- || } +%% || ---- done ----> || || +%% || || -- sync_complete --> || +%% || (Dies) || + %% --------------------------------------------------------------------------- %% Master -- cgit v1.2.1 From e6e360d8d76c2cc38e38b824d9ba35af2ac5f0e8 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 28 Nov 2012 13:57:24 +0000 Subject: Oops --- src/rabbit_mirror_queue_sync.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_mirror_queue_sync.erl b/src/rabbit_mirror_queue_sync.erl index f7901d9c..8d0407f2 100644 --- a/src/rabbit_mirror_queue_sync.erl +++ b/src/rabbit_mirror_queue_sync.erl @@ -35,7 +35,7 @@ %% messages that we might have sent that way already. (credit) is the %% usual credit_flow bump message every so often. %% -%% Master Syncer Slave(s) +%% Master Syncer Slave(s) %% sync_mirrors -> || || %% (from channel) || -- (spawns) --> || || %% || --------- sync_start (over GM) -------> || -- cgit v1.2.1 From 8f9c5c2a0428a9dac54e3b0ff0a514cf740316b4 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 28 Nov 2012 14:04:18 +0000 Subject: We should flush here too, to not leave a junk 'DOWN' message when the syncer exits. Also there's no sync_complete_ok any more... --- src/rabbit_mirror_queue_sync.erl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rabbit_mirror_queue_sync.erl b/src/rabbit_mirror_queue_sync.erl index 8d0407f2..6626ee2e 100644 --- a/src/rabbit_mirror_queue_sync.erl +++ b/src/rabbit_mirror_queue_sync.erl @@ -163,8 +163,7 @@ slave_sync_loop(Args = {Ref, MRef, Syncer, BQ, UpdateRamDur}, TRef, BQS) -> credit_flow:handle_bump_msg(Msg), slave_sync_loop(Args, TRef, BQS); {sync_complete, Ref} -> - Syncer ! {sync_complete_ok, Ref, self()}, - erlang:demonitor(MRef), + erlang:demonitor(MRef, [flush]), credit_flow:peer_down(Syncer), {ok, {TRef, BQS}}; {'$gen_cast', {set_maximum_since_use, Age}} -> -- cgit v1.2.1 From 45d09eb000f05523a384f2f5875ee57c9d44b19c Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 28 Nov 2012 14:23:33 +0000 Subject: Unshorten name. --- src/rabbit_mirror_queue_sync.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_mirror_queue_sync.erl b/src/rabbit_mirror_queue_sync.erl index 6626ee2e..33c7bccc 100644 --- a/src/rabbit_mirror_queue_sync.erl +++ b/src/rabbit_mirror_queue_sync.erl @@ -147,7 +147,7 @@ slave(Ref, TRef, Syncer, BQ, BQS, UpdateRamDuration) -> {_MsgCount, BQS1} = BQ:purge(BQS), slave_sync_loop({Ref, MRef, Syncer, BQ, UpdateRamDuration}, TRef, BQS1). -slave_sync_loop(Args = {Ref, MRef, Syncer, BQ, UpdateRamDur}, TRef, BQS) -> +slave_sync_loop(Args = {Ref, MRef, Syncer, BQ, UpdateRamDuration}, TRef, BQS) -> receive {'DOWN', MRef, process, Syncer, _Reason} -> %% If the master dies half way we are not in the usual @@ -173,7 +173,7 @@ slave_sync_loop(Args = {Ref, MRef, Syncer, BQ, UpdateRamDur}, TRef, BQS) -> BQS1 = BQ:set_ram_duration_target(Duration, BQS), slave_sync_loop(Args, TRef, BQS1); update_ram_duration -> - {TRef2, BQS1} = UpdateRamDur(BQ, BQS), + {TRef2, BQS1} = UpdateRamDuration(BQ, BQS), slave_sync_loop(Args, TRef2, BQS1); {sync_msg, Ref, Msg, Props} -> credit_flow:ack(Syncer, ?CREDIT_DISC_BOUND), -- cgit v1.2.1 From 048b669947b70f08f282905318be6dc78b04cfa5 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 28 Nov 2012 14:24:39 +0000 Subject: There is no TRef1. --- src/rabbit_mirror_queue_sync.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_mirror_queue_sync.erl b/src/rabbit_mirror_queue_sync.erl index 33c7bccc..0fd50e40 100644 --- a/src/rabbit_mirror_queue_sync.erl +++ b/src/rabbit_mirror_queue_sync.erl @@ -173,8 +173,8 @@ slave_sync_loop(Args = {Ref, MRef, Syncer, BQ, UpdateRamDuration}, TRef, BQS) -> BQS1 = BQ:set_ram_duration_target(Duration, BQS), slave_sync_loop(Args, TRef, BQS1); update_ram_duration -> - {TRef2, BQS1} = UpdateRamDuration(BQ, BQS), - slave_sync_loop(Args, TRef2, BQS1); + {TRef1, BQS1} = UpdateRamDuration(BQ, BQS), + slave_sync_loop(Args, TRef1, BQS1); {sync_msg, Ref, Msg, Props} -> credit_flow:ack(Syncer, ?CREDIT_DISC_BOUND), Props1 = Props#message_properties{needs_confirming = false}, -- cgit v1.2.1 From 2b12e2a85add3f01290b4984268a994940890707 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Wed, 28 Nov 2012 14:37:44 +0000 Subject: Provide early termination for backing queue fold --- src/rabbit_backing_queue.erl | 4 +-- src/rabbit_backing_queue_qc.erl | 5 ++-- src/rabbit_tests.erl | 43 ++++++++++++++++++++------------ src/rabbit_variable_queue.erl | 54 ++++++++++++++++++++++++++++------------- 4 files changed, 70 insertions(+), 36 deletions(-) diff --git a/src/rabbit_backing_queue.erl b/src/rabbit_backing_queue.erl index ffa716b6..071962a5 100644 --- a/src/rabbit_backing_queue.erl +++ b/src/rabbit_backing_queue.erl @@ -162,8 +162,8 @@ %% Fold over all the messages in a queue and return the accumulated %% results, leaving the queue undisturbed. -callback fold(fun((rabbit_types:basic_message(), - rabbit_types:message_properties(), A) -> A), - A, state()) -> {A, state()}. + rabbit_types:message_properties(), A) -> + {('stop' | 'cont'), A}), A, state()) -> {A, state()}. %% How long is my queue? -callback len(state()) -> non_neg_integer(). diff --git a/src/rabbit_backing_queue_qc.erl b/src/rabbit_backing_queue_qc.erl index 764911b9..982b2479 100644 --- a/src/rabbit_backing_queue_qc.erl +++ b/src/rabbit_backing_queue_qc.erl @@ -332,7 +332,8 @@ postcondition(S, {call, ?BQMOD, drain_confirmed, _Args}, Res) -> postcondition(S, {call, ?BQMOD, fold, _Args}, {Res, _BQ}) -> #state{messages = Messages} = S, lists:foldl(fun ({_SeqId, {MsgProps, Msg}}, Acc) -> - foldfun(Msg, MsgProps, Acc) + {cont, Acc1} = foldfun(Msg, MsgProps, Acc), + Acc1 end, foldacc(), gb_trees:to_list(Messages)) =:= Res; postcondition(#state{bqstate = BQ, len = Len}, {call, _M, _F, _A}, _Res) -> @@ -393,7 +394,7 @@ rand_choice(List, Selection, N) -> rand_choice(List -- [Picked], [Picked | Selection], N - 1). -foldfun(Msg, _MsgProps, Acc) -> [Msg | Acc]. +foldfun(Msg, _MsgProps, Acc) -> {cont, [Msg | Acc]}. foldacc() -> []. dropfun(Props) -> diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index d5c096a1..6b45b021 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -1697,6 +1697,7 @@ test_backing_queue() -> passed = test_queue_index(), passed = test_queue_index_props(), passed = test_variable_queue(), + passed = test_variable_queue_fold(), passed = test_variable_queue_delete_msg_store_files_callback(), passed = test_queue_recover(), application:set_env(rabbit, queue_index_max_journal_entries, @@ -2299,6 +2300,32 @@ wait_for_confirms(Unconfirmed) -> end end. +test_variable_queue_fold() -> + Count = rabbit_queue_index:next_segment_boundary(0), + [passed = with_fresh_variable_queue( + fun (VQ) -> test_variable_queue_fold_shortcut(VQ, Cut) end) || + Cut <- [0, 1, 2, Count div 2, Count - 1, Count, Count + 1, Count * 2]], + passed. + +test_variable_queue_fold_shortcut(VQ0, Cut) -> + Count = rabbit_queue_index:next_segment_boundary(0), + Msg2Int = fun (#basic_message{ + content = #content{ payload_fragments_rev = P}}) -> + binary_to_term(list_to_binary(lists:reverse(P))) + end, + VQ1 = rabbit_variable_queue:set_ram_duration_target(0, VQ0), + VQ2 = variable_queue_publish( + true, Count, fun (_, P) -> P end, fun erlang:term_to_binary/1, VQ1), + {Acc, VQ3} = rabbit_variable_queue:fold(fun (M, _, A) -> + case Msg2Int(M) =< Cut of + true -> {cont, [M | A]}; + false -> {stop, A} + end + end, [], VQ2), + true = [N || N <- lists:seq(lists:min([Cut, Count]), 1, -1)] == + [Msg2Int(M) || M <- Acc], + VQ3. + test_variable_queue() -> [passed = with_fresh_variable_queue(F) || F <- [fun test_variable_queue_dynamic_duration_change/1, @@ -2310,23 +2337,9 @@ test_variable_queue() -> fun test_dropwhile/1, fun test_dropwhile_varying_ram_duration/1, fun test_variable_queue_ack_limiting/1, - fun test_variable_queue_requeue/1, - fun test_variable_queue_fold/1]], + fun test_variable_queue_requeue/1]], passed. -test_variable_queue_fold(VQ0) -> - Count = rabbit_queue_index:next_segment_boundary(0) * 2 + 1, - VQ1 = rabbit_variable_queue:set_ram_duration_target(0, VQ0), - VQ2 = variable_queue_publish( - true, Count, fun (_, P) -> P end, fun erlang:term_to_binary/1, VQ1), - {Acc, VQ3} = rabbit_variable_queue:fold( - fun (M, _, A) -> [M | A] end, [], VQ2), - true = [term_to_binary(N) || N <- lists:seq(Count, 1, -1)] == - [list_to_binary(lists:reverse(P)) || - #basic_message{ content = #content{ payload_fragments_rev = P}} <- - Acc], - VQ3. - test_variable_queue_requeue(VQ0) -> Interval = 50, Count = rabbit_queue_index:next_segment_boundary(0) + 2 * Interval, diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index b826413a..f1b72036 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -687,13 +687,15 @@ fold(Fun, Acc, #vqstate { q1 = Q1, QFun = fun(MsgStatus, {Acc0, State0}) -> {#msg_status { msg = Msg, msg_props = MsgProps }, State1 } = read_msg(MsgStatus, false, State0), - {Fun(Msg, MsgProps, Acc0), State1} + {StopGo, AccNext} = Fun(Msg, MsgProps, Acc0), + {StopGo, {AccNext, State1}} end, - {Acc1, State1} = ?QUEUE:foldl(QFun, {Acc, State}, Q4), - {Acc2, State2} = ?QUEUE:foldl(QFun, {Acc1, State1}, Q3), - {Acc3, State3} = delta_fold(Fun, Acc2, DeltaSeqId, DeltaSeqIdEnd, State2), - {Acc4, State4} = ?QUEUE:foldl(QFun, {Acc3, State3}, Q2), - {Acc5, State5} = ?QUEUE:foldl(QFun, {Acc4, State4}, Q1), + {Cont1, {Acc1, State1}} = shortcut_qfold(QFun, {cont, {Acc, State}}, Q4), + {Cont2, {Acc2, State2}} = shortcut_qfold(QFun, {Cont1, {Acc1, State1}}, Q3), + {Cont3, {Acc3, State3}} = delta_fold(Fun, {Cont2, Acc2}, + DeltaSeqId, DeltaSeqIdEnd, State2), + {Cont4, {Acc4, State4}} = shortcut_qfold(QFun, {Cont3, {Acc3, State3}}, Q2), + {_, {Acc5, State5}} = shortcut_qfold(QFun, {Cont4, {Acc4, State4}}, Q1), {Acc5, State5}. len(#vqstate { len = Len }) -> Len. @@ -1442,9 +1444,26 @@ beta_limit(Q) -> delta_limit(?BLANK_DELTA_PATTERN(_X)) -> undefined; delta_limit(#delta { start_seq_id = StartSeqId }) -> StartSeqId. -delta_fold(_Fun, Acc, DeltaSeqIdEnd, DeltaSeqIdEnd, State) -> - {Acc, State}; -delta_fold(Fun, Acc, DeltaSeqId, DeltaSeqIdEnd, +shortcut_qfold(_Fun, {stop, _Acc} = A, _Q) -> + A; +shortcut_qfold(Fun, {cont, Acc} = A, Q) -> + case ?QUEUE:out(Q) of + {empty, _Q} -> A; + {{value, V}, Q1} -> shortcut_qfold(Fun, Fun(V, Acc), Q1) + end. + +shortcut_lfold(_Fun, {stop, _Acc} = A, _List) -> + A; +shortcut_lfold(_Fun, {cont, _Acc} = A, []) -> + A; +shortcut_lfold(Fun, {cont, Acc}, [H | Rest]) -> + shortcut_lfold(Fun, Fun(H, Acc), Rest). + +delta_fold(_Fun, {stop, Acc}, _DeltaSeqId, _DeltaSeqIdEnd, State) -> + {stop, {Acc, State}}; +delta_fold(_Fun, {cont, Acc}, DeltaSeqIdEnd, DeltaSeqIdEnd, State) -> + {cont, {Acc, State}}; +delta_fold(Fun, {cont, Acc}, DeltaSeqId, DeltaSeqIdEnd, #vqstate { index_state = IndexState, msg_store_clients = MSCState } = State) -> DeltaSeqId1 = lists:min( @@ -1452,14 +1471,15 @@ delta_fold(Fun, Acc, DeltaSeqId, DeltaSeqIdEnd, DeltaSeqIdEnd]), {List, IndexState1} = rabbit_queue_index:read(DeltaSeqId, DeltaSeqId1, IndexState), - {Acc1, MSCState1} = - lists:foldl(fun ({MsgId, _SeqId, MsgProps, IsPersistent, - _IsDelivered}, {Acc0, MSCState0}) -> - {{ok, Msg = #basic_message {}}, MSCState1} = - msg_store_read(MSCState0, IsPersistent, MsgId), - {Fun(Msg, MsgProps, Acc0), MSCState1} - end, {Acc, MSCState}, List), - delta_fold(Fun, Acc1, DeltaSeqId1, DeltaSeqIdEnd, + {StopCont, {Acc1, MSCState1}} = + shortcut_lfold(fun ({MsgId, _SeqId, MsgProps, IsPersistent, + _IsDelivered}, {Acc0, MSCState0}) -> + {{ok, Msg = #basic_message {}}, MSCState1} = + msg_store_read(MSCState0, IsPersistent, MsgId), + {StopCont, AccNext} = Fun(Msg, MsgProps, Acc0), + {StopCont, {AccNext, MSCState1}} + end, {cont, {Acc, MSCState}}, List), + delta_fold(Fun, {StopCont, Acc1}, DeltaSeqId1, DeltaSeqIdEnd, State #vqstate { index_state = IndexState1, msg_store_clients = MSCState1 }). -- cgit v1.2.1 From 30df1be6eb28a2a914c45c75e0e92508dd82ef14 Mon Sep 17 00:00:00 2001 From: Alvaro Videla Date: Wed, 28 Nov 2012 15:51:43 +0100 Subject: removes unreachable expression --- packaging/standalone/src/rabbit_release.erl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packaging/standalone/src/rabbit_release.erl b/packaging/standalone/src/rabbit_release.erl index 877fd73e..787ec9ec 100644 --- a/packaging/standalone/src/rabbit_release.erl +++ b/packaging/standalone/src/rabbit_release.erl @@ -75,8 +75,7 @@ start() -> systools:script2boot(RootName), %% Make release tarfile make_tar(RootName, RabbitHome), - rabbit_misc:quit(0), - ok. + rabbit_misc:quit(0). make_tar(Release, RabbitHome) -> systools:make_tar(Release, -- cgit v1.2.1 From 1fed273a420f6bd5ed543fc9c9cb18b31aca6398 Mon Sep 17 00:00:00 2001 From: Alvaro Videla Date: Wed, 28 Nov 2012 15:52:20 +0100 Subject: removes unnecessary comment --- packaging/standalone/src/rabbit_release.erl | 1 - 1 file changed, 1 deletion(-) diff --git a/packaging/standalone/src/rabbit_release.erl b/packaging/standalone/src/rabbit_release.erl index 787ec9ec..c759d60d 100644 --- a/packaging/standalone/src/rabbit_release.erl +++ b/packaging/standalone/src/rabbit_release.erl @@ -22,7 +22,6 @@ -define(BaseApps, [rabbit]). -define(ERROR_CODE, 1). -%% This module is based on rabbit_prelaunch.erl from rabbitmq-server source code %% We need to calculate all the ERTS apps we need to ship with a %% standalone rabbit. To acomplish that we need to unpack and load the plugins %% apps that are shiped with rabbit. -- cgit v1.2.1 From 05c66c2565da495571cbf20553a469f69bcef015 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 28 Nov 2012 15:33:48 +0000 Subject: cosmetic --- src/rabbit_mirror_queue_master.erl | 28 +++++++++++++--------------- src/rabbit_mirror_queue_sync.erl | 5 +---- 2 files changed, 14 insertions(+), 19 deletions(-) diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index e8734a8c..0e8748fd 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -127,27 +127,27 @@ stop_mirroring(State = #state { coordinator = CPid, stop_all_slaves(shutdown, State), {BQ, BQS}. -sync_mirrors(State = #state{name = Name}) -> +sync_mirrors(State = #state{name = QName}) -> {ok, #amqqueue{slave_pids = SPids, sync_slave_pids = SSPids}} = - rabbit_amqqueue:lookup(Name), + rabbit_amqqueue:lookup(QName), sync_mirrors(SPids -- SSPids, State). -sync_mirrors([], State = #state{name = Name}) -> +sync_mirrors([], State = #state{name = QName}) -> rabbit_log:info("Synchronising ~s: nothing to do~n", - [rabbit_misc:rs(Name)]), + [rabbit_misc:rs(QName)]), State; -sync_mirrors(SPids, State = #state { name = Name, +sync_mirrors(SPids, State = #state { name = QName, gm = GM, backing_queue = BQ, backing_queue_state = BQS }) -> rabbit_log:info("Synchronising ~s with slaves ~p: ~p messages to do~n", - [rabbit_misc:rs(Name), SPids, BQ:len(BQS)]), + [rabbit_misc:rs(QName), SPids, BQ:len(BQS)]), Ref = make_ref(), Syncer = rabbit_mirror_queue_sync:master_prepare(Ref, SPids), gm:broadcast(GM, {sync_start, Ref, Syncer, SPids}), - BQS1 = rabbit_mirror_queue_sync:master_go(Syncer, Ref, Name, BQ, BQS), + BQS1 = rabbit_mirror_queue_sync:master_go(Syncer, Ref, QName, BQ, BQS), rabbit_log:info("Synchronising ~s: complete~n", - [rabbit_misc:rs(Name)]), + [rabbit_misc:rs(QName)]), State#state{backing_queue_state = BQS1}. terminate({shutdown, dropped} = Reason, @@ -173,11 +173,9 @@ delete_and_terminate(Reason, State = #state { backing_queue = BQ, stop_all_slaves(Reason, State), State#state{backing_queue_state = BQ:delete_and_terminate(Reason, BQS)}. -stop_all_slaves(Reason, #state{name = Name, - gm = GM}) -> - {ok, #amqqueue{slave_pids = SPids}} = rabbit_amqqueue:lookup(Name), - Slaves = [Pid || Pid <- SPids], - MRefs = [erlang:monitor(process, S) || S <- Slaves], +stop_all_slaves(Reason, #state{name = QName, gm = GM}) -> + {ok, #amqqueue{slave_pids = SPids}} = rabbit_amqqueue:lookup(QName), + MRefs = [erlang:monitor(process, SPid) || SPid <- SPids], ok = gm:broadcast(GM, {delete_and_terminate, Reason}), [receive {'DOWN', MRef, process, _Pid, _Info} -> ok end || MRef <- MRefs], %% Normally when we remove a slave another slave or master will @@ -185,11 +183,11 @@ stop_all_slaves(Reason, #state{name = Name, %% have stopped listening ourselves. So manually clean up. rabbit_misc:execute_mnesia_transaction( fun () -> - [Q] = mnesia:read({rabbit_queue, Name}), + [Q] = mnesia:read({rabbit_queue, QName}), rabbit_mirror_queue_misc:store_updated_slaves( Q #amqqueue { gm_pids = [], slave_pids = [] }) end), - ok = gm:forget_group(Name). + ok = gm:forget_group(QName). purge(State = #state { gm = GM, backing_queue = BQ, diff --git a/src/rabbit_mirror_queue_sync.erl b/src/rabbit_mirror_queue_sync.erl index 0fd50e40..b9fb6cb6 100644 --- a/src/rabbit_mirror_queue_sync.erl +++ b/src/rabbit_mirror_queue_sync.erl @@ -83,10 +83,7 @@ master_send({Syncer, Ref, Name}, I, Last, Msg, MsgProps) -> %% Syncer syncer(Ref, MPid, SPids) -> - SPidsMRefs = [begin - MRef = erlang:monitor(process, SPid), - {SPid, MRef} - end || SPid <- SPids], + SPidsMRefs = [{SPid, erlang:monitor(process, SPid)} || SPid <- SPids], %% We wait for a reply from the slaves so that we know they are in %% a receive block and will thus receive messages we send to them %% *without* those messages ending up in their gen_server2 pqueue. -- cgit v1.2.1 From d09b83297be1b7520515dbc1289a6ee25c8339e0 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 28 Nov 2012 16:06:53 +0000 Subject: cosmetic --- src/rabbit_variable_queue.erl | 44 ++++++++++++++++++++----------------------- 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index f1b72036..5bcac0dc 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -690,12 +690,12 @@ fold(Fun, Acc, #vqstate { q1 = Q1, {StopGo, AccNext} = Fun(Msg, MsgProps, Acc0), {StopGo, {AccNext, State1}} end, - {Cont1, {Acc1, State1}} = shortcut_qfold(QFun, {cont, {Acc, State}}, Q4), - {Cont2, {Acc2, State2}} = shortcut_qfold(QFun, {Cont1, {Acc1, State1}}, Q3), + {Cont1, {Acc1, State1}} = qfoldl(QFun, {cont, {Acc, State }}, Q4), + {Cont2, {Acc2, State2}} = qfoldl(QFun, {Cont1, {Acc1, State1}}, Q3), {Cont3, {Acc3, State3}} = delta_fold(Fun, {Cont2, Acc2}, DeltaSeqId, DeltaSeqIdEnd, State2), - {Cont4, {Acc4, State4}} = shortcut_qfold(QFun, {Cont3, {Acc3, State3}}, Q2), - {_, {Acc5, State5}} = shortcut_qfold(QFun, {Cont4, {Acc4, State4}}, Q1), + {Cont4, {Acc4, State4}} = qfoldl(QFun, {Cont3, {Acc3, State3}}, Q2), + {_, {Acc5, State5}} = qfoldl(QFun, {Cont4, {Acc4, State4}}, Q1), {Acc5, State5}. len(#vqstate { len = Len }) -> Len. @@ -1444,26 +1444,22 @@ beta_limit(Q) -> delta_limit(?BLANK_DELTA_PATTERN(_X)) -> undefined; delta_limit(#delta { start_seq_id = StartSeqId }) -> StartSeqId. -shortcut_qfold(_Fun, {stop, _Acc} = A, _Q) -> - A; -shortcut_qfold(Fun, {cont, Acc} = A, Q) -> +qfoldl(_Fun, {stop, _Acc} = A, _Q) -> A; +qfoldl( Fun, {cont, Acc} = A, Q) -> case ?QUEUE:out(Q) of {empty, _Q} -> A; - {{value, V}, Q1} -> shortcut_qfold(Fun, Fun(V, Acc), Q1) + {{value, V}, Q1} -> qfoldl(Fun, Fun(V, Acc), Q1) end. -shortcut_lfold(_Fun, {stop, _Acc} = A, _List) -> - A; -shortcut_lfold(_Fun, {cont, _Acc} = A, []) -> - A; -shortcut_lfold(Fun, {cont, Acc}, [H | Rest]) -> - shortcut_lfold(Fun, Fun(H, Acc), Rest). +lfoldl(_Fun, {stop, _Acc} = A, _L) -> A; +lfoldl(_Fun, {cont, _Acc} = A, []) -> A; +lfoldl( Fun, {cont, Acc}, [H | T]) -> lfoldl(Fun, Fun(H, Acc), T). -delta_fold(_Fun, {stop, Acc}, _DeltaSeqId, _DeltaSeqIdEnd, State) -> +delta_fold(_Fun, {stop, Acc}, _DeltaSeqId, _DeltaSeqIdEnd, State) -> {stop, {Acc, State}}; -delta_fold(_Fun, {cont, Acc}, DeltaSeqIdEnd, DeltaSeqIdEnd, State) -> +delta_fold(_Fun, {cont, Acc}, DeltaSeqIdEnd, DeltaSeqIdEnd, State) -> {cont, {Acc, State}}; -delta_fold(Fun, {cont, Acc}, DeltaSeqId, DeltaSeqIdEnd, +delta_fold( Fun, {cont, Acc}, DeltaSeqId, DeltaSeqIdEnd, #vqstate { index_state = IndexState, msg_store_clients = MSCState } = State) -> DeltaSeqId1 = lists:min( @@ -1472,13 +1468,13 @@ delta_fold(Fun, {cont, Acc}, DeltaSeqId, DeltaSeqIdEnd, {List, IndexState1} = rabbit_queue_index:read(DeltaSeqId, DeltaSeqId1, IndexState), {StopCont, {Acc1, MSCState1}} = - shortcut_lfold(fun ({MsgId, _SeqId, MsgProps, IsPersistent, - _IsDelivered}, {Acc0, MSCState0}) -> - {{ok, Msg = #basic_message {}}, MSCState1} = - msg_store_read(MSCState0, IsPersistent, MsgId), - {StopCont, AccNext} = Fun(Msg, MsgProps, Acc0), - {StopCont, {AccNext, MSCState1}} - end, {cont, {Acc, MSCState}}, List), + lfoldl(fun ({MsgId, _SeqId, MsgProps, IsPersistent, _IsDelivered}, + {Acc0, MSCState0}) -> + {{ok, Msg = #basic_message {}}, MSCState1} = + msg_store_read(MSCState0, IsPersistent, MsgId), + {StopCont, AccNext} = Fun(Msg, MsgProps, Acc0), + {StopCont, {AccNext, MSCState1}} + end, {cont, {Acc, MSCState}}, List), delta_fold(Fun, {StopCont, Acc1}, DeltaSeqId1, DeltaSeqIdEnd, State #vqstate { index_state = IndexState1, msg_store_clients = MSCState1 }). -- cgit v1.2.1 From f28de3bf301671f9a383273b92a487f91eba6da1 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Wed, 28 Nov 2012 16:37:06 +0000 Subject: Better quickcheck test of interruptable backing queue fold --- src/rabbit_backing_queue_qc.erl | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/rabbit_backing_queue_qc.erl b/src/rabbit_backing_queue_qc.erl index 982b2479..61ec02fb 100644 --- a/src/rabbit_backing_queue_qc.erl +++ b/src/rabbit_backing_queue_qc.erl @@ -159,7 +159,7 @@ qc_purge(#state{bqstate = BQ}) -> {call, ?BQMOD, purge, [BQ]}. qc_fold(#state{bqstate = BQ}) -> - {call, ?BQMOD, fold, [fun foldfun/3, foldacc(), BQ]}. + {call, ?BQMOD, fold, [makefoldfun(pos_integer()), foldacc(), BQ]}. %% Preconditions @@ -329,12 +329,14 @@ postcondition(S, {call, ?BQMOD, drain_confirmed, _Args}, Res) -> lists:all(fun (M) -> gb_sets:is_element(M, Confirms) end, ReportedConfirmed); -postcondition(S, {call, ?BQMOD, fold, _Args}, {Res, _BQ}) -> +postcondition(S, {call, ?BQMOD, fold, [FoldFun, Acc0, _BQ0]}, {Res, _BQ1}) -> #state{messages = Messages} = S, - lists:foldl(fun ({_SeqId, {MsgProps, Msg}}, Acc) -> - {cont, Acc1} = foldfun(Msg, MsgProps, Acc), - Acc1 - end, foldacc(), gb_trees:to_list(Messages)) =:= Res; + {_, Model} = lists:foldl(fun ({_SeqId, {_MsgProps, _Msg}}, {stop, Acc}) -> + {stop, Acc}; + ({_SeqId, {MsgProps, Msg}}, {cont, Acc}) -> + FoldFun(Msg, MsgProps, Acc) + end, {cont, Acc0}, gb_trees:to_list(Messages)), + true = Model =:= Res; postcondition(#state{bqstate = BQ, len = Len}, {call, _M, _F, _A}, _Res) -> ?BQMOD:len(BQ) =:= Len. @@ -394,7 +396,13 @@ rand_choice(List, Selection, N) -> rand_choice(List -- [Picked], [Picked | Selection], N - 1). -foldfun(Msg, _MsgProps, Acc) -> {cont, [Msg | Acc]}. +makefoldfun(Size) -> + fun (Msg, _MsgProps, Acc) -> + case length(Acc) > Size of + false -> {cont, [Msg | Acc]}; + true -> {stop, Acc} + end + end. foldacc() -> []. dropfun(Props) -> -- cgit v1.2.1 From aabcfbb00fb7b914bde0b28dabbde81b4e7e233e Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 28 Nov 2012 16:43:47 +0000 Subject: cosmetic --- src/rabbit_backing_queue.erl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/rabbit_backing_queue.erl b/src/rabbit_backing_queue.erl index 071962a5..6315a56a 100644 --- a/src/rabbit_backing_queue.erl +++ b/src/rabbit_backing_queue.erl @@ -162,8 +162,9 @@ %% Fold over all the messages in a queue and return the accumulated %% results, leaving the queue undisturbed. -callback fold(fun((rabbit_types:basic_message(), - rabbit_types:message_properties(), A) -> - {('stop' | 'cont'), A}), A, state()) -> {A, state()}. + rabbit_types:message_properties(), + A) -> {('stop' | 'cont'), A}), + A, state()) -> {A, state()}. %% How long is my queue? -callback len(state()) -> non_neg_integer(). -- cgit v1.2.1 From 853e52016edf7bc637269554ebdd2bcb92e7947f Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 28 Nov 2012 17:01:30 +0000 Subject: tweak --- src/rabbit_tests.erl | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 6b45b021..3f32b003 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -1697,7 +1697,6 @@ test_backing_queue() -> passed = test_queue_index(), passed = test_queue_index_props(), passed = test_variable_queue(), - passed = test_variable_queue_fold(), passed = test_variable_queue_delete_msg_store_files_callback(), passed = test_queue_recover(), application:set_env(rabbit, queue_index_max_journal_entries, @@ -2300,15 +2299,25 @@ wait_for_confirms(Unconfirmed) -> end end. -test_variable_queue_fold() -> +test_variable_queue() -> + [passed = with_fresh_variable_queue(F) || + F <- [fun test_variable_queue_dynamic_duration_change/1, + fun test_variable_queue_partial_segments_delta_thing/1, + fun test_variable_queue_all_the_bits_not_covered_elsewhere1/1, + fun test_variable_queue_all_the_bits_not_covered_elsewhere2/1, + fun test_drop/1, + fun test_variable_queue_fold_msg_on_disk/1, + fun test_dropwhile/1, + fun test_dropwhile_varying_ram_duration/1, + fun test_variable_queue_ack_limiting/1, + fun test_variable_queue_requeue/1]], Count = rabbit_queue_index:next_segment_boundary(0), [passed = with_fresh_variable_queue( - fun (VQ) -> test_variable_queue_fold_shortcut(VQ, Cut) end) || + fun (VQ) -> test_variable_queue_fold(Cut, Count, VQ) end) || Cut <- [0, 1, 2, Count div 2, Count - 1, Count, Count + 1, Count * 2]], passed. -test_variable_queue_fold_shortcut(VQ0, Cut) -> - Count = rabbit_queue_index:next_segment_boundary(0), +test_variable_queue_fold(Cut, Count, VQ0) -> Msg2Int = fun (#basic_message{ content = #content{ payload_fragments_rev = P}}) -> binary_to_term(list_to_binary(lists:reverse(P))) @@ -2326,20 +2335,6 @@ test_variable_queue_fold_shortcut(VQ0, Cut) -> [Msg2Int(M) || M <- Acc], VQ3. -test_variable_queue() -> - [passed = with_fresh_variable_queue(F) || - F <- [fun test_variable_queue_dynamic_duration_change/1, - fun test_variable_queue_partial_segments_delta_thing/1, - fun test_variable_queue_all_the_bits_not_covered_elsewhere1/1, - fun test_variable_queue_all_the_bits_not_covered_elsewhere2/1, - fun test_drop/1, - fun test_variable_queue_fold_msg_on_disk/1, - fun test_dropwhile/1, - fun test_dropwhile_varying_ram_duration/1, - fun test_variable_queue_ack_limiting/1, - fun test_variable_queue_requeue/1]], - passed. - test_variable_queue_requeue(VQ0) -> Interval = 50, Count = rabbit_queue_index:next_segment_boundary(0) + 2 * Interval, -- cgit v1.2.1 From 68d625779bde08a5e1f5100f312c3d0f8d42a043 Mon Sep 17 00:00:00 2001 From: Alvaro Videla Date: Wed, 28 Nov 2012 18:09:27 +0100 Subject: refactors code to extract/load plugin code --- packaging/standalone/src/rabbit_release.erl | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/packaging/standalone/src/rabbit_release.erl b/packaging/standalone/src/rabbit_release.erl index c759d60d..03b18133 100644 --- a/packaging/standalone/src/rabbit_release.erl +++ b/packaging/standalone/src/rabbit_release.erl @@ -34,19 +34,18 @@ start() -> init:get_plain_arguments(), RootName = UnpackedPluginDir ++ "/rabbit", + %% extract the plugins so we can load their apps later prepare_plugins(PluginsDistDir, UnpackedPluginDir), PluginAppNames = [P#plugin.name || P <- rabbit_plugins:list(PluginsDistDir)], - %% we need to call find_plugins because it has the secondary effect of - %% adding the plugin ebin folder to the code path. - %% We need that in order to load the plugin app - RequiredApps = find_plugins(UnpackedPluginDir), + %% add the plugin ebin folder to the code path. + add_plugins_to_path(UnpackedPluginDir), %% Build the entire set of dependencies - this will load the %% applications along the way - AllApps = case catch sets:to_list(expand_dependencies(RequiredApps)) of + AllApps = case catch sets:to_list(expand_dependencies(PluginAppNames)) of {failed_to_load_app, App, Err} -> terminate("failed to load application ~s:~n~p", [App, Err]); @@ -139,19 +138,14 @@ expand_dependencies(Current, [Next|Rest]) -> expand_dependencies(sets:add_element(Next, Current), Rest ++ Unique) end. -find_plugins(PluginDir) -> - [prepare_dir_plugin(PluginName) || +add_plugins_to_path(PluginDir) -> + [add_plugin_to_path(PluginName) || PluginName <- filelib:wildcard(PluginDir ++ "/*/ebin/*.app")]. -prepare_dir_plugin(PluginAppDescFn) -> +add_plugin_to_path(PluginAppDescFn) -> %% Add the plugin ebin directory to the load path PluginEBinDirN = filename:dirname(PluginAppDescFn), - code:add_path(PluginEBinDirN), - - %% We want the second-last token - NameTokens = string:tokens(PluginAppDescFn,"/."), - PluginNameString = lists:nth(length(NameTokens) - 1, NameTokens), - list_to_atom(PluginNameString). + code:add_path(PluginEBinDirN). terminate(Fmt, Args) -> io:format("ERROR: " ++ Fmt ++ "~n", Args), -- cgit v1.2.1 From ffac65f860a497ad7eed12763e3eb79f2a3f1ff6 Mon Sep 17 00:00:00 2001 From: Alvaro Videla Date: Wed, 28 Nov 2012 18:30:34 +0100 Subject: removes empty folder from the release --- packaging/standalone/Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packaging/standalone/Makefile b/packaging/standalone/Makefile index cd85ffca..2730eb66 100644 --- a/packaging/standalone/Makefile +++ b/packaging/standalone/Makefile @@ -38,6 +38,9 @@ dist: # move rabbitmq files to top level folder mv $(RLS_DIR)/lib/rabbit-$(VERSION)/* $(RLS_DIR) +# remove empty lib/rabbit-$(VERSION) folder + rm -rf $(RLS_DIR)/lib/rabbit-$(VERSION) + tar -zcf $(TARGET_TARBALL).tar.gz -C $(TARGET_DIR)/release $(TARGET_DIR) rm -rf $(SOURCE_DIR) $(TARGET_DIR) -- cgit v1.2.1 From 262927855c8fefd66527a373eb158598efd35766 Mon Sep 17 00:00:00 2001 From: Alvaro Videla Date: Wed, 28 Nov 2012 18:33:21 +0100 Subject: cosmetics --- packaging/standalone/src/rabbit_release.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packaging/standalone/src/rabbit_release.erl b/packaging/standalone/src/rabbit_release.erl index 03b18133..26f36d68 100644 --- a/packaging/standalone/src/rabbit_release.erl +++ b/packaging/standalone/src/rabbit_release.erl @@ -37,12 +37,12 @@ start() -> %% extract the plugins so we can load their apps later prepare_plugins(PluginsDistDir, UnpackedPluginDir), - PluginAppNames = [P#plugin.name || - P <- rabbit_plugins:list(PluginsDistDir)], - %% add the plugin ebin folder to the code path. add_plugins_to_path(UnpackedPluginDir), + PluginAppNames = [P#plugin.name || + P <- rabbit_plugins:list(PluginsDistDir)], + %% Build the entire set of dependencies - this will load the %% applications along the way AllApps = case catch sets:to_list(expand_dependencies(PluginAppNames)) of -- cgit v1.2.1 From 8d88d5f511c31f8957038d3fb2b247472cb677c1 Mon Sep 17 00:00:00 2001 From: Alvaro Videla Date: Wed, 28 Nov 2012 18:46:19 +0100 Subject: renames package to include "mac" in it --- packaging/standalone/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packaging/standalone/Makefile b/packaging/standalone/Makefile index 2730eb66..65c72c4d 100644 --- a/packaging/standalone/Makefile +++ b/packaging/standalone/Makefile @@ -1,7 +1,7 @@ VERSION=0.0.0 SOURCE_DIR=rabbitmq-server-$(VERSION) TARGET_DIR=rabbitmq_server-$(VERSION) -TARGET_TARBALL=rabbitmq-server-standalone-$(VERSION) +TARGET_TARBALL=rabbitmq-server-mac-standalone-$(VERSION) RLS_DIR=$(TARGET_DIR)/release/$(TARGET_DIR) ERTS_VSN=$(shell erl -noshell -eval 'io:format("~s", [erlang:system_info(version)]), halt().') @@ -45,7 +45,7 @@ dist: rm -rf $(SOURCE_DIR) $(TARGET_DIR) clean: clean_partial - rm -f rabbitmq-server-standalone-*.tar.gz + rm -f rabbitmq-server-mac-standalone-*.tar.gz clean_partial: rm -rf $(SOURCE_DIR) -- cgit v1.2.1 From 270140dce3540332bbeb8dc52921cc081caa5755 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 28 Nov 2012 19:40:01 +0000 Subject: expand test coverage to at least two iterations of delta_fold previously it had no test coverage at all Also, re-use the same vq for all fold tests, for efficiency. --- src/rabbit_tests.erl | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 3f32b003..91d620a7 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -2310,30 +2310,33 @@ test_variable_queue() -> fun test_dropwhile/1, fun test_dropwhile_varying_ram_duration/1, fun test_variable_queue_ack_limiting/1, - fun test_variable_queue_requeue/1]], - Count = rabbit_queue_index:next_segment_boundary(0), - [passed = with_fresh_variable_queue( - fun (VQ) -> test_variable_queue_fold(Cut, Count, VQ) end) || - Cut <- [0, 1, 2, Count div 2, Count - 1, Count, Count + 1, Count * 2]], + fun test_variable_queue_requeue/1, + fun test_variable_queue_fold/1]], passed. -test_variable_queue_fold(Cut, Count, VQ0) -> - Msg2Int = fun (#basic_message{ - content = #content{ payload_fragments_rev = P}}) -> - binary_to_term(list_to_binary(lists:reverse(P))) - end, +test_variable_queue_fold(VQ0) -> + Count = rabbit_queue_index:next_segment_boundary(0) * 2 + 64, VQ1 = rabbit_variable_queue:set_ram_duration_target(0, VQ0), VQ2 = variable_queue_publish( true, Count, fun (_, P) -> P end, fun erlang:term_to_binary/1, VQ1), - {Acc, VQ3} = rabbit_variable_queue:fold(fun (M, _, A) -> - case Msg2Int(M) =< Cut of - true -> {cont, [M | A]}; - false -> {stop, A} - end - end, [], VQ2), + lists:foldl( + fun (Cut, VQ3) -> test_variable_queue_fold(Cut, Count, VQ3) end, + VQ2, [0, 1, 2, Count div 2, Count - 1, Count, Count + 1, Count * 2]). + +test_variable_queue_fold(Cut, Count, VQ0) -> + {Acc, VQ1} = rabbit_variable_queue:fold( + fun (M, _, A) -> + case msg2int(M) =< Cut of + true -> {cont, [M | A]}; + false -> {stop, A} + end + end, [], VQ0), true = [N || N <- lists:seq(lists:min([Cut, Count]), 1, -1)] == - [Msg2Int(M) || M <- Acc], - VQ3. + [msg2int(M) || M <- Acc], + VQ1. + +msg2int(#basic_message{content = #content{ payload_fragments_rev = P}}) -> + binary_to_term(list_to_binary(lists:reverse(P))). test_variable_queue_requeue(VQ0) -> Interval = 50, -- cgit v1.2.1 From 7d9da3ee2ce62a2b684c40e416c8df39ccd27895 Mon Sep 17 00:00:00 2001 From: Alvaro Videla Date: Wed, 28 Nov 2012 23:49:58 +0100 Subject: moves sed invocation to bash script --- packaging/standalone/Makefile | 18 +----------------- packaging/standalone/fix-rabbitmq-defaults.sh | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 17 deletions(-) create mode 100755 packaging/standalone/fix-rabbitmq-defaults.sh diff --git a/packaging/standalone/Makefile b/packaging/standalone/Makefile index 65c72c4d..fced396f 100644 --- a/packaging/standalone/Makefile +++ b/packaging/standalone/Makefile @@ -22,7 +22,7 @@ dist: MAN_DIR=`pwd`/$(TARGET_DIR)/share/man \ install - $(MAKE) fix_rabbitmq_defaults + ./fix-rabbitmq-defaults.sh $(TARGET_DIR) $(ERTS_VSN) $(VERSION) mkdir -p $(TARGET_DIR)/etc/rabbitmq @@ -62,19 +62,3 @@ generate_release: -s rabbit_release \ -extra "$(RABBITMQ_PLUGINS_DIR)" "$(RABBITMQ_PLUGINS_EXPAND_DIR)" "$(RABBITMQ_HOME)" rm src/rabbit_release.beam - -## Here we set the RABBITMQ_HOME variable, -## then we make ERL_DIR point to our released erl -## and we add the paths to our released start_clean and start_sasl boot scripts -.PHONY : fix_rabbitmq_defaults -fix_rabbitmq_defaults: - sed -e 's:^SYS_PREFIX=$$:SYS_PREFIX=\$${RABBITMQ_HOME}:' \ - $(TARGET_DIR)/sbin/rabbitmq-defaults >$(TARGET_DIR)/sbin/rabbitmq-defaults.tmp \ - && sed -e 's:^ERL_DIR=$$:ERL_DIR=\$${RABBITMQ_HOME}/erts-$(ERTS_VSN)/bin/:' \ - $(TARGET_DIR)/sbin/rabbitmq-defaults.tmp >$(TARGET_DIR)/sbin/rabbitmq-defaults.tmp1 \ - && sed -e 's:start_clean$$:"\$${SYS_PREFIX}/releases/$(VERSION)/start_clean":' \ - $(TARGET_DIR)/sbin/rabbitmq-defaults.tmp1 >$(TARGET_DIR)/sbin/rabbitmq-defaults.tmp \ - && sed -e 's:start_sasl:"\$${SYS_PREFIX}/releases/$(VERSION)/start_sasl":' \ - $(TARGET_DIR)/sbin/rabbitmq-defaults.tmp >$(TARGET_DIR)/sbin/rabbitmq-defaults - - chmod 0755 $(TARGET_DIR)/sbin/rabbitmq-defaults diff --git a/packaging/standalone/fix-rabbitmq-defaults.sh b/packaging/standalone/fix-rabbitmq-defaults.sh new file mode 100755 index 00000000..021c47bc --- /dev/null +++ b/packaging/standalone/fix-rabbitmq-defaults.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +TARGET_DIR=$1 +ERTS_VSN=$2 +VERSION=$3 + +## Here we set the RABBITMQ_HOME variable, +## then we make ERL_DIR point to our released erl +## and we add the paths to our released start_clean and start_sasl boot scripts + +sed -e 's:^SYS_PREFIX=$:SYS_PREFIX=\${RABBITMQ_HOME}:' \ + "${TARGET_DIR}"/sbin/rabbitmq-defaults >"${TARGET_DIR}"/sbin/rabbitmq-defaults.tmp \ + && sed -e 's:^ERL_DIR=$:ERL_DIR=\${RABBITMQ_HOME}/erts-'"${ERTS_VSN}"'/bin/:' \ + "${TARGET_DIR}"/sbin/rabbitmq-defaults.tmp >"${TARGET_DIR}"/sbin/rabbitmq-defaults.tmp1 \ + && sed -e 's:start_clean$:"\${SYS_PREFIX}/releases/'"${VERSION}"'/start_clean":' \ + "${TARGET_DIR}"/sbin/rabbitmq-defaults.tmp1 >"${TARGET_DIR}"/sbin/rabbitmq-defaults.tmp \ + && sed -e 's:start_sasl:"\${SYS_PREFIX}/releases/'"${VERSION}"'/start_sasl":' \ + "${TARGET_DIR}"/sbin/rabbitmq-defaults.tmp >"${TARGET_DIR}"/sbin/rabbitmq-defaults + +chmod 0755 "${TARGET_DIR}"/sbin/rabbitmq-defaults -- cgit v1.2.1 From 3ba3ce551da8594a7d9fcb1895c37b53dc1ebc19 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 29 Nov 2012 11:22:07 +0000 Subject: Oops, didn't even compile that last one. --- src/rabbit_amqqueue_process.erl | 9 ++++----- src/rabbit_mirror_queue_master.erl | 13 ++++++++----- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index c932249e..acbea4e9 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1156,13 +1156,12 @@ handle_call({requeue, AckTags, ChPid}, From, State) -> handle_call(sync_mirrors, From, State = #q{backing_queue = rabbit_mirror_queue_master = BQ, backing_queue_state = BQS}) -> - S = fun(BQSN) -> State#state{backing_queue_state = BQSN} end, + S = fun(BQSN) -> State#q{backing_queue_state = BQSN} end, case BQ:depth(BQS) - BQ:len(BQS) of 0 -> gen_server2:reply(From, ok), - case rabbit_mirror_queue_master:sync_mirrors(BQS) of - {shutdown, Reason, BQS1} -> {stop, Reason, S(BQS1)}; - {ok, BQS1} -> noreply(S(BQS1)) - end + case rabbit_mirror_queue_master:sync_mirrors(BQS) of + {shutdown, Reason, BQS1} -> {stop, Reason, S(BQS1)}; + {ok, BQS1} -> noreply(S(BQS1)) end; _ -> reply({error, pending_acks}, State) end; diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index 0e8748fd..439d1f4b 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -135,7 +135,7 @@ sync_mirrors(State = #state{name = QName}) -> sync_mirrors([], State = #state{name = QName}) -> rabbit_log:info("Synchronising ~s: nothing to do~n", [rabbit_misc:rs(QName)]), - State; + {ok, State}; sync_mirrors(SPids, State = #state { name = QName, gm = GM, backing_queue = BQ, @@ -145,10 +145,13 @@ sync_mirrors(SPids, State = #state { name = QName, Ref = make_ref(), Syncer = rabbit_mirror_queue_sync:master_prepare(Ref, SPids), gm:broadcast(GM, {sync_start, Ref, Syncer, SPids}), - BQS1 = rabbit_mirror_queue_sync:master_go(Syncer, Ref, QName, BQ, BQS), - rabbit_log:info("Synchronising ~s: complete~n", - [rabbit_misc:rs(QName)]), - State#state{backing_queue_state = BQS1}. + S = fun(BQSN) -> State#state{backing_queue_state = BQSN} end, + case rabbit_mirror_queue_sync:master_go(Syncer, Ref, QName, BQ, BQS) of + {shutdown, R, BQS1} -> {stop, R, S(BQS1)}; + {ok, BQS1} -> rabbit_log:info("Synchronising ~s: complete~n", + [rabbit_misc:rs(QName)]), + {ok, S(BQS1)} + end. terminate({shutdown, dropped} = Reason, State = #state { backing_queue = BQ, -- cgit v1.2.1 From a1b7f66d6cce9b53356ec0092925dfcc7cc8413d Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 29 Nov 2012 11:24:47 +0000 Subject: Reverse master/syncer communication. --- src/rabbit_mirror_queue_sync.erl | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/rabbit_mirror_queue_sync.erl b/src/rabbit_mirror_queue_sync.erl index b6d93c0d..acf3e3e3 100644 --- a/src/rabbit_mirror_queue_sync.erl +++ b/src/rabbit_mirror_queue_sync.erl @@ -40,8 +40,8 @@ %% (from channel) || -- (spawns) --> || || %% || --------- sync_start (over GM) -------> || %% || || <--- sync_ready ---- || -%% || ----- msg* ---> || || } -%% || <-- msg_ok* --- || || } loop +%% || <--- next* ---- || || } +%% || ---- msg* ----> || || } loop %% || || ----- sync_msg* ---> || } %% || || <---- (credit)* ---- || } %% || ---- done ----> || || @@ -62,13 +62,15 @@ master_go(Syncer, Ref, Name, BQ, BQS) -> master_send(SendArgs, I, Last, Msg, MsgProps) end, {0, erlang:now()}, BQS), Syncer ! {done, Ref}, + receive + {next, Ref} -> ok + end, case Acc of {shutdown, Reason} -> {shutdown, Reason, BQS1}; _ -> {ok, BQS1} end. master_send({Syncer, Ref, Name}, I, Last, Msg, MsgProps) -> - Syncer ! {msg, Ref, Msg, MsgProps}, Acc = {I + 1, case timer:now_diff(erlang:now(), Last) > ?SYNC_PROGRESS_INTERVAL of true -> rabbit_log:info("Synchronising ~s: ~p messages~n", @@ -77,7 +79,8 @@ master_send({Syncer, Ref, Name}, I, Last, Msg, MsgProps) -> false -> Last end}, receive - {msg_ok, Ref} -> {cont, Acc}; + {next, Ref} -> Syncer ! {msg, Ref, Msg, MsgProps}, + {cont, Acc}; {'EXIT', _Pid, Reason} -> {stop, {shutdown, Reason}} end. @@ -96,9 +99,9 @@ syncer(Ref, MPid, SPids) -> unlink(MPid). syncer_loop({Ref, MPid} = Args, SPidsMRefs) -> + MPid ! {next, Ref}, receive {msg, Ref, Msg, MsgProps} -> - MPid ! {msg_ok, Ref}, SPidsMRefs1 = wait_for_credit(SPidsMRefs, Ref), [begin credit_flow:send(SPid, ?CREDIT_DISC_BOUND), -- cgit v1.2.1 From 9102940de6158c4829156322318aaf33c40c56cb Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 29 Nov 2012 11:49:28 +0000 Subject: Have slaves determine whether they need sync. --- src/rabbit_mirror_queue_master.erl | 26 ++++++++-------------- src/rabbit_mirror_queue_slave.erl | 14 ++++++------ src/rabbit_mirror_queue_sync.erl | 44 +++++++++++++++++++++++++------------- 3 files changed, 44 insertions(+), 40 deletions(-) diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index 439d1f4b..0820f3f9 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -127,24 +127,16 @@ stop_mirroring(State = #state { coordinator = CPid, stop_all_slaves(shutdown, State), {BQ, BQS}. -sync_mirrors(State = #state{name = QName}) -> - {ok, #amqqueue{slave_pids = SPids, sync_slave_pids = SSPids}} = - rabbit_amqqueue:lookup(QName), - sync_mirrors(SPids -- SSPids, State). - -sync_mirrors([], State = #state{name = QName}) -> - rabbit_log:info("Synchronising ~s: nothing to do~n", - [rabbit_misc:rs(QName)]), - {ok, State}; -sync_mirrors(SPids, State = #state { name = QName, - gm = GM, - backing_queue = BQ, - backing_queue_state = BQS }) -> - rabbit_log:info("Synchronising ~s with slaves ~p: ~p messages to do~n", - [rabbit_misc:rs(QName), SPids, BQ:len(BQS)]), +sync_mirrors(State = #state { name = QName, + gm = GM, + backing_queue = BQ, + backing_queue_state = BQS }) -> + rabbit_log:info("Synchronising ~s: ~p messages to synchronise~n", + [rabbit_misc:rs(QName), BQ:len(BQS)]), + {ok, #amqqueue{slave_pids = SPids}} = rabbit_amqqueue:lookup(QName), Ref = make_ref(), - Syncer = rabbit_mirror_queue_sync:master_prepare(Ref, SPids), - gm:broadcast(GM, {sync_start, Ref, Syncer, SPids}), + Syncer = rabbit_mirror_queue_sync:master_prepare(Ref, QName, SPids), + gm:broadcast(GM, {sync_start, Ref, Syncer}), S = fun(BQSN) -> State#state{backing_queue_state = BQSN} end, case rabbit_mirror_queue_sync:master_go(Syncer, Ref, QName, BQ, BQS) of {shutdown, R, BQS1} -> {stop, R, S(BQS1)}; diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 11490b9c..2b216a5f 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -222,8 +222,9 @@ handle_cast({deliver, Delivery = #delivery{sender = Sender}, true, Flow}, end, noreply(maybe_enqueue_message(Delivery, State)); -handle_cast({sync_start, Ref, MPid}, - State = #state { backing_queue = BQ, +handle_cast({sync_start, Ref, Syncer}, + State = #state { depth_delta = DD, + backing_queue = BQ, backing_queue_state = BQS }) -> State1 = #state{rate_timer_ref = TRef} = ensure_rate_timer(State), S = fun({TRefN, BQSN}) -> State1#state{rate_timer_ref = TRefN, @@ -232,7 +233,7 @@ handle_cast({sync_start, Ref, MPid}, %% [1] The master died so we do not need to set_delta even though %% we purged since we will get a depth instruction soon. case rabbit_mirror_queue_sync:slave( - Ref, TRef, MPid, BQ, BQS, + DD, Ref, TRef, Syncer, BQ, BQS, fun (BQN, BQSN) -> BQSN1 = update_ram_duration(BQN, BQSN), TRefN = erlang:send_after(?RAM_DURATION_UPDATE_INTERVAL, @@ -374,11 +375,8 @@ handle_msg([_SPid], _From, process_death) -> handle_msg([CPid], _From, {delete_and_terminate, _Reason} = Msg) -> ok = gen_server2:cast(CPid, {gm, Msg}), {stop, {shutdown, ring_shutdown}}; -handle_msg([SPid], _From, {sync_start, Ref, MPid, SPids}) -> - case lists:member(SPid, SPids) of - true -> ok = gen_server2:cast(SPid, {sync_start, Ref, MPid}); - false -> ok - end; +handle_msg([SPid], _From, {sync_start, Ref, Syncer}) -> + gen_server2:cast(SPid, {sync_start, Ref, Syncer}); handle_msg([SPid], _From, Msg) -> ok = gen_server2:cast(SPid, {gm, Msg}). diff --git a/src/rabbit_mirror_queue_sync.erl b/src/rabbit_mirror_queue_sync.erl index acf3e3e3..9ff853d5 100644 --- a/src/rabbit_mirror_queue_sync.erl +++ b/src/rabbit_mirror_queue_sync.erl @@ -18,7 +18,7 @@ -include("rabbit.hrl"). --export([master_prepare/2, master_go/5, slave/6]). +-export([master_prepare/3, master_go/5, slave/7]). -define(SYNC_PROGRESS_INTERVAL, 1000000). @@ -51,12 +51,12 @@ %% --------------------------------------------------------------------------- %% Master -master_prepare(Ref, SPids) -> +master_prepare(Ref, QName, SPids) -> MPid = self(), - spawn_link(fun () -> syncer(Ref, MPid, SPids) end). + spawn_link(fun () -> syncer(Ref, QName, MPid, SPids) end). -master_go(Syncer, Ref, Name, BQ, BQS) -> - SendArgs = {Syncer, Ref, Name}, +master_go(Syncer, Ref, QName, BQ, BQS) -> + SendArgs = {Syncer, Ref, QName}, {Acc, BQS1} = BQ:fold(fun (Msg, MsgProps, {I, Last}) -> master_send(SendArgs, I, Last, Msg, MsgProps) @@ -70,11 +70,11 @@ master_go(Syncer, Ref, Name, BQ, BQS) -> _ -> {ok, BQS1} end. -master_send({Syncer, Ref, Name}, I, Last, Msg, MsgProps) -> +master_send({Syncer, Ref, QName}, I, Last, Msg, MsgProps) -> Acc = {I + 1, case timer:now_diff(erlang:now(), Last) > ?SYNC_PROGRESS_INTERVAL of true -> rabbit_log:info("Synchronising ~s: ~p messages~n", - [rabbit_misc:rs(Name), I]), + [rabbit_misc:rs(QName), I]), erlang:now(); false -> Last end}, @@ -88,14 +88,23 @@ master_send({Syncer, Ref, Name}, I, Last, Msg, MsgProps) -> %% --------------------------------------------------------------------------- %% Syncer -syncer(Ref, MPid, SPids) -> +syncer(Ref, QName, MPid, SPids) -> SPidsMRefs = [{SPid, erlang:monitor(process, SPid)} || SPid <- SPids], %% We wait for a reply from the slaves so that we know they are in %% a receive block and will thus receive messages we send to them %% *without* those messages ending up in their gen_server2 pqueue. - SPidsMRefs1 = foreach_slave(SPidsMRefs, Ref, fun sync_receive_ready/3), - SPidsMRefs2 = syncer_loop({Ref, MPid}, SPidsMRefs1), - foreach_slave(SPidsMRefs2, Ref, fun sync_send_complete/3), + case foreach_slave(SPidsMRefs, Ref, fun sync_receive_ready/3) of + [] -> + rabbit_log:info("Synchronising ~s: all slaves already synced~n", + [rabbit_misc:rs(QName)]); + SPidsMRefs1 -> + rabbit_log:info("Synchronising ~s: ~p require sync~n", + [rabbit_misc:rs(QName), + [rabbit_misc:pid_to_string(S) || + {S, _} <- SPidsMRefs1]]), + SPidsMRefs2 = syncer_loop({Ref, MPid}, SPidsMRefs1), + foreach_slave(SPidsMRefs2, Ref, fun sync_send_complete/3) + end, unlink(MPid). syncer_loop({Ref, MPid} = Args, SPidsMRefs) -> @@ -121,12 +130,13 @@ wait_for_credit(SPidsMRefs, Ref) -> foreach_slave(SPidsMRefs, Ref, Fun) -> [{SPid, MRef} || {SPid, MRef} <- SPidsMRefs, - Fun(SPid, MRef, Ref) =/= dead]. + Fun(SPid, MRef, Ref) =/= ignore]. sync_receive_ready(SPid, MRef, Ref) -> receive {sync_ready, Ref, SPid} -> SPid; - {'DOWN', MRef, _, SPid, _} -> dead + {sync_deny, Ref, SPid} -> ignore; + {'DOWN', MRef, _, SPid, _} -> ignore end. sync_receive_credit(SPid, MRef, _Ref) -> @@ -134,7 +144,7 @@ sync_receive_credit(SPid, MRef, _Ref) -> {bump_credit, {SPid, _} = Msg} -> credit_flow:handle_bump_msg(Msg), SPid; {'DOWN', MRef, _, SPid, _} -> credit_flow:peer_down(SPid), - dead + ignore end. sync_send_complete(SPid, _MRef, Ref) -> @@ -144,7 +154,11 @@ sync_send_complete(SPid, _MRef, Ref) -> %% --------------------------------------------------------------------------- %% Slave -slave(Ref, TRef, Syncer, BQ, BQS, UpdateRamDuration) -> +slave(0, Ref, TRef, Syncer, _BQ, BQS, _UpdateRamDuration) -> + Syncer ! {sync_deny, Ref, self()}, + {ok, {TRef, BQS}}; + +slave(_DD, Ref, TRef, Syncer, BQ, BQS, UpdateRamDuration) -> MRef = erlang:monitor(process, Syncer), Syncer ! {sync_ready, Ref, self()}, {_MsgCount, BQS1} = BQ:purge(BQS), -- cgit v1.2.1 From 4debb61fb625758cb850d2cf27af93e2cd00b0ea Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 29 Nov 2012 12:05:23 +0000 Subject: Only respond to 'EXIT's from parent / syncer. --- src/rabbit_mirror_queue_master.erl | 11 +++++---- src/rabbit_mirror_queue_sync.erl | 16 ++++++++----- src/rabbit_misc.erl | 46 +++++++++++++++++++++++++++++++++++++- 3 files changed, 62 insertions(+), 11 deletions(-) diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index 0820f3f9..2f9f4c02 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -139,10 +139,13 @@ sync_mirrors(State = #state { name = QName, gm:broadcast(GM, {sync_start, Ref, Syncer}), S = fun(BQSN) -> State#state{backing_queue_state = BQSN} end, case rabbit_mirror_queue_sync:master_go(Syncer, Ref, QName, BQ, BQS) of - {shutdown, R, BQS1} -> {stop, R, S(BQS1)}; - {ok, BQS1} -> rabbit_log:info("Synchronising ~s: complete~n", - [rabbit_misc:rs(QName)]), - {ok, S(BQS1)} + {shutdown, R, BQS1} -> {stop, R, S(BQS1)}; + {sync_died, R, BQS1} -> rabbit_log:info("Synchronising ~s: ~p~n", + [rabbit_misc:rs(QName), R]), + {ok, S(BQS1)}; + {ok, BQS1} -> rabbit_log:info("Synchronising ~s: complete~n", + [rabbit_misc:rs(QName)]), + {ok, S(BQS1)} end. terminate({shutdown, dropped} = Reason, diff --git a/src/rabbit_mirror_queue_sync.erl b/src/rabbit_mirror_queue_sync.erl index 9ff853d5..94f5ae0f 100644 --- a/src/rabbit_mirror_queue_sync.erl +++ b/src/rabbit_mirror_queue_sync.erl @@ -66,8 +66,9 @@ master_go(Syncer, Ref, QName, BQ, BQS) -> {next, Ref} -> ok end, case Acc of - {shutdown, Reason} -> {shutdown, Reason, BQS1}; - _ -> {ok, BQS1} + {shutdown, Reason} -> {shutdown, Reason, BQS1}; + {sync_died, Reason} -> {sync_died, Reason, BQS1}; + _ -> {ok, BQS1} end. master_send({Syncer, Ref, QName}, I, Last, Msg, MsgProps) -> @@ -78,10 +79,12 @@ master_send({Syncer, Ref, QName}, I, Last, Msg, MsgProps) -> erlang:now(); false -> Last end}, + Parent = rabbit_misc:get_parent(), receive - {next, Ref} -> Syncer ! {msg, Ref, Msg, MsgProps}, - {cont, Acc}; - {'EXIT', _Pid, Reason} -> {stop, {shutdown, Reason}} + {next, Ref} -> Syncer ! {msg, Ref, Msg, MsgProps}, + {cont, Acc}; + {'EXIT', Syncer, Reason} -> {stop, {sync_died, Reason}}; + {'EXIT', Parent, Reason} -> {stop, {shutdown, Reason}} end. %% Master @@ -165,6 +168,7 @@ slave(_DD, Ref, TRef, Syncer, BQ, BQS, UpdateRamDuration) -> slave_sync_loop({Ref, MRef, Syncer, BQ, UpdateRamDuration}, TRef, BQS1). slave_sync_loop(Args = {Ref, MRef, Syncer, BQ, UpdateRamDuration}, TRef, BQS) -> + Parent = rabbit_misc:get_parent(), receive {'DOWN', MRef, process, Syncer, _Reason} -> %% If the master dies half way we are not in the usual @@ -197,6 +201,6 @@ slave_sync_loop(Args = {Ref, MRef, Syncer, BQ, UpdateRamDuration}, TRef, BQS) -> Props1 = Props#message_properties{needs_confirming = false}, BQS1 = BQ:publish(Msg, Props1, true, none, BQS), slave_sync_loop(Args, TRef, BQS1); - {'EXIT', _Pid, Reason} -> + {'EXIT', Parent, Reason} -> {stop, Reason, {TRef, BQS}} end. diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 81bb6769..cd83e3b8 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -66,6 +66,7 @@ -export([check_expiry/1]). -export([base64url/1]). -export([interval_operation/4]). +-export([get_parent/0]). %% Horrible macro to use in guards -define(IS_BENIGN_EXIT(R), @@ -239,7 +240,7 @@ -spec(interval_operation/4 :: ({atom(), atom(), any()}, float(), non_neg_integer(), non_neg_integer()) -> {any(), non_neg_integer()}). - +-spec(get_parent/0 :: () -> pid()). -endif. %%---------------------------------------------------------------------------- @@ -1034,3 +1035,46 @@ interval_operation({M, F, A}, MaxRatio, IdealInterval, LastInterval) -> {false, false} -> lists:max([IdealInterval, round(LastInterval / 1.5)]) end}. + +%% ------------------------------------------------------------------------- +%% Begin copypasta from gen_server.erl + +get_parent() -> + case get('$ancestors') of + [Parent | _] when is_pid(Parent)-> + Parent; + [Parent | _] when is_atom(Parent)-> + name_to_pid(Parent); + _ -> + exit(process_was_not_started_by_proc_lib) + end. + +name_to_pid(Name) -> + case whereis(Name) of + undefined -> + case whereis_name(Name) of + undefined -> + exit(could_not_find_registerd_name); + Pid -> + Pid + end; + Pid -> + Pid + end. + +whereis_name(Name) -> + case ets:lookup(global_names, Name) of + [{_Name, Pid, _Method, _RPid, _Ref}] -> + if node(Pid) == node() -> + case erlang:is_process_alive(Pid) of + true -> Pid; + false -> undefined + end; + true -> + Pid + end; + [] -> undefined + end. + +%% End copypasta from gen_server.erl +%% ------------------------------------------------------------------------- -- cgit v1.2.1 From e6768457d3788fc3e6576a0468c98a3733afefbb Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 29 Nov 2012 12:09:57 +0000 Subject: Respond to FHC. --- src/rabbit_mirror_queue_sync.erl | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/rabbit_mirror_queue_sync.erl b/src/rabbit_mirror_queue_sync.erl index 94f5ae0f..0b9bd42a 100644 --- a/src/rabbit_mirror_queue_sync.erl +++ b/src/rabbit_mirror_queue_sync.erl @@ -80,6 +80,12 @@ master_send({Syncer, Ref, QName}, I, Last, Msg, MsgProps) -> false -> Last end}, Parent = rabbit_misc:get_parent(), + receive + {'$gen_cast', {set_maximum_since_use, Age}} -> + ok = file_handle_cache:set_maximum_since_use(Age) + after 0 -> + ok + end, receive {next, Ref} -> Syncer ! {msg, Ref, Msg, MsgProps}, {cont, Acc}; @@ -101,7 +107,7 @@ syncer(Ref, QName, MPid, SPids) -> rabbit_log:info("Synchronising ~s: all slaves already synced~n", [rabbit_misc:rs(QName)]); SPidsMRefs1 -> - rabbit_log:info("Synchronising ~s: ~p require sync~n", + rabbit_log:info("Synchronising ~s: ~p to sync~n", [rabbit_misc:rs(QName), [rabbit_misc:pid_to_string(S) || {S, _} <- SPidsMRefs1]]), -- cgit v1.2.1 From 0675c23b66b5f8804cee5d20ce586aa7082471f3 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 29 Nov 2012 12:14:13 +0000 Subject: Call get_parent/0 less often... --- src/rabbit_mirror_queue_sync.erl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/rabbit_mirror_queue_sync.erl b/src/rabbit_mirror_queue_sync.erl index 0b9bd42a..29f6af2c 100644 --- a/src/rabbit_mirror_queue_sync.erl +++ b/src/rabbit_mirror_queue_sync.erl @@ -56,7 +56,7 @@ master_prepare(Ref, QName, SPids) -> spawn_link(fun () -> syncer(Ref, QName, MPid, SPids) end). master_go(Syncer, Ref, QName, BQ, BQS) -> - SendArgs = {Syncer, Ref, QName}, + SendArgs = {Syncer, Ref, QName, rabbit_misc:get_parent()}, {Acc, BQS1} = BQ:fold(fun (Msg, MsgProps, {I, Last}) -> master_send(SendArgs, I, Last, Msg, MsgProps) @@ -71,7 +71,7 @@ master_go(Syncer, Ref, QName, BQ, BQS) -> _ -> {ok, BQS1} end. -master_send({Syncer, Ref, QName}, I, Last, Msg, MsgProps) -> +master_send({Syncer, Ref, QName, Parent}, I, Last, Msg, MsgProps) -> Acc = {I + 1, case timer:now_diff(erlang:now(), Last) > ?SYNC_PROGRESS_INTERVAL of true -> rabbit_log:info("Synchronising ~s: ~p messages~n", @@ -79,7 +79,6 @@ master_send({Syncer, Ref, QName}, I, Last, Msg, MsgProps) -> erlang:now(); false -> Last end}, - Parent = rabbit_misc:get_parent(), receive {'$gen_cast', {set_maximum_since_use, Age}} -> ok = file_handle_cache:set_maximum_since_use(Age) @@ -171,10 +170,11 @@ slave(_DD, Ref, TRef, Syncer, BQ, BQS, UpdateRamDuration) -> MRef = erlang:monitor(process, Syncer), Syncer ! {sync_ready, Ref, self()}, {_MsgCount, BQS1} = BQ:purge(BQS), - slave_sync_loop({Ref, MRef, Syncer, BQ, UpdateRamDuration}, TRef, BQS1). + slave_sync_loop({Ref, MRef, Syncer, BQ, UpdateRamDuration, + rabbit_misc:get_parent()}, TRef, BQS1). -slave_sync_loop(Args = {Ref, MRef, Syncer, BQ, UpdateRamDuration}, TRef, BQS) -> - Parent = rabbit_misc:get_parent(), +slave_sync_loop(Args = {Ref, MRef, Syncer, BQ, UpdateRamDuration, Parent}, + TRef, BQS) -> receive {'DOWN', MRef, process, Syncer, _Reason} -> %% If the master dies half way we are not in the usual -- cgit v1.2.1 From 3f7332481753ef93e37ef768747e30fa89fc66ff Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 29 Nov 2012 12:35:00 +0000 Subject: Make sure newly-started slaves don't respond and confuse things. --- src/rabbit_mirror_queue_master.erl | 2 +- src/rabbit_mirror_queue_slave.erl | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index 2f9f4c02..d8737938 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -136,7 +136,7 @@ sync_mirrors(State = #state { name = QName, {ok, #amqqueue{slave_pids = SPids}} = rabbit_amqqueue:lookup(QName), Ref = make_ref(), Syncer = rabbit_mirror_queue_sync:master_prepare(Ref, QName, SPids), - gm:broadcast(GM, {sync_start, Ref, Syncer}), + gm:broadcast(GM, {sync_start, Ref, Syncer, SPids}), S = fun(BQSN) -> State#state{backing_queue_state = BQSN} end, case rabbit_mirror_queue_sync:master_go(Syncer, Ref, QName, BQ, BQS) of {shutdown, R, BQS1} -> {stop, R, S(BQS1)}; diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 2b216a5f..1658b2ad 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -375,8 +375,11 @@ handle_msg([_SPid], _From, process_death) -> handle_msg([CPid], _From, {delete_and_terminate, _Reason} = Msg) -> ok = gen_server2:cast(CPid, {gm, Msg}), {stop, {shutdown, ring_shutdown}}; -handle_msg([SPid], _From, {sync_start, Ref, Syncer}) -> - gen_server2:cast(SPid, {sync_start, Ref, Syncer}); +handle_msg([SPid], _From, {sync_start, Ref, Syncer, SPids}) -> + case lists:member(SPid, SPids) of + true -> gen_server2:cast(SPid, {sync_start, Ref, Syncer}); + false -> ok + end; handle_msg([SPid], _From, Msg) -> ok = gen_server2:cast(SPid, {gm, Msg}). -- cgit v1.2.1 From 64b0a9b0ae9a9e5137bbc10436547c4b6457b4e0 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 29 Nov 2012 12:36:24 +0000 Subject: Update diagram. --- src/rabbit_mirror_queue_sync.erl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/rabbit_mirror_queue_sync.erl b/src/rabbit_mirror_queue_sync.erl index 29f6af2c..b717f1f7 100644 --- a/src/rabbit_mirror_queue_sync.erl +++ b/src/rabbit_mirror_queue_sync.erl @@ -40,10 +40,12 @@ %% (from channel) || -- (spawns) --> || || %% || --------- sync_start (over GM) -------> || %% || || <--- sync_ready ---- || +%% || || (or) || +%% || || <--- sync_deny ----- || %% || <--- next* ---- || || } %% || ---- msg* ----> || || } loop -%% || || ----- sync_msg* ---> || } -%% || || <---- (credit)* ---- || } +%% || || ---- sync_msg* ----> || } +%% || || <--- (credit)* ----- || } %% || ---- done ----> || || %% || || -- sync_complete --> || %% || (Dies) || -- cgit v1.2.1 From 36f4adc8c01bf680d64ea1cce8bbda2ac757d494 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 29 Nov 2012 12:51:21 +0000 Subject: cosmetic, plus correctly identify provenance --- src/rabbit_misc.erl | 43 +++++++++++++++++-------------------------- 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index cd83e3b8..ccf3d8be 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -1037,44 +1037,35 @@ interval_operation({M, F, A}, MaxRatio, IdealInterval, LastInterval) -> end}. %% ------------------------------------------------------------------------- -%% Begin copypasta from gen_server.erl +%% Begin copypasta from gen_server2.erl get_parent() -> case get('$ancestors') of - [Parent | _] when is_pid(Parent)-> - Parent; - [Parent | _] when is_atom(Parent)-> - name_to_pid(Parent); - _ -> - exit(process_was_not_started_by_proc_lib) + [Parent | _] when is_pid (Parent) -> Parent; + [Parent | _] when is_atom(Parent) -> name_to_pid(Parent); + _ -> exit(process_was_not_started_by_proc_lib) end. name_to_pid(Name) -> case whereis(Name) of - undefined -> - case whereis_name(Name) of - undefined -> - exit(could_not_find_registerd_name); - Pid -> - Pid - end; - Pid -> - Pid + undefined -> case whereis_name(Name) of + undefined -> exit(could_not_find_registerd_name); + Pid -> Pid + end; + Pid -> Pid end. whereis_name(Name) -> case ets:lookup(global_names, Name) of - [{_Name, Pid, _Method, _RPid, _Ref}] -> - if node(Pid) == node() -> - case erlang:is_process_alive(Pid) of - true -> Pid; - false -> undefined + [{_Name, Pid, _Method, _RPid, _Ref}] -> + if node(Pid) == node() -> case erlang:is_process_alive(Pid) of + true -> Pid; + false -> undefined + end; + true -> Pid end; - true -> - Pid - end; - [] -> undefined + [] -> undefined end. -%% End copypasta from gen_server.erl +%% End copypasta from gen_server2.erl %% ------------------------------------------------------------------------- -- cgit v1.2.1 From 3e605d8d940c708f4586b5dd1064bff44d97b88c Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 29 Nov 2012 12:54:28 +0000 Subject: Set depth_delta to undefined if something goes wrong. Or indeed if something goes right, but in that case we are about to overwrite it. --- src/rabbit_mirror_queue_slave.erl | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 1658b2ad..b12f85b8 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -227,11 +227,10 @@ handle_cast({sync_start, Ref, Syncer}, backing_queue = BQ, backing_queue_state = BQS }) -> State1 = #state{rate_timer_ref = TRef} = ensure_rate_timer(State), - S = fun({TRefN, BQSN}) -> State1#state{rate_timer_ref = TRefN, + S = fun({TRefN, BQSN}) -> State1#state{depth_delta = undefined, + rate_timer_ref = TRefN, backing_queue_state = BQSN} end, %% [0] We can only sync when there are no pending acks - %% [1] The master died so we do not need to set_delta even though - %% we purged since we will get a depth instruction soon. case rabbit_mirror_queue_sync:slave( DD, Ref, TRef, Syncer, BQ, BQS, fun (BQN, BQSN) -> @@ -241,7 +240,7 @@ handle_cast({sync_start, Ref, Syncer}, {TRefN, BQSN1} end) of {ok, Res} -> noreply(set_delta(0, S(Res))); %% [0] - {failed, Res} -> noreply(S(Res)); %% [1] + {failed, Res} -> noreply(S(Res)); {stop, Reason, Res} -> {stop, Reason, S(Res)} end; -- 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(-) 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(+) 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 b81fd50dda7dd65f97903a85615d684804bed501 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 29 Nov 2012 13:09:15 +0000 Subject: Don't do anything if we decided not to do anything. --- src/rabbit_mirror_queue_slave.erl | 1 + src/rabbit_mirror_queue_sync.erl | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index b12f85b8..53564f09 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -239,6 +239,7 @@ handle_cast({sync_start, Ref, Syncer}, self(), update_ram_duration), {TRefN, BQSN1} end) of + denied -> noreply(State1); {ok, Res} -> noreply(set_delta(0, S(Res))); %% [0] {failed, Res} -> noreply(S(Res)); {stop, Reason, Res} -> {stop, Reason, S(Res)} diff --git a/src/rabbit_mirror_queue_sync.erl b/src/rabbit_mirror_queue_sync.erl index b717f1f7..266465ec 100644 --- a/src/rabbit_mirror_queue_sync.erl +++ b/src/rabbit_mirror_queue_sync.erl @@ -166,7 +166,7 @@ sync_send_complete(SPid, _MRef, Ref) -> slave(0, Ref, TRef, Syncer, _BQ, BQS, _UpdateRamDuration) -> Syncer ! {sync_deny, Ref, self()}, - {ok, {TRef, BQS}}; + denied; slave(_DD, Ref, TRef, Syncer, BQ, BQS, UpdateRamDuration) -> MRef = erlang:monitor(process, Syncer), -- 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(-) 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 0ce06f9e0bac7a52785ce7d242206ef43f4235d2 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 29 Nov 2012 13:25:38 +0000 Subject: no more unused vars --- src/rabbit_mirror_queue_sync.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_mirror_queue_sync.erl b/src/rabbit_mirror_queue_sync.erl index 266465ec..d838d636 100644 --- a/src/rabbit_mirror_queue_sync.erl +++ b/src/rabbit_mirror_queue_sync.erl @@ -164,7 +164,7 @@ sync_send_complete(SPid, _MRef, Ref) -> %% --------------------------------------------------------------------------- %% Slave -slave(0, Ref, TRef, Syncer, _BQ, BQS, _UpdateRamDuration) -> +slave(0, Ref, _TRef, Syncer, _BQ, _BQS, _UpdateRamDuration) -> Syncer ! {sync_deny, Ref, self()}, denied; -- cgit v1.2.1 From 8a2c6dac21a2a99b7fcbc73ed2831c37475802a5 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 29 Nov 2012 14:00:16 +0000 Subject: extract logging --- src/rabbit_mirror_queue_master.erl | 17 +++++++++-------- src/rabbit_mirror_queue_sync.erl | 30 ++++++++++++------------------ 2 files changed, 21 insertions(+), 26 deletions(-) diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index d8737938..3d7f902c 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -131,20 +131,21 @@ sync_mirrors(State = #state { name = QName, gm = GM, backing_queue = BQ, backing_queue_state = BQS }) -> - rabbit_log:info("Synchronising ~s: ~p messages to synchronise~n", - [rabbit_misc:rs(QName), BQ:len(BQS)]), + Log = fun (Fmt, Params) -> + rabbit_log:info("Synchronising ~s: " ++ Fmt ++ "~n", + [rabbit_misc:rs(QName) | Params]) + end, + Log("~p messages to synchronise", [BQ:len(BQS)]), {ok, #amqqueue{slave_pids = SPids}} = rabbit_amqqueue:lookup(QName), Ref = make_ref(), - Syncer = rabbit_mirror_queue_sync:master_prepare(Ref, QName, SPids), + Syncer = rabbit_mirror_queue_sync:master_prepare(Ref, Log, SPids), gm:broadcast(GM, {sync_start, Ref, Syncer, SPids}), S = fun(BQSN) -> State#state{backing_queue_state = BQSN} end, - case rabbit_mirror_queue_sync:master_go(Syncer, Ref, QName, BQ, BQS) of + case rabbit_mirror_queue_sync:master_go(Syncer, Ref, Log, BQ, BQS) of {shutdown, R, BQS1} -> {stop, R, S(BQS1)}; - {sync_died, R, BQS1} -> rabbit_log:info("Synchronising ~s: ~p~n", - [rabbit_misc:rs(QName), R]), + {sync_died, R, BQS1} -> Log("~p", [R]), {ok, S(BQS1)}; - {ok, BQS1} -> rabbit_log:info("Synchronising ~s: complete~n", - [rabbit_misc:rs(QName)]), + {ok, BQS1} -> Log("complete", []), {ok, S(BQS1)} end. diff --git a/src/rabbit_mirror_queue_sync.erl b/src/rabbit_mirror_queue_sync.erl index d838d636..bddfb9dc 100644 --- a/src/rabbit_mirror_queue_sync.erl +++ b/src/rabbit_mirror_queue_sync.erl @@ -53,12 +53,12 @@ %% --------------------------------------------------------------------------- %% Master -master_prepare(Ref, QName, SPids) -> +master_prepare(Ref, Log, SPids) -> MPid = self(), - spawn_link(fun () -> syncer(Ref, QName, MPid, SPids) end). + spawn_link(fun () -> syncer(Ref, Log, MPid, SPids) end). -master_go(Syncer, Ref, QName, BQ, BQS) -> - SendArgs = {Syncer, Ref, QName, rabbit_misc:get_parent()}, +master_go(Syncer, Ref, Log, BQ, BQS) -> + SendArgs = {Syncer, Ref, Log, rabbit_misc:get_parent()}, {Acc, BQS1} = BQ:fold(fun (Msg, MsgProps, {I, Last}) -> master_send(SendArgs, I, Last, Msg, MsgProps) @@ -73,11 +73,10 @@ master_go(Syncer, Ref, QName, BQ, BQS) -> _ -> {ok, BQS1} end. -master_send({Syncer, Ref, QName, Parent}, I, Last, Msg, MsgProps) -> +master_send({Syncer, Ref, Log, Parent}, I, Last, Msg, MsgProps) -> Acc = {I + 1, case timer:now_diff(erlang:now(), Last) > ?SYNC_PROGRESS_INTERVAL of - true -> rabbit_log:info("Synchronising ~s: ~p messages~n", - [rabbit_misc:rs(QName), I]), + true -> Log("~p messages", [I]), erlang:now(); false -> Last end}, @@ -98,22 +97,17 @@ master_send({Syncer, Ref, QName, Parent}, I, Last, Msg, MsgProps) -> %% --------------------------------------------------------------------------- %% Syncer -syncer(Ref, QName, MPid, SPids) -> +syncer(Ref, Log, MPid, SPids) -> SPidsMRefs = [{SPid, erlang:monitor(process, SPid)} || SPid <- SPids], %% We wait for a reply from the slaves so that we know they are in %% a receive block and will thus receive messages we send to them %% *without* those messages ending up in their gen_server2 pqueue. case foreach_slave(SPidsMRefs, Ref, fun sync_receive_ready/3) of - [] -> - rabbit_log:info("Synchronising ~s: all slaves already synced~n", - [rabbit_misc:rs(QName)]); - SPidsMRefs1 -> - rabbit_log:info("Synchronising ~s: ~p to sync~n", - [rabbit_misc:rs(QName), - [rabbit_misc:pid_to_string(S) || - {S, _} <- SPidsMRefs1]]), - SPidsMRefs2 = syncer_loop({Ref, MPid}, SPidsMRefs1), - foreach_slave(SPidsMRefs2, Ref, fun sync_send_complete/3) + [] -> Log("all slaves already synced", []); + SPidsMRefs1 -> Log("~p to sync", [[rabbit_misc:pid_to_string(S) || + {S, _} <- SPidsMRefs1]]), + SPidsMRefs2 = syncer_loop({Ref, MPid}, SPidsMRefs1), + foreach_slave(SPidsMRefs2, Ref, fun sync_send_complete/3) end, unlink(MPid). -- cgit v1.2.1 From 69f561481424051a2e456c4138af9e0d22790d7b Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 29 Nov 2012 14:11:56 +0000 Subject: cosmetic --- src/rabbit_mirror_queue_sync.erl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rabbit_mirror_queue_sync.erl b/src/rabbit_mirror_queue_sync.erl index bddfb9dc..3a8a68b8 100644 --- a/src/rabbit_mirror_queue_sync.erl +++ b/src/rabbit_mirror_queue_sync.erl @@ -63,10 +63,10 @@ master_go(Syncer, Ref, Log, BQ, BQS) -> BQ:fold(fun (Msg, MsgProps, {I, Last}) -> master_send(SendArgs, I, Last, Msg, MsgProps) end, {0, erlang:now()}, BQS), - Syncer ! {done, Ref}, receive {next, Ref} -> ok end, + Syncer ! {done, Ref}, case Acc of {shutdown, Reason} -> {shutdown, Reason, BQS1}; {sync_died, Reason} -> {sync_died, Reason, BQS1}; @@ -89,8 +89,8 @@ master_send({Syncer, Ref, Log, Parent}, I, Last, Msg, MsgProps) -> receive {next, Ref} -> Syncer ! {msg, Ref, Msg, MsgProps}, {cont, Acc}; - {'EXIT', Syncer, Reason} -> {stop, {sync_died, Reason}}; - {'EXIT', Parent, Reason} -> {stop, {shutdown, Reason}} + {'EXIT', Parent, Reason} -> {stop, {shutdown, Reason}}; + {'EXIT', Syncer, Reason} -> {stop, {sync_died, Reason}} end. %% Master @@ -139,7 +139,7 @@ foreach_slave(SPidsMRefs, Ref, Fun) -> sync_receive_ready(SPid, MRef, Ref) -> receive {sync_ready, Ref, SPid} -> SPid; - {sync_deny, Ref, SPid} -> ignore; + {sync_deny, Ref, SPid} -> ignore; {'DOWN', MRef, _, SPid, _} -> ignore end. -- cgit v1.2.1 From d9a00cfa67c05583ffcddbfa0a003cecc102bee9 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 29 Nov 2012 14:37:13 +0000 Subject: handle the case of the Syncer dying right at the end which could previously leave the master blocked, waiting for 'next'. And move the unlinking, which allows us to ensure we don't end up with stray 'EXIT's. --- src/rabbit_mirror_queue_sync.erl | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/src/rabbit_mirror_queue_sync.erl b/src/rabbit_mirror_queue_sync.erl index 3a8a68b8..c654cde5 100644 --- a/src/rabbit_mirror_queue_sync.erl +++ b/src/rabbit_mirror_queue_sync.erl @@ -58,19 +58,13 @@ master_prepare(Ref, Log, SPids) -> spawn_link(fun () -> syncer(Ref, Log, MPid, SPids) end). master_go(Syncer, Ref, Log, BQ, BQS) -> - SendArgs = {Syncer, Ref, Log, rabbit_misc:get_parent()}, - {Acc, BQS1} = - BQ:fold(fun (Msg, MsgProps, {I, Last}) -> - master_send(SendArgs, I, Last, Msg, MsgProps) - end, {0, erlang:now()}, BQS), - receive - {next, Ref} -> ok - end, - Syncer ! {done, Ref}, - case Acc of - {shutdown, Reason} -> {shutdown, Reason, BQS1}; - {sync_died, Reason} -> {sync_died, Reason, BQS1}; - _ -> {ok, BQS1} + Args = {Syncer, Ref, Log, rabbit_misc:get_parent()}, + case BQ:fold(fun (Msg, MsgProps, {I, Last}) -> + master_send(Args, I, Last, Msg, MsgProps) + end, {0, erlang:now()}, BQS) of + {{shutdown, Reason}, BQS1} -> {shutdown, Reason, BQS1}; + {{sync_died, Reason}, BQS1} -> {sync_died, Reason, BQS1}; + {_, BQS1} -> master_done(Args, BQS1) end. master_send({Syncer, Ref, Log, Parent}, I, Last, Msg, MsgProps) -> @@ -93,6 +87,18 @@ master_send({Syncer, Ref, Log, Parent}, I, Last, Msg, MsgProps) -> {'EXIT', Syncer, Reason} -> {stop, {sync_died, Reason}} end. +master_done({Syncer, Ref, _Log, Parent}, BQS) -> + receive + {next, Ref} -> unlink(Syncer), + Syncer ! {done, Ref}, + receive {'EXIT', Syncer, _} -> ok + after 0 -> ok + end, + {ok, BQS}; + {'EXIT', Parent, Reason} -> {shutdown, Reason, BQS}; + {'EXIT', Syncer, Reason} -> {sync_died, Reason, BQS} + end. + %% Master %% --------------------------------------------------------------------------- %% Syncer @@ -108,8 +114,7 @@ syncer(Ref, Log, MPid, SPids) -> {S, _} <- SPidsMRefs1]]), SPidsMRefs2 = syncer_loop({Ref, MPid}, SPidsMRefs1), foreach_slave(SPidsMRefs2, Ref, fun sync_send_complete/3) - end, - unlink(MPid). + end. syncer_loop({Ref, MPid} = Args, SPidsMRefs) -> MPid ! {next, Ref}, -- cgit v1.2.1 From 66f11afe7300881f00496fc5431c7d362aadf8f0 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 29 Nov 2012 14:39:17 +0000 Subject: correct docs for final handshake --- src/rabbit_mirror_queue_sync.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rabbit_mirror_queue_sync.erl b/src/rabbit_mirror_queue_sync.erl index c654cde5..4cb534ff 100644 --- a/src/rabbit_mirror_queue_sync.erl +++ b/src/rabbit_mirror_queue_sync.erl @@ -46,6 +46,7 @@ %% || ---- msg* ----> || || } loop %% || || ---- sync_msg* ----> || } %% || || <--- (credit)* ----- || } +%% || <--- next ---- || || %% || ---- done ----> || || %% || || -- sync_complete --> || %% || (Dies) || -- 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(-) 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 ff172f664ccb1271fbd934bd1c7748ef007f0490 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 29 Nov 2012 22:51:37 +0000 Subject: return to a simpler, better BQ:dropwhile, and introduce 'fetchwhile' ...to cover the remaining required functionality, including the ability to process messages along the way, pass around an accumulator, and get hold of the IsDelivered flag (not needed in our use case but included for similarity with 'fetch'). --- src/rabbit_amqqueue_process.erl | 15 ++++++++----- src/rabbit_backing_queue.erl | 32 +++++++++++++++++--------- src/rabbit_backing_queue_qc.erl | 4 ++-- src/rabbit_mirror_queue_master.erl | 46 ++++++++++++++++++++++---------------- src/rabbit_tests.erl | 14 +++++------- src/rabbit_variable_queue.erl | 40 ++++++++++++++++++--------------- 6 files changed, 87 insertions(+), 64 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 74717ace..a6b3829b 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -725,14 +725,15 @@ drop_expired_messages(State = #q{dlx = DLX, Now = now_micros(), ExpirePred = fun (#message_properties{expiry = Exp}) -> Now >= Exp end, {Props, BQS1} = case DLX of - undefined -> {Next, undefined, BQS2} = - BQ:dropwhile(ExpirePred, false, BQS), - {Next, BQS2}; - _ -> {Next, Msgs, BQS2} = - BQ:dropwhile(ExpirePred, true, BQS), + undefined -> BQ:dropwhile(ExpirePred, BQS); + _ -> {Next, Msgs, BQS2} = + BQ:fetchwhile(ExpirePred, + fun accumulate_msgs/4, + [], BQS), case Msgs of [] -> ok; - _ -> (dead_letter_fun(expired))(Msgs) + _ -> (dead_letter_fun(expired))( + lists:reverse(Msgs)) end, {Next, BQS2} end, @@ -741,6 +742,8 @@ drop_expired_messages(State = #q{dlx = DLX, #message_properties{expiry = Exp} -> Exp end, State#q{backing_queue_state = BQS1}). +accumulate_msgs(Msg, _IsDelivered, AckTag, Acc) -> [{Msg, AckTag} | Acc]. + ensure_ttl_timer(undefined, State) -> State; ensure_ttl_timer(Expiry, State = #q{ttl_timer_ref = undefined}) -> diff --git a/src/rabbit_backing_queue.erl b/src/rabbit_backing_queue.erl index 96c58cb9..272df5c1 100644 --- a/src/rabbit_backing_queue.erl +++ b/src/rabbit_backing_queue.erl @@ -124,16 +124,25 @@ %% be ignored. -callback drain_confirmed(state()) -> {msg_ids(), state()}. -%% Drop messages from the head of the queue while the supplied predicate returns -%% true. Also accepts a boolean parameter that determines whether the messages -%% necessitate an ack or not. If they do, the function returns a list of -%% messages with the respective acktags. --callback dropwhile(msg_pred(), true, state()) - -> {rabbit_types:message_properties() | undefined, - [{rabbit_types:basic_message(), ack()}], state()}; - (msg_pred(), false, state()) - -> {rabbit_types:message_properties() | undefined, - undefined, state()}. +%% Drop messages from the head of the queue while the supplied +%% predicate on message properties returns true. Returns the first +%% message properties for which the predictate returned false, or +%% 'undefined' if the whole backing queue was traversed w/o the +%% predicate ever returning false. +-callback dropwhile(msg_pred(), state()) + -> {rabbit_types:message_properties() | undefined, state()}. + +%% Like dropwhile, except messages are fetched in "require +%% acknowledgement" mode and are passed, together with their Delivered +%% flag and ack tag, to the supplied function. The function is also +%% fed an accumulator. The result of fetchwhile is as for dropwhile +%% plus the accumulator. +-callback fetchwhile(msg_pred(), + fun ((rabbit_types:basic_message(), boolean(), ack(), A) + -> A), + A, state()) + -> {rabbit_types:message_properties() | undefined, + A, state()}. %% Produce the next message. -callback fetch(true, state()) -> {fetch_result(ack()), state()}; @@ -222,7 +231,8 @@ behaviour_info(callbacks) -> [{start, 1}, {stop, 0}, {init, 3}, {terminate, 2}, {delete_and_terminate, 2}, {purge, 1}, {publish, 5}, - {publish_delivered, 4}, {discard, 3}, {drain_confirmed, 1}, {dropwhile, 3}, + {publish_delivered, 4}, {discard, 3}, {drain_confirmed, 1}, + {dropwhile, 2}, {fetchwhile, 4}, {fetch, 2}, {ack, 2}, {foreach_ack, 3}, {requeue, 2}, {fold, 3}, {len, 1}, {is_empty, 1}, {depth, 1}, {set_ram_duration_target, 2}, {ram_duration, 1}, {needs_timeout, 1}, {timeout, 1}, diff --git a/src/rabbit_backing_queue_qc.erl b/src/rabbit_backing_queue_qc.erl index a5d0a008..e337580c 100644 --- a/src/rabbit_backing_queue_qc.erl +++ b/src/rabbit_backing_queue_qc.erl @@ -147,7 +147,7 @@ qc_drain_confirmed(#state{bqstate = BQ}) -> {call, ?BQMOD, drain_confirmed, [BQ]}. qc_dropwhile(#state{bqstate = BQ}) -> - {call, ?BQMOD, dropwhile, [fun dropfun/1, false, BQ]}. + {call, ?BQMOD, dropwhile, [fun dropfun/1, BQ]}. qc_is_empty(#state{bqstate = BQ}) -> {call, ?BQMOD, is_empty, [BQ]}. @@ -262,7 +262,7 @@ next_state(S, Res, {call, ?BQMOD, drain_confirmed, _Args}) -> S#state{bqstate = BQ1}; next_state(S, Res, {call, ?BQMOD, dropwhile, _Args}) -> - BQ = {call, erlang, element, [3, Res]}, + BQ = {call, erlang, element, [2, Res]}, #state{messages = Messages} = S, Msgs1 = drop_messages(Messages), S#state{bqstate = BQ, len = gb_trees:size(Msgs1), messages = Msgs1}; diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index c8a361b1..0ae10d89 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -20,7 +20,7 @@ purge/1, publish/5, publish_delivered/4, discard/3, fetch/2, drop/2, ack/2, requeue/2, fold/3, len/1, is_empty/1, depth/1, drain_confirmed/1, - dropwhile/3, set_ram_duration_target/2, ram_duration/1, + dropwhile/2, fetchwhile/4, set_ram_duration_target/2, ram_duration/1, needs_timeout/1, timeout/1, handle_pre_hibernate/1, status/1, invoke/3, is_duplicate/2, foreach_ack/3]). @@ -216,19 +216,17 @@ discard(MsgId, ChPid, State = #state { gm = GM, State end. -dropwhile(Pred, AckRequired, - State = #state{gm = GM, - backing_queue = BQ, - backing_queue_state = BQS }) -> +dropwhile(Pred, State = #state{backing_queue = BQ, + backing_queue_state = BQS }) -> Len = BQ:len(BQS), - {Next, Msgs, BQS1} = BQ:dropwhile(Pred, AckRequired, BQS), - Len1 = BQ:len(BQS1), - Dropped = Len - Len1, - case Dropped of - 0 -> ok; - _ -> ok = gm:broadcast(GM, {drop, Len1, Dropped, AckRequired}) - end, - {Next, Msgs, State #state { backing_queue_state = BQS1 } }. + {Next, BQS1} = BQ:dropwhile(Pred, BQS), + {Next, drop(Len, false, State #state { backing_queue_state = BQS1 })}. + +fetchwhile(Pred, Fun, Acc, State = #state{backing_queue = BQ, + backing_queue_state = BQS }) -> + Len = BQ:len(BQS), + {Next, Acc1, BQS1} = BQ:fetchwhile(Pred, Fun, Acc, BQS), + {Next, Acc1, drop(Len, true, State #state { backing_queue_state = BQS1 })}. drain_confirmed(State = #state { backing_queue = BQ, backing_queue_state = BQS, @@ -268,7 +266,7 @@ fetch(AckRequired, State = #state { backing_queue = BQ, empty -> {Result, State1}; {#basic_message{id = MsgId}, _IsDelivered, AckTag} -> - {Result, drop(MsgId, AckTag, State1)} + {Result, drop_one(MsgId, AckTag, State1)} end. drop(AckRequired, State = #state { backing_queue = BQ, @@ -277,7 +275,7 @@ drop(AckRequired, State = #state { backing_queue = BQ, State1 = State #state { backing_queue_state = BQS1 }, {Result, case Result of empty -> State1; - {MsgId, AckTag} -> drop(MsgId, AckTag, State1) + {MsgId, AckTag} -> drop_one(MsgId, AckTag, State1) end}. ack(AckTags, State = #state { gm = GM, @@ -440,13 +438,23 @@ depth_fun() -> %% Helpers %% --------------------------------------------------------------------------- -drop(MsgId, AckTag, State = #state { ack_msg_id = AM, - gm = GM, - backing_queue = BQ, - backing_queue_state = BQS }) -> +drop_one(MsgId, AckTag, State = #state { ack_msg_id = AM, + gm = GM, + backing_queue = BQ, + backing_queue_state = BQS }) -> ok = gm:broadcast(GM, {drop, BQ:len(BQS), 1, AckTag =/= undefined}), State #state { ack_msg_id = maybe_store_acktag(AckTag, MsgId, AM) }. +drop(PrevLen, AckRequired, State = #state { gm = GM, + backing_queue = BQ, + backing_queue_state = BQS }) -> + Len = BQ:len(BQS), + case PrevLen - Len of + 0 -> State; + Dropped -> ok = gm:broadcast(GM, {drop, Len, Dropped, AckRequired}), + State + end. + maybe_store_acktag(undefined, _MsgId, AM) -> AM; maybe_store_acktag(AckTag, MsgId, AM) -> dict:store(AckTag, MsgId, AM). diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index df8544a4..d6d40b14 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -2418,10 +2418,10 @@ test_dropwhile(VQ0) -> fun (N, Props) -> Props#message_properties{expiry = N} end, VQ0), %% drop the first 5 messages - {_, undefined, VQ2} = rabbit_variable_queue:dropwhile( - fun(#message_properties { expiry = Expiry }) -> - Expiry =< 5 - end, false, VQ1), + {_, VQ2} = rabbit_variable_queue:dropwhile( + fun(#message_properties { expiry = Expiry }) -> + Expiry =< 5 + end, VQ1), %% fetch five now VQ3 = lists:foldl(fun (_N, VQN) -> @@ -2438,12 +2438,10 @@ test_dropwhile(VQ0) -> test_dropwhile_varying_ram_duration(VQ0) -> VQ1 = variable_queue_publish(false, 1, VQ0), VQ2 = rabbit_variable_queue:set_ram_duration_target(0, VQ1), - {_, undefined, VQ3} = rabbit_variable_queue:dropwhile( - fun(_) -> false end, false, VQ2), + {_, VQ3} = rabbit_variable_queue:dropwhile(fun(_) -> false end, VQ2), VQ4 = rabbit_variable_queue:set_ram_duration_target(infinity, VQ3), VQ5 = variable_queue_publish(false, 1, VQ4), - {_, undefined, VQ6} = - rabbit_variable_queue:dropwhile(fun(_) -> false end, false, VQ5), + {_, VQ6} = rabbit_variable_queue:dropwhile(fun(_) -> false end, VQ5), VQ6. test_variable_queue_dynamic_duration_change(VQ0) -> diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 30ab96f5..3e4c7c86 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -18,7 +18,8 @@ -export([init/3, terminate/2, delete_and_terminate/2, purge/1, publish/5, publish_delivered/4, discard/3, drain_confirmed/1, - dropwhile/3, fetch/2, drop/2, ack/2, requeue/2, fold/3, len/1, + dropwhile/2, fetchwhile/4, + fetch/2, drop/2, ack/2, requeue/2, fold/3, len/1, is_empty/1, depth/1, set_ram_duration_target/2, ram_duration/1, needs_timeout/1, timeout/1, handle_pre_hibernate/1, status/1, invoke/3, is_duplicate/2, multiple_routing_keys/0, foreach_ack/3]). @@ -577,27 +578,30 @@ drain_confirmed(State = #vqstate { confirmed = C }) -> confirmed = gb_sets:new() }} end. -dropwhile(Pred, AckRequired, State) -> dropwhile(Pred, AckRequired, State, []). +dropwhile(Pred, State) -> + case queue_out(State) of + {empty, State1} -> + {undefined, a(State1)}; + {{value, MsgStatus = #msg_status { msg_props = MsgProps }}, State1} -> + case Pred(MsgProps) of + true -> {_, State2} = internal_fetch(false, MsgStatus, State1), + dropwhile(Pred, State2); + false -> {MsgProps, a(in_r(MsgStatus, State1))} + end + end. -dropwhile(Pred, AckRequired, State, Msgs) -> - End = fun(Next, S) when AckRequired -> {Next, lists:reverse(Msgs), S}; - (Next, S) -> {Next, undefined, S} - end, +fetchwhile(Pred, Fun, Acc, State) -> case queue_out(State) of {empty, State1} -> - End(undefined, a(State1)); + {undefined, Acc, a(State1)}; {{value, MsgStatus = #msg_status { msg_props = MsgProps }}, State1} -> - case {Pred(MsgProps), AckRequired} of - {true, true} -> - {MsgStatus1, State2} = read_msg(MsgStatus, State1), - {{Msg, _IsDelivered, AckTag}, State3} = - internal_fetch(true, MsgStatus1, State2), - dropwhile(Pred, AckRequired, State3, [{Msg, AckTag} | Msgs]); - {true, false} -> - {_, State2} = internal_fetch(false, MsgStatus, State1), - dropwhile(Pred, AckRequired, State2, undefined); - {false, _} -> - End(MsgProps, a(in_r(MsgStatus, State1))) + case Pred(MsgProps) of + true -> {MsgStatus1, State2} = read_msg(MsgStatus, State1), + {{Msg, IsDelivered, AckTag}, State3} = + internal_fetch(true, MsgStatus1, State2), + Acc1 = Fun(Msg, IsDelivered, AckTag, Acc), + fetchwhile(Pred, Fun, Acc1, State3); + false -> {MsgProps, Acc, a(in_r(MsgStatus, State1))} end end. -- cgit v1.2.1 From bdee19cebc803a712999ea7d201b45ef6e78f238 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 30 Nov 2012 00:28:05 +0000 Subject: remove unused state var We were diligently maintaining ack_msg_id, but never actually using it for anything. And it's always been like that. --- src/rabbit_mirror_queue_master.erl | 43 +++++++++++++------------------------- 1 file changed, 14 insertions(+), 29 deletions(-) diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index c8a361b1..009971bb 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -40,7 +40,6 @@ backing_queue_state, seen_status, confirmed, - ack_msg_id, known_senders }). @@ -56,7 +55,6 @@ backing_queue_state :: any(), seen_status :: dict(), confirmed :: [rabbit_guid:guid()], - ack_msg_id :: dict(), known_senders :: set() }). @@ -114,7 +112,6 @@ init_with_existing_bq(Q = #amqqueue{name = QName}, BQ, BQS) -> backing_queue_state = BQS, seen_status = dict:new(), confirmed = [], - ack_msg_id = dict:new(), known_senders = sets:new() }. stop_mirroring(State = #state { coordinator = CPid, @@ -187,13 +184,11 @@ publish_delivered(Msg = #basic_message { id = MsgId }, MsgProps, ChPid, State = #state { gm = GM, seen_status = SS, backing_queue = BQ, - backing_queue_state = BQS, - ack_msg_id = AM }) -> + backing_queue_state = BQS }) -> false = dict:is_key(MsgId, SS), %% ASSERTION ok = gm:broadcast(GM, {publish_delivered, ChPid, MsgProps, Msg}), {AckTag, BQS1} = BQ:publish_delivered(Msg, MsgProps, ChPid, BQS), - AM1 = maybe_store_acktag(AckTag, MsgId, AM), - State1 = State #state { backing_queue_state = BQS1, ack_msg_id = AM1 }, + State1 = State #state { backing_queue_state = BQS1 }, {AckTag, ensure_monitoring(ChPid, State1)}. discard(MsgId, ChPid, State = #state { gm = GM, @@ -264,34 +259,29 @@ fetch(AckRequired, State = #state { backing_queue = BQ, backing_queue_state = BQS }) -> {Result, BQS1} = BQ:fetch(AckRequired, BQS), State1 = State #state { backing_queue_state = BQS1 }, - case Result of - empty -> - {Result, State1}; - {#basic_message{id = MsgId}, _IsDelivered, AckTag} -> - {Result, drop(MsgId, AckTag, State1)} - end. + {Result, case Result of + empty -> State1; + {_MsgId, _IsDelivered, AckTag} -> drop_one(AckTag, State1) + end}. drop(AckRequired, State = #state { backing_queue = BQ, backing_queue_state = BQS }) -> {Result, BQS1} = BQ:drop(AckRequired, BQS), State1 = State #state { backing_queue_state = BQS1 }, {Result, case Result of - empty -> State1; - {MsgId, AckTag} -> drop(MsgId, AckTag, State1) + empty -> State1; + {_MsgId, AckTag} -> drop_one(AckTag, State1) end}. ack(AckTags, State = #state { gm = GM, backing_queue = BQ, - backing_queue_state = BQS, - ack_msg_id = AM }) -> + backing_queue_state = BQS }) -> {MsgIds, BQS1} = BQ:ack(AckTags, BQS), case MsgIds of [] -> ok; _ -> ok = gm:broadcast(GM, {ack, MsgIds}) end, - AM1 = lists:foldl(fun dict:erase/2, AM, AckTags), - {MsgIds, State #state { backing_queue_state = BQS1, - ack_msg_id = AM1 }}. + {MsgIds, State #state { backing_queue_state = BQS1 }}. foreach_ack(MsgFun, State = #state { backing_queue = BQ, backing_queue_state = BQS }, AckTags) -> @@ -408,7 +398,6 @@ promote_backing_queue_state(CPid, BQ, BQS, GM, AckTags, SeenStatus, KS) -> backing_queue_state = BQS1, seen_status = SeenStatus, confirmed = [], - ack_msg_id = dict:new(), known_senders = sets:from_list(KS) }. sender_death_fun() -> @@ -440,15 +429,11 @@ depth_fun() -> %% Helpers %% --------------------------------------------------------------------------- -drop(MsgId, AckTag, State = #state { ack_msg_id = AM, - gm = GM, - backing_queue = BQ, - backing_queue_state = BQS }) -> +drop_one(AckTag, State = #state { gm = GM, + backing_queue = BQ, + backing_queue_state = BQS }) -> ok = gm:broadcast(GM, {drop, BQ:len(BQS), 1, AckTag =/= undefined}), - State #state { ack_msg_id = maybe_store_acktag(AckTag, MsgId, AM) }. - -maybe_store_acktag(undefined, _MsgId, AM) -> AM; -maybe_store_acktag(AckTag, MsgId, AM) -> dict:store(AckTag, MsgId, AM). + State. ensure_monitoring(ChPid, State = #state { coordinator = CPid, known_senders = KS }) -> -- cgit v1.2.1 From fb7011f1e3b1487431b0e72031f4d844e53041af Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 30 Nov 2012 00:53:15 +0000 Subject: unbreak qc (hopefully) --- src/rabbit_backing_queue_qc.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_backing_queue_qc.erl b/src/rabbit_backing_queue_qc.erl index a5d0a008..a7e0a5e7 100644 --- a/src/rabbit_backing_queue_qc.erl +++ b/src/rabbit_backing_queue_qc.erl @@ -115,7 +115,7 @@ qc_publish(#state{bqstate = BQ}) -> #message_properties{needs_confirming = frequency([{1, true}, {20, false}]), expiry = oneof([undefined | lists:seq(1, 10)])}, - self(), BQ]}. + false, self(), BQ]}. qc_publish_multiple(#state{}) -> {call, ?MODULE, publish_multiple, [resize(?QUEUE_MAXLEN, pos_integer())]}. @@ -182,7 +182,7 @@ precondition(#state{len = Len}, {call, ?MODULE, publish_multiple, _Arg}) -> %% Model updates -next_state(S, BQ, {call, ?BQMOD, publish, [Msg, MsgProps, _Pid, _BQ]}) -> +next_state(S, BQ, {call, ?BQMOD, publish, [Msg, MsgProps, _Del, _Pid, _BQ]}) -> #state{len = Len, messages = Messages, confirms = Confirms, -- 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(-) 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(-) 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 91a39d3368f2a6590776d7defc896ba325b7fc8a Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 30 Nov 2012 16:37:45 +0000 Subject: Emit events on vhost creation / deletion. --- src/rabbit_vhost.erl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/rabbit_vhost.erl b/src/rabbit_vhost.erl index 297fa56f..4fe5c169 100644 --- a/src/rabbit_vhost.erl +++ b/src/rabbit_vhost.erl @@ -70,6 +70,7 @@ add(VHostPath) -> {<<"amq.rabbitmq.trace">>, topic}]], ok end), + rabbit_event:notify(vhost_created, info(VHostPath)), R. delete(VHostPath) -> @@ -87,6 +88,7 @@ delete(VHostPath) -> with(VHostPath, fun () -> ok = internal_delete(VHostPath) end)), + ok = rabbit_event:notify(vhost_deleted, [{name, VHostPath}]), R. internal_delete(VHostPath) -> -- cgit v1.2.1 From cf229b48130743509783e65927c6ca77928c6dc8 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Mon, 3 Dec 2012 13:02:30 +0000 Subject: Whitespace --- src/rabbit_amqqueue_process.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index a6b3829b..2ffa2a1a 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -743,7 +743,7 @@ drop_expired_messages(State = #q{dlx = DLX, end, State#q{backing_queue_state = BQS1}). accumulate_msgs(Msg, _IsDelivered, AckTag, Acc) -> [{Msg, AckTag} | Acc]. - + ensure_ttl_timer(undefined, State) -> State; ensure_ttl_timer(Expiry, State = #q{ttl_timer_ref = undefined}) -> -- cgit v1.2.1 From 5c23d01c49572a8c1b9a4d732461d3db26beee45 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 3 Dec 2012 13:51:43 +0000 Subject: move generic cross-node funs from rabbit_amqqueue to delegate since the latter changes less frequently --- src/delegate.erl | 12 +++++++++- src/rabbit_amqqueue.erl | 59 ++++++++++++++++++------------------------------- 2 files changed, 32 insertions(+), 39 deletions(-) diff --git a/src/delegate.erl b/src/delegate.erl index d595e481..9222c34c 100644 --- a/src/delegate.erl +++ b/src/delegate.erl @@ -18,7 +18,7 @@ -behaviour(gen_server2). --export([start_link/1, invoke_no_result/2, invoke/2]). +-export([start_link/1, invoke_no_result/2, invoke/2, call/2, cast/2]). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). @@ -35,6 +35,10 @@ [{pid(), term()}]}). -spec(invoke_no_result/2 :: (pid() | [pid()], fun ((pid()) -> any())) -> 'ok'). +-spec(call/2 :: + ( pid(), any()) -> any(); + ([pid()], any()) -> {[{pid(), any()}], [{pid(), term()}]}). +-spec(cast/2 :: (pid() | [pid()], any()) -> 'ok'). -endif. @@ -96,6 +100,12 @@ invoke_no_result(Pids, Fun) when is_list(Pids) -> safe_invoke(LocalPids, Fun), %% must not die ok. +call(PidOrPids, Msg) -> + invoke(PidOrPids, fun (P) -> gen_server2:call(P, Msg, infinity) end). + +cast(PidOrPids, Msg) -> + invoke_no_result(PidOrPids, fun (P) -> gen_server2:cast(P, Msg) end). + %%---------------------------------------------------------------------------- group_pids_by_node(Pids) -> diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index c48aa6dd..3b54c1c3 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -438,10 +438,10 @@ info_keys() -> rabbit_amqqueue_process:info_keys(). map(VHostPath, F) -> rabbit_misc:filter_exit_map(F, list(VHostPath)). -info(#amqqueue{ pid = QPid }) -> delegate_call(QPid, info). +info(#amqqueue{ pid = QPid }) -> delegate:call(QPid, info). info(#amqqueue{ pid = QPid }, Items) -> - case delegate_call(QPid, {info, Items}) of + case delegate:call(QPid, {info, Items}) of {ok, Res} -> Res; {error, Error} -> throw(Error) end. @@ -472,7 +472,7 @@ force_event_refresh(QNames) -> wake_up(#amqqueue{pid = QPid}) -> gen_server2:cast(QPid, wake_up). -consumers(#amqqueue{ pid = QPid }) -> delegate_call(QPid, consumers). +consumers(#amqqueue{ pid = QPid }) -> delegate:call(QPid, consumers). consumer_info_keys() -> ?CONSUMER_INFO_KEYS. @@ -486,27 +486,27 @@ consumers_all(VHostPath) -> {ChPid, ConsumerTag, AckRequired} <- consumers(Q)] end)). -stat(#amqqueue{pid = QPid}) -> delegate_call(QPid, stat). +stat(#amqqueue{pid = QPid}) -> delegate:call(QPid, stat). delete_immediately(QPids) -> [gen_server2:cast(QPid, delete_immediately) || QPid <- QPids], ok. delete(#amqqueue{ pid = QPid }, IfUnused, IfEmpty) -> - delegate_call(QPid, {delete, IfUnused, IfEmpty}). + delegate:call(QPid, {delete, IfUnused, IfEmpty}). -purge(#amqqueue{ pid = QPid }) -> delegate_call(QPid, purge). +purge(#amqqueue{ pid = QPid }) -> delegate:call(QPid, purge). deliver(Qs, Delivery) -> deliver(Qs, Delivery, noflow). deliver_flow(Qs, Delivery) -> deliver(Qs, Delivery, flow). -requeue(QPid, MsgIds, ChPid) -> delegate_call(QPid, {requeue, MsgIds, ChPid}). +requeue(QPid, MsgIds, ChPid) -> delegate:call(QPid, {requeue, MsgIds, ChPid}). -ack(QPid, MsgIds, ChPid) -> delegate_cast(QPid, {ack, MsgIds, ChPid}). +ack(QPid, MsgIds, ChPid) -> delegate:cast(QPid, {ack, MsgIds, ChPid}). reject(QPid, MsgIds, Requeue, ChPid) -> - delegate_cast(QPid, {reject, MsgIds, Requeue, ChPid}). + delegate:cast(QPid, {reject, MsgIds, Requeue, ChPid}). notify_down_all(QPids, ChPid) -> safe_delegate_call_ok( @@ -514,19 +514,18 @@ notify_down_all(QPids, ChPid) -> QPids). limit_all(QPids, ChPid, Limiter) -> - delegate:invoke_no_result( - QPids, fun (QPid) -> gen_server2:cast(QPid, {limit, ChPid, Limiter}) end). + delegate:cast(QPids, {limit, ChPid, Limiter}). basic_get(#amqqueue{pid = QPid}, ChPid, NoAck) -> - delegate_call(QPid, {basic_get, ChPid, NoAck}). + delegate:call(QPid, {basic_get, ChPid, NoAck}). basic_consume(#amqqueue{pid = QPid}, NoAck, ChPid, Limiter, ConsumerTag, ExclusiveConsume, OkMsg) -> - delegate_call(QPid, {basic_consume, NoAck, ChPid, + delegate:call(QPid, {basic_consume, NoAck, ChPid, Limiter, ConsumerTag, ExclusiveConsume, OkMsg}). basic_cancel(#amqqueue{pid = QPid}, ChPid, ConsumerTag, OkMsg) -> - delegate_call(QPid, {basic_cancel, ChPid, ConsumerTag, OkMsg}). + delegate:call(QPid, {basic_cancel, ChPid, ConsumerTag, OkMsg}). notify_sent(QPid, ChPid) -> Key = {consumer_credit_to, QPid}, @@ -545,11 +544,9 @@ notify_sent_queue_down(QPid) -> erase({consumer_credit_to, QPid}), ok. -unblock(QPid, ChPid) -> delegate_cast(QPid, {unblock, ChPid}). +unblock(QPid, ChPid) -> delegate:cast(QPid, {unblock, ChPid}). -flush_all(QPids, ChPid) -> - delegate:invoke_no_result( - QPids, fun (QPid) -> gen_server2:cast(QPid, {flush, ChPid}) end). +flush_all(QPids, ChPid) -> delegate:cast(QPids, {flush, ChPid}). internal_delete1(QueueName) -> ok = mnesia:delete({rabbit_queue, QueueName}), @@ -588,8 +585,8 @@ set_ram_duration_target(QPid, Duration) -> set_maximum_since_use(QPid, Age) -> gen_server2:cast(QPid, {set_maximum_since_use, Age}). -start_mirroring(QPid) -> ok = delegate_cast(QPid, start_mirroring). -stop_mirroring(QPid) -> ok = delegate_cast(QPid, stop_mirroring). +start_mirroring(QPid) -> ok = delegate:cast(QPid, start_mirroring). +stop_mirroring(QPid) -> ok = delegate:cast(QPid, stop_mirroring). on_node_down(Node) -> rabbit_misc:execute_mnesia_tx_with_tail( @@ -652,10 +649,8 @@ deliver(Qs, Delivery = #delivery{mandatory = false}, Flow) -> %% done with it. MMsg = {deliver, Delivery, false, Flow}, SMsg = {deliver, Delivery, true, Flow}, - delegate:invoke_no_result(MPids, - fun (QPid) -> gen_server2:cast(QPid, MMsg) end), - delegate:invoke_no_result(SPids, - fun (QPid) -> gen_server2:cast(QPid, SMsg) end), + delegate:cast(MPids, MMsg), + delegate:cast(SPids, SMsg), {routed, QPids}; deliver(Qs, Delivery, _Flow) -> @@ -663,14 +658,8 @@ deliver(Qs, Delivery, _Flow) -> %% see comment above MMsg = {deliver, Delivery, false}, SMsg = {deliver, Delivery, true}, - {MRouted, _} = delegate:invoke( - MPids, fun (QPid) -> - ok = gen_server2:call(QPid, MMsg, infinity) - end), - {SRouted, _} = delegate:invoke( - SPids, fun (QPid) -> - ok = gen_server2:call(QPid, SMsg, infinity) - end), + {MRouted, _} = delegate:call(MPids, MMsg), + {SRouted, _} = delegate:call(SPids, SMsg), case MRouted ++ SRouted of [] -> {unroutable, []}; R -> {routed, [QPid || {QPid, ok} <- R]} @@ -696,9 +685,3 @@ safe_delegate_call_ok(F, Pids) -> [] -> ok; Bads1 -> {error, Bads1} end. - -delegate_call(Pid, Msg) -> - delegate:invoke(Pid, fun (P) -> gen_server2:call(P, Msg, infinity) end). - -delegate_cast(Pid, Msg) -> - delegate:invoke_no_result(Pid, fun (P) -> gen_server2:cast(P, Msg) end). -- cgit v1.2.1 From 95df21c84139e849fa1fb836b17020fa1ae26f04 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 3 Dec 2012 14:26:14 +0000 Subject: get rid of last remaining cross-node fun in rabbit_amqqueue There was no need for the rabbit_misc:with_exit_handler in safe_delegate_call_ok, since the error condition caught be that is also being caught by the check in 'filter'. Getting rid of the superfluous check allows us to just invoke delegate:call and thus get rid of the funs. Plus inline the lot. --- src/rabbit_amqqueue.erl | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 3b54c1c3..52884410 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -509,9 +509,14 @@ reject(QPid, MsgIds, Requeue, ChPid) -> delegate:cast(QPid, {reject, MsgIds, Requeue, ChPid}). notify_down_all(QPids, ChPid) -> - safe_delegate_call_ok( - fun (QPid) -> gen_server2:call(QPid, {notify_down, ChPid}, infinity) end, - QPids). + {_, Bads} = delegate:call(QPids, {notify_down, ChPid}), + case lists:filter( + fun ({_Pid, {exit, {R, _}, _}}) -> rabbit_misc:is_abnormal_exit(R); + ({_Pid, _}) -> false + end, Bads) of + [] -> ok; + Bads1 -> {error, Bads1} + end. limit_all(QPids, ChPid, Limiter) -> delegate:cast(QPids, {limit, ChPid, Limiter}). @@ -671,17 +676,3 @@ qpids(Qs) -> {[QPid | MPidAcc], [SPids | SPidAcc]} end, {[], []}, Qs), {MPids, lists:append(SPids)}. - -safe_delegate_call_ok(F, Pids) -> - {_, Bads} = delegate:invoke(Pids, fun (Pid) -> - rabbit_misc:with_exit_handler( - fun () -> ok end, - fun () -> F(Pid) end) - end), - case lists:filter( - fun ({_Pid, {exit, {R, _}, _}}) -> rabbit_misc:is_abnormal_exit(R); - ({_Pid, _}) -> false - end, Bads) of - [] -> ok; - Bads1 -> {error, Bads1} - end. -- cgit v1.2.1 From 92aa87030a88df36935df3b0d71333c6069db5f2 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 3 Dec 2012 16:22:49 +0000 Subject: Compare RabbitMQ versions ignoring the "patch" version (as semver.org calls it). Not that we are full on semver, but close enough. --- src/rabbit_mnesia.erl | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 942048f9..de288224 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -757,18 +757,34 @@ check_nodes_consistency(Node, RemoteStatus = {RemoteAllNodes, _, _}) -> [node(), Node, Node])}} end. -check_version_consistency(This, Remote, _) when This =:= Remote -> - ok; check_version_consistency(This, Remote, Name) -> - {error, {inconsistent_cluster, - rabbit_misc:format("~s version mismatch: local node is ~s, " - "remote node ~s", [Name, This, Remote])}}. + check_version_consistency(This, Remote, Name, fun (A, B) -> A =:= B end). + +check_version_consistency(This, Remote, Name, Comp) -> + case Comp(This, Remote) of + true -> ok; + false -> {error, {inconsistent_cluster, + rabbit_misc:format( + "~s version mismatch: local node is ~s, " + "remote node ~s", [Name, This, Remote])}} + end. check_otp_consistency(Remote) -> check_version_consistency(erlang:system_info(otp_release), Remote, "OTP"). check_rabbit_consistency(Remote) -> - check_version_consistency(rabbit_misc:version(), Remote, "Rabbit"). + check_version_consistency( + rabbit_misc:version(), Remote, "Rabbit", + fun(A, B) -> + %% a.b.c and a.b.d match, but a.b.c and a.d.e don't. If + %% versions do not match that pattern, just compare them. + {ok, RE} = re:compile("(\\d+\\.\\d+)(\\.\\d+)*"), + Opts = [{capture, all_but_first, list}], + case {re:run(A, RE, Opts), re:run(B, RE, Opts)} of + {{match, [A1|_]}, {match, [B1|_]}} -> A1 =:= B1; + _ -> A =:= B + end + end). %% 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 -- cgit v1.2.1 From cfb072ffe9519536bcee15efdd846f73886aeb98 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 3 Dec 2012 18:44:11 +0000 Subject: add test for vq:fetchwhile --- src/rabbit_tests.erl | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index d6d40b14..d9ef4439 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -2307,7 +2307,7 @@ test_variable_queue() -> fun test_variable_queue_all_the_bits_not_covered_elsewhere2/1, fun test_drop/1, fun test_variable_queue_fold_msg_on_disk/1, - fun test_dropwhile/1, + fun test_dropfetchwhile/1, fun test_dropwhile_varying_ram_duration/1, fun test_variable_queue_ack_limiting/1, fun test_variable_queue_requeue/1, @@ -2409,7 +2409,7 @@ test_drop(VQ0) -> true = rabbit_variable_queue:is_empty(VQ5), VQ5. -test_dropwhile(VQ0) -> +test_dropfetchwhile(VQ0) -> Count = 10, %% add messages with sequential expiry @@ -2417,23 +2417,32 @@ test_dropwhile(VQ0) -> false, Count, fun (N, Props) -> Props#message_properties{expiry = N} end, VQ0), + %% fetch the first 5 messages + {#message_properties{expiry = 6}, AckTags, VQ2} = + rabbit_variable_queue:fetchwhile( + fun (#message_properties{expiry = Expiry}) -> Expiry =< 5 end, + fun (_Msg, _Delivered, AckTag, Acc) -> [AckTag | Acc] end, [], VQ1), + 5 = length(AckTags), + + %% requeue them + {_MsgIds, VQ3} = rabbit_variable_queue:requeue(AckTags, VQ2), + %% drop the first 5 messages - {_, VQ2} = rabbit_variable_queue:dropwhile( - fun(#message_properties { expiry = Expiry }) -> - Expiry =< 5 - end, VQ1), + {#message_properties{expiry = 6}, VQ4} = + rabbit_variable_queue:dropwhile( + fun (#message_properties {expiry = Expiry}) -> Expiry =< 5 end, VQ3), - %% fetch five now - VQ3 = lists:foldl(fun (_N, VQN) -> + %% fetch 5 now + VQ5 = lists:foldl(fun (_N, VQN) -> {{#basic_message{}, _, _}, VQM} = rabbit_variable_queue:fetch(false, VQN), VQM - end, VQ2, lists:seq(6, Count)), + end, VQ4, lists:seq(6, Count)), %% should be empty now - {empty, VQ4} = rabbit_variable_queue:fetch(false, VQ3), + {empty, VQ6} = rabbit_variable_queue:fetch(false, VQ5), - VQ4. + VQ6. test_dropwhile_varying_ram_duration(VQ0) -> VQ1 = variable_queue_publish(false, 1, VQ0), -- cgit v1.2.1 From ccd92d8620f0441002c184d65a60275abb066cbf Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 3 Dec 2012 18:56:33 +0000 Subject: test more --- src/rabbit_tests.erl | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index d9ef4439..42c16b34 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -2415,14 +2415,17 @@ test_dropfetchwhile(VQ0) -> %% add messages with sequential expiry VQ1 = variable_queue_publish( false, Count, - fun (N, Props) -> Props#message_properties{expiry = N} end, VQ0), + fun (N, Props) -> Props#message_properties{expiry = N} end, + fun erlang:term_to_binary/1, VQ0), %% fetch the first 5 messages - {#message_properties{expiry = 6}, AckTags, VQ2} = + {#message_properties{expiry = 6}, {Msgs, AckTags}, VQ2} = rabbit_variable_queue:fetchwhile( fun (#message_properties{expiry = Expiry}) -> Expiry =< 5 end, - fun (_Msg, _Delivered, AckTag, Acc) -> [AckTag | Acc] end, [], VQ1), - 5 = length(AckTags), + fun (Msg, _Delivered, AckTag, {MsgAcc, AckAcc}) -> + {[Msg | MsgAcc], [AckTag | AckAcc]} + end, {[], []}, VQ1), + true = lists:seq(1, 5) == [msg2int(M) || M <- lists:reverse(Msgs)], %% requeue them {_MsgIds, VQ3} = rabbit_variable_queue:requeue(AckTags, VQ2), @@ -2432,17 +2435,18 @@ test_dropfetchwhile(VQ0) -> rabbit_variable_queue:dropwhile( fun (#message_properties {expiry = Expiry}) -> Expiry =< 5 end, VQ3), - %% fetch 5 now - VQ5 = lists:foldl(fun (_N, VQN) -> - {{#basic_message{}, _, _}, VQM} = + %% fetch 5 + VQ5 = lists:foldl(fun (N, VQN) -> + {{Msg, _, _}, VQM} = rabbit_variable_queue:fetch(false, VQN), + true = msg2int(Msg) == N, VQM end, VQ4, lists:seq(6, Count)), %% should be empty now - {empty, VQ6} = rabbit_variable_queue:fetch(false, VQ5), + true = rabbit_variable_queue:is_empty(VQ5), - VQ6. + VQ5. test_dropwhile_varying_ram_duration(VQ0) -> VQ1 = variable_queue_publish(false, 1, VQ0), -- cgit v1.2.1 From a0cf7e4ab2261a85299deaaa3ae6229795c2df28 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 3 Dec 2012 19:14:41 +0000 Subject: yet more tests --- src/rabbit_tests.erl | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 42c16b34..2226f445 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -2309,6 +2309,7 @@ test_variable_queue() -> fun test_variable_queue_fold_msg_on_disk/1, fun test_dropfetchwhile/1, fun test_dropwhile_varying_ram_duration/1, + fun test_fetchwhile_varying_ram_duration/1, fun test_variable_queue_ack_limiting/1, fun test_variable_queue_requeue/1, fun test_variable_queue_fold/1]], @@ -2449,12 +2450,30 @@ test_dropfetchwhile(VQ0) -> VQ5. test_dropwhile_varying_ram_duration(VQ0) -> + test_dropfetchwhile_varying_ram_duration( + fun (VQ1) -> + {_, VQ2} = rabbit_variable_queue:dropwhile( + fun (_) -> false end, VQ1), + VQ2 + end, VQ0). + +test_fetchwhile_varying_ram_duration(VQ0) -> + test_dropfetchwhile_varying_ram_duration( + fun (VQ1) -> + {_, ok, VQ2} = rabbit_variable_queue:fetchwhile( + fun (_) -> false end, + fun (_, _, _, A) -> A end, + ok, VQ1), + VQ2 + end, VQ0). + +test_dropfetchwhile_varying_ram_duration(Fun, VQ0) -> VQ1 = variable_queue_publish(false, 1, VQ0), VQ2 = rabbit_variable_queue:set_ram_duration_target(0, VQ1), - {_, VQ3} = rabbit_variable_queue:dropwhile(fun(_) -> false end, VQ2), + VQ3 = Fun(VQ2), VQ4 = rabbit_variable_queue:set_ram_duration_target(infinity, VQ3), VQ5 = variable_queue_publish(false, 1, VQ4), - {_, VQ6} = rabbit_variable_queue:dropwhile(fun(_) -> false end, VQ5), + VQ6 = Fun(VQ5), VQ6. test_variable_queue_dynamic_duration_change(VQ0) -> -- cgit v1.2.1 From 89f60604808dbbf7e0d6ea88192bf68e4cafe113 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 4 Dec 2012 12:01:14 +0000 Subject: remove pre-0-9-1 cruft --- src/rabbit_channel.erl | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index b1ef3b6b..a3c82865 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -529,16 +529,12 @@ check_not_default_exchange(_) -> %% check that an exchange/queue name does not contain the reserved %% "amq." prefix. %% -%% One, quite reasonable, interpretation of the spec, taken by the -%% QPid M1 Java client, is that the exclusion of "amq." prefixed names +%% As per the AMQP 0-9-1 spec, the exclusion of "amq." prefixed names %% only applies on actual creation, and not in the cases where the -%% entity already exists. This is how we use this function in the code -%% below. However, AMQP JIRA 123 changes that in 0-10, and possibly -%% 0-9SP1, making it illegal to attempt to declare an exchange/queue -%% with an amq.* name when passive=false. So this will need -%% revisiting. +%% entity already exists or passive=true. %% -%% TODO: enforce other constraints on name. See AMQP JIRA 69. +%% NB: We deliberately do not enforce the other constraints on names +%% required by the spec. check_name(Kind, NameBin = <<"amq.", _/binary>>) -> rabbit_misc:protocol_error( access_refused, -- 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(-) 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 7246ac2347492a65ccd5d30cb4d65239e76eff27 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 4 Dec 2012 16:37:37 +0000 Subject: Send messages immediately if we are not busy. --- src/gm.erl | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/gm.erl b/src/gm.erl index 4a95de0d..1ee5f616 100644 --- a/src/gm.erl +++ b/src/gm.erl @@ -667,6 +667,10 @@ handle_info(flush, State) -> noreply( flush_broadcast_buffer(State #state { broadcast_timer = undefined })); +handle_info(timeout, State) -> + noreply( + flush_broadcast_buffer(State #state { broadcast_timer = undefined })); + handle_info({'DOWN', MRef, process, _Pid, Reason}, State = #state { self = Self, left = Left, @@ -834,10 +838,13 @@ handle_msg({activity, _NotLeft, _Activity}, State) -> noreply(State) -> - {noreply, ensure_broadcast_timer(State), hibernate}. + {noreply, ensure_broadcast_timer(State), flush_timeout(State)}. reply(Reply, State) -> - {reply, Reply, ensure_broadcast_timer(State), hibernate}. + {reply, Reply, ensure_broadcast_timer(State), flush_timeout(State)}. + +flush_timeout(#state{broadcast_buffer = []}) -> hibernate; +flush_timeout(_) -> 0. ensure_broadcast_timer(State = #state { broadcast_buffer = [], broadcast_timer = undefined }) -> -- cgit v1.2.1 From 7cebcb282e0427c526063a78f09bce777fdd4c6a Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 4 Dec 2012 19:57:53 +0000 Subject: reduce wrongness --- src/gm.erl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/gm.erl b/src/gm.erl index 1ee5f616..2057b1f5 100644 --- a/src/gm.erl +++ b/src/gm.erl @@ -668,8 +668,7 @@ handle_info(flush, State) -> flush_broadcast_buffer(State #state { broadcast_timer = undefined })); handle_info(timeout, State) -> - noreply( - flush_broadcast_buffer(State #state { broadcast_timer = undefined })); + noreply(flush_broadcast_buffer(State)); handle_info({'DOWN', MRef, process, _Pid, Reason}, State = #state { self = Self, -- cgit v1.2.1 From 2d9e8374bd77ef1b3541ecdf734503f36b58a82d Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 5 Dec 2012 11:23:27 +0000 Subject: Don't blow up if we end up talking to not-a-supervisor. --- src/rabbit_vm.erl | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/rabbit_vm.erl b/src/rabbit_vm.erl index 53f3df18..e9679276 100644 --- a/src/rabbit_vm.erl +++ b/src/rabbit_vm.erl @@ -84,7 +84,15 @@ sup_memory(Sup) -> sup_children(Sup) -> rabbit_misc:with_exit_handler( - rabbit_misc:const([]), fun () -> supervisor:which_children(Sup) end). + rabbit_misc:const([]), + fun () -> + %% Just in case we end up talking to something that is + %% not a supervisor by mistake. + case supervisor:which_children(Sup) of + L when is_list(L) -> L; + _ -> [] + end + end). pid_memory(Pid) when is_pid(Pid) -> case process_info(Pid, memory) of {memory, M} -> M; -- 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(-) 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(-) 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(-) 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(-) 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(-) 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(-) 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 1f8ce4db2177b3652995c94960082b88dd3b0861 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 5 Dec 2012 13:01:58 +0000 Subject: Move the version check fun to misc, and anchor the regex. --- src/rabbit_misc.erl | 12 ++++++++++++ src/rabbit_mnesia.erl | 11 +---------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 81bb6769..70ab6c71 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -46,6 +46,7 @@ -export([sort_field_table/1]). -export([pid_to_string/1, string_to_pid/1]). -export([version_compare/2, version_compare/3]). +-export([version_minor_equivalent/2]). -export([dict_cons/3, orddict_cons/3, gb_trees_cons/3]). -export([gb_trees_fold/3, gb_trees_foreach/2]). -export([parse_arguments/3]). @@ -191,6 +192,7 @@ -spec(version_compare/3 :: (string(), string(), ('lt' | 'lte' | 'eq' | 'gte' | 'gt')) -> boolean()). +-spec(version_minor_equivalent/2 :: (string(), string()) -> boolean()). -spec(dict_cons/3 :: (any(), any(), dict()) -> dict()). -spec(orddict_cons/3 :: (any(), any(), orddict:orddict()) -> orddict:orddict()). -spec(gb_trees_cons/3 :: (any(), any(), gb_tree()) -> gb_tree()). @@ -734,6 +736,16 @@ version_compare(A, B) -> ANum > BNum -> gt end. +%% a.b.c and a.b.d match, but a.b.c and a.d.e don't. If +%% versions do not match that pattern, just compare them. +version_minor_equivalent(A, B) -> + {ok, RE} = re:compile("^(\\d+\\.\\d+)(\\.\\d+)*\$"), + Opts = [{capture, all_but_first, list}], + case {re:run(A, RE, Opts), re:run(B, RE, Opts)} of + {{match, [A1|_]}, {match, [B1|_]}} -> A1 =:= B1; + _ -> A =:= B + end. + dropdot(A) -> lists:dropwhile(fun (X) -> X =:= $. end, A). dict_cons(Key, Value, Dict) -> diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index de288224..779ac073 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -775,16 +775,7 @@ check_otp_consistency(Remote) -> check_rabbit_consistency(Remote) -> check_version_consistency( rabbit_misc:version(), Remote, "Rabbit", - fun(A, B) -> - %% a.b.c and a.b.d match, but a.b.c and a.d.e don't. If - %% versions do not match that pattern, just compare them. - {ok, RE} = re:compile("(\\d+\\.\\d+)(\\.\\d+)*"), - Opts = [{capture, all_but_first, list}], - case {re:run(A, RE, Opts), re:run(B, RE, Opts)} of - {{match, [A1|_]}, {match, [B1|_]}} -> A1 =:= B1; - _ -> A =:= B - end - end). + fun rabbit_misc:version_minor_equivalent/2). %% 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 -- cgit v1.2.1 From 98d871ee77528da4e732dae179d5986b9350720e Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 5 Dec 2012 13:10:12 +0000 Subject: Special case the 3.0.0 version check. --- src/rabbit_mnesia.erl | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 779ac073..6576ba52 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -763,15 +763,22 @@ check_version_consistency(This, Remote, Name) -> check_version_consistency(This, Remote, Name, Comp) -> case Comp(This, Remote) of true -> ok; - false -> {error, {inconsistent_cluster, - rabbit_misc:format( - "~s version mismatch: local node is ~s, " - "remote node ~s", [Name, This, Remote])}} + false -> version_error(Name, This, Remote) end. +version_error(Name, This, Remote) -> + {error, {inconsistent_cluster, + rabbit_misc:format("~s version mismatch: local node is ~s, " + "remote node ~s", [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 bd4dad1150be114357b8c5730c9942b43b425c5b Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 5 Dec 2012 13:50:04 +0000 Subject: Normalise credit flow quantities. --- src/rabbit_mirror_queue_sync.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_mirror_queue_sync.erl b/src/rabbit_mirror_queue_sync.erl index 4cb534ff..a80f8f50 100644 --- a/src/rabbit_mirror_queue_sync.erl +++ b/src/rabbit_mirror_queue_sync.erl @@ -123,7 +123,7 @@ syncer_loop({Ref, MPid} = Args, SPidsMRefs) -> {msg, Ref, Msg, MsgProps} -> SPidsMRefs1 = wait_for_credit(SPidsMRefs, Ref), [begin - credit_flow:send(SPid, ?CREDIT_DISC_BOUND), + credit_flow:send(SPid), SPid ! {sync_msg, Ref, Msg, MsgProps} end || {SPid, _} <- SPidsMRefs1], syncer_loop(Args, SPidsMRefs1); @@ -205,7 +205,7 @@ slave_sync_loop(Args = {Ref, MRef, Syncer, BQ, UpdateRamDuration, Parent}, {TRef1, BQS1} = UpdateRamDuration(BQ, BQS), slave_sync_loop(Args, TRef1, BQS1); {sync_msg, Ref, Msg, Props} -> - credit_flow:ack(Syncer, ?CREDIT_DISC_BOUND), + credit_flow:ack(Syncer), Props1 = Props#message_properties{needs_confirming = false}, BQS1 = BQ:publish(Msg, Props1, true, none, BQS), slave_sync_loop(Args, TRef, BQS1); -- cgit v1.2.1 From a97c9e6921ef7eced6f1f2d1afb47ed3ba8f2823 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 5 Dec 2012 14:08:22 +0000 Subject: Tweak the regex, align, and a test. --- src/rabbit_misc.erl | 4 ++-- src/rabbit_tests.erl | 11 +++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 70ab6c71..4efde50e 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -739,10 +739,10 @@ version_compare(A, B) -> %% a.b.c and a.b.d match, but a.b.c and a.d.e don't. If %% versions do not match that pattern, just compare them. version_minor_equivalent(A, B) -> - {ok, RE} = re:compile("^(\\d+\\.\\d+)(\\.\\d+)*\$"), + {ok, RE} = re:compile("^(\\d+\\.\\d+)(\\.\\d+)\$"), Opts = [{capture, all_but_first, list}], case {re:run(A, RE, Opts), re:run(B, RE, Opts)} of - {{match, [A1|_]}, {match, [B1|_]}} -> A1 =:= B1; + {{match, [A1|_]}, {match, [B1|_]}} -> A1 =:= B1; _ -> A =:= B end. diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 983abf29..a68caadb 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -38,6 +38,7 @@ all_tests() -> passed = mirrored_supervisor_tests:all_tests(), application:set_env(rabbit, file_handles_high_watermark, 10, infinity), ok = file_handle_cache:set_limit(10), + passed = test_version_equivalance(), passed = test_multi_call(), passed = test_file_handle_cache(), passed = test_backing_queue(), @@ -141,6 +142,16 @@ run_cluster_dependent_tests(SecondaryNode) -> passed. +test_version_equivalance() -> + true = rabbit_misc:version_minor_equivalent("3.0.0", "3.0.0"), + true = rabbit_misc:version_minor_equivalent("3.0.0", "3.0.1"), + true = rabbit_misc:version_minor_equivalent("%%VSN%%", "%%VSN%%"), + false = rabbit_misc:version_minor_equivalent("3.0.0", "3.1.0"), + false = rabbit_misc:version_minor_equivalent("3.0.0", "3.0"), + false = rabbit_misc:version_minor_equivalent("3.0.0", "3.0.0.1"), + false = rabbit_misc:version_minor_equivalent("3.0.0", "3.0.foo"), + passed. + test_multi_call() -> Fun = fun() -> receive -- cgit v1.2.1 From 3761e87b40caaf58962655130bb5eea3f9d20860 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 5 Dec 2012 16:28:25 +0000 Subject: send expired messages to self() one at a time which simplifies the code a fair bit and also means we don't build up a potentially huge intermediate data structure. --- src/rabbit_amqqueue_process.erl | 69 ++++++++++++++++++----------------------- 1 file changed, 31 insertions(+), 38 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 2ffa2a1a..2bb476fb 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -724,26 +724,24 @@ drop_expired_messages(State = #q{dlx = DLX, backing_queue = BQ }) -> Now = now_micros(), ExpirePred = fun (#message_properties{expiry = Exp}) -> Now >= Exp end, - {Props, BQS1} = case DLX of - undefined -> BQ:dropwhile(ExpirePred, BQS); - _ -> {Next, Msgs, BQS2} = - BQ:fetchwhile(ExpirePred, - fun accumulate_msgs/4, - [], BQS), - case Msgs of - [] -> ok; - _ -> (dead_letter_fun(expired))( - lists:reverse(Msgs)) - end, - {Next, BQS2} - end, + {Props, BQS1} = + case DLX of + undefined -> BQ:dropwhile(ExpirePred, BQS); + _ -> DLXFun = dead_letter_fun(expired), + {Next, ok, BQS2} = + BQ:fetchwhile( + ExpirePred, + fun (Msg, _IsDelivered, AckTag, Acc) -> + DLXFun(Msg, AckTag), + Acc + end, ok, BQS), + {Next, BQS2} + end, ensure_ttl_timer(case Props of undefined -> undefined; #message_properties{expiry = Exp} -> Exp end, State#q{backing_queue_state = BQS1}). -accumulate_msgs(Msg, _IsDelivered, AckTag, Acc) -> [{Msg, AckTag} | Acc]. - ensure_ttl_timer(undefined, State) -> State; ensure_ttl_timer(Expiry, State = #q{ttl_timer_ref = undefined}) -> @@ -764,7 +762,9 @@ ensure_ttl_timer(_Expiry, State) -> State. dead_letter_fun(Reason) -> - fun(Msgs) -> gen_server2:cast(self(), {dead_letter, Msgs, Reason}) end. + fun(Msg, AckTag) -> + gen_server2:cast(self(), {dead_letter, Msg, AckTag, Reason}) + end. dead_letter_publish(Msg, Reason, X, State = #q{publish_seqno = MsgSeqNo}) -> DLMsg = make_dead_letter_msg(Reason, Msg, State), @@ -1213,8 +1213,7 @@ handle_cast({reject, AckTags, false, ChPid}, State) -> ChPid, AckTags, State, fun (State1 = #q{backing_queue = BQ, backing_queue_state = BQS}) -> - BQS1 = BQ:foreach_ack(fun(M, A) -> DLXFun([{M, A}]) end, - BQS, AckTags), + BQS1 = BQ:foreach_ack(DLXFun, BQS, AckTags), State1#q{backing_queue_state = BQS1} end)); @@ -1262,29 +1261,23 @@ handle_cast({set_maximum_since_use, Age}, State) -> ok = file_handle_cache:set_maximum_since_use(Age), noreply(State); -handle_cast({dead_letter, Msgs, Reason}, State = #q{dlx = XName}) -> +handle_cast({dead_letter, Msg, AckTag, Reason}, + State = #q{dlx = XName, + publish_seqno = SeqNo, + unconfirmed = UC, + queue_monitors = QMons}) -> case rabbit_exchange:lookup(XName) of {ok, X} -> - {AckImmediately, State2} = - lists:foldl( - fun({Msg, AckTag}, - {Acks, State1 = #q{publish_seqno = SeqNo, - unconfirmed = UC, - queue_monitors = QMons}}) -> - case dead_letter_publish(Msg, Reason, X, State1) of - [] -> {[AckTag | Acks], State1}; - QPids -> UC1 = dtree:insert( - SeqNo, QPids, AckTag, UC), - QMons1 = pmon:monitor_all(QPids, QMons), - {Acks, - State1#q{publish_seqno = SeqNo + 1, - unconfirmed = UC1, - queue_monitors = QMons1}} - end - end, {[], State}, Msgs), - cleanup_after_confirm(AckImmediately, State2); + case dead_letter_publish(Msg, Reason, X, State) of + [] -> cleanup_after_confirm([AckTag], State); + QPids -> UC1 = dtree:insert(SeqNo, QPids, AckTag, UC), + QMons1 = pmon:monitor_all(QPids, QMons), + State#q{publish_seqno = SeqNo + 1, + unconfirmed = UC1, + queue_monitors = QMons1} + end; {error, not_found} -> - cleanup_after_confirm([AckTag || {_, AckTag} <- Msgs], State) + cleanup_after_confirm([AckTag], State) end; handle_cast(start_mirroring, State = #q{backing_queue = BQ, -- cgit v1.2.1 From 37b41dcea184592e39a55ec943f34bd33380e3fb Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 6 Dec 2012 13:50:47 +0000 Subject: Package changelogs for 3.0.1. --- packaging/RPMS/Fedora/rabbitmq-server.spec | 3 +++ packaging/debs/Debian/debian/changelog | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/packaging/RPMS/Fedora/rabbitmq-server.spec b/packaging/RPMS/Fedora/rabbitmq-server.spec index 5d9b9e2e..23fd4f46 100644 --- a/packaging/RPMS/Fedora/rabbitmq-server.spec +++ b/packaging/RPMS/Fedora/rabbitmq-server.spec @@ -123,6 +123,9 @@ done rm -rf %{buildroot} %changelog +* Thu Dec 6 2012 simon@rabbitmq.com 3.0.1-1 +- New Upstream Release + * Fri Nov 16 2012 simon@rabbitmq.com 3.0.0-1 - New Upstream Release diff --git a/packaging/debs/Debian/debian/changelog b/packaging/debs/Debian/debian/changelog index 17327133..82c06ac2 100644 --- a/packaging/debs/Debian/debian/changelog +++ b/packaging/debs/Debian/debian/changelog @@ -1,3 +1,9 @@ +rabbitmq-server (3.0.1-1) unstable; urgency=low + + * New Upstream Release + + -- Simon MacMullen Thu, 06 Dec 2012 13:33:24 +0000 + rabbitmq-server (3.0.0-1) unstable; urgency=low * New Upstream Release -- cgit v1.2.1 -- cgit v1.2.1 From 1ca44d16f3f9bd4fe9933a01ffcc84a990bd0320 Mon Sep 17 00:00:00 2001 From: Alvaro Videla Date: Thu, 6 Dec 2012 16:46:54 +0100 Subject: refactors the sed bits inside the Makefile --- packaging/standalone/Makefile | 14 +++++++++++++- packaging/standalone/fix-rabbitmq-defaults.sh | 20 -------------------- 2 files changed, 13 insertions(+), 21 deletions(-) delete mode 100755 packaging/standalone/fix-rabbitmq-defaults.sh diff --git a/packaging/standalone/Makefile b/packaging/standalone/Makefile index fced396f..50d1385a 100644 --- a/packaging/standalone/Makefile +++ b/packaging/standalone/Makefile @@ -13,6 +13,10 @@ RABBITMQ_EBIN_ROOT=$(RABBITMQ_HOME)/ebin RABBITMQ_PLUGINS_DIR=$(RABBITMQ_HOME)/plugins RABBITMQ_PLUGINS_EXPAND_DIR=$(RABBITMQ_PLUGINS_DIR)/expand +RABBITMQ_DEFAULTS=$(TARGET_DIR)/sbin/rabbitmq-defaults +fix_defaults = sed -e $(1) $(RABBITMQ_DEFAULTS) > $(RABBITMQ_DEFAULTS).tmp \ + && mv $(RABBITMQ_DEFAULTS).tmp $(RABBITMQ_DEFAULTS) + dist: tar -zxf ../../dist/$(SOURCE_DIR).tar.gz @@ -22,7 +26,15 @@ dist: MAN_DIR=`pwd`/$(TARGET_DIR)/share/man \ install - ./fix-rabbitmq-defaults.sh $(TARGET_DIR) $(ERTS_VSN) $(VERSION) +## Here we set the RABBITMQ_HOME variable, +## then we make ERL_DIR point to our released erl +## and we add the paths to our released start_clean and start_sasl boot scripts + $(call fix_defaults,'s:^SYS_PREFIX=$$:SYS_PREFIX=\$${RABBITMQ_HOME}:') + $(call fix_defaults,'s:^ERL_DIR=$$:ERL_DIR=\$${RABBITMQ_HOME}/erts-$(ERTS_VSN)/bin/:') + $(call fix_defaults,'s:start_clean$$:"\$${SYS_PREFIX}/releases/$(VERSION)/start_clean":') + $(call fix_defaults,'s:start_sasl:"\$${SYS_PREFIX}/releases/$(VERSION)/start_sasl":') + + chmod 0755 $(RABBITMQ_DEFAULTS) mkdir -p $(TARGET_DIR)/etc/rabbitmq diff --git a/packaging/standalone/fix-rabbitmq-defaults.sh b/packaging/standalone/fix-rabbitmq-defaults.sh deleted file mode 100755 index 021c47bc..00000000 --- a/packaging/standalone/fix-rabbitmq-defaults.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash - -TARGET_DIR=$1 -ERTS_VSN=$2 -VERSION=$3 - -## Here we set the RABBITMQ_HOME variable, -## then we make ERL_DIR point to our released erl -## and we add the paths to our released start_clean and start_sasl boot scripts - -sed -e 's:^SYS_PREFIX=$:SYS_PREFIX=\${RABBITMQ_HOME}:' \ - "${TARGET_DIR}"/sbin/rabbitmq-defaults >"${TARGET_DIR}"/sbin/rabbitmq-defaults.tmp \ - && sed -e 's:^ERL_DIR=$:ERL_DIR=\${RABBITMQ_HOME}/erts-'"${ERTS_VSN}"'/bin/:' \ - "${TARGET_DIR}"/sbin/rabbitmq-defaults.tmp >"${TARGET_DIR}"/sbin/rabbitmq-defaults.tmp1 \ - && sed -e 's:start_clean$:"\${SYS_PREFIX}/releases/'"${VERSION}"'/start_clean":' \ - "${TARGET_DIR}"/sbin/rabbitmq-defaults.tmp1 >"${TARGET_DIR}"/sbin/rabbitmq-defaults.tmp \ - && sed -e 's:start_sasl:"\${SYS_PREFIX}/releases/'"${VERSION}"'/start_sasl":' \ - "${TARGET_DIR}"/sbin/rabbitmq-defaults.tmp >"${TARGET_DIR}"/sbin/rabbitmq-defaults - -chmod 0755 "${TARGET_DIR}"/sbin/rabbitmq-defaults -- cgit v1.2.1 From 5b6b87c7b519688bdd4eecfeae983dac66fb6c88 Mon Sep 17 00:00:00 2001 From: Alvaro Videla Date: Thu, 6 Dec 2012 17:18:51 +0100 Subject: cosmetics --- packaging/standalone/Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packaging/standalone/Makefile b/packaging/standalone/Makefile index 50d1385a..0dde6fa6 100644 --- a/packaging/standalone/Makefile +++ b/packaging/standalone/Makefile @@ -65,7 +65,10 @@ clean_partial: .PHONY : generate_release generate_release: - erlc -I $(TARGET_DIR)/include/ -o src -Wall -v +debug_info -Duse_specs -Duse_proper_qc -pa $(TARGET_DIR)/ebin/ src/rabbit_release.erl + erlc \ + -I $(TARGET_DIR)/include/ -o src -Wall \ + -v +debug_info -Duse_specs -Duse_proper_qc \ + -pa $(TARGET_DIR)/ebin/ src/rabbit_release.erl erl \ -pa "$(RABBITMQ_EBIN_ROOT)" \ -pa src \ -- cgit v1.2.1 From 8f088d8fe0403274b417bf7baad16de317ac23bb Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 6 Dec 2012 16:52:13 +0000 Subject: Add rabbitmqctl sync_queue, and make it synchronous. Also fix the fact that we were treating "no queues to sync" as a 'normal' crash of the syncer, which... no. --- docs/rabbitmqctl.1.xml | 31 +++++++++++++++++++++++++++++++ src/rabbit_amqqueue.erl | 14 ++++++++++---- src/rabbit_amqqueue_process.erl | 7 +++---- src/rabbit_control_main.erl | 7 +++++++ src/rabbit_mirror_queue_master.erl | 11 ++++++----- src/rabbit_mirror_queue_sync.erl | 11 ++++++++++- 6 files changed, 67 insertions(+), 14 deletions(-) diff --git a/docs/rabbitmqctl.1.xml b/docs/rabbitmqctl.1.xml index 34947b66..1583ab98 100644 --- a/docs/rabbitmqctl.1.xml +++ b/docs/rabbitmqctl.1.xml @@ -446,6 +446,37 @@ + + sync_queue queue + + + + + queue + + + The name of the queue to synchronise. + + + + + + Instructs a mirrored queue with unsynchronised slaves to + synchronise itself. The queue will block while + synchronisation takes place (all publishers to and + consumers from the queue will block). The queue must be + mirrored, must have unsynchronised slaves, and must not + have any pending unacknowledged messages for this + command to succeed. + + + Note that unsynchronised queues from which messages are + being drained will become synchronised eventually. This + command is primarily useful for queues which are not + being drained. + + + diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 4bdab0bc..ae96f739 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -31,7 +31,7 @@ -export([notify_down_all/2, limit_all/3]). -export([on_node_down/1]). -export([update/2, store_queue/1, policy_changed/2]). --export([start_mirroring/1, stop_mirroring/1, sync_mirrors/1]). +-export([start_mirroring/1, stop_mirroring/1, sync/2]). %% internal -export([internal_declare/2, internal_delete/1, run_backing_queue/3, @@ -173,8 +173,9 @@ (rabbit_types:amqqueue(), rabbit_types:amqqueue()) -> 'ok'). -spec(start_mirroring/1 :: (pid()) -> 'ok'). -spec(stop_mirroring/1 :: (pid()) -> 'ok'). --spec(sync_mirrors/1 :: (rabbit_types:amqqueue()) -> - 'ok' | rabbit_types:error('pending_acks' | 'not_mirrored')). +-spec(sync/2 :: (binary(), rabbit_types:vhost()) -> + 'ok' | rabbit_types:error('pending_acks' | 'not_mirrored' | + 'already_synced')). -endif. @@ -592,7 +593,12 @@ set_maximum_since_use(QPid, Age) -> start_mirroring(QPid) -> ok = delegate_cast(QPid, start_mirroring). stop_mirroring(QPid) -> ok = delegate_cast(QPid, stop_mirroring). -sync_mirrors(#amqqueue{pid = QPid}) -> delegate_call(QPid, sync_mirrors). +sync(QNameBin, VHostBin) -> + QName = rabbit_misc:r(VHostBin, queue, QNameBin), + case lookup(QName) of + {ok, #amqqueue{pid = QPid}} -> delegate_call(QPid, sync_mirrors); + E -> E + end. on_node_down(Node) -> rabbit_misc:execute_mnesia_tx_with_tail( diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index acbea4e9..730c235e 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1153,15 +1153,14 @@ handle_call({requeue, AckTags, ChPid}, From, State) -> gen_server2:reply(From, ok), noreply(requeue(AckTags, ChPid, State)); -handle_call(sync_mirrors, From, +handle_call(sync_mirrors, _From, State = #q{backing_queue = rabbit_mirror_queue_master = BQ, backing_queue_state = BQS}) -> S = fun(BQSN) -> State#q{backing_queue_state = BQSN} end, case BQ:depth(BQS) - BQ:len(BQS) of - 0 -> gen_server2:reply(From, ok), - case rabbit_mirror_queue_master:sync_mirrors(BQS) of + 0 -> case rabbit_mirror_queue_master:sync_mirrors(BQS) of {shutdown, Reason, BQS1} -> {stop, Reason, S(BQS1)}; - {ok, BQS1} -> noreply(S(BQS1)) + {Result, BQS1} -> reply(Result, S(BQS1)) end; _ -> reply({error, pending_acks}, State) end; diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index 669a0787..b4272555 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -50,6 +50,7 @@ update_cluster_nodes, {forget_cluster_node, [?OFFLINE_DEF]}, cluster_status, + {sync_queue, [?VHOST_DEF]}, add_user, delete_user, @@ -280,6 +281,12 @@ action(forget_cluster_node, Node, [ClusterNodeS], Opts, Inform) -> rpc_call(Node, rabbit_mnesia, forget_cluster_node, [ClusterNode, RemoveWhenOffline]); +action(sync_queue, Node, [Queue], Opts, Inform) -> + VHost = proplists:get_value(?VHOST_OPT, Opts), + Inform("Synchronising queue ~s in ~s", [Queue, VHost]), + rpc_call(Node, rabbit_amqqueue, sync, + [list_to_binary(Queue), list_to_binary(VHost)]); + action(wait, Node, [PidFile], _Opts, Inform) -> Inform("Waiting for ~p", [Node]), wait_for_application(Node, PidFile, rabbit_and_plugins, Inform); diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index 3d7f902c..03a712d6 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -142,11 +142,12 @@ sync_mirrors(State = #state { name = QName, gm:broadcast(GM, {sync_start, Ref, Syncer, SPids}), S = fun(BQSN) -> State#state{backing_queue_state = BQSN} end, case rabbit_mirror_queue_sync:master_go(Syncer, Ref, Log, BQ, BQS) of - {shutdown, R, BQS1} -> {stop, R, S(BQS1)}; - {sync_died, R, BQS1} -> Log("~p", [R]), - {ok, S(BQS1)}; - {ok, BQS1} -> Log("complete", []), - {ok, S(BQS1)} + {shutdown, R, BQS1} -> {stop, R, S(BQS1)}; + {sync_died, R, BQS1} -> Log("~p", [R]), + {ok, S(BQS1)}; + {already_synced, BQS1} -> {{error, already_synced}, S(BQS1)}; + {ok, BQS1} -> Log("complete", []), + {ok, S(BQS1)} end. terminate({shutdown, dropped} = Reason, diff --git a/src/rabbit_mirror_queue_sync.erl b/src/rabbit_mirror_queue_sync.erl index a80f8f50..f56cf5b3 100644 --- a/src/rabbit_mirror_queue_sync.erl +++ b/src/rabbit_mirror_queue_sync.erl @@ -42,6 +42,7 @@ %% || || <--- sync_ready ---- || %% || || (or) || %% || || <--- sync_deny ----- || +%% || <--- ready ---- || || %% || <--- next* ---- || || } %% || ---- msg* ----> || || } loop %% || || ---- sync_msg* ----> || } @@ -60,6 +61,13 @@ master_prepare(Ref, Log, SPids) -> master_go(Syncer, Ref, Log, BQ, BQS) -> Args = {Syncer, Ref, Log, rabbit_misc:get_parent()}, + receive + {'EXIT', Syncer, normal} -> {already_synced, BQS}; + {'EXIT', Syncer, Reason} -> {sync_died, Reason, BQS}; + {ready, Syncer} -> master_go0(Args, BQ, BQS) + end. + +master_go0(Args, BQ, BQS) -> case BQ:fold(fun (Msg, MsgProps, {I, Last}) -> master_send(Args, I, Last, Msg, MsgProps) end, {0, erlang:now()}, BQS) of @@ -111,7 +119,8 @@ syncer(Ref, Log, MPid, SPids) -> %% *without* those messages ending up in their gen_server2 pqueue. case foreach_slave(SPidsMRefs, Ref, fun sync_receive_ready/3) of [] -> Log("all slaves already synced", []); - SPidsMRefs1 -> Log("~p to sync", [[rabbit_misc:pid_to_string(S) || + SPidsMRefs1 -> MPid ! {ready, self()}, + Log("~p to sync", [[rabbit_misc:pid_to_string(S) || {S, _} <- SPidsMRefs1]]), SPidsMRefs2 = syncer_loop({Ref, MPid}, SPidsMRefs1), foreach_slave(SPidsMRefs2, Ref, fun sync_send_complete/3) -- cgit v1.2.1 From 02d057d4ecb12ecc53c0bc43c08ea7552d7e812a Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 6 Dec 2012 17:00:37 +0000 Subject: Make syncing idempotent. --- src/rabbit_amqqueue.erl | 3 +-- src/rabbit_mirror_queue_master.erl | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index ae96f739..3169948b 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -174,8 +174,7 @@ -spec(start_mirroring/1 :: (pid()) -> 'ok'). -spec(stop_mirroring/1 :: (pid()) -> 'ok'). -spec(sync/2 :: (binary(), rabbit_types:vhost()) -> - 'ok' | rabbit_types:error('pending_acks' | 'not_mirrored' | - 'already_synced')). + 'ok' | rabbit_types:error('pending_acks' | 'not_mirrored')). -endif. diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index 03a712d6..c9b6269b 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -145,7 +145,7 @@ sync_mirrors(State = #state { name = QName, {shutdown, R, BQS1} -> {stop, R, S(BQS1)}; {sync_died, R, BQS1} -> Log("~p", [R]), {ok, S(BQS1)}; - {already_synced, BQS1} -> {{error, already_synced}, S(BQS1)}; + {already_synced, BQS1} -> {ok, S(BQS1)}; {ok, BQS1} -> Log("complete", []), {ok, S(BQS1)} end. -- cgit v1.2.1 From 46eb0f2520a82055dfb88151982111b6e56d06e4 Mon Sep 17 00:00:00 2001 From: Alvaro Videla Date: Thu, 6 Dec 2012 18:07:00 +0100 Subject: passes the release OS as a variable --- packaging/standalone/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packaging/standalone/Makefile b/packaging/standalone/Makefile index 0dde6fa6..1e548789 100644 --- a/packaging/standalone/Makefile +++ b/packaging/standalone/Makefile @@ -1,7 +1,7 @@ VERSION=0.0.0 SOURCE_DIR=rabbitmq-server-$(VERSION) TARGET_DIR=rabbitmq_server-$(VERSION) -TARGET_TARBALL=rabbitmq-server-mac-standalone-$(VERSION) +TARGET_TARBALL=rabbitmq-server-$(OS)-standalone-$(VERSION) RLS_DIR=$(TARGET_DIR)/release/$(TARGET_DIR) ERTS_VSN=$(shell erl -noshell -eval 'io:format("~s", [erlang:system_info(version)]), halt().') @@ -57,7 +57,7 @@ dist: rm -rf $(SOURCE_DIR) $(TARGET_DIR) clean: clean_partial - rm -f rabbitmq-server-mac-standalone-*.tar.gz + rm -f rabbitmq-server-$(OS)-standalone-*.tar.gz clean_partial: rm -rf $(SOURCE_DIR) -- cgit v1.2.1 From d11ec980f2224650a5c1936db7ff69d2ca1dc032 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 6 Dec 2012 17:33:01 +0000 Subject: fix spec bugs --- src/rabbit_auth_backend_internal.erl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/rabbit_auth_backend_internal.erl b/src/rabbit_auth_backend_internal.erl index 7b9df81e..919be3f3 100644 --- a/src/rabbit_auth_backend_internal.erl +++ b/src/rabbit_auth_backend_internal.erl @@ -49,7 +49,7 @@ -spec(hash_password/1 :: (rabbit_types:password()) -> rabbit_types:password_hash()). -spec(set_tags/2 :: (rabbit_types:username(), [atom()]) -> 'ok'). --spec(list_users/0 :: () -> rabbit_types:infos()). +-spec(list_users/0 :: () -> [rabbit_types:infos()]). -spec(user_info_keys/0 :: () -> rabbit_types:info_keys()). -spec(lookup_user/1 :: (rabbit_types:username()) -> rabbit_types:ok(rabbit_types:internal_user()) @@ -58,14 +58,14 @@ regexp(), regexp(), regexp()) -> 'ok'). -spec(clear_permissions/2 :: (rabbit_types:username(), rabbit_types:vhost()) -> 'ok'). --spec(list_permissions/0 :: () -> rabbit_types:infos()). +-spec(list_permissions/0 :: () -> [rabbit_types:infos()]). -spec(list_vhost_permissions/1 :: - (rabbit_types:vhost()) -> rabbit_types:infos()). + (rabbit_types:vhost()) -> [rabbit_types:infos()]). -spec(list_user_permissions/1 :: - (rabbit_types:username()) -> rabbit_types:infos()). + (rabbit_types:username()) -> [rabbit_types:infos()]). -spec(list_user_vhost_permissions/2 :: (rabbit_types:username(), rabbit_types:vhost()) - -> rabbit_types:infos()). + -> [rabbit_types:infos()]). -spec(perms_info_keys/0 :: () -> rabbit_types:info_keys()). -spec(vhost_perms_info_keys/0 :: () -> rabbit_types:info_keys()). -spec(user_perms_info_keys/0 :: () -> rabbit_types:info_keys()). -- 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(-) 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 777672262d6a353ff593ce80fc7832b27bf0c439 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 7 Dec 2012 16:23:04 +0000 Subject: Remove spurious 3.0.1 changelogs. --- packaging/RPMS/Fedora/rabbitmq-server.spec | 3 --- packaging/debs/Debian/debian/changelog | 6 ------ 2 files changed, 9 deletions(-) diff --git a/packaging/RPMS/Fedora/rabbitmq-server.spec b/packaging/RPMS/Fedora/rabbitmq-server.spec index 23fd4f46..5d9b9e2e 100644 --- a/packaging/RPMS/Fedora/rabbitmq-server.spec +++ b/packaging/RPMS/Fedora/rabbitmq-server.spec @@ -123,9 +123,6 @@ done rm -rf %{buildroot} %changelog -* Thu Dec 6 2012 simon@rabbitmq.com 3.0.1-1 -- New Upstream Release - * Fri Nov 16 2012 simon@rabbitmq.com 3.0.0-1 - New Upstream Release diff --git a/packaging/debs/Debian/debian/changelog b/packaging/debs/Debian/debian/changelog index 82c06ac2..17327133 100644 --- a/packaging/debs/Debian/debian/changelog +++ b/packaging/debs/Debian/debian/changelog @@ -1,9 +1,3 @@ -rabbitmq-server (3.0.1-1) unstable; urgency=low - - * New Upstream Release - - -- Simon MacMullen Thu, 06 Dec 2012 13:33:24 +0000 - rabbitmq-server (3.0.0-1) unstable; urgency=low * New Upstream Release -- cgit v1.2.1 -- cgit v1.2.1 From 1031bf128dfce310530ca3fc063bcd6e8d629ff9 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 10 Dec 2012 13:30:19 +0000 Subject: Fix a couple of partition-related specs. --- src/rabbit_mnesia.erl | 3 ++- src/rabbit_node_monitor.erl | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 6576ba52..6a442fec 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -68,7 +68,8 @@ %% Various queries to get the status of the db -spec(status/0 :: () -> [{'nodes', [{node_type(), [node()]}]} | - {'running_nodes', [node()]}]). + {'running_nodes', [node()]} | + {'partitions', [{node(), [node()]}]}]). -spec(is_clustered/0 :: () -> boolean()). -spec(cluster_nodes/1 :: ('all' | 'disc' | 'ram' | 'running') -> [node()]). -spec(node_type/0 :: () -> node_type()). diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index 8d0e4456..258ac0ce 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -53,7 +53,7 @@ -spec(notify_joined_cluster/0 :: () -> 'ok'). -spec(notify_left_cluster/1 :: (node()) -> 'ok'). --spec(partitions/0 :: () -> {node(), [{atom(), node()}]}). +-spec(partitions/0 :: () -> {node(), [node()]}). -endif. -- cgit v1.2.1 From 0127b2ca8afc604ac4fa2fc3a9c2f0d4d5469d75 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 10 Dec 2012 13:57:48 +0000 Subject: application_controller:get_master/1 can return 'undefined'. --- src/rabbit_vm.erl | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/rabbit_vm.erl b/src/rabbit_vm.erl index e9679276..560a9f98 100644 --- a/src/rabbit_vm.erl +++ b/src/rabbit_vm.erl @@ -127,10 +127,12 @@ plugin_memory() -> is_plugin(atom_to_list(App))]). plugin_memory(App) -> - case catch application_master:get_child( - application_controller:get_master(App)) of - {Pid, _} -> sup_memory(Pid); - _ -> 0 + case catch application_controller:get_master(App) of + undefined -> 0; + Master -> case catch application_master:get_child(Master) of + {Pid, _} -> sup_memory(Pid); + _ -> 0 + end end. is_plugin("rabbitmq_" ++ _) -> true; -- cgit v1.2.1 From 9377b5623f1e8fff956b204112982d0ae0aedb25 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 10 Dec 2012 14:51:40 +0000 Subject: Call application_master:get_child/1 more like OTP does. --- src/rabbit_vm.erl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/rabbit_vm.erl b/src/rabbit_vm.erl index 560a9f98..62feca79 100644 --- a/src/rabbit_vm.erl +++ b/src/rabbit_vm.erl @@ -130,8 +130,9 @@ plugin_memory(App) -> case catch application_controller:get_master(App) of undefined -> 0; Master -> case catch application_master:get_child(Master) of - {Pid, _} -> sup_memory(Pid); - _ -> 0 + {Pid, _} when is_pid(Pid) -> sup_memory(Pid); + Pid when is_pid(Pid) -> sup_memory(Pid); + _ -> 0 end end. -- cgit v1.2.1 From 79c8fc4161be23a8363201b879edc6766aac6a9d Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 10 Dec 2012 18:09:45 +0000 Subject: Those catches do no really do anything. --- src/rabbit_vm.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_vm.erl b/src/rabbit_vm.erl index 62feca79..db674f91 100644 --- a/src/rabbit_vm.erl +++ b/src/rabbit_vm.erl @@ -127,9 +127,9 @@ plugin_memory() -> is_plugin(atom_to_list(App))]). plugin_memory(App) -> - case catch application_controller:get_master(App) of + case application_controller:get_master(App) of undefined -> 0; - Master -> case catch application_master:get_child(Master) of + Master -> case application_master:get_child(Master) of {Pid, _} when is_pid(Pid) -> sup_memory(Pid); Pid when is_pid(Pid) -> sup_memory(Pid); _ -> 0 -- cgit v1.2.1 -- cgit v1.2.1 -- cgit v1.2.1 From 2a2fb30a44c38b2e0ef346422feb7883f507a787 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 11 Dec 2012 14:22:51 +0000 Subject: Changelogs for 3.0.1. --- packaging/RPMS/Fedora/rabbitmq-server.spec | 3 +++ packaging/debs/Debian/debian/changelog | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/packaging/RPMS/Fedora/rabbitmq-server.spec b/packaging/RPMS/Fedora/rabbitmq-server.spec index 5d9b9e2e..6e02c7a4 100644 --- a/packaging/RPMS/Fedora/rabbitmq-server.spec +++ b/packaging/RPMS/Fedora/rabbitmq-server.spec @@ -123,6 +123,9 @@ done rm -rf %{buildroot} %changelog +* Tue Dec 11 2012 simon@rabbitmq.com 3.0.1-1 +- New Upstream Release + * Fri Nov 16 2012 simon@rabbitmq.com 3.0.0-1 - New Upstream Release diff --git a/packaging/debs/Debian/debian/changelog b/packaging/debs/Debian/debian/changelog index 17327133..aed68b96 100644 --- a/packaging/debs/Debian/debian/changelog +++ b/packaging/debs/Debian/debian/changelog @@ -1,3 +1,9 @@ +rabbitmq-server (3.0.1-1) unstable; urgency=low + + * New Upstream Release + + -- Simon MacMullen Tue, 11 Dec 2012 11:29:55 +0000 + rabbitmq-server (3.0.0-1) unstable; urgency=low * New Upstream Release -- cgit v1.2.1 From 4d90b2208ca460ad285f1c38b45f0749c88d7223 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 11 Dec 2012 21:55:00 +0000 Subject: make sure the stats and rate timers don't keep each other alive --- src/rabbit_amqqueue_process.erl | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index f4459e45..9381774d 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -284,21 +284,17 @@ terminate_shutdown(Fun, State) -> end. reply(Reply, NewState) -> - assert_invariant(NewState), {NewState1, Timeout} = next_state(NewState), - {reply, Reply, NewState1, Timeout}. + {reply, Reply, ensure_stats_timer(ensure_rate_timer(NewState1)), Timeout}. noreply(NewState) -> - assert_invariant(NewState), {NewState1, Timeout} = next_state(NewState), - {noreply, NewState1, Timeout}. + {noreply, ensure_stats_timer(ensure_rate_timer(NewState1)), Timeout}. next_state(State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> + assert_invariant(State), {MsgIds, BQS1} = BQ:drain_confirmed(BQS), - State1 = ensure_stats_timer( - ensure_rate_timer( - confirm_messages(MsgIds, State#q{ - backing_queue_state = BQS1}))), + State1 = confirm_messages(MsgIds, State#q{backing_queue_state = BQS1}), case BQ:needs_timeout(BQS1) of false -> {stop_sync_timer(State1), hibernate }; idle -> {stop_sync_timer(State1), ?SYNC_INTERVAL}; @@ -328,15 +324,11 @@ ensure_rate_timer(State = #q{rate_timer_ref = undefined}) -> TRef = erlang:send_after( ?RAM_DURATION_UPDATE_INTERVAL, self(), update_ram_duration), State#q{rate_timer_ref = TRef}; -ensure_rate_timer(State = #q{rate_timer_ref = just_measured}) -> - State#q{rate_timer_ref = undefined}; ensure_rate_timer(State) -> State. stop_rate_timer(State = #q{rate_timer_ref = undefined}) -> State; -stop_rate_timer(State = #q{rate_timer_ref = just_measured}) -> - State#q{rate_timer_ref = undefined}; stop_rate_timer(State = #q{rate_timer_ref = TRef}) -> erlang:cancel_timer(TRef), State#q{rate_timer_ref = undefined}. @@ -1322,10 +1314,9 @@ handle_info(drop_expired, State) -> handle_info(emit_stats, State) -> emit_stats(State), - {noreply, State1, Timeout} = noreply(State), - %% Need to reset *after* we've been through noreply/1 so we do not - %% just create another timer always and therefore never hibernate - {noreply, rabbit_event:reset_stats_timer(State1, #q.stats_timer), Timeout}; + {State1, Timeout} = next_state(rabbit_event:reset_stats_timer( + State, #q.stats_timer)), + {noreply, State1, Timeout}; handle_info({'DOWN', _MonitorRef, process, DownPid, _Reason}, State = #q{q = #amqqueue{exclusive_owner = DownPid}}) -> @@ -1349,8 +1340,9 @@ handle_info(update_ram_duration, State = #q{backing_queue = BQ, DesiredDuration = rabbit_memory_monitor:report_ram_duration(self(), RamDuration), BQS2 = BQ:set_ram_duration_target(DesiredDuration, BQS1), - noreply(State#q{rate_timer_ref = just_measured, - backing_queue_state = BQS2}); + {State1, Timeout} = next_state(State#q{rate_timer_ref = undefined, + backing_queue_state = BQS2}), + {noreply, State1, Timeout}; handle_info(sync_timeout, State) -> noreply(backing_queue_timeout(State#q{sync_timer_ref = undefined})); -- cgit v1.2.1 From a381c38a2965c0888589635c233afd7298838be9 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Wed, 12 Dec 2012 17:48:56 +0000 Subject: Limit queue depth Without dead-lettering yet --- src/rabbit_amqqueue.erl | 9 ++++++++- src/rabbit_amqqueue_process.erl | 39 +++++++++++++++++++++++++++++++++++---- 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 1b6cc223..9c089973 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -387,7 +387,8 @@ check_declare_arguments(QueueName, Args) -> Checks = [{<<"x-expires">>, fun check_expires_arg/2}, {<<"x-message-ttl">>, fun check_message_ttl_arg/2}, {<<"x-dead-letter-exchange">>, fun check_string_arg/2}, - {<<"x-dead-letter-routing-key">>, fun check_dlxrk_arg/2}], + {<<"x-dead-letter-routing-key">>, fun check_dlxrk_arg/2}, + {<<"x-maxdepth">>, fun check_maxdepth_arg/2}], [case rabbit_misc:table_lookup(Args, Key) of undefined -> ok; TypeVal -> case Fun(TypeVal, Args) of @@ -410,6 +411,12 @@ check_int_arg({Type, _}, _) -> false -> {error, {unacceptable_type, Type}} end. +check_maxdepth_arg({Type, Val}, Args) -> + case check_int_arg({Type, Val}, Args) of + ok when Val =< 0 -> {error, {value_non_positive, Val}}; + X -> X + end. + check_expires_arg({Type, Val}, Args) -> case check_int_arg({Type, Val}, Args) of ok when Val == 0 -> {error, {value_zero, Val}}; diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 2ffa2a1a..0de9b4e4 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -54,7 +54,8 @@ delayed_stop, queue_monitors, dlx, - dlx_routing_key + dlx_routing_key, + max_depth }). -record(consumer, {tag, ack_required}). @@ -134,6 +135,7 @@ init(Q) -> senders = pmon:new(), dlx = undefined, dlx_routing_key = undefined, + max_depth = undefined, publish_seqno = 1, unconfirmed = dtree:empty(), delayed_stop = undefined, @@ -159,6 +161,7 @@ init_with_backing_queue_state(Q = #amqqueue{exclusive_owner = Owner}, BQ, BQS, rate_timer_ref = RateTRef, expiry_timer_ref = undefined, ttl = undefined, + max_depth = undefined, senders = Senders, publish_seqno = 1, unconfirmed = dtree:empty(), @@ -258,7 +261,8 @@ process_args(State = #q{q = #amqqueue{arguments = Arguments}}) -> [{<<"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-message-ttl">>, fun init_ttl/2}, + {<<"x-maxdepth">>, fun init_maxdepth/2}]). init_expires(Expires, State) -> ensure_expiry_timer(State#q{expires = Expires}). @@ -270,6 +274,9 @@ init_dlx(DLX, State = #q{q = #amqqueue{name = QName}}) -> init_dlx_routing_key(RoutingKey, State) -> State#q{dlx_routing_key = RoutingKey}. +init_maxdepth(MaxDepth, State) -> + State#q{max_depth = MaxDepth}. + terminate_shutdown(Fun, State) -> State1 = #q{backing_queue_state = BQS} = stop_sync_timer(stop_rate_timer(State)), @@ -571,12 +578,36 @@ deliver_or_enqueue(Delivery = #delivery{message = Message, sender = SenderPid}, %% The next one is an optimisation {false, State2 = #q{ttl = 0, dlx = undefined}} -> discard(Delivery, State2); - {false, State2 = #q{backing_queue = BQ, backing_queue_state = BQS}} -> - BQS1 = BQ:publish(Message, Props, Delivered, SenderPid, BQS), + {false, State2} -> + BQS1 = publish_max(Message, Props, Delivered, SenderPid, State2), ensure_ttl_timer(Props#message_properties.expiry, State2#q{backing_queue_state = BQS1}) end. +publish_max(Message, Props, Delivered, SenderPid, + State = #q{backing_queue = BQ, + backing_queue_state = BQS, + max_depth = undefined }) -> + BQ:publish(Message, Props, Delivered, SenderPid, BQS); +publish_max(Message, Props, Delivered, SenderPid, + State = #q{backing_queue = BQ, + backing_queue_state = BQS, + max_depth = MaxDepth }) -> + Depth = BQ:depth(BQS), + case Depth >= MaxDepth of + true -> + Length = BQ:len(BQS), + case Length >= MaxDepth of + false -> + BQS; + true -> + {M, BQS1} = BQ:fetch(false, BQS), + BQ:publish(Message, Props, Delivered, SenderPid, BQS1) + end; + false-> + BQ:publish(Message, Props, Delivered, SenderPid, BQS) + end. + requeue_and_run(AckTags, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> {_MsgIds, BQS1} = BQ:requeue(AckTags, BQS), -- cgit v1.2.1 From b26a13371e30a09281d2f3d4d1d8db804682403d Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 12 Dec 2012 18:11:24 +0000 Subject: Reminder --- src/rabbit_amqqueue_process.erl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 9381774d..ab735be6 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1314,6 +1314,7 @@ handle_info(drop_expired, State) -> handle_info(emit_stats, State) -> emit_stats(State), + %% Don't call noreply/1, we don't want to set timers {State1, Timeout} = next_state(rabbit_event:reset_stats_timer( State, #q.stats_timer)), {noreply, State1, Timeout}; @@ -1340,6 +1341,7 @@ handle_info(update_ram_duration, State = #q{backing_queue = BQ, DesiredDuration = rabbit_memory_monitor:report_ram_duration(self(), RamDuration), BQS2 = BQ:set_ram_duration_target(DesiredDuration, BQS1), + %% Don't call noreply/1, we don't want to set timers {State1, Timeout} = next_state(State#q{rate_timer_ref = undefined, backing_queue_state = BQS2}), {noreply, State1, Timeout}; -- cgit v1.2.1 From c0dd43566ea63c1ac36c4c0109100f8b7e57d681 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 14 Dec 2012 11:33:45 +0000 Subject: It's important to respond to this too. --- src/rabbit_mirror_queue_sync.erl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/rabbit_mirror_queue_sync.erl b/src/rabbit_mirror_queue_sync.erl index f56cf5b3..bb12cf49 100644 --- a/src/rabbit_mirror_queue_sync.erl +++ b/src/rabbit_mirror_queue_sync.erl @@ -219,5 +219,8 @@ slave_sync_loop(Args = {Ref, MRef, Syncer, BQ, UpdateRamDuration, Parent}, BQS1 = BQ:publish(Msg, Props1, true, none, BQS), slave_sync_loop(Args, TRef, BQS1); {'EXIT', Parent, Reason} -> - {stop, Reason, {TRef, BQS}} + {stop, Reason, {TRef, BQS}}; + {'$gen_cast', {gm, {delete_and_terminate, Reason}}} -> + BQS1 = BQ:delete_and_terminate(Reason, BQS), + {stop, Reason, {TRef, BQS1}} end. -- cgit v1.2.1 From 05988c78f535247c15415766117f28fc93ab1390 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 14 Dec 2012 11:36:02 +0000 Subject: Ensure rabbitmqctl list_queues remains responsive during sync, and emit stats events too. --- docs/rabbitmqctl.1.xml | 6 ++++++ src/rabbit_amqqueue_process.erl | 30 +++++++++++++++++++++++++----- src/rabbit_mirror_queue_master.erl | 8 +++++--- src/rabbit_mirror_queue_sync.erl | 18 ++++++++++-------- 4 files changed, 46 insertions(+), 16 deletions(-) diff --git a/docs/rabbitmqctl.1.xml b/docs/rabbitmqctl.1.xml index 1583ab98..d18a0f9e 100644 --- a/docs/rabbitmqctl.1.xml +++ b/docs/rabbitmqctl.1.xml @@ -1140,6 +1140,12 @@ i.e. those which could take over from the master without message loss. + + status + The status of the queue. Normally + 'running', but may be different if the queue is + synchronising. + If no queueinfoitems are specified then queue name and depth are diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 730c235e..5d9da204 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -54,7 +54,8 @@ delayed_stop, queue_monitors, dlx, - dlx_routing_key + dlx_routing_key, + status }). -record(consumer, {tag, ack_required}). @@ -97,7 +98,8 @@ memory, slave_pids, synchronised_slave_pids, - backing_queue_status + backing_queue_status, + status ]). -define(CREATION_EVENT_KEYS, @@ -138,7 +140,8 @@ init(Q) -> unconfirmed = dtree:empty(), delayed_stop = undefined, queue_monitors = pmon:new(), - msg_id_to_channel = gb_trees:empty()}, + msg_id_to_channel = gb_trees:empty(), + status = running}, {ok, rabbit_event:init_stats_timer(State, #q.stats_timer), hibernate, {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}. @@ -164,7 +167,8 @@ init_with_backing_queue_state(Q = #amqqueue{exclusive_owner = Owner}, BQ, BQS, unconfirmed = dtree:empty(), delayed_stop = undefined, queue_monitors = pmon:new(), - msg_id_to_channel = MTC}, + msg_id_to_channel = MTC, + status = running}, State1 = process_args(rabbit_event:init_stats_timer(State, #q.stats_timer)), lists:foldl(fun (Delivery, StateN) -> deliver_or_enqueue(Delivery, true, StateN) @@ -934,6 +938,8 @@ i(synchronised_slave_pids, #q{q = #amqqueue{name = Name}}) -> false -> ''; true -> SSPids end; +i(status, #q{status = Status}) -> + Status; i(backing_queue_status, #q{backing_queue_state = BQS, backing_queue = BQ}) -> BQ:status(BQS); i(Item, _) -> @@ -1157,8 +1163,22 @@ handle_call(sync_mirrors, _From, State = #q{backing_queue = rabbit_mirror_queue_master = BQ, backing_queue_state = BQS}) -> S = fun(BQSN) -> State#q{backing_queue_state = BQSN} end, + InfoPull = fun (Status) -> + receive {'$gen_call', From, {info, Items}} -> + Infos = infos(Items, State#q{status = Status}), + gen_server2:reply(From, {ok, Infos}) + after 0 -> + ok + end + end, + InfoPush = fun (Status) -> + rabbit_event:if_enabled( + State, #q.stats_timer, + fun() -> emit_stats(State#q{status = Status}) end) + end, case BQ:depth(BQS) - BQ:len(BQS) of - 0 -> case rabbit_mirror_queue_master:sync_mirrors(BQS) of + 0 -> case rabbit_mirror_queue_master:sync_mirrors( + InfoPull, InfoPush, BQS) of {shutdown, Reason, BQS1} -> {stop, Reason, S(BQS1)}; {Result, BQS1} -> reply(Result, S(BQS1)) end; diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index c9b6269b..601649ef 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -28,7 +28,7 @@ -export([promote_backing_queue_state/8, sender_death_fun/0, depth_fun/0]). --export([init_with_existing_bq/3, stop_mirroring/1, sync_mirrors/1]). +-export([init_with_existing_bq/3, stop_mirroring/1, sync_mirrors/3]). -behaviour(rabbit_backing_queue). @@ -127,7 +127,8 @@ stop_mirroring(State = #state { coordinator = CPid, stop_all_slaves(shutdown, State), {BQ, BQS}. -sync_mirrors(State = #state { name = QName, +sync_mirrors(InfoPull, InfoPush, + State = #state { name = QName, gm = GM, backing_queue = BQ, backing_queue_state = BQS }) -> @@ -141,7 +142,8 @@ sync_mirrors(State = #state { name = QName, Syncer = rabbit_mirror_queue_sync:master_prepare(Ref, Log, SPids), gm:broadcast(GM, {sync_start, Ref, Syncer, SPids}), S = fun(BQSN) -> State#state{backing_queue_state = BQSN} end, - case rabbit_mirror_queue_sync:master_go(Syncer, Ref, Log, BQ, BQS) of + case rabbit_mirror_queue_sync:master_go( + Syncer, Ref, Log, InfoPull, InfoPush, BQ, BQS) of {shutdown, R, BQS1} -> {stop, R, S(BQS1)}; {sync_died, R, BQS1} -> Log("~p", [R]), {ok, S(BQS1)}; diff --git a/src/rabbit_mirror_queue_sync.erl b/src/rabbit_mirror_queue_sync.erl index bb12cf49..c10d8fd3 100644 --- a/src/rabbit_mirror_queue_sync.erl +++ b/src/rabbit_mirror_queue_sync.erl @@ -18,7 +18,7 @@ -include("rabbit.hrl"). --export([master_prepare/3, master_go/5, slave/7]). +-export([master_prepare/3, master_go/7, slave/7]). -define(SYNC_PROGRESS_INTERVAL, 1000000). @@ -59,16 +59,17 @@ master_prepare(Ref, Log, SPids) -> MPid = self(), spawn_link(fun () -> syncer(Ref, Log, MPid, SPids) end). -master_go(Syncer, Ref, Log, BQ, BQS) -> - Args = {Syncer, Ref, Log, rabbit_misc:get_parent()}, +master_go(Syncer, Ref, Log, InfoPull, InfoPush, BQ, BQS) -> + Args = {Syncer, Ref, Log, InfoPush, rabbit_misc:get_parent()}, receive {'EXIT', Syncer, normal} -> {already_synced, BQS}; {'EXIT', Syncer, Reason} -> {sync_died, Reason, BQS}; - {ready, Syncer} -> master_go0(Args, BQ, BQS) + {ready, Syncer} -> master_go0(InfoPull, Args, BQ, BQS) end. -master_go0(Args, BQ, BQS) -> +master_go0(InfoPull, Args, BQ, BQS) -> case BQ:fold(fun (Msg, MsgProps, {I, Last}) -> + InfoPull({synchronising, I}), master_send(Args, I, Last, Msg, MsgProps) end, {0, erlang:now()}, BQS) of {{shutdown, Reason}, BQS1} -> {shutdown, Reason, BQS1}; @@ -76,10 +77,11 @@ master_go0(Args, BQ, BQS) -> {_, BQS1} -> master_done(Args, BQS1) end. -master_send({Syncer, Ref, Log, Parent}, I, Last, Msg, MsgProps) -> +master_send({Syncer, Ref, Log, InfoPush, Parent}, I, Last, Msg, MsgProps) -> Acc = {I + 1, case timer:now_diff(erlang:now(), Last) > ?SYNC_PROGRESS_INTERVAL of - true -> Log("~p messages", [I]), + true -> InfoPush({synchronising, I}), + Log("~p messages", [I]), erlang:now(); false -> Last end}, @@ -96,7 +98,7 @@ master_send({Syncer, Ref, Log, Parent}, I, Last, Msg, MsgProps) -> {'EXIT', Syncer, Reason} -> {stop, {sync_died, Reason}} end. -master_done({Syncer, Ref, _Log, Parent}, BQS) -> +master_done({Syncer, Ref, _Log, _InfoPush, Parent}, BQS) -> receive {next, Ref} -> unlink(Syncer), Syncer ! {done, Ref}, -- cgit v1.2.1 From 01db5e5c77b2bccf0133ff17c836b269d5931562 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Fri, 14 Dec 2012 11:51:41 +0000 Subject: Better maxdepth checking --- src/rabbit_amqqueue_process.erl | 37 ++++++++++++++++--------------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 0de9b4e4..b61df6d6 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -584,28 +584,23 @@ deliver_or_enqueue(Delivery = #delivery{message = Message, sender = SenderPid}, State2#q{backing_queue_state = BQS1}) end. -publish_max(Message, Props, Delivered, SenderPid, - State = #q{backing_queue = BQ, - backing_queue_state = BQS, - max_depth = undefined }) -> +publish_max(Message, Props, Delivered, SenderPid, #q{backing_queue = BQ, + backing_queue_state = BQS, + max_depth = undefined }) -> BQ:publish(Message, Props, Delivered, SenderPid, BQS); -publish_max(Message, Props, Delivered, SenderPid, - State = #q{backing_queue = BQ, - backing_queue_state = BQS, - max_depth = MaxDepth }) -> - Depth = BQ:depth(BQS), - case Depth >= MaxDepth of - true -> - Length = BQ:len(BQS), - case Length >= MaxDepth of - false -> - BQS; - true -> - {M, BQS1} = BQ:fetch(false, BQS), - BQ:publish(Message, Props, Delivered, SenderPid, BQS1) - end; - false-> - BQ:publish(Message, Props, Delivered, SenderPid, BQS) +publish_max(Message, Props, Delivered, SenderPid, #q{backing_queue = BQ, + backing_queue_state = BQS, + dlx = XName, + max_depth = MaxDepth }) -> + {Depth, Len} = {BQ:depth(BQS), BQ:len(BQS)}, + case {Depth >= MaxDepth, Len =:= 0} of + {false, _} -> + BQ:publish(Message, Props, Delivered, SenderPid, BQS); + {true, true} -> + BQS; + {true, false} -> + {{Msg, _IsDelivered, AckTag}, BQS1} = BQ:fetch(false, BQS), + BQ:publish(Message, Props, Delivered, SenderPid, BQS1) end. requeue_and_run(AckTags, State = #q{backing_queue = BQ, -- cgit v1.2.1 From c52adc5c6768378e2a876ec116e96f70493931c3 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 14 Dec 2012 11:53:25 +0000 Subject: Shorter --- src/rabbit_mirror_queue_sync.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_mirror_queue_sync.erl b/src/rabbit_mirror_queue_sync.erl index c10d8fd3..457f658b 100644 --- a/src/rabbit_mirror_queue_sync.erl +++ b/src/rabbit_mirror_queue_sync.erl @@ -69,7 +69,7 @@ master_go(Syncer, Ref, Log, InfoPull, InfoPush, BQ, BQS) -> master_go0(InfoPull, Args, BQ, BQS) -> case BQ:fold(fun (Msg, MsgProps, {I, Last}) -> - InfoPull({synchronising, I}), + InfoPull({syncing, I}), master_send(Args, I, Last, Msg, MsgProps) end, {0, erlang:now()}, BQS) of {{shutdown, Reason}, BQS1} -> {shutdown, Reason, BQS1}; @@ -80,7 +80,7 @@ master_go0(InfoPull, Args, BQ, BQS) -> master_send({Syncer, Ref, Log, InfoPush, Parent}, I, Last, Msg, MsgProps) -> Acc = {I + 1, case timer:now_diff(erlang:now(), Last) > ?SYNC_PROGRESS_INTERVAL of - true -> InfoPush({synchronising, I}), + true -> InfoPush({syncing, I}), Log("~p messages", [I]), erlang:now(); false -> Last -- cgit v1.2.1 From 1db2d04c860e4299b0f0722488f6244f2932113a Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 14 Dec 2012 13:07:21 +0000 Subject: Cancel sync --- docs/rabbitmqctl.1.xml | 20 ++++++++++++++++++++ src/rabbit_amqqueue.erl | 11 ++++++++++- src/rabbit_amqqueue_process.erl | 4 ++++ src/rabbit_control_main.erl | 7 +++++++ src/rabbit_mirror_queue_sync.erl | 26 ++++++++++++++++++++++---- 5 files changed, 63 insertions(+), 5 deletions(-) diff --git a/docs/rabbitmqctl.1.xml b/docs/rabbitmqctl.1.xml index d18a0f9e..56fce227 100644 --- a/docs/rabbitmqctl.1.xml +++ b/docs/rabbitmqctl.1.xml @@ -477,6 +477,26 @@ + + cancel_sync_queue queue + + + + + queue + + + The name of the queue to cancel synchronisation for. + + + + + + Instructs a synchronising mirrored queue to stop + synchronising itself. + + + diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 3169948b..c49d5bee 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -31,7 +31,7 @@ -export([notify_down_all/2, limit_all/3]). -export([on_node_down/1]). -export([update/2, store_queue/1, policy_changed/2]). --export([start_mirroring/1, stop_mirroring/1, sync/2]). +-export([start_mirroring/1, stop_mirroring/1, sync/2, cancel_sync/2]). %% internal -export([internal_declare/2, internal_delete/1, run_backing_queue/3, @@ -175,6 +175,8 @@ -spec(stop_mirroring/1 :: (pid()) -> 'ok'). -spec(sync/2 :: (binary(), rabbit_types:vhost()) -> 'ok' | rabbit_types:error('pending_acks' | 'not_mirrored')). +-spec(cancel_sync/2 :: (binary(), rabbit_types:vhost()) -> + 'ok' | rabbit_types:error('not_mirrored')). -endif. @@ -599,6 +601,13 @@ sync(QNameBin, VHostBin) -> E -> E end. +cancel_sync(QNameBin, VHostBin) -> + QName = rabbit_misc:r(VHostBin, queue, QNameBin), + case lookup(QName) of + {ok, #amqqueue{pid = QPid}} -> delegate_call(QPid, cancel_sync_mirrors); + E -> E + end. + on_node_down(Node) -> rabbit_misc:execute_mnesia_tx_with_tail( fun () -> QsDels = diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 5d9da204..26c0edbe 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1188,6 +1188,10 @@ handle_call(sync_mirrors, _From, handle_call(sync_mirrors, _From, State) -> reply({error, not_mirrored}, State); +%% By definition if we get this message here we do not have to do anything. +handle_call(cancel_sync_mirrors, _From, State) -> + reply({error, not_syncing}, State); + handle_call(force_event_refresh, _From, State = #q{exclusive_consumer = Exclusive}) -> rabbit_event:notify(queue_created, infos(?CREATION_EVENT_KEYS, State)), diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index b4272555..12096ff5 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -51,6 +51,7 @@ {forget_cluster_node, [?OFFLINE_DEF]}, cluster_status, {sync_queue, [?VHOST_DEF]}, + {cancel_sync_queue, [?VHOST_DEF]}, add_user, delete_user, @@ -287,6 +288,12 @@ action(sync_queue, Node, [Queue], Opts, Inform) -> rpc_call(Node, rabbit_amqqueue, sync, [list_to_binary(Queue), list_to_binary(VHost)]); +action(cancel_sync_queue, Node, [Queue], Opts, Inform) -> + VHost = proplists:get_value(?VHOST_OPT, Opts), + Inform("Stopping synchronising queue ~s in ~s", [Queue, VHost]), + rpc_call(Node, rabbit_amqqueue, cancel_sync, + [list_to_binary(Queue), list_to_binary(VHost)]); + action(wait, Node, [PidFile], _Opts, Inform) -> Inform("Waiting for ~p", [Node]), wait_for_application(Node, PidFile, rabbit_and_plugins, Inform); diff --git a/src/rabbit_mirror_queue_sync.erl b/src/rabbit_mirror_queue_sync.erl index 457f658b..53f18c5b 100644 --- a/src/rabbit_mirror_queue_sync.erl +++ b/src/rabbit_mirror_queue_sync.erl @@ -92,6 +92,14 @@ master_send({Syncer, Ref, Log, InfoPush, Parent}, I, Last, Msg, MsgProps) -> ok end, receive + {'$gen_call', From, + cancel_sync_mirrors} -> unlink(Syncer), + Syncer ! {cancel, Ref}, + receive {'EXIT', Syncer, _} -> ok + after 0 -> ok + end, + gen_server2:reply(From, ok), + {stop, cancelled}; {next, Ref} -> Syncer ! {msg, Ref, Msg, MsgProps}, {cont, Acc}; {'EXIT', Parent, Reason} -> {stop, {shutdown, Reason}}; @@ -124,8 +132,16 @@ syncer(Ref, Log, MPid, SPids) -> SPidsMRefs1 -> MPid ! {ready, self()}, Log("~p to sync", [[rabbit_misc:pid_to_string(S) || {S, _} <- SPidsMRefs1]]), - SPidsMRefs2 = syncer_loop({Ref, MPid}, SPidsMRefs1), - foreach_slave(SPidsMRefs2, Ref, fun sync_send_complete/3) + case syncer_loop({Ref, MPid}, SPidsMRefs1) of + {done, SPidsMRefs2} -> + foreach_slave(SPidsMRefs2, Ref, + fun sync_send_complete/3); + cancelled -> + %% We don't tell the slaves we will die + %% - so when we do they interpret that + %% as a failure, which is what we want. + ok + end end. syncer_loop({Ref, MPid} = Args, SPidsMRefs) -> @@ -135,11 +151,13 @@ syncer_loop({Ref, MPid} = Args, SPidsMRefs) -> SPidsMRefs1 = wait_for_credit(SPidsMRefs, Ref), [begin credit_flow:send(SPid), - SPid ! {sync_msg, Ref, Msg, MsgProps} + SPid ! Msg end || {SPid, _} <- SPidsMRefs1], syncer_loop(Args, SPidsMRefs1); + {cancel, Ref} -> + cancelled; {done, Ref} -> - SPidsMRefs + {done, SPidsMRefs} end. wait_for_credit(SPidsMRefs, Ref) -> -- cgit v1.2.1 From 952e5a00af0a217bbc29b6dac1eacd42b3c625ba Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 14 Dec 2012 14:25:44 +0000 Subject: Fix accidental change --- src/rabbit_mirror_queue_sync.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_mirror_queue_sync.erl b/src/rabbit_mirror_queue_sync.erl index 53f18c5b..b39f7a35 100644 --- a/src/rabbit_mirror_queue_sync.erl +++ b/src/rabbit_mirror_queue_sync.erl @@ -151,7 +151,7 @@ syncer_loop({Ref, MPid} = Args, SPidsMRefs) -> SPidsMRefs1 = wait_for_credit(SPidsMRefs, Ref), [begin credit_flow:send(SPid), - SPid ! Msg + SPid ! {sync_msg, Ref, Msg, MsgProps} end || {SPid, _} <- SPidsMRefs1], syncer_loop(Args, SPidsMRefs1); {cancel, Ref} -> -- cgit v1.2.1 From 46fddb9b1b910869f90480b4fb8549b26fca0cf4 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 14 Dec 2012 15:16:43 +0000 Subject: Emit an info item as soon as we start, for greater responsiveness. --- src/rabbit_mirror_queue_sync.erl | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/rabbit_mirror_queue_sync.erl b/src/rabbit_mirror_queue_sync.erl index b39f7a35..a9aea966 100644 --- a/src/rabbit_mirror_queue_sync.erl +++ b/src/rabbit_mirror_queue_sync.erl @@ -60,24 +60,26 @@ master_prepare(Ref, Log, SPids) -> spawn_link(fun () -> syncer(Ref, Log, MPid, SPids) end). master_go(Syncer, Ref, Log, InfoPull, InfoPush, BQ, BQS) -> - Args = {Syncer, Ref, Log, InfoPush, rabbit_misc:get_parent()}, + Args = {Syncer, Ref, Log, rabbit_misc:get_parent()}, receive {'EXIT', Syncer, normal} -> {already_synced, BQS}; {'EXIT', Syncer, Reason} -> {sync_died, Reason, BQS}; - {ready, Syncer} -> master_go0(InfoPull, Args, BQ, BQS) + {ready, Syncer} -> master_go0( + InfoPull, InfoPush, Args, BQ, BQS) end. -master_go0(InfoPull, Args, BQ, BQS) -> +master_go0(InfoPull, InfoPush, Args, BQ, BQS) -> + InfoPush({syncing, 0}), case BQ:fold(fun (Msg, MsgProps, {I, Last}) -> InfoPull({syncing, I}), - master_send(Args, I, Last, Msg, MsgProps) + master_send(Args, InfoPush, I, Last, Msg, MsgProps) end, {0, erlang:now()}, BQS) of {{shutdown, Reason}, BQS1} -> {shutdown, Reason, BQS1}; {{sync_died, Reason}, BQS1} -> {sync_died, Reason, BQS1}; {_, BQS1} -> master_done(Args, BQS1) end. -master_send({Syncer, Ref, Log, InfoPush, Parent}, I, Last, Msg, MsgProps) -> +master_send({Syncer, Ref, Log, Parent}, InfoPush, I, Last, Msg, MsgProps) -> Acc = {I + 1, case timer:now_diff(erlang:now(), Last) > ?SYNC_PROGRESS_INTERVAL of true -> InfoPush({syncing, I}), @@ -106,7 +108,7 @@ master_send({Syncer, Ref, Log, InfoPush, Parent}, I, Last, Msg, MsgProps) -> {'EXIT', Syncer, Reason} -> {stop, {sync_died, Reason}} end. -master_done({Syncer, Ref, _Log, _InfoPush, Parent}, BQS) -> +master_done({Syncer, Ref, _Log, Parent}, BQS) -> receive {next, Ref} -> unlink(Syncer), Syncer ! {done, Ref}, -- cgit v1.2.1 From dedee77a913c7b2f1f6d11e6f649a163797f12c4 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Fri, 14 Dec 2012 16:26:07 +0000 Subject: Maxdepth checking with confirms and DLX --- src/rabbit_amqqueue_process.erl | 43 +++++++++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index b61df6d6..bcbdb0ad 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -568,7 +568,9 @@ attempt_delivery(Delivery = #delivery{sender = SenderPid, message = Message}, {false, State#q{backing_queue_state = BQS1}} end. -deliver_or_enqueue(Delivery = #delivery{message = Message, sender = SenderPid}, +deliver_or_enqueue(Delivery = #delivery{message = Message, + msg_seq_no = MsgSeqNo, + sender = SenderPid}, Delivered, State) -> {Confirm, State1} = send_or_record_confirm(Delivery, State), Props = message_properties(Message, Confirm, State), @@ -579,27 +581,48 @@ deliver_or_enqueue(Delivery = #delivery{message = Message, sender = SenderPid}, {false, State2 = #q{ttl = 0, dlx = undefined}} -> discard(Delivery, State2); {false, State2} -> - BQS1 = publish_max(Message, Props, Delivered, SenderPid, State2), + BQS1 = publish_max(Delivery, Props, Delivered, State2), ensure_ttl_timer(Props#message_properties.expiry, State2#q{backing_queue_state = BQS1}) end. -publish_max(Message, Props, Delivered, SenderPid, #q{backing_queue = BQ, - backing_queue_state = BQS, - max_depth = undefined }) -> +publish_max(#delivery{message = Message, + sender = SenderPid}, + Props, Delivered, State = #q{backing_queue = BQ, + backing_queue_state = BQS, + max_depth = undefined}) -> BQ:publish(Message, Props, Delivered, SenderPid, BQS); -publish_max(Message, Props, Delivered, SenderPid, #q{backing_queue = BQ, - backing_queue_state = BQS, - dlx = XName, - max_depth = MaxDepth }) -> +publish_max(#delivery{message = Message, + msg_seq_no = MsgSeqNo, + sender = SenderPid}, + Props = #message_properties{needs_confirming = Confirm}, + Delivered, + State = #q{backing_queue = BQ, + backing_queue_state = BQS, + dlx = XName, + max_depth = MaxDepth }) -> {Depth, Len} = {BQ:depth(BQS), BQ:len(BQS)}, case {Depth >= MaxDepth, Len =:= 0} of {false, _} -> BQ:publish(Message, Props, Delivered, SenderPid, BQS); {true, true} -> + case XName of + undefined -> + ok; + _ -> + case rabbit_exchange:lookup(XName) of + {ok, X} -> dead_letter_publish(Message, maxdepth, X, State); + {error, not_found} -> ok + end + end, + case Confirm of + true -> rabbit_misc:confirm_to_sender(SenderPid, [MsgSeqNo]); + _ -> ok + end, BQS; {true, false} -> - {{Msg, _IsDelivered, AckTag}, BQS1} = BQ:fetch(false, BQS), + {{Msg, _IsDelivered, AckTag}, BQS1} = BQ:fetch(true, BQS), + (dead_letter_fun(maxdepth))([{Msg, AckTag}]), BQ:publish(Message, Props, Delivered, SenderPid, BQS1) end. -- cgit v1.2.1 From 5fc15bf23c0911a9fed976620264a42afd61cd9b Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Fri, 14 Dec 2012 16:47:14 +0000 Subject: Shortcut --- src/rabbit_amqqueue_process.erl | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index bcbdb0ad..90e58f25 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -568,9 +568,7 @@ attempt_delivery(Delivery = #delivery{sender = SenderPid, message = Message}, {false, State#q{backing_queue_state = BQS1}} end. -deliver_or_enqueue(Delivery = #delivery{message = Message, - msg_seq_no = MsgSeqNo, - sender = SenderPid}, +deliver_or_enqueue(Delivery = #delivery{message = Message}, Delivered, State) -> {Confirm, State1} = send_or_record_confirm(Delivery, State), Props = message_properties(Message, Confirm, State), @@ -606,18 +604,13 @@ publish_max(#delivery{message = Message, {false, _} -> BQ:publish(Message, Props, Delivered, SenderPid, BQS); {true, true} -> - case XName of - undefined -> - ok; - _ -> - case rabbit_exchange:lookup(XName) of - {ok, X} -> dead_letter_publish(Message, maxdepth, X, State); - {error, not_found} -> ok - end + case rabbit_exchange:lookup(XName) of + {ok, X} -> dead_letter_publish(Message, maxdepth, X, State); + {error, _} -> ok end, case Confirm of - true -> rabbit_misc:confirm_to_sender(SenderPid, [MsgSeqNo]); - _ -> ok + true -> rabbit_misc:confirm_to_sender(SenderPid, [MsgSeqNo]); + false -> ok end, BQS; {true, false} -> -- cgit v1.2.1 From 19e6ab32a1b4da7c16dc10161b11f513c1383436 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Mon, 17 Dec 2012 11:52:57 +0000 Subject: Ensure TTL conditionally --- src/rabbit_amqqueue_process.erl | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 90e58f25..ffff464d 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -579,9 +579,13 @@ deliver_or_enqueue(Delivery = #delivery{message = Message}, {false, State2 = #q{ttl = 0, dlx = undefined}} -> discard(Delivery, State2); {false, State2} -> - BQS1 = publish_max(Delivery, Props, Delivered, State2), - ensure_ttl_timer(Props#message_properties.expiry, - State2#q{backing_queue_state = BQS1}) + case publish_max(Delivery, Props, Delivered, State2) of + nopub -> + State2; + BQS1 -> + ensure_ttl_timer(Props#message_properties.expiry, + State2#q{backing_queue_state = BQS1}) + end end. publish_max(#delivery{message = Message, @@ -612,7 +616,7 @@ publish_max(#delivery{message = Message, true -> rabbit_misc:confirm_to_sender(SenderPid, [MsgSeqNo]); false -> ok end, - BQS; + nopub; {true, false} -> {{Msg, _IsDelivered, AckTag}, BQS1} = BQ:fetch(true, BQS), (dead_letter_fun(maxdepth))([{Msg, AckTag}]), -- cgit v1.2.1 From 73c50523e13404389fd4b45a326bb8df19c13b7b Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Mon, 17 Dec 2012 11:54:27 +0000 Subject: Whitespace --- src/rabbit_amqqueue_process.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index ffff464d..922cb6cb 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -588,8 +588,8 @@ deliver_or_enqueue(Delivery = #delivery{message = Message}, end end. -publish_max(#delivery{message = Message, - sender = SenderPid}, +publish_max(#delivery{message = Message, + sender = SenderPid}, Props, Delivered, State = #q{backing_queue = BQ, backing_queue_state = BQS, max_depth = undefined}) -> @@ -602,7 +602,7 @@ publish_max(#delivery{message = Message, State = #q{backing_queue = BQ, backing_queue_state = BQS, dlx = XName, - max_depth = MaxDepth }) -> + max_depth = MaxDepth}) -> {Depth, Len} = {BQ:depth(BQS), BQ:len(BQS)}, case {Depth >= MaxDepth, Len =:= 0} of {false, _} -> -- cgit v1.2.1 From 882907e0ec1cc8cbfb11f692a3d79e75133eabd0 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Mon, 17 Dec 2012 14:05:15 +0000 Subject: Dead-letter persistent messages differently --- src/rabbit_amqqueue_process.erl | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 922cb6cb..86064141 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -608,10 +608,7 @@ publish_max(#delivery{message = Message, {false, _} -> BQ:publish(Message, Props, Delivered, SenderPid, BQS); {true, true} -> - case rabbit_exchange:lookup(XName) of - {ok, X} -> dead_letter_publish(Message, maxdepth, X, State); - {error, _} -> ok - end, + (dead_letter_fun(maxdepth))([{Message, undefined}]), case Confirm of true -> rabbit_misc:confirm_to_sender(SenderPid, [MsgSeqNo]); false -> ok @@ -857,7 +854,7 @@ cleanup_after_confirm(AckTags, State = #q{delayed_stop = DS, unconfirmed = UC, backing_queue = BQ, backing_queue_state = BQS}) -> - {_Guids, BQS1} = BQ:ack(AckTags, BQS), + {_Guids, BQS1} = BQ:ack([Ack || Ack <- AckTags, Ack /= undefined], BQS), State1 = State#q{backing_queue_state = BQS1}, case dtree:is_empty(UC) andalso DS =/= undefined of true -> case DS of -- cgit v1.2.1 From 7008dd09a161e026db0a574436eebe4506ac6101 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Mon, 17 Dec 2012 14:08:15 +0000 Subject: Remove unused variables --- src/rabbit_amqqueue_process.erl | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 86064141..f5e2b400 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -590,19 +590,18 @@ deliver_or_enqueue(Delivery = #delivery{message = Message}, publish_max(#delivery{message = Message, sender = SenderPid}, - Props, Delivered, State = #q{backing_queue = BQ, - backing_queue_state = BQS, - max_depth = undefined}) -> + Props, Delivered, #q{backing_queue = BQ, + backing_queue_state = BQS, + max_depth = undefined}) -> BQ:publish(Message, Props, Delivered, SenderPid, BQS); publish_max(#delivery{message = Message, msg_seq_no = MsgSeqNo, sender = SenderPid}, Props = #message_properties{needs_confirming = Confirm}, Delivered, - State = #q{backing_queue = BQ, - backing_queue_state = BQS, - dlx = XName, - max_depth = MaxDepth}) -> + #q{backing_queue = BQ, + backing_queue_state = BQS, + max_depth = MaxDepth}) -> {Depth, Len} = {BQ:depth(BQS), BQ:len(BQS)}, case {Depth >= MaxDepth, Len =:= 0} of {false, _} -> -- cgit v1.2.1 From 0309aee655406ca54e4ed6ac4535677ef866717b Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 18 Dec 2012 13:46:27 +0000 Subject: Emit final stats event for connections, queues and channels just before the deleted event. --- src/rabbit_amqqueue_process.erl | 2 ++ src/rabbit_channel.erl | 2 ++ src/rabbit_reader.erl | 23 ++++++++++++++++------- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 03bcdf43..cb83d28c 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -180,6 +180,8 @@ terminate(Reason, State = #q{q = #amqqueue{name = QName}, fun (BQS) -> BQS1 = BQ:delete_and_terminate(Reason, BQS), %% don't care if the internal delete doesn't return 'ok'. + rabbit_event:if_enabled(State, #q.stats_timer, + fun() -> emit_stats(State) end), rabbit_amqqueue:internal_delete(QName), BQS1 end, State). diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index a3c82865..91eab01b 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -363,6 +363,8 @@ terminate(Reason, State) -> _ -> ok end, pg_local:leave(rabbit_channels, self()), + rabbit_event:if_enabled(State, #ch.stats_timer, + fun() -> emit_stats(State) end), rabbit_event:notify(channel_closed, [{pid, self()}]). code_change(_OldVsn, State, _Extra) -> diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 928786e9..80886b46 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -280,11 +280,13 @@ mainloop(Deb, State = #v1{sock = Sock, buf = Buf, buf_len = BufLen}) -> {data, Data} -> recvloop(Deb, State#v1{buf = [Data | Buf], buf_len = BufLen + size(Data), pending_recv = false}); - closed -> case State#v1.connection_state of + closed -> maybe_emit_stats(State), + case State#v1.connection_state of closed -> State; _ -> throw(connection_closed_abruptly) end; - {error, Reason} -> throw({inet_error, Reason}); + {error, Reason} -> maybe_emit_stats(State), + throw({inet_error, Reason}); {other, Other} -> handle_other(Other, Deb, State) end. @@ -305,9 +307,11 @@ handle_other({'EXIT', Parent, Reason}, _Deb, State = #v1{parent = Parent}) -> %% ordinary error case. However, since this termination is %% initiated by our parent it is probably more important to exit %% quickly. + maybe_emit_stats(State), exit(Reason); handle_other({channel_exit, _Channel, E = {writer, send_failed, _Error}}, - _Deb, _State) -> + _Deb, State) -> + maybe_emit_stats(State), throw(E); handle_other({channel_exit, Channel, Reason}, Deb, State) -> mainloop(Deb, handle_exception(State, Channel, Reason)); @@ -324,7 +328,8 @@ handle_other(handshake_timeout, _Deb, State) -> throw({handshake_timeout, State#v1.callback}); handle_other(heartbeat_timeout, Deb, State = #v1{connection_state = closed}) -> mainloop(Deb, State); -handle_other(heartbeat_timeout, _Deb, #v1{connection_state = S}) -> +handle_other(heartbeat_timeout, _Deb, State = #v1{connection_state = S}) -> + maybe_emit_stats(State), throw({heartbeat_timeout, S}); handle_other({'$gen_call', From, {shutdown, Explanation}}, Deb, State) -> {ForceTermination, NewState} = terminate(Explanation, State), @@ -358,8 +363,9 @@ handle_other({system, From, Request}, Deb, State = #v1{parent = Parent}) -> handle_other({bump_credit, Msg}, Deb, State) -> credit_flow:handle_bump_msg(Msg), recvloop(Deb, control_throttle(State)); -handle_other(Other, _Deb, _State) -> +handle_other(Other, _Deb, State) -> %% internal error -> something worth dying for + maybe_emit_stats(State), exit({unexpected_message, Other}). switch_callback(State, Callback, Length) -> @@ -805,8 +811,7 @@ handle_method0(#'connection.open'{virtual_host = VHostPath}, rabbit_event:notify(connection_created, [{type, network} | infos(?CREATION_EVENT_KEYS, State1)]), - rabbit_event:if_enabled(State1, #v1.stats_timer, - fun() -> emit_stats(State1) end), + maybe_emit_stats(State1), State1; handle_method0(#'connection.close'{}, State) when ?IS_RUNNING(State) -> lists:foreach(fun rabbit_channel:shutdown/1, all_channels()), @@ -977,6 +982,10 @@ cert_info(F, #v1{sock = Sock}) -> {ok, Cert} -> list_to_binary(F(Cert)) end. +maybe_emit_stats(State) -> + rabbit_event:if_enabled(State, #v1.stats_timer, + fun() -> emit_stats(State) end). + emit_stats(State) -> rabbit_event:notify(connection_stats, infos(?STATISTICS_KEYS, State)), rabbit_event:reset_stats_timer(State, #v1.stats_timer). -- cgit v1.2.1 From 4b9cc57601b69ad1e3a6f7e9649382dbb1cd8ef3 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Tue, 18 Dec 2012 17:20:02 +0000 Subject: Confirms for HA --- src/dtree.erl | 24 +++++++++++++++++++++++- src/rabbit_amqqueue_process.erl | 21 +++++++-------------- src/rabbit_channel.erl | 12 ++++++++++-- src/rabbit_misc.erl | 5 ++++- 4 files changed, 44 insertions(+), 18 deletions(-) diff --git a/src/dtree.erl b/src/dtree.erl index ca2d30cf..c59243bb 100644 --- a/src/dtree.erl +++ b/src/dtree.erl @@ -32,7 +32,7 @@ -module(dtree). --export([empty/0, insert/4, take/3, take/2, take_all/2, +-export([empty/0, insert/4, take/3, take/2, take_all/2, take_prim/2, is_defined/2, is_empty/1, smallest/1, size/1]). %%---------------------------------------------------------------------------- @@ -53,6 +53,7 @@ -spec(take/3 :: ([pk()], sk(), ?MODULE()) -> {[kv()], ?MODULE()}). -spec(take/2 :: (sk(), ?MODULE()) -> {[kv()], ?MODULE()}). -spec(take_all/2 :: (sk(), ?MODULE()) -> {[kv()], ?MODULE()}). +-spec(take_prim/2 :: (pk(), ?MODULE()) -> {[kv()], ?MODULE()}). -spec(is_defined/2 :: (sk(), ?MODULE()) -> boolean()). -spec(is_empty/1 :: (?MODULE()) -> boolean()). -spec(smallest/1 :: (?MODULE()) -> kv()). @@ -120,6 +121,13 @@ take_all(SK, {P, S}) -> {KVs, {P1, prune(SKS, PKS, S)}} end. +%% Drop the entry with the given primary key +take_prim(PK, {P, S} = DTree) -> + case gb_trees:lookup(PK, P) of + none -> {[], DTree}; + {value, {SKS, V}} -> {[{PK, V}], take_prim2(PK, SKS, DTree)} + end. + is_defined(SK, {_P, S}) -> gb_trees:is_defined(SK, S). is_empty({P, _S}) -> gb_trees:is_empty(P). @@ -149,6 +157,20 @@ take_all2(PKS, P) -> gb_trees:delete(PK, P0)} end, {[], gb_sets:empty(), P}, PKS). +take_prim2(PK, SKS, {P, S}) -> + {gb_trees:delete(PK, P), + rabbit_misc:gb_trees_fold( + fun (SK0, PKS, S1) -> + case gb_sets:is_member(SK0, SKS) of + false -> S1; + true -> PKS1 = gb_sets:delete(PK, PKS), + case gb_sets:is_empty(PKS1) of + true -> gb_trees:delete(SK0, S1); + false -> gb_trees:update(SK0, PKS1, S1) + end + end + end, S, S)}. + prune(SKS, PKS, S) -> gb_sets:fold(fun (SK0, S0) -> PKS1 = gb_trees:get(SK0, S0), diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index f5e2b400..01125819 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -580,11 +580,9 @@ deliver_or_enqueue(Delivery = #delivery{message = Message}, discard(Delivery, State2); {false, State2} -> case publish_max(Delivery, Props, Delivered, State2) of - nopub -> - State2; - BQS1 -> - ensure_ttl_timer(Props#message_properties.expiry, - State2#q{backing_queue_state = BQS1}) + nopub -> State2; + BQS1 -> ensure_ttl_timer(Props#message_properties.expiry, + State2#q{backing_queue_state = BQS1}) end end. @@ -597,21 +595,16 @@ publish_max(#delivery{message = Message, publish_max(#delivery{message = Message, msg_seq_no = MsgSeqNo, sender = SenderPid}, - Props = #message_properties{needs_confirming = Confirm}, - Delivered, - #q{backing_queue = BQ, - backing_queue_state = BQS, - max_depth = MaxDepth}) -> + Props, Delivered, #q{backing_queue = BQ, + backing_queue_state = BQS, + max_depth = MaxDepth}) -> {Depth, Len} = {BQ:depth(BQS), BQ:len(BQS)}, case {Depth >= MaxDepth, Len =:= 0} of {false, _} -> BQ:publish(Message, Props, Delivered, SenderPid, BQS); {true, true} -> (dead_letter_fun(maxdepth))([{Message, undefined}]), - case Confirm of - true -> rabbit_misc:confirm_to_sender(SenderPid, [MsgSeqNo]); - false -> ok - end, + rabbit_misc:confirm_all(SenderPid, MsgSeqNo), nopub; {true, false} -> {{Msg, _IsDelivered, AckTag}, BQS1} = BQ:fetch(true, BQS), diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index a3c82865..ae29861e 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -224,8 +224,9 @@ prioritise_call(Msg, _From, _State) -> prioritise_cast(Msg, _State) -> case Msg of - {confirm, _MsgSeqNos, _QPid} -> 5; - _ -> 0 + {confirm, _MsgSeqNos, _QPid} -> 5; + {confirm_all, _MsgSeqNo, _QPid} -> 5; + _ -> 0 end. prioritise_info(Msg, _State) -> @@ -316,6 +317,9 @@ handle_cast(force_event_refresh, State) -> noreply(State); handle_cast({confirm, MsgSeqNos, From}, State) -> State1 = #ch{confirmed = C} = confirm(MsgSeqNos, From, State), + noreply([send_confirms], State1, case C of [] -> hibernate; _ -> 0 end); +handle_cast({confirm_all, MsgSeqNo, _From}, State) -> + State1 = #ch{confirmed = C} = confirm_all(MsgSeqNo, State), noreply([send_confirms], State1, case C of [] -> hibernate; _ -> 0 end). handle_info({bump_credit, Msg}, State) -> @@ -571,6 +575,10 @@ confirm(MsgSeqNos, QPid, State = #ch{unconfirmed = UC}) -> {MXs, UC1} = dtree:take(MsgSeqNos, QPid, UC), record_confirms(MXs, State#ch{unconfirmed = UC1}). +confirm_all(MsgSeqNo, State = #ch{unconfirmed = UC}) -> + {MXs, UC1} = dtree:take_prim(MsgSeqNo, UC), + record_confirms(MXs, State#ch{unconfirmed = UC1}). + handle_method(#'channel.open'{}, _, State = #ch{state = starting}) -> {reply, #'channel.open_ok'{}, State#ch{state = running}}; diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 4efde50e..05569599 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -28,7 +28,7 @@ -export([enable_cover/0, report_cover/0]). -export([enable_cover/1, report_cover/1]). -export([start_cover/1]). --export([confirm_to_sender/2]). +-export([confirm_to_sender/2, confirm_all/2]). -export([throw_on_error/2, with_exit_handler/2, is_abnormal_exit/1, filter_exit_map/2]). -export([with_user/2, with_user_and_vhost/3]). @@ -428,6 +428,9 @@ report_coverage_percentage(File, Cov, NotCov, Mod) -> confirm_to_sender(Pid, MsgSeqNos) -> gen_server2:cast(Pid, {confirm, MsgSeqNos, self()}). +confirm_all(Pid, MsgSeqNo) -> + gen_server2:cast(Pid, {confirm_all, MsgSeqNo, self()}). + %% @doc Halts the emulator returning the given status code to the os. %% On Windows this function will block indefinitely so as to give the io %% subsystem time to flush stdout completely. -- cgit v1.2.1 From 726580efafc8fdaf70a409a17f89e0247a595891 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Tue, 18 Dec 2012 17:27:44 +0000 Subject: Maxdepth argument checking --- src/rabbit_amqqueue.erl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 9c089973..51f5bfea 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -413,8 +413,9 @@ check_int_arg({Type, _}, _) -> check_maxdepth_arg({Type, Val}, Args) -> case check_int_arg({Type, Val}, Args) of - ok when Val =< 0 -> {error, {value_non_positive, Val}}; - X -> X + ok when Val > 0 -> ok; + ok -> {error, {value_not_positive, Val}}; + Error -> Error end. check_expires_arg({Type, Val}, Args) -> -- cgit v1.2.1 From 653d599979edb2f0a38c6916c2de49a793d4fb56 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Tue, 18 Dec 2012 17:34:11 +0000 Subject: Inline --- src/rabbit_amqqueue_process.erl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 01125819..933c4029 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -598,8 +598,7 @@ publish_max(#delivery{message = Message, Props, Delivered, #q{backing_queue = BQ, backing_queue_state = BQS, max_depth = MaxDepth}) -> - {Depth, Len} = {BQ:depth(BQS), BQ:len(BQS)}, - case {Depth >= MaxDepth, Len =:= 0} of + case {BQ:depth(BQS) >= MaxDepth, BQ:len(BQS) =:= 0} of {false, _} -> BQ:publish(Message, Props, Delivered, SenderPid, BQS); {true, true} -> -- cgit v1.2.1 From 2956e6dda62c838ace81cc60b22a698927f561b8 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 27 Dec 2012 21:57:29 +0000 Subject: get rid of bump_reductions it hurts performance in some cases and credit_flow does a better job --- src/rabbit_reader.erl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 928786e9..0296537d 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -622,8 +622,7 @@ post_process_frame({method, MethodName, _}, _ChPid, State = #v1{connection = #connection{ protocol = Protocol}}) -> case Protocol:method_has_content(MethodName) of - true -> erlang:bump_reductions(2000), - maybe_block(control_throttle(State)); + true -> maybe_block(control_throttle(State)); false -> control_throttle(State) end; post_process_frame(_Frame, _ChPid, State) -> -- cgit v1.2.1 From 902e542160fd0c345dde7dee7848af783a15b556 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 27 Dec 2012 22:30:21 +0000 Subject: replace credit_flow:update/3 with a macro un order to eliminate closure creation and thus improve performance. Also... - get rid of a 'get/2' call by processing result of 'erase' instead - inline remaining get/2 calls for performance --- src/credit_flow.erl | 72 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 41 insertions(+), 31 deletions(-) diff --git a/src/credit_flow.erl b/src/credit_flow.erl index ba99811f..ec9b2c36 100644 --- a/src/credit_flow.erl +++ b/src/credit_flow.erl @@ -52,6 +52,19 @@ %%---------------------------------------------------------------------------- +%% process dict update macro - eliminates the performance-hurting +%% closure creation a HOF would introduce +-define(UPDATE(Key, Default, Var, Expr), + begin + case get(Key) of + undefined -> Var = Default; + Var -> ok + end, + put(Key, Expr) + end). + +%%---------------------------------------------------------------------------- + %% There are two "flows" here; of messages and of credit, going in %% opposite directions. The variable names "From" and "To" refer to %% the flow of credit, but the function names refer to the flow of @@ -66,29 +79,33 @@ send(From) -> send(From, ?DEFAULT_CREDIT). send(From, {InitialCredit, _MoreCreditAfter}) -> - update({credit_from, From}, InitialCredit, - fun (1) -> block(From), - 0; - (C) -> C - 1 - end). + ?UPDATE({credit_from, From}, InitialCredit, C, + if C == 1 -> block(From), + 0; + true -> C - 1 + end). ack(To) -> ack(To, ?DEFAULT_CREDIT). ack(To, {_InitialCredit, MoreCreditAfter}) -> - update({credit_to, To}, MoreCreditAfter, - fun (1) -> grant(To, MoreCreditAfter), - MoreCreditAfter; - (C) -> C - 1 - end). + ?UPDATE({credit_to, To}, MoreCreditAfter, C, + if C == 1 -> grant(To, MoreCreditAfter), + MoreCreditAfter; + true -> C - 1 + end). handle_bump_msg({From, MoreCredit}) -> - update({credit_from, From}, 0, - fun (C) when C =< 0 andalso C + MoreCredit > 0 -> unblock(From), - C + MoreCredit; - (C) -> C + MoreCredit - end). - -blocked() -> get(credit_blocked, []) =/= []. + ?UPDATE({credit_from, From}, 0, C, + if C =< 0 andalso C + MoreCredit > 0 -> unblock(From), + C + MoreCredit; + true -> C + MoreCredit + end). + +blocked() -> case get(credit_blocked) of + undefined -> false; + [] -> false; + _ -> true + end. peer_down(Peer) -> %% In theory we could also remove it from credit_deferred here, but it @@ -105,24 +122,17 @@ grant(To, Quantity) -> Msg = {bump_credit, {self(), Quantity}}, case blocked() of false -> To ! Msg; - true -> update(credit_deferred, [], - fun (Deferred) -> [{To, Msg} | Deferred] end) + true -> ?UPDATE(credit_deferred, [], Deferred, [{To, Msg} | Deferred]) end. -block(From) -> update(credit_blocked, [], fun (Blocks) -> [From | Blocks] end). +block(From) -> ?UPDATE(credit_blocked, [], Blocks, [From | Blocks]). unblock(From) -> - update(credit_blocked, [], fun (Blocks) -> Blocks -- [From] end), + ?UPDATE(credit_blocked, [], Blocks, Blocks -- [From]), case blocked() of - false -> [To ! Msg || {To, Msg} <- get(credit_deferred, [])], - erase(credit_deferred); + false -> case erase(credit_deferred) of + undefined -> ok; + Credits -> [To ! Msg || {To, Msg} <- Credits] + end; true -> ok end. - -get(Key, Default) -> - case get(Key) of - undefined -> Default; - Value -> Value - end. - -update(Key, Default, Fun) -> put(Key, Fun(get(Key, Default))), ok. -- cgit v1.2.1 -- cgit v1.2.1 From 08bc06b2af10de919da2e8ea74b21392b2e4f03b Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 27 Dec 2012 23:25:38 +0000 Subject: only invoke control_throttle when necesseary --- src/rabbit_reader.erl | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 0296537d..83622a9f 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -598,35 +598,34 @@ process_frame(Frame, Channel, State) -> undefined -> create_channel(Channel, State); Other -> Other end, - case process_channel_frame(Frame, ChPid, AState) of - {ok, NewAState} -> put({channel, Channel}, {ChPid, NewAState}), - post_process_frame(Frame, ChPid, State); - {error, Reason} -> handle_exception(State, Channel, Reason) - end. - -process_channel_frame(Frame, ChPid, AState) -> case rabbit_command_assembler:process(Frame, AState) of - {ok, NewAState} -> {ok, NewAState}; - {ok, Method, NewAState} -> rabbit_channel:do(ChPid, Method), - {ok, NewAState}; - {ok, Method, Content, NewAState} -> rabbit_channel:do_flow( - ChPid, Method, Content), - {ok, NewAState}; - {error, Reason} -> {error, Reason} + {ok, NewAState} -> + put({channel, Channel}, {ChPid, NewAState}), + post_process_frame(Frame, ChPid, State); + {ok, Method, NewAState} -> + rabbit_channel:do(ChPid, Method), + put({channel, Channel}, {ChPid, NewAState}), + post_process_frame(Frame, ChPid, State); + {ok, Method, Content, NewAState} -> + rabbit_channel:do_flow(ChPid, Method, Content), + put({channel, Channel}, {ChPid, NewAState}), + post_process_frame(Frame, ChPid, control_throttle(State)); + {error, Reason} -> + {error, Reason} end. post_process_frame({method, 'channel.close_ok', _}, ChPid, State) -> channel_cleanup(ChPid), - control_throttle(State); + State; post_process_frame({method, MethodName, _}, _ChPid, State = #v1{connection = #connection{ protocol = Protocol}}) -> case Protocol:method_has_content(MethodName) of - true -> maybe_block(control_throttle(State)); - false -> control_throttle(State) + true -> maybe_block(State); + false -> State end; post_process_frame(_Frame, _ChPid, State) -> - control_throttle(State). + State. %%-------------------------------------------------------------------------- -- cgit v1.2.1 From 5ddf261aa6e75fbaa758a2c6fc214a39c3c15ad5 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 28 Dec 2012 13:40:44 +0000 Subject: get rid of rabbit_channel MASKED_CALL macro which simplifies the code and eliminates a performance hotspot --- src/rabbit_channel.erl | 36 +++++++++++------------------------- 1 file changed, 11 insertions(+), 25 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index a3c82865..b6f3d750 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -314,9 +314,12 @@ handle_cast({deliver, ConsumerTag, AckRequired, handle_cast(force_event_refresh, State) -> rabbit_event:notify(channel_created, infos(?CREATION_EVENT_KEYS, State)), noreply(State); + handle_cast({confirm, MsgSeqNos, From}, State) -> State1 = #ch{confirmed = C} = confirm(MsgSeqNos, From, State), - noreply([send_confirms], State1, case C of [] -> hibernate; _ -> 0 end). + Timeout = case C of [] -> hibernate; _ -> 0 end, + %% NB: don't call noreply/1 since we don't want to send confirms. + {noreply, ensure_stats_timer(State1), Timeout}. handle_info({bump_credit, Msg}, State) -> credit_flow:handle_bump_msg(Msg), @@ -327,8 +330,10 @@ handle_info(timeout, State) -> handle_info(emit_stats, State) -> emit_stats(State), - noreply([ensure_stats_timer], - rabbit_event:reset_stats_timer(State, #ch.stats_timer)); + State1 = rabbit_event:reset_stats_timer(State, #ch.stats_timer), + %% NB: don't call noreply/1 since we don't want to kick off the + %% stats timer. + {noreply, send_confirms(State1), hibernate}; handle_info({'DOWN', _MRef, process, QPid, Reason}, State) -> State1 = handle_publishing_queue_down(QPid, Reason, State), @@ -372,30 +377,11 @@ format_message_queue(Opt, MQ) -> rabbit_misc:format_message_queue(Opt, MQ). %%--------------------------------------------------------------------------- -reply(Reply, NewState) -> reply(Reply, [], NewState). - -reply(Reply, Mask, NewState) -> reply(Reply, Mask, NewState, hibernate). - -reply(Reply, Mask, NewState, Timeout) -> - {reply, Reply, next_state(Mask, NewState), Timeout}. - -noreply(NewState) -> noreply([], NewState). - -noreply(Mask, NewState) -> noreply(Mask, NewState, hibernate). - -noreply(Mask, NewState, Timeout) -> - {noreply, next_state(Mask, NewState), Timeout}. +reply(Reply, NewState) -> {reply, Reply, next_state(NewState), hibernate}. --define(MASKED_CALL(Fun, Mask, State), - case lists:member(Fun, Mask) of - true -> State; - false -> Fun(State) - end). +noreply(NewState) -> {noreply, next_state(NewState), hibernate}. -next_state(Mask, State) -> - State1 = ?MASKED_CALL(ensure_stats_timer, Mask, State), - State2 = ?MASKED_CALL(send_confirms, Mask, State1), - State2. +next_state(State) -> ensure_stats_timer(send_confirms(State)). ensure_stats_timer(State) -> rabbit_event:ensure_stats_timer(State, #ch.stats_timer, emit_stats). -- cgit v1.2.1 From 30b9c29d9eafabcdcf0f6defdb49719b7de0b812 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 28 Dec 2012 14:30:10 +0000 Subject: make reader react to flow control sooner ...by blocking when encountering content headers/body frames, rather than method frames of content-bearing commands. --- src/rabbit_reader.erl | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 83622a9f..079de5e1 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -617,13 +617,10 @@ process_frame(Frame, Channel, State) -> post_process_frame({method, 'channel.close_ok', _}, ChPid, State) -> channel_cleanup(ChPid), State; -post_process_frame({method, MethodName, _}, _ChPid, - State = #v1{connection = #connection{ - protocol = Protocol}}) -> - case Protocol:method_has_content(MethodName) of - true -> maybe_block(State); - false -> State - end; +post_process_frame({content_header, _, _, _, _}, _ChPid, State) -> + maybe_block(State); +post_process_frame({content_body, _}, _ChPid, State) -> + maybe_block(State); post_process_frame(_Frame, _ChPid, State) -> State. -- cgit v1.2.1 From bfb47ab54fc39d1ef4554dc537bc2d83aaa95e2d Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 28 Dec 2012 17:59:38 +0000 Subject: move a bunch of reader state fields into 'connection' These fields are constant, seldom read, and associated with the connection. Also, move #connection definition from rabbit.hrl to rabbit_reader - nothing else is using this and it is really private to the reader. --- include/rabbit.hrl | 3 --- src/rabbit_reader.erl | 67 ++++++++++++++++++++++++--------------------------- 2 files changed, 31 insertions(+), 39 deletions(-) diff --git a/include/rabbit.hrl b/include/rabbit.hrl index 0ccb80bf..7385b4b3 100644 --- a/include/rabbit.hrl +++ b/include/rabbit.hrl @@ -27,9 +27,6 @@ -record(vhost, {virtual_host, dummy}). --record(connection, {protocol, user, timeout_sec, frame_max, vhost, - client_properties, capabilities}). - -record(content, {class_id, properties, %% either 'none', or a decoded record/tuple diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 928786e9..924e7c77 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -35,12 +35,15 @@ %%-------------------------------------------------------------------------- --record(v1, {parent, sock, name, connection, callback, recv_len, pending_recv, +-record(v1, {parent, sock, connection, callback, recv_len, pending_recv, connection_state, queue_collector, heartbeater, stats_timer, channel_sup_sup_pid, start_heartbeat_fun, buf, buf_len, auth_mechanism, auth_state, conserve_resources, - last_blocked_by, last_blocked_at, host, peer_host, - port, peer_port}). + last_blocked_by, last_blocked_at}). + +-record(connection, {name, host, peer_host, port, peer_port, + protocol, user, timeout_sec, frame_max, vhost, + client_properties, capabilities}). -define(STATISTICS_KEYS, [pid, recv_oct, recv_cnt, send_oct, send_cnt, send_pend, state, last_blocked_by, last_blocked_age, @@ -205,8 +208,12 @@ start_connection(Parent, ChannelSupSupPid, Collector, StartHeartbeatFun, Deb, {PeerHost, PeerPort, Host, Port} = socket_ends(Sock), State = #v1{parent = Parent, sock = ClientSock, - name = list_to_binary(Name), connection = #connection{ + name = list_to_binary(Name), + host = Host, + peer_host = PeerHost, + port = Port, + peer_port = PeerPort, protocol = none, user = none, timeout_sec = ?HANDSHAKE_TIMEOUT, @@ -228,11 +235,7 @@ start_connection(Parent, ChannelSupSupPid, Collector, StartHeartbeatFun, Deb, auth_state = none, conserve_resources = false, last_blocked_by = none, - last_blocked_at = never, - host = Host, - peer_host = PeerHost, - port = Port, - peer_port = PeerPort}, + last_blocked_at = never}, try ok = inet_op(fun () -> rabbit_net:tune_buffer_size(ClientSock) end), recvloop(Deb, switch_callback(rabbit_event:init_stats_timer( @@ -531,9 +534,10 @@ payload_snippet(<>) -> %%-------------------------------------------------------------------------- create_channel(Channel, State) -> - #v1{sock = Sock, name = Name, queue_collector = Collector, + #v1{sock = Sock, queue_collector = Collector, channel_sup_sup_pid = ChanSupSup, - connection = #connection{protocol = Protocol, + connection = #connection{name = Name, + protocol = Protocol, frame_max = FrameMax, user = User, vhost = VHost, @@ -901,11 +905,6 @@ auth_phase(Response, infos(Items, State) -> [{Item, i(Item, State)} || Item <- Items]. i(pid, #v1{}) -> self(); -i(name, #v1{name = Name}) -> Name; -i(host, #v1{host = Host}) -> Host; -i(peer_host, #v1{peer_host = PeerHost}) -> PeerHost; -i(port, #v1{port = Port}) -> Port; -i(peer_port, #v1{peer_port = PeerPort}) -> PeerPort; i(SockStat, S) when SockStat =:= recv_oct; SockStat =:= recv_cnt; SockStat =:= send_oct; @@ -932,26 +931,22 @@ i(auth_mechanism, #v1{auth_mechanism = none}) -> none; i(auth_mechanism, #v1{auth_mechanism = Mechanism}) -> proplists:get_value(name, Mechanism:description()); -i(protocol, #v1{connection = #connection{protocol = none}}) -> - none; -i(protocol, #v1{connection = #connection{protocol = Protocol}}) -> - Protocol:version(); -i(user, #v1{connection = #connection{user = none}}) -> - ''; -i(user, #v1{connection = #connection{user = #user{ - username = Username}}}) -> - Username; -i(vhost, #v1{connection = #connection{vhost = VHost}}) -> - VHost; -i(timeout, #v1{connection = #connection{timeout_sec = Timeout}}) -> - Timeout; -i(frame_max, #v1{connection = #connection{frame_max = FrameMax}}) -> - FrameMax; -i(client_properties, #v1{connection = #connection{client_properties = - ClientProperties}}) -> - ClientProperties; -i(Item, #v1{}) -> - throw({bad_argument, Item}). +i(Item, #v1{connection = Conn}) -> ic(Item, Conn). + +ic(name, #connection{name = Name}) -> Name; +ic(host, #connection{host = Host}) -> Host; +ic(peer_host, #connection{peer_host = PeerHost}) -> PeerHost; +ic(port, #connection{port = Port}) -> Port; +ic(peer_port, #connection{peer_port = PeerPort}) -> PeerPort; +ic(protocol, #connection{protocol = none}) -> none; +ic(protocol, #connection{protocol = P}) -> P:version(); +ic(user, #connection{user = none}) -> ''; +ic(user, #connection{user = U}) -> U#user.username; +ic(vhost, #connection{vhost = VHost}) -> VHost; +ic(timeout, #connection{timeout_sec = Timeout}) -> Timeout; +ic(frame_max, #connection{frame_max = FrameMax}) -> FrameMax; +ic(client_properties, #connection{client_properties = CP}) -> CP; +ic(Item, #connection{}) -> throw({bad_argument, Item}). socket_info(Get, Select, #v1{sock = Sock}) -> case Get(Sock) of -- cgit v1.2.1 From a931bba3333622c0dc18d8d91156d7233194a738 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 28 Dec 2012 18:13:05 +0000 Subject: move some more reader state into #connection and also clear auth_state once auth has been completed, so we don't hang on to some potentially large piece of state. --- src/rabbit_reader.erl | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 924e7c77..a21a061b 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -38,12 +38,12 @@ -record(v1, {parent, sock, connection, callback, recv_len, pending_recv, connection_state, queue_collector, heartbeater, stats_timer, channel_sup_sup_pid, start_heartbeat_fun, buf, buf_len, - auth_mechanism, auth_state, conserve_resources, - last_blocked_by, last_blocked_at}). + conserve_resources, last_blocked_by, last_blocked_at}). -record(connection, {name, host, peer_host, port, peer_port, protocol, user, timeout_sec, frame_max, vhost, - client_properties, capabilities}). + client_properties, capabilities, + auth_mechanism, auth_state}). -define(STATISTICS_KEYS, [pid, recv_oct, recv_cnt, send_oct, send_cnt, send_pend, state, last_blocked_by, last_blocked_age, @@ -220,7 +220,9 @@ start_connection(Parent, ChannelSupSupPid, Collector, StartHeartbeatFun, Deb, frame_max = ?FRAME_MIN_SIZE, vhost = none, client_properties = none, - capabilities = []}, + capabilities = [], + auth_mechanism = none, + auth_state = none}, callback = uninitialized_callback, recv_len = 0, pending_recv = false, @@ -231,8 +233,6 @@ start_connection(Parent, ChannelSupSupPid, Collector, StartHeartbeatFun, Deb, start_heartbeat_fun = StartHeartbeatFun, buf = [], buf_len = 0, - auth_mechanism = none, - auth_state = none, conserve_resources = false, last_blocked_by = none, last_blocked_at = never}, @@ -750,13 +750,13 @@ handle_method0(#'connection.start_ok'{mechanism = Mechanism, {table, Capabilities1} -> Capabilities1; _ -> [] end, - State = State0#v1{auth_mechanism = AuthMechanism, - auth_state = AuthMechanism:init(Sock), - connection_state = securing, + State = State0#v1{connection_state = securing, connection = Connection#connection{ client_properties = ClientProperties, - capabilities = Capabilities}}, + capabilities = Capabilities, + auth_mechanism = AuthMechanism, + auth_state = AuthMechanism:init(Sock)}}, auth_phase(Response, State); handle_method0(#'connection.secure_ok'{response = Response}, @@ -874,10 +874,10 @@ auth_mechanisms_binary(Sock) -> string:join([atom_to_list(A) || A <- auth_mechanisms(Sock)], " ")). auth_phase(Response, - State = #v1{auth_mechanism = AuthMechanism, - auth_state = AuthState, - connection = Connection = - #connection{protocol = Protocol}, + State = #v1{connection = Connection = + #connection{protocol = Protocol, + auth_mechanism = AuthMechanism, + auth_state = AuthState}, sock = Sock}) -> case AuthMechanism:handle_response(Response, AuthState) of {refused, Msg, Args} -> @@ -890,14 +890,16 @@ auth_phase(Response, {challenge, Challenge, AuthState1} -> Secure = #'connection.secure'{challenge = Challenge}, ok = send_on_channel0(Sock, Secure, Protocol), - State#v1{auth_state = AuthState1}; + State#v1{connection = Connection#connection{ + auth_state = AuthState1}}; {ok, User} -> Tune = #'connection.tune'{channel_max = 0, frame_max = server_frame_max(), heartbeat = server_heartbeat()}, ok = send_on_channel0(Sock, Tune, Protocol), State#v1{connection_state = tuning, - connection = Connection#connection{user = User}} + connection = Connection#connection{user = User, + auth_state = none}} end. %%-------------------------------------------------------------------------- @@ -927,10 +929,6 @@ i(last_blocked_age, #v1{last_blocked_at = never}) -> i(last_blocked_age, #v1{last_blocked_at = T}) -> timer:now_diff(erlang:now(), T) / 1000000; i(channels, #v1{}) -> length(all_channels()); -i(auth_mechanism, #v1{auth_mechanism = none}) -> - none; -i(auth_mechanism, #v1{auth_mechanism = Mechanism}) -> - proplists:get_value(name, Mechanism:description()); i(Item, #v1{connection = Conn}) -> ic(Item, Conn). ic(name, #connection{name = Name}) -> Name; @@ -946,6 +944,10 @@ ic(vhost, #connection{vhost = VHost}) -> VHost; ic(timeout, #connection{timeout_sec = Timeout}) -> Timeout; ic(frame_max, #connection{frame_max = FrameMax}) -> FrameMax; ic(client_properties, #connection{client_properties = CP}) -> CP; +ic(auth_mechanism, #connection{auth_mechanism = none}) -> + none; +ic(auth_mechanism, #connection{auth_mechanism = Mechanism}) -> + proplists:get_value(name, Mechanism:description()); ic(Item, #connection{}) -> throw({bad_argument, Item}). socket_info(Get, Select, #v1{sock = Sock}) -> -- cgit v1.2.1 From 6a99fcb1a67220e7e771178983a5e0b9ad485f83 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 28 Dec 2012 18:46:06 +0000 Subject: move throttle related reader state into sub-state --- src/rabbit_reader.erl | 60 +++++++++++++++++++++++++++++---------------------- 1 file changed, 34 insertions(+), 26 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index a21a061b..154fa394 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -37,14 +37,15 @@ -record(v1, {parent, sock, connection, callback, recv_len, pending_recv, connection_state, queue_collector, heartbeater, stats_timer, - channel_sup_sup_pid, start_heartbeat_fun, buf, buf_len, - conserve_resources, last_blocked_by, last_blocked_at}). + channel_sup_sup_pid, start_heartbeat_fun, buf, buf_len, throttle}). -record(connection, {name, host, peer_host, port, peer_port, protocol, user, timeout_sec, frame_max, vhost, client_properties, capabilities, auth_mechanism, auth_state}). +-record(throttle, {conserve_resources, last_blocked_by, last_blocked_at}). + -define(STATISTICS_KEYS, [pid, recv_oct, recv_cnt, send_oct, send_cnt, send_pend, state, last_blocked_by, last_blocked_age, channels]). @@ -233,9 +234,10 @@ start_connection(Parent, ChannelSupSupPid, Collector, StartHeartbeatFun, Deb, start_heartbeat_fun = StartHeartbeatFun, buf = [], buf_len = 0, - conserve_resources = false, - last_blocked_by = none, - last_blocked_at = never}, + throttle = #throttle{ + conserve_resources = false, + last_blocked_by = none, + last_blocked_at = never}}, try ok = inet_op(fun () -> rabbit_net:tune_buffer_size(ClientSock) end), recvloop(Deb, switch_callback(rabbit_event:init_stats_timer( @@ -291,8 +293,10 @@ 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, State) -> - recvloop(Deb, control_throttle(State#v1{conserve_resources = Conserve})); +handle_other({conserve_resources, Conserve}, Deb, + State = #v1{throttle = Throttle}) -> + Throttle1 = Throttle#throttle{conserve_resources = Conserve}, + recvloop(Deb, control_throttle(State#v1{throttle = Throttle1})); handle_other({channel_closing, ChPid}, Deb, State) -> ok = rabbit_channel:ready_for_close(ChPid), channel_cleanup(ChPid), @@ -375,29 +379,31 @@ terminate(Explanation, State) when ?IS_RUNNING(State) -> terminate(_Explanation, State) -> {force, State}. -control_throttle(State = #v1{connection_state = CS, - conserve_resources = Mem}) -> - case {CS, Mem orelse credit_flow:blocked()} of +control_throttle(State = #v1{connection_state = CS, throttle = Throttle}) -> + 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}; - {blocked, true} -> update_last_blocked_by(State); + {blocked, true} -> State#v1{throttle = update_last_blocked_by( + Throttle)}; {_, _} -> State end. -maybe_block(State = #v1{connection_state = blocking}) -> +maybe_block(State = #v1{connection_state = blocking, throttle = Throttle}) -> ok = rabbit_heartbeat:pause_monitor(State#v1.heartbeater), - update_last_blocked_by(State#v1{connection_state = blocked, - last_blocked_at = erlang:now()}); + State#v1{connection_state = blocked, + throttle = update_last_blocked_by( + Throttle#throttle{last_blocked_at = erlang:now()})}; maybe_block(State) -> State. -update_last_blocked_by(State = #v1{conserve_resources = true}) -> - State#v1{last_blocked_by = resource}; -update_last_blocked_by(State = #v1{conserve_resources = false}) -> - State#v1{last_blocked_by = flow}. +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}. %%-------------------------------------------------------------------------- %% error handling / termination @@ -794,10 +800,11 @@ handle_method0(#'connection.tune_ok'{frame_max = FrameMax, handle_method0(#'connection.open'{virtual_host = VHostPath}, State = #v1{connection_state = opening, - connection = Connection = #connection{ - user = User, - protocol = Protocol}, - sock = Sock}) -> + connection = Connection = #connection{ + user = User, + protocol = Protocol}, + sock = Sock, + throttle = Throttle}) -> ok = rabbit_access_control:check_vhost_access(User, VHostPath), NewConnection = Connection#connection{vhost = VHostPath}, ok = send_on_channel0(Sock, #'connection.open_ok'{}, Protocol), @@ -805,7 +812,8 @@ handle_method0(#'connection.open'{virtual_host = VHostPath}, State1 = control_throttle( State#v1{connection_state = running, connection = NewConnection, - conserve_resources = Conserve}), + throttle = Throttle#throttle{ + conserve_resources = Conserve}}), rabbit_event:notify(connection_created, [{type, network} | infos(?CREATION_EVENT_KEYS, State1)]), @@ -923,10 +931,10 @@ i(peer_cert_issuer, S) -> cert_info(fun rabbit_ssl:peer_cert_issuer/1, S); i(peer_cert_subject, S) -> cert_info(fun rabbit_ssl:peer_cert_subject/1, S); i(peer_cert_validity, S) -> cert_info(fun rabbit_ssl:peer_cert_validity/1, S); i(state, #v1{connection_state = CS}) -> CS; -i(last_blocked_by, #v1{last_blocked_by = By}) -> By; -i(last_blocked_age, #v1{last_blocked_at = never}) -> +i(last_blocked_by, #v1{throttle = #throttle{last_blocked_by = By}}) -> By; +i(last_blocked_age, #v1{throttle = #throttle{last_blocked_at = never}}) -> infinity; -i(last_blocked_age, #v1{last_blocked_at = T}) -> +i(last_blocked_age, #v1{throttle = #throttle{last_blocked_at = T}}) -> timer:now_diff(erlang:now(), T) / 1000000; i(channels, #v1{}) -> length(all_channels()); i(Item, #v1{connection = Conn}) -> ic(Item, Conn). -- cgit v1.2.1 From 601f5b9fad8f64f9d4115765eec1e69602b058ed Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 28 Dec 2012 21:15:46 +0000 Subject: optimise rabbit_channel:process_routing_result/6 - put most common clause first - inline record_confirm/3 --- src/rabbit_channel.erl | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index a3c82865..4d3a6f2c 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -555,11 +555,6 @@ queue_blocked(QPid, State = #ch{blocking = Blocking}) -> State#ch{blocking = Blocking1} end. -record_confirm(undefined, _, State) -> - State; -record_confirm(MsgSeqNo, XName, State) -> - record_confirms([{MsgSeqNo, XName}], State). - record_confirms([], State) -> State; record_confirms(MXs, State = #ch{confirmed = C}) -> @@ -1382,17 +1377,20 @@ deliver_to_queues({Delivery = #delivery{message = Message = #basic_message{ publish, State1), State1. -process_routing_result(unroutable, _, XName, MsgSeqNo, Msg, State) -> - ok = basic_return(Msg, State, no_route), - incr_stats([{exchange_stats, XName, 1}], return_unroutable, State), - record_confirm(MsgSeqNo, XName, State); -process_routing_result(routed, [], XName, MsgSeqNo, _, State) -> - record_confirm(MsgSeqNo, XName, State); process_routing_result(routed, _, _, undefined, _, State) -> State; +process_routing_result(routed, [], XName, MsgSeqNo, _, State) -> + record_confirms([{MsgSeqNo, XName}], State); process_routing_result(routed, QPids, XName, MsgSeqNo, _, State) -> State#ch{unconfirmed = dtree:insert(MsgSeqNo, QPids, XName, - State#ch.unconfirmed)}. + State#ch.unconfirmed)}; +process_routing_result(unroutable, _, XName, MsgSeqNo, Msg, State) -> + ok = basic_return(Msg, State, no_route), + incr_stats([{exchange_stats, XName, 1}], return_unroutable, State), + case MsgSeqNo of + undefined -> State; + _ -> record_confirms([{MsgSeqNo, XName}], State) + end. send_nacks([], State) -> State; -- cgit v1.2.1 From 25bc0fabb9538906bf9b6eb0b72ae34e18fcf5c0 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 28 Dec 2012 21:29:12 +0000 Subject: optimise pmon:monitor_all common cases --- src/pmon.erl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/pmon.erl b/src/pmon.erl index 1aeebb72..37898119 100644 --- a/src/pmon.erl +++ b/src/pmon.erl @@ -19,6 +19,8 @@ -export([new/0, monitor/2, monitor_all/2, demonitor/2, is_monitored/2, erase/2, monitored/1, is_empty/1]). +-compile({no_auto_import, [monitor/2]}). + -ifdef(use_specs). %%---------------------------------------------------------------------------- @@ -48,7 +50,9 @@ monitor(Item, M) -> false -> dict:store(Item, erlang:monitor(process, Item), M) end. -monitor_all(Items, M) -> lists:foldl(fun monitor/2, M, Items). +monitor_all([], M) -> M; %% optimisation +monitor_all([Item], M) -> monitor(Item, M); %% optimisation +monitor_all(Items, M) -> lists:foldl(fun monitor/2, M, Items). demonitor(Item, M) -> case dict:find(Item, M) of -- cgit v1.2.1 From aa6ed6e1b531a9a598039984d25d3219817280ef Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 28 Dec 2012 21:37:55 +0000 Subject: optimise rabbit_amqqueue:lookup/1 common cases --- src/rabbit_amqqueue.erl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 1b6cc223..1a270364 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -302,6 +302,8 @@ add_default_binding(#amqqueue{name = QueueName}) -> key = RoutingKey, args = []}). +lookup([]) -> []; %% optimisation +lookup([Name]) -> ets:lookup(rabbit_queue, Name); %% optimisation lookup(Names) when is_list(Names) -> %% Normally we'd call mnesia:dirty_read/1 here, but that is quite %% expensive for reasons explained in rabbit_misc:dirty_read/1. -- cgit v1.2.1 From f0fe8744f80e98980594470eb03309d6611f50c5 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 28 Dec 2012 21:58:33 +0000 Subject: refactor: shorter/better names for rabbit_trace functions --- src/rabbit_channel.erl | 4 ++-- src/rabbit_trace.erl | 18 ++++++++---------- src/rabbit_vhost.erl | 2 +- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index a3c82865..617ea25f 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -635,7 +635,7 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, end, case rabbit_basic:message(ExchangeName, RoutingKey, DecodedContent) of {ok, Message} -> - rabbit_trace:tap_trace_in(Message, TraceState), + rabbit_trace:tap_in(Message, TraceState), Delivery = rabbit_basic:delivery(Mandatory, Message, MsgSeqNo), QNames = rabbit_exchange:route(Exchange, Delivery), {noreply, @@ -1253,7 +1253,7 @@ record_sent(ConsumerTag, AckRequired, true -> incr_stats([{queue_stats, QName, 1}], redeliver, State); false -> ok end, - rabbit_trace:tap_trace_out(Msg, TraceState), + rabbit_trace:tap_out(Msg, TraceState), UAMQ1 = case AckRequired of true -> queue:in({DeliveryTag, ConsumerTag, {QPid, MsgId}}, UAMQ); diff --git a/src/rabbit_trace.erl b/src/rabbit_trace.erl index 3a5b96de..b9a7cc15 100644 --- a/src/rabbit_trace.erl +++ b/src/rabbit_trace.erl @@ -16,7 +16,7 @@ -module(rabbit_trace). --export([init/1, tracing/1, tap_trace_in/2, tap_trace_out/2, start/1, stop/1]). +-export([init/1, enabled/1, tap_in/2, tap_out/2, start/1, stop/1]). -include("rabbit.hrl"). -include("rabbit_framing.hrl"). @@ -31,9 +31,9 @@ -type(state() :: rabbit_types:exchange() | 'none'). -spec(init/1 :: (rabbit_types:vhost()) -> state()). --spec(tracing/1 :: (rabbit_types:vhost()) -> boolean()). --spec(tap_trace_in/2 :: (rabbit_types:basic_message(), state()) -> 'ok'). --spec(tap_trace_out/2 :: (rabbit_amqqueue:qmsg(), state()) -> 'ok'). +-spec(enabled/1 :: (rabbit_types:vhost()) -> boolean()). +-spec(tap_in/2 :: (rabbit_types:basic_message(), state()) -> 'ok'). +-spec(tap_out/2 :: (rabbit_amqqueue:qmsg(), state()) -> 'ok'). -spec(start/1 :: (rabbit_types:vhost()) -> 'ok'). -spec(stop/1 :: (rabbit_types:vhost()) -> 'ok'). @@ -43,23 +43,21 @@ %%---------------------------------------------------------------------------- init(VHost) -> - case tracing(VHost) of + case enabled(VHost) of false -> none; true -> {ok, X} = rabbit_exchange:lookup( rabbit_misc:r(VHost, exchange, ?XNAME)), X end. -tracing(VHost) -> +enabled(VHost) -> {ok, VHosts} = application:get_env(rabbit, ?TRACE_VHOSTS), lists:member(VHost, VHosts). -tap_trace_in(Msg = #basic_message{exchange_name = #resource{name = XName}}, - TraceX) -> +tap_in(Msg = #basic_message{exchange_name = #resource{name = XName}}, TraceX) -> maybe_trace(TraceX, Msg, <<"publish">>, XName, []). -tap_trace_out({#resource{name = QName}, _QPid, _QMsgId, Redelivered, Msg}, - TraceX) -> +tap_out({#resource{name = QName}, _QPid, _QMsgId, Redelivered, Msg}, TraceX) -> RedeliveredNum = case Redelivered of true -> 1; false -> 0 end, maybe_trace(TraceX, Msg, <<"deliver">>, QName, [{<<"redelivered">>, signedint, RedeliveredNum}]). diff --git a/src/rabbit_vhost.erl b/src/rabbit_vhost.erl index 297fa56f..0bb18f4c 100644 --- a/src/rabbit_vhost.erl +++ b/src/rabbit_vhost.erl @@ -123,7 +123,7 @@ with(VHostPath, Thunk) -> infos(Items, X) -> [{Item, i(Item, X)} || Item <- Items]. i(name, VHost) -> VHost; -i(tracing, VHost) -> rabbit_trace:tracing(VHost); +i(tracing, VHost) -> rabbit_trace:enabled(VHost); i(Item, _) -> throw({bad_argument, Item}). info(VHost) -> infos(?INFO_KEYS, VHost). -- cgit v1.2.1 From 542ad2ef89fe9e5fae4746866acdec06cd907ab9 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 28 Dec 2012 22:03:28 +0000 Subject: optimise rabbit_trace:tap_{in,out} common cases --- src/rabbit_trace.erl | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/rabbit_trace.erl b/src/rabbit_trace.erl index b9a7cc15..601656da 100644 --- a/src/rabbit_trace.erl +++ b/src/rabbit_trace.erl @@ -54,13 +54,15 @@ enabled(VHost) -> {ok, VHosts} = application:get_env(rabbit, ?TRACE_VHOSTS), lists:member(VHost, VHosts). +tap_in(_Msg, none) -> ok; tap_in(Msg = #basic_message{exchange_name = #resource{name = XName}}, TraceX) -> - maybe_trace(TraceX, Msg, <<"publish">>, XName, []). + trace(TraceX, Msg, <<"publish">>, XName, []). +tap_out(_Msg, none) -> ok; tap_out({#resource{name = QName}, _QPid, _QMsgId, Redelivered, Msg}, TraceX) -> RedeliveredNum = case Redelivered of true -> 1; false -> 0 end, - maybe_trace(TraceX, Msg, <<"deliver">>, QName, - [{<<"redelivered">>, signedint, RedeliveredNum}]). + trace(TraceX, Msg, <<"deliver">>, QName, + [{<<"redelivered">>, signedint, RedeliveredNum}]). %%---------------------------------------------------------------------------- @@ -81,14 +83,11 @@ update_config(Fun) -> %%---------------------------------------------------------------------------- -maybe_trace(none, _Msg, _RKPrefix, _RKSuffix, _Extra) -> +trace(#exchange{name = Name}, #basic_message{exchange_name = Name}, + _RKPrefix, _RKSuffix, _Extra) -> ok; -maybe_trace(#exchange{name = Name}, #basic_message{exchange_name = Name}, - _RKPrefix, _RKSuffix, _Extra) -> - ok; -maybe_trace(X, Msg = #basic_message{content = #content{ - payload_fragments_rev = PFR}}, - RKPrefix, RKSuffix, Extra) -> +trace(X, Msg = #basic_message{content = #content{payload_fragments_rev = PFR}}, + RKPrefix, RKSuffix, Extra) -> {ok, _, _} = rabbit_basic:publish( X, <>, #'P_basic'{headers = msg_to_table(Msg) ++ Extra}, PFR), -- cgit v1.2.1 From f9af8cabdfd67e437792f0ba30b5312fe121e604 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 28 Dec 2012 22:19:47 +0000 Subject: optimisation: inline rabbit_guid:blocks_to_binary/1 --- src/rabbit_guid.erl | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/rabbit_guid.erl b/src/rabbit_guid.erl index cedbbdb3..8ee9ad5b 100644 --- a/src/rabbit_guid.erl +++ b/src/rabbit_guid.erl @@ -104,8 +104,6 @@ advance_blocks({B1, B2, B3, B4}, I) -> B5 = erlang:phash2({B1, I}, 4294967296), {{(B2 bxor B5), (B3 bxor B5), (B4 bxor B5), B5}, I+1}. -blocks_to_binary({B1, B2, B3, B4}) -> <>. - %% generate a GUID. This function should be used when performance is a %% priority and predictability is not an issue. Otherwise use %% gen_secure/0. @@ -114,14 +112,15 @@ gen() -> %% time we need a new guid we rotate them producing a new hash %% with the aid of the counter. Look at the comments in %% advance_blocks/2 for details. - {BS, I} = case get(guid) of - undefined -> <> = - erlang:md5(term_to_binary(fresh())), - {{B1,B2,B3,B4}, 0}; - {BS0, I0} -> advance_blocks(BS0, I0) - end, - put(guid, {BS, I}), - blocks_to_binary(BS). + case get(guid) of + undefined -> <> = Res = + erlang:md5(term_to_binary(fresh())), + put(guid, {{B1, B2, B3, B4}, 0}), + Res; + {BS, I} -> {{B1, B2, B3, B4}, _} = S = advance_blocks(BS, I), + put(guid, S), + <> + end. %% generate a non-predictable GUID. %% -- cgit v1.2.1 From 664c619c91e994c3645980be4903a20fab632e43 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 28 Dec 2012 22:29:25 +0000 Subject: optimisation: ditch guards on rabbit_misc:r/{2,3} --- src/rabbit_misc.erl | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 4efde50e..edaa7198 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -352,13 +352,12 @@ set_table_value(Table, Key, Type, Value) -> sort_field_table( lists:keystore(Key, 1, Table, {Key, Type, Value})). -r(#resource{virtual_host = VHostPath}, Kind, Name) - when is_binary(Name) -> +r(#resource{virtual_host = VHostPath}, Kind, Name) -> #resource{virtual_host = VHostPath, kind = Kind, name = Name}; -r(VHostPath, Kind, Name) when is_binary(Name) andalso is_binary(VHostPath) -> +r(VHostPath, Kind, Name) -> #resource{virtual_host = VHostPath, kind = Kind, name = Name}. -r(VHostPath, Kind) when is_binary(VHostPath) -> +r(VHostPath, Kind) -> #resource{virtual_host = VHostPath, kind = Kind, name = '_'}. r_arg(#resource{virtual_host = VHostPath}, Kind, Table, Key) -> -- cgit v1.2.1 From 46d572342e6b1af9944fc170bebf92ca7a86f9f4 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 29 Dec 2012 05:00:27 +0000 Subject: cosmetic --- src/rabbit_amqqueue_process.erl | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 03bcdf43..781546af 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -791,12 +791,9 @@ stop(State) -> stop(undefined, noreply, State). stop(From, Reply, State = #q{unconfirmed = UC}) -> case {dtree:is_empty(UC), Reply} of - {true, noreply} -> - {stop, normal, State}; - {true, _} -> - {stop, normal, Reply, State}; - {false, _} -> - noreply(State#q{delayed_stop = {From, Reply}}) + {true, noreply} -> {stop, normal, State}; + {true, _} -> {stop, normal, Reply, State}; + {false, _} -> noreply(State#q{delayed_stop = {From, Reply}}) end. cleanup_after_confirm(AckTags, State = #q{delayed_stop = DS, -- cgit v1.2.1 From 6da6dbf02b3791fa5340444d4589bf683487bbeb Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 29 Dec 2012 13:02:02 +0000 Subject: move tx related state fields into sub-record --- src/rabbit_channel.erl | 135 +++++++++++++++++++++++-------------------------- 1 file changed, 64 insertions(+), 71 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 617ea25f..dd858758 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -33,14 +33,15 @@ -export([list_local/0]). -record(ch, {state, protocol, channel, reader_pid, writer_pid, conn_pid, - conn_name, limiter, tx_status, next_tag, unacked_message_q, - uncommitted_message_q, uncommitted_acks, uncommitted_nacks, user, + conn_name, limiter, tx, next_tag, unacked_message_q, user, virtual_host, most_recently_declared_queue, queue_names, queue_monitors, consumer_mapping, blocking, queue_consumers, delivering_queues, queue_collector_pid, stats_timer, confirm_enabled, publish_seqno, unconfirmed, confirmed, capabilities, trace_state}). +-record(tx, {msgs, acks, nacks}). + -define(MAX_PERMISSION_CACHE_SIZE, 12). -define(STATISTICS_KEYS, @@ -186,12 +187,9 @@ init([Channel, ReaderPid, WriterPid, ConnPid, ConnName, Protocol, User, VHost, conn_pid = ConnPid, conn_name = ConnName, limiter = Limiter, - tx_status = none, + tx = none, next_tag = 1, unacked_message_q = queue:new(), - uncommitted_message_q = queue:new(), - uncommitted_acks = [], - uncommitted_nacks = [], user = User, virtual_host = VHost, most_recently_declared_queue = <<>>, @@ -599,8 +597,8 @@ handle_method(#'channel.close'{}, _, State = #ch{reader_pid = ReaderPid}) -> %% while waiting for the reply to a synchronous command, we generally %% do allow this...except in the case of a pending tx.commit, where %% it could wreak havoc. -handle_method(_Method, _, #ch{tx_status = TxStatus}) - when TxStatus =/= none andalso TxStatus =/= in_progress -> +handle_method(_Method, _, #ch{tx = Tx}) + when Tx =:= committing orelse Tx =:= failed -> rabbit_misc:protocol_error( channel_error, "unexpected command while processing 'tx.commit'", []); @@ -614,7 +612,7 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, routing_key = RoutingKey, mandatory = Mandatory}, Content, State = #ch{virtual_host = VHostPath, - tx_status = TxStatus, + tx = Tx, confirm_enabled = ConfirmEnabled, trace_state = TraceState}) -> ExchangeName = rabbit_misc:r(VHostPath, exchange, ExchangeNameBin), @@ -628,7 +626,7 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, check_user_id_header(Props, State), check_expiration_header(Props), {MsgSeqNo, State1} = - case {TxStatus, ConfirmEnabled} of + case {Tx, ConfirmEnabled} of {none, false} -> {undefined, State}; {_, _} -> SeqNo = State#ch.publish_seqno, {SeqNo, State#ch{publish_seqno = SeqNo + 1}} @@ -638,12 +636,12 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, rabbit_trace:tap_in(Message, TraceState), Delivery = rabbit_basic:delivery(Mandatory, Message, MsgSeqNo), QNames = rabbit_exchange:route(Exchange, Delivery), + DQ = {Delivery, QNames}, {noreply, - case TxStatus of - none -> deliver_to_queues({Delivery, QNames}, State1); - in_progress -> TMQ = State1#ch.uncommitted_message_q, - NewTMQ = queue:in({Delivery, QNames}, TMQ), - State1#ch{uncommitted_message_q = NewTMQ} + case Tx of + none -> deliver_to_queues(DQ, State1); + #tx{msgs = Msgs} -> Msgs1 = queue:in(DQ, Msgs), + State1#ch{tx = Tx#tx{msgs = Msgs1}} end}; {error, Reason} -> precondition_failed("invalid message: ~p", [Reason]) @@ -657,15 +655,14 @@ handle_method(#'basic.nack'{delivery_tag = DeliveryTag, handle_method(#'basic.ack'{delivery_tag = DeliveryTag, multiple = Multiple}, - _, State = #ch{unacked_message_q = UAMQ, tx_status = TxStatus}) -> + _, State = #ch{unacked_message_q = UAMQ, tx = Tx}) -> {Acked, Remaining} = collect_acks(UAMQ, DeliveryTag, Multiple), State1 = State#ch{unacked_message_q = Remaining}, {noreply, - case TxStatus of - none -> ack(Acked, State1), - State1; - in_progress -> State1#ch{uncommitted_acks = - Acked ++ State1#ch.uncommitted_acks} + case Tx of + none -> ack(Acked, State1), + State1; + #tx{acks = Acks} -> State1#ch{tx = Tx#tx{acks = Acked ++ Acks}} end}; handle_method(#'basic.get'{queue = QueueNameBin, @@ -1043,34 +1040,37 @@ handle_method(#'queue.purge'{queue = QueueNameBin, handle_method(#'tx.select'{}, _, #ch{confirm_enabled = true}) -> precondition_failed("cannot switch from confirm to tx mode"); +handle_method(#'tx.select'{}, _, State = #ch{tx = none}) -> + {reply, #'tx.select_ok'{}, State#ch{tx = new_tx()}}; + handle_method(#'tx.select'{}, _, State) -> - {reply, #'tx.select_ok'{}, State#ch{tx_status = in_progress}}; + {reply, #'tx.select_ok'{}, State}; -handle_method(#'tx.commit'{}, _, #ch{tx_status = none}) -> +handle_method(#'tx.commit'{}, _, #ch{tx = none}) -> precondition_failed("channel is not transactional"); -handle_method(#'tx.commit'{}, _, - State = #ch{uncommitted_message_q = TMQ, - uncommitted_acks = TAL, - uncommitted_nacks = TNL, - limiter = Limiter}) -> - State1 = rabbit_misc:queue_fold(fun deliver_to_queues/2, State, TMQ), - ack(TAL, State1), +handle_method(#'tx.commit'{}, _, State = #ch{tx = #tx{msgs = Msgs, + acks = Acks, + nacks = Nacks}, + limiter = Limiter}) -> + State1 = rabbit_misc:queue_fold(fun deliver_to_queues/2, State, Msgs), + ack(Acks, State1), lists:foreach( - fun({Requeue, Acked}) -> reject(Requeue, Acked, Limiter) end, TNL), - {noreply, maybe_complete_tx(new_tx(State1#ch{tx_status = committing}))}; + fun({Requeue, Acked}) -> reject(Requeue, Acked, Limiter) end, Nacks), + {noreply, maybe_complete_tx(State1#ch{tx = committing})}; -handle_method(#'tx.rollback'{}, _, #ch{tx_status = none}) -> +handle_method(#'tx.rollback'{}, _, #ch{tx = none}) -> precondition_failed("channel is not transactional"); handle_method(#'tx.rollback'{}, _, State = #ch{unacked_message_q = UAMQ, - uncommitted_acks = TAL, - uncommitted_nacks = TNL}) -> - TNL1 = lists:append([L || {_, L} <- TNL]), - UAMQ1 = queue:from_list(lists:usort(TAL ++ TNL1 ++ queue:to_list(UAMQ))), - {reply, #'tx.rollback_ok'{}, new_tx(State#ch{unacked_message_q = UAMQ1})}; - -handle_method(#'confirm.select'{}, _, #ch{tx_status = in_progress}) -> + tx = #tx{acks = Acks, + nacks = Nacks}}) -> + NacksL = lists:append([L || {_, L} <- Nacks]), + UAMQ1 = queue:from_list(lists:usort(Acks ++ NacksL ++ queue:to_list(UAMQ))), + {reply, #'tx.rollback_ok'{}, State#ch{unacked_message_q = UAMQ1, + tx = new_tx()}}; + +handle_method(#'confirm.select'{}, _, #ch{tx = #tx{}}) -> precondition_failed("cannot switch from tx to confirm mode"); handle_method(#'confirm.select'{nowait = NoWait}, _, State) -> @@ -1218,17 +1218,15 @@ basic_return(#basic_message{exchange_name = ExchangeName, Content). reject(DeliveryTag, Requeue, Multiple, - State = #ch{unacked_message_q = UAMQ, tx_status = TxStatus}) -> + State = #ch{unacked_message_q = UAMQ, tx = Tx}) -> {Acked, Remaining} = collect_acks(UAMQ, DeliveryTag, Multiple), State1 = State#ch{unacked_message_q = Remaining}, {noreply, - case TxStatus of - none -> - reject(Requeue, Acked, State1#ch.limiter), - State1; - in_progress -> - State1#ch{uncommitted_nacks = - [{Requeue, Acked} | State1#ch.uncommitted_nacks]} + case Tx of + none -> reject(Requeue, Acked, State1#ch.limiter), + State1; + #tx{nacks = Nacks} -> Nacks1 = [{Requeue, Acked} | Nacks], + State1#ch{tx = Tx#tx{nacks = Nacks1}} end}. reject(Requeue, Acked, Limiter) -> @@ -1296,9 +1294,7 @@ ack(Acked, State = #ch{queue_names = QNames}) -> ok = notify_limiter(State#ch.limiter, Acked), incr_stats(Incs, ack, State). -new_tx(State) -> State#ch{uncommitted_message_q = queue:new(), - uncommitted_acks = [], - uncommitted_nacks = []}. +new_tx() -> #tx{msgs = queue:new(), acks = [], nacks = []}. notify_queues(State = #ch{state = closing}) -> {ok, State}; @@ -1396,18 +1392,18 @@ process_routing_result(routed, QPids, XName, MsgSeqNo, _, State) -> send_nacks([], State) -> State; -send_nacks(MXs, State = #ch{tx_status = none}) -> +send_nacks(MXs, State = #ch{tx = none}) -> coalesce_and_send([MsgSeqNo || {MsgSeqNo, _} <- MXs], fun(MsgSeqNo, Multiple) -> #'basic.nack'{delivery_tag = MsgSeqNo, multiple = Multiple} end, State); send_nacks(_, State) -> - maybe_complete_tx(State#ch{tx_status = failed}). + maybe_complete_tx(State#ch{tx = failed}). -send_confirms(State = #ch{tx_status = none, confirmed = []}) -> +send_confirms(State = #ch{tx = none, confirmed = []}) -> State; -send_confirms(State = #ch{tx_status = none, confirmed = C}) -> +send_confirms(State = #ch{tx = none, confirmed = C}) -> MsgSeqNos = lists:foldl( fun ({MsgSeqNo, XName}, MSNs) -> @@ -1447,7 +1443,7 @@ coalesce_and_send(MsgSeqNos, MkMsgFun, WriterPid, MkMsgFun(SeqNo, false)) || SeqNo <- Ss], State. -maybe_complete_tx(State = #ch{tx_status = in_progress}) -> +maybe_complete_tx(State = #ch{tx = #tx{}}) -> State; maybe_complete_tx(State = #ch{unconfirmed = UC}) -> case dtree:is_empty(UC) of @@ -1455,16 +1451,16 @@ maybe_complete_tx(State = #ch{unconfirmed = UC}) -> true -> complete_tx(State#ch{confirmed = []}) end. -complete_tx(State = #ch{tx_status = committing}) -> +complete_tx(State = #ch{tx = committing}) -> ok = rabbit_writer:send_command(State#ch.writer_pid, #'tx.commit_ok'{}), - State#ch{tx_status = in_progress}; -complete_tx(State = #ch{tx_status = failed}) -> + State#ch{tx = new_tx()}; +complete_tx(State = #ch{tx = failed}) -> {noreply, State1} = handle_exception( rabbit_misc:amqp_error( precondition_failed, "partial tx completion", [], 'tx.commit'), State), - State1#ch{tx_status = in_progress}. + State1#ch{tx = new_tx()}. infos(Items, State) -> [{Item, i(Item, State)} || Item <- Items]. @@ -1473,19 +1469,16 @@ i(connection, #ch{conn_pid = ConnPid}) -> ConnPid; i(number, #ch{channel = Channel}) -> Channel; i(user, #ch{user = User}) -> User#user.username; i(vhost, #ch{virtual_host = VHost}) -> VHost; -i(transactional, #ch{tx_status = TE}) -> TE =/= none; +i(transactional, #ch{tx = Tx}) -> Tx =/= none; i(confirm, #ch{confirm_enabled = CE}) -> CE; i(name, State) -> name(State); -i(consumer_count, #ch{consumer_mapping = ConsumerMapping}) -> - dict:size(ConsumerMapping); -i(messages_unconfirmed, #ch{unconfirmed = UC}) -> - dtree:size(UC); -i(messages_unacknowledged, #ch{unacked_message_q = UAMQ}) -> - queue:len(UAMQ); -i(messages_uncommitted, #ch{uncommitted_message_q = TMQ}) -> - queue:len(TMQ); -i(acks_uncommitted, #ch{uncommitted_acks = TAL}) -> - length(TAL); +i(consumer_count, #ch{consumer_mapping = CM}) -> dict:size(CM); +i(messages_unconfirmed, #ch{unconfirmed = UC}) -> dtree:size(UC); +i(messages_unacknowledged, #ch{unacked_message_q = UAMQ}) -> queue:len(UAMQ); +i(messages_uncommitted, #ch{tx = #tx{msgs = Msgs}}) -> queue:len(Msgs); +i(messages_uncommitted, #ch{}) -> 0; +i(acks_uncommitted, #ch{tx = #tx{acks = Acks}}) -> length(Acks); +i(acks_uncommitted, #ch{}) -> 0; i(prefetch_count, #ch{limiter = Limiter}) -> rabbit_limiter:get_limit(Limiter); i(client_flow_blocked, #ch{limiter = Limiter}) -> -- cgit v1.2.1 From 8b8767feb898435d9bdb22f0956cdc148c490800 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 29 Dec 2012 15:49:15 +0000 Subject: change rabbit_exchange:route/2's traversal order from breadth-first to depth-first, thus eliminating the queue data structure and hence simplifying the code and improving performance. Plus a little bit of refactoring in the area. --- src/rabbit_exchange.erl | 43 +++++++++++++++++++++---------------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index e72cbafe..9339161f 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -310,22 +310,19 @@ route(#exchange{name = #resource{name = <<"">>, virtual_host = VHost}}, [rabbit_misc:r(VHost, queue, RK) || RK <- lists:usort(RKs)]; route(X = #exchange{name = XName}, Delivery) -> - route1(Delivery, {queue:from_list([X]), XName, []}). - -route1(Delivery, {WorkList, SeenXs, QNames}) -> - case queue:out(WorkList) of - {empty, _WorkList} -> - lists:usort(QNames); - {{value, X = #exchange{type = Type}}, WorkList1} -> - DstNames = process_alternate( - X, ((type_to_module(Type)):route(X, Delivery))), - route1(Delivery, - lists:foldl(fun process_route/2, {WorkList1, SeenXs, QNames}, - DstNames)) - end. + route1(Delivery, {[X], XName, []}). + +route1(_, {[], _, QNames}) -> + lists:usort(QNames); +route1(Delivery, {[X = #exchange{type = Type} | WorkList], SeenXs, QNames}) -> + DstNames = process_alternate( + X, ((type_to_module(Type)):route(X, Delivery))), + route1(Delivery, + lists:foldl(fun process_route/2, {WorkList, SeenXs, QNames}, + DstNames)). process_alternate(#exchange{arguments = []}, Results) -> %% optimisation - Results; + Results; process_alternate(#exchange{name = XName, arguments = Args}, []) -> case rabbit_misc:r_arg(XName, exchange, Args, <<"alternate-exchange">>) of undefined -> []; @@ -339,23 +336,25 @@ process_route(#resource{kind = exchange} = XName, Acc; process_route(#resource{kind = exchange} = XName, {WorkList, #resource{kind = exchange} = SeenX, QNames}) -> - {case lookup(XName) of - {ok, X} -> queue:in(X, WorkList); - {error, not_found} -> WorkList - end, gb_sets:from_list([SeenX, XName]), QNames}; + {cons_if_present(XName, WorkList), + gb_sets:from_list([SeenX, XName]), QNames}; process_route(#resource{kind = exchange} = XName, {WorkList, SeenXs, QNames} = Acc) -> case gb_sets:is_element(XName, SeenXs) of true -> Acc; - false -> {case lookup(XName) of - {ok, X} -> queue:in(X, WorkList); - {error, not_found} -> WorkList - end, gb_sets:add_element(XName, SeenXs), QNames} + false -> {cons_if_present(XName, WorkList), + gb_sets:add_element(XName, SeenXs), QNames} end; process_route(#resource{kind = queue} = QName, {WorkList, SeenXs, QNames}) -> {WorkList, SeenXs, [QName | QNames]}. +cons_if_present(XName, L) -> + case lookup(XName) of + {ok, X} -> [X | L]; + {error, not_found} -> L + end. + call_with_exchange(XName, Fun) -> rabbit_misc:execute_mnesia_tx_with_tail( fun () -> case mnesia:read({rabbit_exchange, XName}) of -- cgit v1.2.1 From 764f6ad7f60ba0b07aa00559a83ee3ffbe38f4f7 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 29 Dec 2012 20:40:49 +0000 Subject: small refactor --- src/rabbit_reader.erl | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 83622a9f..840f430e 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -594,21 +594,22 @@ handle_frame(Type, Channel, Payload, State) -> unexpected_frame(Type, Channel, Payload, State). process_frame(Frame, Channel, State) -> - {ChPid, AState} = case get({channel, Channel}) of + ChKey = {channel, Channel}, + {ChPid, AState} = case get(ChKey) of undefined -> create_channel(Channel, State); Other -> Other end, case rabbit_command_assembler:process(Frame, AState) of {ok, NewAState} -> - put({channel, Channel}, {ChPid, NewAState}), + put(ChKey, {ChPid, NewAState}), post_process_frame(Frame, ChPid, State); {ok, Method, NewAState} -> rabbit_channel:do(ChPid, Method), - put({channel, Channel}, {ChPid, NewAState}), + put(ChKey, {ChPid, NewAState}), post_process_frame(Frame, ChPid, State); {ok, Method, Content, NewAState} -> rabbit_channel:do_flow(ChPid, Method, Content), - put({channel, Channel}, {ChPid, NewAState}), + put(ChKey, {ChPid, NewAState}), post_process_frame(Frame, ChPid, control_throttle(State)); {error, Reason} -> {error, Reason} -- cgit v1.2.1 From efba5a5ebb0f0141e2856228b77eaad0d1f6a603 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 29 Dec 2012 22:10:25 +0000 Subject: optimisation: macro-fy rabbit_channel:incr_stats/3 so we can avoid constructing complex terms for stats when stats emission is disabled --- src/rabbit_channel.erl | 44 +++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 1b1bccf7..c19f9c3a 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -65,6 +65,12 @@ -define(INFO_KEYS, ?CREATION_EVENT_KEYS ++ ?STATISTICS_KEYS -- [pid]). +-define(INCR_STATS(Incs, Measure, State), + case rabbit_event:stats_level(State, #ch.stats_timer) of + fine -> incr_stats(Incs, Measure); + _ -> ok + end). + %%---------------------------------------------------------------------------- -ifdef(use_specs). @@ -1238,14 +1244,14 @@ record_sent(ConsumerTag, AckRequired, State = #ch{unacked_message_q = UAMQ, next_tag = DeliveryTag, trace_state = TraceState}) -> - incr_stats([{queue_stats, QName, 1}], case {ConsumerTag, AckRequired} of - {none, true} -> get; - {none, false} -> get_no_ack; - {_ , true} -> deliver; - {_ , false} -> deliver_no_ack - end, State), + ?INCR_STATS([{queue_stats, QName, 1}], case {ConsumerTag, AckRequired} of + {none, true} -> get; + {none, false} -> get_no_ack; + {_ , true} -> deliver; + {_ , false} -> deliver_no_ack + end, State), case Redelivered of - true -> incr_stats([{queue_stats, QName, 1}], redeliver, State); + true -> ?INCR_STATS([{queue_stats, QName, 1}], redeliver, State); false -> ok end, rabbit_trace:tap_out(Msg, TraceState), @@ -1289,7 +1295,7 @@ ack(Acked, State = #ch{queue_names = QNames}) -> end end, [], Acked), ok = notify_limiter(State#ch.limiter, Acked), - incr_stats(Incs, ack, State). + ?INCR_STATS(Incs, ack, State). new_tx(State) -> State#ch{uncommitted_message_q = queue:new(), uncommitted_acks = [], @@ -1370,11 +1376,11 @@ deliver_to_queues({Delivery = #delivery{message = Message = #basic_message{ XName, MsgSeqNo, Message, State#ch{queue_names = QNames1, queue_monitors = QMons1}), - incr_stats([{exchange_stats, XName, 1} | - [{queue_exchange_stats, {QName, XName}, 1} || - QPid <- DeliveredQPids, - {ok, QName} <- [dict:find(QPid, QNames1)]]], - publish, State1), + ?INCR_STATS([{exchange_stats, XName, 1} | + [{queue_exchange_stats, {QName, XName}, 1} || + QPid <- DeliveredQPids, + {ok, QName} <- [dict:find(QPid, QNames1)]]], + publish, State1), State1. process_routing_result(routed, _, _, undefined, _, State) -> @@ -1386,7 +1392,7 @@ process_routing_result(routed, QPids, XName, MsgSeqNo, _, State) -> State#ch.unconfirmed)}; process_routing_result(unroutable, _, XName, MsgSeqNo, Msg, State) -> ok = basic_return(Msg, State, no_route), - incr_stats([{exchange_stats, XName, 1}], return_unroutable, State), + ?INCR_STATS([{exchange_stats, XName, 1}], return_unroutable, State), case MsgSeqNo of undefined -> State; _ -> record_confirms([{MsgSeqNo, XName}], State) @@ -1409,7 +1415,7 @@ send_confirms(State = #ch{tx_status = none, confirmed = C}) -> MsgSeqNos = lists:foldl( fun ({MsgSeqNo, XName}, MSNs) -> - incr_stats([{exchange_stats, XName, 1}], confirm, State), + ?INCR_STATS([{exchange_stats, XName, 1}], confirm, State), [MsgSeqNo | MSNs] end, [], lists:append(C)), send_confirms(MsgSeqNos, State#ch{confirmed = []}); @@ -1494,12 +1500,8 @@ i(Item, _) -> name(#ch{conn_name = ConnName, channel = Channel}) -> list_to_binary(rabbit_misc:format("~s (~p)", [ConnName, Channel])). -incr_stats(Incs, Measure, State) -> - case rabbit_event:stats_level(State, #ch.stats_timer) of - fine -> [update_measures(Type, Key, Inc, Measure) || - {Type, Key, Inc} <- Incs]; - _ -> ok - end. +incr_stats(Incs, Measure) -> + [update_measures(Type, Key, Inc, Measure) || {Type, Key, Inc} <- Incs]. update_measures(Type, Key, Inc, Measure) -> Measures = case get({Type, Key}) of -- cgit v1.2.1 From c17877b449ba4f6a39ae9dbb9c930a2b02c2c5ed Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sun, 30 Dec 2012 15:22:22 +0000 Subject: consistency --- src/rabbit_control_main.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index b4272555..819435ee 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -283,7 +283,7 @@ action(forget_cluster_node, Node, [ClusterNodeS], Opts, Inform) -> action(sync_queue, Node, [Queue], Opts, Inform) -> VHost = proplists:get_value(?VHOST_OPT, Opts), - Inform("Synchronising queue ~s in ~s", [Queue, VHost]), + Inform("Synchronising queue \"~s\" in vhost \"~s\"", [Queue, VHost]), rpc_call(Node, rabbit_amqqueue, sync, [list_to_binary(Queue), list_to_binary(VHost)]); -- cgit v1.2.1 From 261a7e89ff29e0d35957e3f97c833301bf34b1c6 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sun, 30 Dec 2012 15:52:33 +0000 Subject: correct docs to reflect idempotence --- docs/rabbitmqctl.1.xml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/rabbitmqctl.1.xml b/docs/rabbitmqctl.1.xml index 1583ab98..a95f7b3d 100644 --- a/docs/rabbitmqctl.1.xml +++ b/docs/rabbitmqctl.1.xml @@ -465,9 +465,8 @@ synchronise itself. The queue will block while synchronisation takes place (all publishers to and consumers from the queue will block). The queue must be - mirrored, must have unsynchronised slaves, and must not - have any pending unacknowledged messages for this - command to succeed. + mirrored, and must not have any pending unacknowledged + messages for this command to succeed. Note that unsynchronised queues from which messages are -- cgit v1.2.1 From 66033c0ae9b6c79ac34dcf6859dc31fd20b802e5 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sun, 30 Dec 2012 18:13:16 +0000 Subject: cosmetic(ish) refactors on mq_sync:master_send - more sensible arg order (a folding function should generally take the element first and the acc last) - somewhat neater handling of the acc --- src/rabbit_mirror_queue_sync.erl | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/rabbit_mirror_queue_sync.erl b/src/rabbit_mirror_queue_sync.erl index bb12cf49..f9502219 100644 --- a/src/rabbit_mirror_queue_sync.erl +++ b/src/rabbit_mirror_queue_sync.erl @@ -68,21 +68,20 @@ master_go(Syncer, Ref, Log, BQ, BQS) -> end. master_go0(Args, BQ, BQS) -> - case BQ:fold(fun (Msg, MsgProps, {I, Last}) -> - master_send(Args, I, Last, Msg, MsgProps) + case BQ:fold(fun (Msg, MsgProps, Acc) -> + master_send(Msg, MsgProps, Args, Acc) end, {0, erlang:now()}, BQS) of {{shutdown, Reason}, BQS1} -> {shutdown, Reason, BQS1}; {{sync_died, Reason}, BQS1} -> {sync_died, Reason, BQS1}; {_, BQS1} -> master_done(Args, BQS1) end. -master_send({Syncer, Ref, Log, Parent}, I, Last, Msg, MsgProps) -> - Acc = {I + 1, - case timer:now_diff(erlang:now(), Last) > ?SYNC_PROGRESS_INTERVAL of - true -> Log("~p messages", [I]), - erlang:now(); - false -> Last - end}, +master_send(Msg, MsgProps, {Syncer, Ref, Log, Parent}, {I, Last}) -> + T = case timer:now_diff(erlang:now(), Last) > ?SYNC_PROGRESS_INTERVAL of + true -> Log("~p messages", [I]), + erlang:now(); + false -> Last + end, receive {'$gen_cast', {set_maximum_since_use, Age}} -> ok = file_handle_cache:set_maximum_since_use(Age) @@ -91,7 +90,7 @@ master_send({Syncer, Ref, Log, Parent}, I, Last, Msg, MsgProps) -> end, receive {next, Ref} -> Syncer ! {msg, Ref, Msg, MsgProps}, - {cont, Acc}; + {cont, {I + 1, T}}; {'EXIT', Parent, Reason} -> {stop, {shutdown, Reason}}; {'EXIT', Syncer, Reason} -> {stop, {sync_died, Reason}} end. -- cgit v1.2.1 From 5c56a6702ca13de0bbc67daf5c9bcb098011dd42 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 1 Jan 2013 12:37:23 +0000 Subject: better fun names --- src/rabbit_amqqueue_process.erl | 28 ++++++++++++++-------------- src/rabbit_mirror_queue_master.erl | 4 ++-- src/rabbit_mirror_queue_sync.erl | 12 ++++++------ 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 26c0edbe..0cc686f4 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1163,22 +1163,22 @@ handle_call(sync_mirrors, _From, State = #q{backing_queue = rabbit_mirror_queue_master = BQ, backing_queue_state = BQS}) -> S = fun(BQSN) -> State#q{backing_queue_state = BQSN} end, - InfoPull = fun (Status) -> - receive {'$gen_call', From, {info, Items}} -> - Infos = infos(Items, State#q{status = Status}), - gen_server2:reply(From, {ok, Infos}) - after 0 -> - ok - end - end, - InfoPush = fun (Status) -> - rabbit_event:if_enabled( - State, #q.stats_timer, - fun() -> emit_stats(State#q{status = Status}) end) - end, + HandleInfo = fun (Status) -> + receive {'$gen_call', From, {info, Items}} -> + Infos = infos(Items, State#q{status = Status}), + gen_server2:reply(From, {ok, Infos}) + after 0 -> + ok + end + end, + EmitStats = fun (Status) -> + rabbit_event:if_enabled( + State, #q.stats_timer, + fun() -> emit_stats(State#q{status = Status}) end) + end, case BQ:depth(BQS) - BQ:len(BQS) of 0 -> case rabbit_mirror_queue_master:sync_mirrors( - InfoPull, InfoPush, BQS) of + HandleInfo, EmitStats, BQS) of {shutdown, Reason, BQS1} -> {stop, Reason, S(BQS1)}; {Result, BQS1} -> reply(Result, S(BQS1)) end; diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index 601649ef..db0308b7 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -127,7 +127,7 @@ stop_mirroring(State = #state { coordinator = CPid, stop_all_slaves(shutdown, State), {BQ, BQS}. -sync_mirrors(InfoPull, InfoPush, +sync_mirrors(HandleInfo, EmitStats, State = #state { name = QName, gm = GM, backing_queue = BQ, @@ -143,7 +143,7 @@ sync_mirrors(InfoPull, InfoPush, gm:broadcast(GM, {sync_start, Ref, Syncer, SPids}), S = fun(BQSN) -> State#state{backing_queue_state = BQSN} end, case rabbit_mirror_queue_sync:master_go( - Syncer, Ref, Log, InfoPull, InfoPush, BQ, BQS) of + Syncer, Ref, Log, HandleInfo, EmitStats, BQ, BQS) of {shutdown, R, BQS1} -> {stop, R, S(BQS1)}; {sync_died, R, BQS1} -> Log("~p", [R]), {ok, S(BQS1)}; diff --git a/src/rabbit_mirror_queue_sync.erl b/src/rabbit_mirror_queue_sync.erl index 5f0307fc..a1c54517 100644 --- a/src/rabbit_mirror_queue_sync.erl +++ b/src/rabbit_mirror_queue_sync.erl @@ -59,12 +59,12 @@ master_prepare(Ref, Log, SPids) -> MPid = self(), spawn_link(fun () -> syncer(Ref, Log, MPid, SPids) end). -master_go(Syncer, Ref, Log, InfoPull, InfoPush, BQ, BQS) -> - Args = {Syncer, Ref, Log, InfoPull, InfoPush, rabbit_misc:get_parent()}, +master_go(Syncer, Ref, Log, HandleInfo, EmitStats, BQ, BQS) -> + Args = {Syncer, Ref, Log, HandleInfo, EmitStats, rabbit_misc:get_parent()}, receive {'EXIT', Syncer, normal} -> {already_synced, BQS}; {'EXIT', Syncer, Reason} -> {sync_died, Reason, BQS}; - {ready, Syncer} -> InfoPush({syncing, 0}), + {ready, Syncer} -> EmitStats({syncing, 0}), master_go0(Args, BQ, BQS) end. @@ -77,15 +77,15 @@ master_go0(Args, BQ, BQS) -> {_, BQS1} -> master_done(Args, BQS1) end. -master_send(Msg, MsgProps, {Syncer, Ref, Log, InfoPull, InfoPush, Parent}, +master_send(Msg, MsgProps, {Syncer, Ref, Log, HandleInfo, EmitStats, Parent}, {I, Last}) -> T = case timer:now_diff(erlang:now(), Last) > ?SYNC_PROGRESS_INTERVAL of - true -> InfoPush({syncing, I}), + true -> EmitStats({syncing, I}), Log("~p messages", [I]), erlang:now(); false -> Last end, - InfoPull({syncing, I}), + HandleInfo({syncing, I}), receive {'$gen_cast', {set_maximum_since_use, Age}} -> ok = file_handle_cache:set_maximum_since_use(Age) -- cgit v1.2.1 From bd366efb9320c18f17ff2bfb229533c68233454a Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 1 Jan 2013 12:40:17 +0000 Subject: oops --- src/rabbit_mirror_queue_sync.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_mirror_queue_sync.erl b/src/rabbit_mirror_queue_sync.erl index a1c54517..e3edecb5 100644 --- a/src/rabbit_mirror_queue_sync.erl +++ b/src/rabbit_mirror_queue_sync.erl @@ -107,7 +107,7 @@ master_send(Msg, MsgProps, {Syncer, Ref, Log, HandleInfo, EmitStats, Parent}, {'EXIT', Syncer, Reason} -> {stop, {sync_died, Reason}} end. -master_done({Syncer, Ref, _Log, Parent}, BQS) -> +master_done({Syncer, Ref, _Log, _HandleInfo, _EmitStats, Parent}, BQS) -> receive {next, Ref} -> unlink(Syncer), Syncer ! {done, Ref}, -- cgit v1.2.1 From 4aa86fc039e0fdd7adf41b3b4e50b821c753a129 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 1 Jan 2013 12:44:45 +0000 Subject: refactor: extract stop_syncer function --- src/rabbit_mirror_queue_sync.erl | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/rabbit_mirror_queue_sync.erl b/src/rabbit_mirror_queue_sync.erl index e3edecb5..38a18e4a 100644 --- a/src/rabbit_mirror_queue_sync.erl +++ b/src/rabbit_mirror_queue_sync.erl @@ -94,11 +94,7 @@ master_send(Msg, MsgProps, {Syncer, Ref, Log, HandleInfo, EmitStats, Parent}, end, receive {'$gen_call', From, - cancel_sync_mirrors} -> unlink(Syncer), - Syncer ! {cancel, Ref}, - receive {'EXIT', Syncer, _} -> ok - after 0 -> ok - end, + cancel_sync_mirrors} -> stop_syncer(Syncer, {cancel, Ref}), gen_server2:reply(From, ok), {stop, cancelled}; {next, Ref} -> Syncer ! {msg, Ref, Msg, MsgProps}, @@ -109,16 +105,19 @@ master_send(Msg, MsgProps, {Syncer, Ref, Log, HandleInfo, EmitStats, Parent}, master_done({Syncer, Ref, _Log, _HandleInfo, _EmitStats, Parent}, BQS) -> receive - {next, Ref} -> unlink(Syncer), - Syncer ! {done, Ref}, - receive {'EXIT', Syncer, _} -> ok - after 0 -> ok - end, + {next, Ref} -> stop_syncer(Syncer, {done, Ref}), {ok, BQS}; {'EXIT', Parent, Reason} -> {shutdown, Reason, BQS}; {'EXIT', Syncer, Reason} -> {sync_died, Reason, BQS} end. +stop_syncer(Syncer, Msg) -> + unlink(Syncer), + Syncer ! Msg, + receive {'EXIT', Syncer, _} -> ok + after 0 -> ok + end. + %% Master %% --------------------------------------------------------------------------- %% Syncer -- cgit v1.2.1 From 3eeb54407bc7efe970e401d81fc1ab5f8a512c8e Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 1 Jan 2013 15:06:09 +0000 Subject: refactor: less passing around of State in dl publishing --- src/rabbit_amqqueue_process.erl | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 5feaf9bc..461b5a6d 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -758,8 +758,8 @@ dead_letter_fun(Reason) -> gen_server2:cast(self(), {dead_letter, Msg, AckTag, Reason}) end. -dead_letter_publish(Msg, Reason, X, State = #q{publish_seqno = MsgSeqNo}) -> - DLMsg = make_dead_letter_msg(Reason, Msg, State), +dead_letter_publish(Msg, Reason, X, RK, MsgSeqNo, QName) -> + DLMsg = make_dead_letter_msg(Msg, Reason, X#exchange.name, RK, QName), Delivery = rabbit_basic:delivery(false, DLMsg, MsgSeqNo), {Queues, Cycles} = detect_dead_letter_cycles( DLMsg, rabbit_exchange:route(X, Delivery)), @@ -838,19 +838,16 @@ detect_dead_letter_cycles(#basic_message{content = Content}, Queues) -> end end. -make_dead_letter_msg(Reason, - Msg = #basic_message{content = Content, +make_dead_letter_msg(Msg = #basic_message{content = Content, exchange_name = Exchange, routing_keys = RoutingKeys}, - State = #q{dlx = DLX, dlx_routing_key = DlxRoutingKey}) -> + Reason, DLX, RK, #resource{name = QName}) -> {DeathRoutingKeys, HeadersFun1} = - case DlxRoutingKey of + case RK of undefined -> {RoutingKeys, fun (H) -> H end}; - _ -> {[DlxRoutingKey], - fun (H) -> lists:keydelete(<<"CC">>, 1, H) end} + _ -> {[RK], fun (H) -> lists:keydelete(<<"CC">>, 1, H) end} end, ReasonBin = list_to_binary(atom_to_list(Reason)), - #resource{name = QName} = qname(State), TimeSec = rabbit_misc:now_ms() div 1000, HeadersFun2 = fun (Headers) -> @@ -1251,13 +1248,14 @@ handle_cast({set_maximum_since_use, Age}, State) -> noreply(State); handle_cast({dead_letter, Msg, AckTag, Reason}, - State = #q{dlx = XName, - publish_seqno = SeqNo, - unconfirmed = UC, - queue_monitors = QMons}) -> + State = #q{dlx = XName, + dlx_routing_key = RK, + publish_seqno = SeqNo, + unconfirmed = UC, + queue_monitors = QMons}) -> case rabbit_exchange:lookup(XName) of {ok, X} -> - case dead_letter_publish(Msg, Reason, X, State) of + case dead_letter_publish(Msg, Reason, X, RK, SeqNo, qname(State)) of [] -> cleanup_after_confirm([AckTag], State); QPids -> UC1 = dtree:insert(SeqNo, QPids, AckTag, UC), QMons1 = pmon:monitor_all(QPids, QMons), -- cgit v1.2.1 From 37ef7fd3b3621fe7c91a67c325a4678cb579aeca Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 1 Jan 2013 16:22:57 +0000 Subject: don't send expired messages to self() during bulk expiry since they all end up in the mailbox, consuming memory --- src/rabbit_amqqueue_process.erl | 47 ++++++++++++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 461b5a6d..057d9391 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -716,23 +716,46 @@ drop_expired_messages(State = #q{dlx = DLX, backing_queue = BQ }) -> Now = now_micros(), ExpirePred = fun (#message_properties{expiry = Exp}) -> Now >= Exp end, - {Props, BQS1} = + {Props, State1} = case DLX of - undefined -> BQ:dropwhile(ExpirePred, BQS); - _ -> DLXFun = dead_letter_fun(expired), - {Next, ok, BQS2} = - BQ:fetchwhile( - ExpirePred, - fun (Msg, _IsDelivered, AckTag, Acc) -> - DLXFun(Msg, AckTag), - Acc - end, ok, BQS), - {Next, BQS2} + undefined -> {Next, BQS1} = BQ:dropwhile(ExpirePred, BQS), + {Next, State#q{backing_queue_state = BQS1}}; + _ -> case rabbit_exchange:lookup(DLX) of + {ok, X} -> + drop_expired_messages(ExpirePred, X, State); + {error, not_found} -> + {Next, BQS1} = BQ:dropwhile(ExpirePred, BQS), + {Next, State#q{backing_queue_state = BQS1}} + end end, ensure_ttl_timer(case Props of undefined -> undefined; #message_properties{expiry = Exp} -> Exp - end, State#q{backing_queue_state = BQS1}). + end, State1). + +drop_expired_messages(ExpirePred, X, State = #q{dlx_routing_key = RK, + publish_seqno = SeqNo0, + unconfirmed = UC0, + queue_monitors = QMons0, + backing_queue_state = BQS, + backing_queue = BQ}) -> + QName = qname(State), + {Next, {ConfirmImm1, SeqNo1, UC1, QMons1}, BQS1} = + BQ:fetchwhile( + ExpirePred, + fun (Msg, _IsDelivered, AckTag, {ConfirmImm, SeqNo, UC, QMons}) -> + case dead_letter_publish(Msg, expired, X, RK, SeqNo, QName) of + [] -> {[AckTag | ConfirmImm], SeqNo, UC, QMons}; + QPids -> {ConfirmImm, SeqNo + 1, + dtree:insert(SeqNo, QPids, AckTag, UC), + pmon:monitor_all(QPids, QMons)} + end + end, {[], SeqNo0, UC0, QMons0}, BQS), + {_Guids, BQS2} = BQ:ack(ConfirmImm1, BQS1), + {Next, State#q{publish_seqno = SeqNo1, + unconfirmed = UC1, + queue_monitors = QMons1, + backing_queue_state = BQS2}}. ensure_ttl_timer(undefined, State) -> State; -- cgit v1.2.1 From 75d9df8f53a5dfe92386b65ac21e7c1789532ed2 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 1 Jan 2013 18:56:06 +0000 Subject: replace bq:foreach_ack with a more versatile ackfold --- src/rabbit_amqqueue_process.erl | 6 ++++-- src/rabbit_backing_queue.erl | 20 +++++++++----------- src/rabbit_mirror_queue_master.erl | 15 ++++++++------- src/rabbit_tests.erl | 4 ++-- src/rabbit_variable_queue.erl | 24 ++++++++++++------------ 5 files changed, 35 insertions(+), 34 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 781546af..2ee5122c 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1202,8 +1202,10 @@ handle_cast({reject, AckTags, false, ChPid}, State) -> ChPid, AckTags, State, fun (State1 = #q{backing_queue = BQ, backing_queue_state = BQS}) -> - BQS1 = BQ:foreach_ack(fun(M, A) -> DLXFun([{M, A}]) end, - BQS, AckTags), + {ok, BQS1} = BQ:ackfold( + fun (Msg, _IsDelivered, AckTag, ok) -> + DLXFun([{Msg, AckTag}]) + end, ok, BQS, AckTags), State1#q{backing_queue_state = BQS1} end)); diff --git a/src/rabbit_backing_queue.erl b/src/rabbit_backing_queue.erl index 272df5c1..da7ff10d 100644 --- a/src/rabbit_backing_queue.erl +++ b/src/rabbit_backing_queue.erl @@ -35,8 +35,8 @@ fun ((atom(), fun ((atom(), state()) -> state())) -> 'ok')). -type(duration() :: ('undefined' | 'infinity' | number())). --type(msg_fun() :: fun((rabbit_types:basic_message(), ack()) -> 'ok') | - 'undefined'). +-type(msg_fun(A) :: fun ((rabbit_types:basic_message(), boolean(), ack(), A) + -> A)). -type(msg_pred() :: fun ((rabbit_types:message_properties()) -> boolean())). %% Called on startup with a list of durable queue names. The queues @@ -137,10 +137,7 @@ %% flag and ack tag, to the supplied function. The function is also %% fed an accumulator. The result of fetchwhile is as for dropwhile %% plus the accumulator. --callback fetchwhile(msg_pred(), - fun ((rabbit_types:basic_message(), boolean(), ack(), A) - -> A), - A, state()) +-callback fetchwhile(msg_pred(), msg_fun(A), A, state()) -> {rabbit_types:message_properties() | undefined, A, state()}. @@ -156,14 +153,15 @@ %% about. Must return 1 msg_id per Ack, in the same order as Acks. -callback ack([ack()], state()) -> {msg_ids(), state()}. -%% Acktags supplied are for messages which should be processed. The -%% provided callback function is called with each message. --callback foreach_ack(msg_fun(), state(), [ack()]) -> state(). - %% Reinsert messages into the queue which have already been delivered %% and were pending acknowledgement. -callback requeue([ack()], state()) -> {msg_ids(), state()}. +%% Fold over messages by ack tag. The supplied function is called with +%% each message, its IsDelivered flag, its ack tag, and an +%% accumulator. +-callback ackfold(msg_fun(A), A, state(), [ack()]) -> {A, state()}. + %% Fold over all the messages in a queue and return the accumulated %% results, leaving the queue undisturbed. -callback fold(fun((rabbit_types:basic_message(), @@ -233,7 +231,7 @@ behaviour_info(callbacks) -> {delete_and_terminate, 2}, {purge, 1}, {publish, 5}, {publish_delivered, 4}, {discard, 3}, {drain_confirmed, 1}, {dropwhile, 2}, {fetchwhile, 4}, - {fetch, 2}, {ack, 2}, {foreach_ack, 3}, {requeue, 2}, {fold, 3}, {len, 1}, + {fetch, 2}, {ack, 2}, {requeue, 2}, {ackfold, 4}, {fold, 3}, {len, 1}, {is_empty, 1}, {depth, 1}, {set_ram_duration_target, 2}, {ram_duration, 1}, {needs_timeout, 1}, {timeout, 1}, {handle_pre_hibernate, 1}, {status, 1}, {invoke, 3}, {is_duplicate, 2}] ; diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index e3d967bc..e857f395 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -18,11 +18,11 @@ -export([init/3, terminate/2, delete_and_terminate/2, purge/1, publish/5, publish_delivered/4, - discard/3, fetch/2, drop/2, ack/2, - requeue/2, fold/3, len/1, is_empty/1, depth/1, drain_confirmed/1, + discard/3, fetch/2, drop/2, ack/2, requeue/2, ackfold/4, fold/3, + len/1, is_empty/1, depth/1, drain_confirmed/1, dropwhile/2, fetchwhile/4, set_ram_duration_target/2, ram_duration/1, needs_timeout/1, timeout/1, handle_pre_hibernate/1, - status/1, invoke/3, is_duplicate/2, foreach_ack/3]). + status/1, invoke/3, is_duplicate/2]). -export([start/1, stop/0]). @@ -281,10 +281,6 @@ ack(AckTags, State = #state { gm = GM, end, {MsgIds, State #state { backing_queue_state = BQS1 }}. -foreach_ack(MsgFun, State = #state { backing_queue = BQ, - backing_queue_state = BQS }, AckTags) -> - State #state { backing_queue_state = BQ:foreach_ack(MsgFun, BQS, AckTags) }. - requeue(AckTags, State = #state { gm = GM, backing_queue = BQ, backing_queue_state = BQS }) -> @@ -292,6 +288,11 @@ requeue(AckTags, State = #state { gm = GM, ok = gm:broadcast(GM, {requeue, MsgIds}), {MsgIds, State #state { backing_queue_state = BQS1 }}. +ackfold(MsgFun, Acc, State = #state { backing_queue = BQ, + backing_queue_state = BQS }, AckTags) -> + {Acc1, BQS1} = BQ:ackfold(MsgFun, Acc, BQS, AckTags), + {Acc1, State #state { backing_queue_state = BQS1 }}. + fold(Fun, Acc, State = #state { backing_queue = BQ, backing_queue_state = BQS }) -> {Result, BQS1} = BQ:fold(Fun, Acc, BQS), diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index b499c59b..30606fdb 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -2608,8 +2608,8 @@ test_variable_queue_all_the_bits_not_covered_elsewhere2(VQ0) -> test_variable_queue_fold_msg_on_disk(VQ0) -> VQ1 = variable_queue_publish(true, 1, VQ0), {VQ2, AckTags} = variable_queue_fetch(1, true, false, 1, VQ1), - VQ3 = rabbit_variable_queue:foreach_ack(fun (_M, _A) -> ok end, - VQ2, AckTags), + {ok, VQ3} = rabbit_variable_queue:ackfold(fun (_M, _D, _A, ok) -> ok end, + ok, VQ2, AckTags), VQ3. test_queue_recover() -> diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 3e4c7c86..ce43200d 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -19,10 +19,10 @@ -export([init/3, terminate/2, delete_and_terminate/2, purge/1, publish/5, publish_delivered/4, discard/3, drain_confirmed/1, dropwhile/2, fetchwhile/4, - fetch/2, drop/2, ack/2, requeue/2, fold/3, len/1, + fetch/2, drop/2, ack/2, requeue/2, ackfold/4, fold/3, len/1, is_empty/1, depth/1, set_ram_duration_target/2, ram_duration/1, needs_timeout/1, timeout/1, handle_pre_hibernate/1, status/1, invoke/3, - is_duplicate/2, multiple_routing_keys/0, foreach_ack/3]). + is_duplicate/2, multiple_routing_keys/0]). -export([start/1, stop/0]). @@ -650,16 +650,6 @@ ack(AckTags, State) -> persistent_count = PCount1, ack_out_counter = AckOutCount + length(AckTags) })}. -foreach_ack(undefined, State, _AckTags) -> - State; -foreach_ack(MsgFun, State = #vqstate{pending_ack = PA}, AckTags) -> - a(lists:foldl(fun(SeqId, State1) -> - {MsgStatus, State2} = - read_msg(gb_trees:get(SeqId, PA), false, State1), - MsgFun(MsgStatus#msg_status.msg, SeqId), - State2 - end, State, AckTags)). - requeue(AckTags, #vqstate { delta = Delta, q3 = Q3, q4 = Q4, @@ -681,6 +671,16 @@ requeue(AckTags, #vqstate { delta = Delta, in_counter = InCounter + MsgCount, len = Len + MsgCount }))}. +ackfold(MsgFun, Acc, State, AckTags) -> + {AccN, StateN} = + lists:foldl( + fun(SeqId, {Acc0, State0 = #vqstate{ pending_ack = PA }}) -> + {#msg_status { msg = Msg, is_delivered = IsDelivered }, + State1 } = read_msg(gb_trees:get(SeqId, PA), false, State0), + {MsgFun(Msg, IsDelivered, SeqId, Acc0), State1} + end, {Acc, State}, AckTags), + {AccN, a(StateN)}. + fold(Fun, Acc, #vqstate { q1 = Q1, q2 = Q2, delta = #delta { start_seq_id = DeltaSeqId, -- cgit v1.2.1 From 0c603bc18f3276b7e1a72712f63f32aeb6de35e0 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 1 Jan 2013 19:55:38 +0000 Subject: drop IsDelivered from bq:{fetchwhile,ackfold} since we don't need it --- src/rabbit_amqqueue_process.erl | 9 ++++----- src/rabbit_backing_queue.erl | 14 ++++++-------- src/rabbit_tests.erl | 6 +++--- src/rabbit_variable_queue.erl | 11 +++++------ 4 files changed, 18 insertions(+), 22 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 2ee5122c..b5ad1ac0 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -720,7 +720,7 @@ drop_expired_messages(State = #q{dlx = DLX, undefined -> BQ:dropwhile(ExpirePred, BQS); _ -> {Next, Msgs, BQS2} = BQ:fetchwhile(ExpirePred, - fun accumulate_msgs/4, + fun accumulate_msgs/3, [], BQS), case Msgs of [] -> ok; @@ -734,7 +734,7 @@ drop_expired_messages(State = #q{dlx = DLX, #message_properties{expiry = Exp} -> Exp end, State#q{backing_queue_state = BQS1}). -accumulate_msgs(Msg, _IsDelivered, AckTag, Acc) -> [{Msg, AckTag} | Acc]. +accumulate_msgs(Msg, AckTag, Acc) -> [{Msg, AckTag} | Acc]. ensure_ttl_timer(undefined, State) -> State; @@ -1203,9 +1203,8 @@ handle_cast({reject, AckTags, false, ChPid}, State) -> fun (State1 = #q{backing_queue = BQ, backing_queue_state = BQS}) -> {ok, BQS1} = BQ:ackfold( - fun (Msg, _IsDelivered, AckTag, ok) -> - DLXFun([{Msg, AckTag}]) - end, ok, BQS, AckTags), + fun (M, A, ok) -> DLXFun([{M, A}]) end, + ok, BQS, AckTags), State1#q{backing_queue_state = BQS1} end)); diff --git a/src/rabbit_backing_queue.erl b/src/rabbit_backing_queue.erl index da7ff10d..99b5946e 100644 --- a/src/rabbit_backing_queue.erl +++ b/src/rabbit_backing_queue.erl @@ -35,8 +35,7 @@ fun ((atom(), fun ((atom(), state()) -> state())) -> 'ok')). -type(duration() :: ('undefined' | 'infinity' | number())). --type(msg_fun(A) :: fun ((rabbit_types:basic_message(), boolean(), ack(), A) - -> A)). +-type(msg_fun(A) :: fun ((rabbit_types:basic_message(), ack(), A) -> A)). -type(msg_pred() :: fun ((rabbit_types:message_properties()) -> boolean())). %% Called on startup with a list of durable queue names. The queues @@ -133,10 +132,10 @@ -> {rabbit_types:message_properties() | undefined, state()}. %% Like dropwhile, except messages are fetched in "require -%% acknowledgement" mode and are passed, together with their Delivered -%% flag and ack tag, to the supplied function. The function is also -%% fed an accumulator. The result of fetchwhile is as for dropwhile -%% plus the accumulator. +%% acknowledgement" mode and are passed, together with their ack tag, +%% to the supplied function. The function is also fed an +%% accumulator. The result of fetchwhile is as for dropwhile plus the +%% accumulator. -callback fetchwhile(msg_pred(), msg_fun(A), A, state()) -> {rabbit_types:message_properties() | undefined, A, state()}. @@ -158,8 +157,7 @@ -callback requeue([ack()], state()) -> {msg_ids(), state()}. %% Fold over messages by ack tag. The supplied function is called with -%% each message, its IsDelivered flag, its ack tag, and an -%% accumulator. +%% each message, its ack tag, and an accumulator. -callback ackfold(msg_fun(A), A, state(), [ack()]) -> {A, state()}. %% Fold over all the messages in a queue and return the accumulated diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 30606fdb..09ed3d08 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -2434,7 +2434,7 @@ test_dropfetchwhile(VQ0) -> {#message_properties{expiry = 6}, {Msgs, AckTags}, VQ2} = rabbit_variable_queue:fetchwhile( fun (#message_properties{expiry = Expiry}) -> Expiry =< 5 end, - fun (Msg, _Delivered, AckTag, {MsgAcc, AckAcc}) -> + fun (Msg, AckTag, {MsgAcc, AckAcc}) -> {[Msg | MsgAcc], [AckTag | AckAcc]} end, {[], []}, VQ1), true = lists:seq(1, 5) == [msg2int(M) || M <- lists:reverse(Msgs)], @@ -2473,7 +2473,7 @@ test_fetchwhile_varying_ram_duration(VQ0) -> fun (VQ1) -> {_, ok, VQ2} = rabbit_variable_queue:fetchwhile( fun (_) -> false end, - fun (_, _, _, A) -> A end, + fun (_, _, A) -> A end, ok, VQ1), VQ2 end, VQ0). @@ -2608,7 +2608,7 @@ test_variable_queue_all_the_bits_not_covered_elsewhere2(VQ0) -> test_variable_queue_fold_msg_on_disk(VQ0) -> VQ1 = variable_queue_publish(true, 1, VQ0), {VQ2, AckTags} = variable_queue_fetch(1, true, false, 1, VQ1), - {ok, VQ3} = rabbit_variable_queue:ackfold(fun (_M, _D, _A, ok) -> ok end, + {ok, VQ3} = rabbit_variable_queue:ackfold(fun (_M, _A, ok) -> ok end, ok, VQ2, AckTags), VQ3. diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index ce43200d..05468a6e 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -597,10 +597,9 @@ fetchwhile(Pred, Fun, Acc, State) -> {{value, MsgStatus = #msg_status { msg_props = MsgProps }}, State1} -> case Pred(MsgProps) of true -> {MsgStatus1, State2} = read_msg(MsgStatus, State1), - {{Msg, IsDelivered, AckTag}, State3} = + {{Msg, _IsDelivered, AckTag}, State3} = internal_fetch(true, MsgStatus1, State2), - Acc1 = Fun(Msg, IsDelivered, AckTag, Acc), - fetchwhile(Pred, Fun, Acc1, State3); + fetchwhile(Pred, Fun, Fun(Msg, AckTag, Acc), State3); false -> {MsgProps, Acc, a(in_r(MsgStatus, State1))} end end. @@ -675,9 +674,9 @@ ackfold(MsgFun, Acc, State, AckTags) -> {AccN, StateN} = lists:foldl( fun(SeqId, {Acc0, State0 = #vqstate{ pending_ack = PA }}) -> - {#msg_status { msg = Msg, is_delivered = IsDelivered }, - State1 } = read_msg(gb_trees:get(SeqId, PA), false, State0), - {MsgFun(Msg, IsDelivered, SeqId, Acc0), State1} + {#msg_status { msg = Msg }, State1} = + read_msg(gb_trees:get(SeqId, PA), false, State0), + {MsgFun(Msg, SeqId, Acc0), State1} end, {Acc, State}, AckTags), {AccN, a(StateN)}. -- cgit v1.2.1 From 36ab39f88197ec18898b4568736def82af6deced Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 1 Jan 2013 20:24:36 +0000 Subject: refactor: rename drop_expired_messages to drop_expired_msgs --- src/rabbit_amqqueue_process.erl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 781546af..d2a2769b 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -262,7 +262,7 @@ process_args(State = #q{q = #amqqueue{arguments = Arguments}}) -> init_expires(Expires, State) -> ensure_expiry_timer(State#q{expires = Expires}). -init_ttl(TTL, State) -> drop_expired_messages(State#q{ttl = TTL}). +init_ttl(TTL, State) -> drop_expired_msgs(State#q{ttl = TTL}). init_dlx(DLX, State = #q{q = #amqqueue{name = QName}}) -> State#q{dlx = rabbit_misc:r(QName, exchange, DLX)}. @@ -479,7 +479,7 @@ deliver_msg_to_consumer(DeliverFun, deliver_from_queue_deliver(AckRequired, State) -> {Result, State1} = fetch(AckRequired, State), State2 = #q{backing_queue = BQ, backing_queue_state = BQS} = - drop_expired_messages(State1), + drop_expired_msgs(State1), {Result, BQ:is_empty(BQS), State2}. confirm_messages([], State) -> @@ -526,7 +526,7 @@ discard(#delivery{sender = SenderPid, message = #basic_message{id = MsgId}}, run_message_queue(State) -> State1 = #q{backing_queue = BQ, backing_queue_state = BQS} = - drop_expired_messages(State), + drop_expired_msgs(State), {_IsEmpty1, State2} = deliver_msgs_to_consumers( fun deliver_from_queue_deliver/2, BQ:is_empty(BQS), State1), @@ -711,7 +711,7 @@ calculate_msg_expiry(#basic_message{content = Content}, TTL) -> T -> now_micros() + T * 1000 end. -drop_expired_messages(State = #q{dlx = DLX, +drop_expired_msgs(State = #q{dlx = DLX, backing_queue_state = BQS, backing_queue = BQ }) -> Now = now_micros(), @@ -1050,7 +1050,7 @@ handle_call({basic_get, ChPid, NoAck}, _From, State = #q{q = #amqqueue{name = QName}}) -> AckRequired = not NoAck, State1 = ensure_expiry_timer(State), - case fetch(AckRequired, drop_expired_messages(State1)) of + case fetch(AckRequired, drop_expired_msgs(State1)) of {empty, State2} -> reply(empty, State2); {{Message, IsDelivered, AckTag}, State2} -> @@ -1123,7 +1123,7 @@ handle_call({basic_cancel, ChPid, ConsumerTag, OkMsg}, From, handle_call(stat, _From, State) -> State1 = #q{backing_queue = BQ, backing_queue_state = BQS} = - drop_expired_messages(ensure_expiry_timer(State)), + drop_expired_msgs(ensure_expiry_timer(State)), reply({ok, BQ:len(BQS), active_consumer_count()}, State1); handle_call({delete, IfUnused, IfEmpty}, From, @@ -1312,7 +1312,7 @@ handle_info(maybe_expire, State) -> end; handle_info(drop_expired, State) -> - noreply(drop_expired_messages(State#q{ttl_timer_ref = undefined})); + noreply(drop_expired_msgs(State#q{ttl_timer_ref = undefined})); handle_info(emit_stats, State) -> emit_stats(State), -- cgit v1.2.1 From 90be903d63138c5c18ba7f95ed9458876e176259 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 1 Jan 2013 20:25:53 +0000 Subject: cosmetic --- src/rabbit_amqqueue_process.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index d2a2769b..ce3b4ce8 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -712,8 +712,8 @@ calculate_msg_expiry(#basic_message{content = Content}, TTL) -> end. drop_expired_msgs(State = #q{dlx = DLX, - backing_queue_state = BQS, - backing_queue = BQ }) -> + backing_queue_state = BQS, + backing_queue = BQ }) -> Now = now_micros(), ExpirePred = fun (#message_properties{expiry = Exp}) -> Now >= Exp end, {Props, BQS1} = case DLX of -- cgit v1.2.1 From 2ae774b2f225b08ff9b1240d70f0d62c948e3362 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 1 Jan 2013 20:46:17 +0000 Subject: rename --- src/rabbit_amqqueue_process.erl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 6b8f8c61..b908361c 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -722,7 +722,7 @@ drop_expired_msgs(State = #q{dlx = DLX, {Next, State#q{backing_queue_state = BQS1}}; _ -> case rabbit_exchange:lookup(DLX) of {ok, X} -> - drop_expired_messages(ExpirePred, X, State); + dead_letter_expired_msgs(ExpirePred, X, State); {error, not_found} -> {Next, BQS1} = BQ:dropwhile(ExpirePred, BQS), {Next, State#q{backing_queue_state = BQS1}} @@ -733,12 +733,12 @@ drop_expired_msgs(State = #q{dlx = DLX, #message_properties{expiry = Exp} -> Exp end, State1). -drop_expired_messages(ExpirePred, X, State = #q{dlx_routing_key = RK, - publish_seqno = SeqNo0, - unconfirmed = UC0, - queue_monitors = QMons0, - backing_queue_state = BQS, - backing_queue = BQ}) -> +dead_letter_expired_msgs(ExpirePred, X, State = #q{dlx_routing_key = RK, + publish_seqno = SeqNo0, + unconfirmed = UC0, + queue_monitors = QMons0, + backing_queue_state = BQS, + backing_queue = BQ}) -> QName = qname(State), {Next, {ConfirmImm1, SeqNo1, UC1, QMons1}, BQS1} = BQ:fetchwhile( -- cgit v1.2.1 From de7441ce679272f8f7200b768c00cf1e06996823 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 1 Jan 2013 21:41:29 +0000 Subject: don't send dead-lettered messages to self() during 'reject' handling since they all end up in the mailbox, consuming memory --- src/rabbit_amqqueue_process.erl | 69 ++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 36 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index b908361c..8e20f4e1 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -757,6 +757,29 @@ dead_letter_expired_msgs(ExpirePred, X, State = #q{dlx_routing_key = RK, queue_monitors = QMons1, backing_queue_state = BQS2}}. +dead_letter_rejected_msgs(AckTags, X, State = #q{dlx_routing_key = RK, + publish_seqno = SeqNo0, + unconfirmed = UC0, + queue_monitors = QMons0, + backing_queue_state = BQS, + backing_queue = BQ}) -> + QName = qname(State), + {{ConfirmImm1, SeqNo1, UC1, QMons1}, BQS1} = + BQ:ackfold( + fun (Msg, AckTag, {ConfirmImm, SeqNo, UC, QMons}) -> + case dead_letter_publish(Msg, rejected, X, RK, SeqNo, QName) of + [] -> {[AckTag | ConfirmImm], SeqNo, UC, QMons}; + QPids -> {ConfirmImm, SeqNo + 1, + dtree:insert(SeqNo, QPids, AckTag, UC), + pmon:monitor_all(QPids, QMons)} + end + end, {[], SeqNo0, UC0, QMons0}, BQS, AckTags), + {_Guids, BQS2} = BQ:ack(ConfirmImm1, BQS1), + State#q{publish_seqno = SeqNo1, + unconfirmed = UC1, + queue_monitors = QMons1, + backing_queue_state = BQS2}. + ensure_ttl_timer(undefined, State) -> State; ensure_ttl_timer(Expiry, State = #q{ttl_timer_ref = undefined}) -> @@ -776,11 +799,6 @@ ensure_ttl_timer(Expiry, State = #q{ttl_timer_ref = TRef, ensure_ttl_timer(_Expiry, State) -> State. -dead_letter_fun(Reason) -> - fun(Msg, AckTag) -> - gen_server2:cast(self(), {dead_letter, Msg, AckTag, Reason}) - end. - dead_letter_publish(Msg, Reason, X, RK, MsgSeqNo, QName) -> DLMsg = make_dead_letter_msg(Msg, Reason, X#exchange.name, RK, QName), Delivery = rabbit_basic:delivery(false, DLMsg, MsgSeqNo), @@ -1216,17 +1234,16 @@ handle_cast({reject, AckTags, true, ChPid}, State) -> handle_cast({reject, AckTags, false, ChPid}, State = #q{dlx = undefined}) -> noreply(ack(AckTags, ChPid, State)); -handle_cast({reject, AckTags, false, ChPid}, State) -> - DLXFun = dead_letter_fun(rejected), - noreply(subtract_acks( - ChPid, AckTags, State, - fun (State1 = #q{backing_queue = BQ, - backing_queue_state = BQS}) -> - {ok, BQS1} = BQ:ackfold( - fun (M, A, ok) -> DLXFun([{M, A}]) end, - ok, BQS, AckTags), - State1#q{backing_queue_state = BQS1} - end)); +handle_cast({reject, AckTags, false, ChPid}, State = #q{dlx = DLX}) -> + noreply(case rabbit_exchange:lookup(DLX) of + {ok, X} -> subtract_acks( + ChPid, AckTags, State, + fun (State1) -> + dead_letter_rejected_msgs( + AckTags, X, State1) + end); + {error, not_found} -> ack(AckTags, ChPid, State) + end); handle_cast(delete_immediately, State) -> stop(State); @@ -1272,26 +1289,6 @@ handle_cast({set_maximum_since_use, Age}, State) -> ok = file_handle_cache:set_maximum_since_use(Age), noreply(State); -handle_cast({dead_letter, Msg, AckTag, Reason}, - State = #q{dlx = XName, - dlx_routing_key = RK, - publish_seqno = SeqNo, - unconfirmed = UC, - queue_monitors = QMons}) -> - case rabbit_exchange:lookup(XName) of - {ok, X} -> - case dead_letter_publish(Msg, Reason, X, RK, SeqNo, qname(State)) of - [] -> cleanup_after_confirm([AckTag], State); - QPids -> UC1 = dtree:insert(SeqNo, QPids, AckTag, UC), - QMons1 = pmon:monitor_all(QPids, QMons), - State#q{publish_seqno = SeqNo + 1, - unconfirmed = UC1, - queue_monitors = QMons1} - end; - {error, not_found} -> - cleanup_after_confirm([AckTag], State) - end; - handle_cast(start_mirroring, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> %% lookup again to get policy for init_with_existing_bq -- cgit v1.2.1 From 5cf5346c7cf124b13f18dec81ba57d5e12983a75 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 1 Jan 2013 22:48:49 +0000 Subject: refactor: extract dead lettering commonality --- src/rabbit_amqqueue_process.erl | 80 ++++++++++++++++++----------------------- 1 file changed, 35 insertions(+), 45 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 8e20f4e1..66e48024 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -733,52 +733,42 @@ drop_expired_msgs(State = #q{dlx = DLX, #message_properties{expiry = Exp} -> Exp end, State1). -dead_letter_expired_msgs(ExpirePred, X, State = #q{dlx_routing_key = RK, - publish_seqno = SeqNo0, - unconfirmed = UC0, - queue_monitors = QMons0, - backing_queue_state = BQS, - backing_queue = BQ}) -> +dead_letter_expired_msgs(ExpirePred, X, State = #q{backing_queue = BQ}) -> + dead_letter_msgs(fun (DLFun, Acc, BQS1) -> + BQ:fetchwhile(ExpirePred, DLFun, Acc, BQS1) + end, expired, X, State). + +dead_letter_rejected_msgs(AckTags, X, State = #q{backing_queue = BQ}) -> + {ok, State1} = + dead_letter_msgs( + fun (DLFun, Acc, BQS) -> + {Acc1, BQS1} = BQ:ackfold(DLFun, Acc, BQS, AckTags), + {ok, Acc1, BQS1} + end, rejected, X, State), + State1. + +dead_letter_msgs(Fun, Reason, X, State = #q{dlx_routing_key = RK, + publish_seqno = SeqNo0, + unconfirmed = UC0, + queue_monitors = QMons0, + backing_queue_state = BQS, + backing_queue = BQ}) -> QName = qname(State), - {Next, {ConfirmImm1, SeqNo1, UC1, QMons1}, BQS1} = - BQ:fetchwhile( - ExpirePred, - fun (Msg, AckTag, {ConfirmImm, SeqNo, UC, QMons}) -> - case dead_letter_publish(Msg, expired, X, RK, SeqNo, QName) of - [] -> {[AckTag | ConfirmImm], SeqNo, UC, QMons}; - QPids -> {ConfirmImm, SeqNo + 1, - dtree:insert(SeqNo, QPids, AckTag, UC), - pmon:monitor_all(QPids, QMons)} - end - end, {[], SeqNo0, UC0, QMons0}, BQS), - {_Guids, BQS2} = BQ:ack(ConfirmImm1, BQS1), - {Next, State#q{publish_seqno = SeqNo1, - unconfirmed = UC1, - queue_monitors = QMons1, - backing_queue_state = BQS2}}. - -dead_letter_rejected_msgs(AckTags, X, State = #q{dlx_routing_key = RK, - publish_seqno = SeqNo0, - unconfirmed = UC0, - queue_monitors = QMons0, - backing_queue_state = BQS, - backing_queue = BQ}) -> - QName = qname(State), - {{ConfirmImm1, SeqNo1, UC1, QMons1}, BQS1} = - BQ:ackfold( - fun (Msg, AckTag, {ConfirmImm, SeqNo, UC, QMons}) -> - case dead_letter_publish(Msg, rejected, X, RK, SeqNo, QName) of - [] -> {[AckTag | ConfirmImm], SeqNo, UC, QMons}; - QPids -> {ConfirmImm, SeqNo + 1, - dtree:insert(SeqNo, QPids, AckTag, UC), - pmon:monitor_all(QPids, QMons)} - end - end, {[], SeqNo0, UC0, QMons0}, BQS, AckTags), - {_Guids, BQS2} = BQ:ack(ConfirmImm1, BQS1), - State#q{publish_seqno = SeqNo1, - unconfirmed = UC1, - queue_monitors = QMons1, - backing_queue_state = BQS2}. + {Res, {AckImm1, SeqNo1, UC1, QMons1}, BQS1} = + Fun(fun (Msg, AckTag, {AckImm, SeqNo, UC, QMons}) -> + case dead_letter_publish(Msg, Reason, + X, RK, SeqNo, QName) of + [] -> {[AckTag | AckImm], SeqNo, UC, QMons}; + QPids -> {AckImm, SeqNo + 1, + dtree:insert(SeqNo, QPids, AckTag, UC), + pmon:monitor_all(QPids, QMons)} + end + end, {[], SeqNo0, UC0, QMons0}, BQS), + {_Guids, BQS2} = BQ:ack(AckImm1, BQS1), + {Res, State#q{publish_seqno = SeqNo1, + unconfirmed = UC1, + queue_monitors = QMons1, + backing_queue_state = BQS2}}. ensure_ttl_timer(undefined, State) -> State; -- cgit v1.2.1 From 5d494c7a596291cb4d598fd80808bdfa38285235 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 2 Jan 2013 09:16:18 +0000 Subject: cosmetic --- src/rabbit_variable_queue.erl | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 3e4c7c86..42592482 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -1132,12 +1132,11 @@ internal_fetch(AckRequired, MsgStatus = #msg_status { ok = msg_store_remove(MSCState, IsPersistent, [MsgId]) end, Ack = fun () -> rabbit_queue_index:ack([SeqId], IndexState1) end, - IndexState2 = - case {AckRequired, MsgOnDisk, IndexOnDisk} of - {false, true, false} -> Rem(), IndexState1; - {false, true, true} -> Rem(), Ack(); - _ -> IndexState1 - end, + IndexState2 = case {AckRequired, MsgOnDisk, IndexOnDisk} of + {false, true, false} -> Rem(), IndexState1; + {false, true, true} -> Rem(), Ack(); + _ -> IndexState1 + end, %% 3. If an ack is required, add something sensible to PA {AckTag, State1} = case AckRequired of -- cgit v1.2.1 From 86e2fa5e69b026ebf3e7a018a593f479c93f10c3 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 2 Jan 2013 09:17:47 +0000 Subject: cosmetic --- src/rabbit_variable_queue.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 42592482..230aa612 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -1147,7 +1147,7 @@ internal_fetch(AckRequired, MsgStatus = #msg_status { false -> {undefined, State} end, - PCount1 = PCount - one_if(IsPersistent andalso not AckRequired), + PCount1 = PCount - one_if(IsPersistent andalso not AckRequired), RamMsgCount1 = RamMsgCount - one_if(Msg =/= undefined), {{Msg, IsDelivered, AckTag}, -- cgit v1.2.1 From a545a3b732e9f20ab860d0b25d99c704d5770dd4 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 2 Jan 2013 09:33:15 +0000 Subject: refactor: rename vq:intern_fetch to 'remove' --- src/rabbit_variable_queue.erl | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 230aa612..8e23b591 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -584,7 +584,7 @@ dropwhile(Pred, State) -> {undefined, a(State1)}; {{value, MsgStatus = #msg_status { msg_props = MsgProps }}, State1} -> case Pred(MsgProps) of - true -> {_, State2} = internal_fetch(false, MsgStatus, State1), + true -> {_, State2} = remove(false, MsgStatus, State1), dropwhile(Pred, State2); false -> {MsgProps, a(in_r(MsgStatus, State1))} end @@ -598,7 +598,7 @@ fetchwhile(Pred, Fun, Acc, State) -> case Pred(MsgProps) of true -> {MsgStatus1, State2} = read_msg(MsgStatus, State1), {{Msg, IsDelivered, AckTag}, State3} = - internal_fetch(true, MsgStatus1, State2), + remove(true, MsgStatus1, State2), Acc1 = Fun(Msg, IsDelivered, AckTag, Acc), fetchwhile(Pred, Fun, Acc1, State3); false -> {MsgProps, Acc, a(in_r(MsgStatus, State1))} @@ -613,7 +613,7 @@ fetch(AckRequired, State) -> %% it is possible that the message wasn't read from disk %% at this point, so read it in. {MsgStatus1, State2} = read_msg(MsgStatus, State1), - {Res, State3} = internal_fetch(AckRequired, MsgStatus1, State2), + {Res, State3} = remove(AckRequired, MsgStatus1, State2), {Res, a(State3)} end. @@ -623,7 +623,7 @@ drop(AckRequired, State) -> {empty, a(State1)}; {{value, MsgStatus}, State1} -> {{_Msg, _IsDelivered, AckTag}, State2} = - internal_fetch(AckRequired, MsgStatus, State1), + remove(AckRequired, MsgStatus, State1), {{MsgStatus#msg_status.msg_id, AckTag}, a(State2)} end. @@ -1108,20 +1108,20 @@ read_msg(MsgStatus = #msg_status { msg = undefined, read_msg(MsgStatus, _CountDiskToRam, State) -> {MsgStatus, State}. -internal_fetch(AckRequired, MsgStatus = #msg_status { - seq_id = SeqId, - msg_id = MsgId, - msg = Msg, - is_persistent = IsPersistent, - is_delivered = IsDelivered, - msg_on_disk = MsgOnDisk, - index_on_disk = IndexOnDisk }, - State = #vqstate {ram_msg_count = RamMsgCount, - out_counter = OutCount, - index_state = IndexState, - msg_store_clients = MSCState, - len = Len, - persistent_count = PCount }) -> +remove(AckRequired, MsgStatus = #msg_status { + seq_id = SeqId, + msg_id = MsgId, + msg = Msg, + is_persistent = IsPersistent, + is_delivered = IsDelivered, + msg_on_disk = MsgOnDisk, + index_on_disk = IndexOnDisk }, + State = #vqstate {ram_msg_count = RamMsgCount, + out_counter = OutCount, + index_state = IndexState, + msg_store_clients = MSCState, + len = Len, + persistent_count = PCount }) -> %% 1. Mark it delivered if necessary IndexState1 = maybe_write_delivered( IndexOnDisk andalso not IsDelivered, -- cgit v1.2.1 From a7de37ccf7ec5b75fab1f63bc0dc8feb186a86ba Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 2 Jan 2013 14:54:51 +0000 Subject: refactor: return less from vq:remove --- src/rabbit_variable_queue.erl | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index eabfe136..9508b9c8 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -596,9 +596,9 @@ fetchwhile(Pred, Fun, Acc, State) -> {undefined, Acc, a(State1)}; {{value, MsgStatus = #msg_status { msg_props = MsgProps }}, State1} -> case Pred(MsgProps) of - true -> {MsgStatus1, State2} = read_msg(MsgStatus, State1), - {{Msg, _IsDelivered, AckTag}, State3} = - remove(true, MsgStatus1, State2), + true -> {MsgStatus1 = #msg_status { msg = Msg }, State2} = + read_msg(MsgStatus, State1), + {AckTag, State3} = remove(true, MsgStatus1, State2), fetchwhile(Pred, Fun, Fun(Msg, AckTag, Acc), State3); false -> {MsgProps, Acc, a(in_r(MsgStatus, State1))} end @@ -611,9 +611,11 @@ fetch(AckRequired, State) -> {{value, MsgStatus}, State1} -> %% it is possible that the message wasn't read from disk %% at this point, so read it in. - {MsgStatus1, State2} = read_msg(MsgStatus, State1), - {Res, State3} = remove(AckRequired, MsgStatus1, State2), - {Res, a(State3)} + {MsgStatus1 = #msg_status { msg = Msg, + is_delivered = IsDelivered }, State2} = + read_msg(MsgStatus, State1), + {AckTag, State3} = remove(AckRequired, MsgStatus1, State2), + {{Msg, IsDelivered, AckTag}, a(State3)} end. drop(AckRequired, State) -> @@ -621,8 +623,7 @@ drop(AckRequired, State) -> {empty, State1} -> {empty, a(State1)}; {{value, MsgStatus}, State1} -> - {{_Msg, _IsDelivered, AckTag}, State2} = - remove(AckRequired, MsgStatus, State1), + {AckTag, State2} = remove(AckRequired, MsgStatus, State1), {{MsgStatus#msg_status.msg_id, AckTag}, a(State2)} end. @@ -1149,12 +1150,11 @@ remove(AckRequired, MsgStatus = #msg_status { PCount1 = PCount - one_if(IsPersistent andalso not AckRequired), RamMsgCount1 = RamMsgCount - one_if(Msg =/= undefined), - {{Msg, IsDelivered, AckTag}, - State1 #vqstate { ram_msg_count = RamMsgCount1, - out_counter = OutCount + 1, - index_state = IndexState2, - len = Len - 1, - persistent_count = PCount1 }}. + {AckTag, State1 #vqstate { ram_msg_count = RamMsgCount1, + out_counter = OutCount + 1, + index_state = IndexState2, + len = Len - 1, + persistent_count = PCount1 }}. purge_betas_and_deltas(LensByStore, State = #vqstate { q3 = Q3, -- cgit v1.2.1 From 87cc957f8b05a985dd3ee09f11e4d56ca684d126 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 2 Jan 2013 16:58:55 +0000 Subject: only retain ram msgs when inserting into pending_ack which keeps memory use constant during fetch operations --- src/rabbit_variable_queue.erl | 51 ++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 9508b9c8..37ca6de0 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -596,9 +596,8 @@ fetchwhile(Pred, Fun, Acc, State) -> {undefined, Acc, a(State1)}; {{value, MsgStatus = #msg_status { msg_props = MsgProps }}, State1} -> case Pred(MsgProps) of - true -> {MsgStatus1 = #msg_status { msg = Msg }, State2} = - read_msg(MsgStatus, State1), - {AckTag, State3} = remove(true, MsgStatus1, State2), + true -> {Msg, State2} = read_msg(MsgStatus, false, State1), + {AckTag, State3} = remove(true, MsgStatus, State2), fetchwhile(Pred, Fun, Fun(Msg, AckTag, Acc), State3); false -> {MsgProps, Acc, a(in_r(MsgStatus, State1))} end @@ -611,11 +610,9 @@ fetch(AckRequired, State) -> {{value, MsgStatus}, State1} -> %% it is possible that the message wasn't read from disk %% at this point, so read it in. - {MsgStatus1 = #msg_status { msg = Msg, - is_delivered = IsDelivered }, State2} = - read_msg(MsgStatus, State1), - {AckTag, State3} = remove(AckRequired, MsgStatus1, State2), - {{Msg, IsDelivered, AckTag}, a(State3)} + {Msg, State2} = read_msg(MsgStatus, false, State1), + {AckTag, State3} = remove(AckRequired, MsgStatus, State2), + {{Msg, MsgStatus#msg_status.is_delivered, AckTag}, a(State3)} end. drop(AckRequired, State) -> @@ -675,8 +672,8 @@ ackfold(MsgFun, Acc, State, AckTags) -> {AccN, StateN} = lists:foldl( fun(SeqId, {Acc0, State0 = #vqstate{ pending_ack = PA }}) -> - {#msg_status { msg = Msg }, State1} = - read_msg(gb_trees:get(SeqId, PA), false, State0), + MsgStatus = gb_trees:get(SeqId, PA), + {Msg, State1} = read_msg(MsgStatus, false, State0), {MsgFun(Msg, SeqId, Acc0), State1} end, {Acc, State}, AckTags), {AccN, a(StateN)}. @@ -688,9 +685,9 @@ fold(Fun, Acc, #vqstate { q1 = Q1, q3 = Q3, q4 = Q4 } = State) -> QFun = fun(MsgStatus, {Acc0, State0}) -> - {#msg_status { msg = Msg, msg_props = MsgProps }, State1 } = - read_msg(MsgStatus, false, State0), - {StopGo, AccNext} = Fun(Msg, MsgProps, Acc0), + {Msg, State1} = read_msg(MsgStatus, false, State0), + {StopGo, AccNext} = + Fun(Msg, MsgStatus#msg_status.msg_props, Acc0), {StopGo, {AccNext, State1}} end, {Cont1, {Acc1, State1}} = qfoldl(QFun, {cont, {Acc, State }}, Q4), @@ -1075,9 +1072,10 @@ in_r(MsgStatus = #msg_status { msg = undefined }, State = #vqstate { q3 = Q3, q4 = Q4 }) -> case ?QUEUE:is_empty(Q4) of true -> State #vqstate { q3 = ?QUEUE:in_r(MsgStatus, Q3) }; - false -> {MsgStatus1, State1 = #vqstate { q4 = Q4a }} = - read_msg(MsgStatus, State), - State1 #vqstate { q4 = ?QUEUE:in_r(MsgStatus1, Q4a) } + false -> {Msg, State1 = #vqstate { q4 = Q4a }} = + read_msg(MsgStatus, true, State), + State1 #vqstate { q4 = ?QUEUE:in_r(MsgStatus#msg_status { + msg = Msg }, Q4a) } end; in_r(MsgStatus, State = #vqstate { q4 = Q4 }) -> State #vqstate { q4 = ?QUEUE:in_r(MsgStatus, Q4) }. @@ -1093,20 +1091,18 @@ queue_out(State = #vqstate { q4 = Q4 }) -> {{value, MsgStatus}, State #vqstate { q4 = Q4a }} end. -read_msg(MsgStatus, State) -> read_msg(MsgStatus, true, State). - -read_msg(MsgStatus = #msg_status { msg = undefined, - msg_id = MsgId, - is_persistent = IsPersistent }, +read_msg(#msg_status { msg = undefined, + msg_id = MsgId, + is_persistent = IsPersistent }, CountDiskToRam, State = #vqstate { ram_msg_count = RamMsgCount, msg_store_clients = MSCState}) -> {{ok, Msg = #basic_message {}}, MSCState1} = msg_store_read(MSCState, IsPersistent, MsgId), - {MsgStatus #msg_status { msg = Msg }, - State #vqstate { ram_msg_count = RamMsgCount + one_if(CountDiskToRam), - msg_store_clients = MSCState1 }}; -read_msg(MsgStatus, _CountDiskToRam, State) -> - {MsgStatus, State}. + RamMsgCount1 = RamMsgCount + one_if(CountDiskToRam), + {Msg, State #vqstate { ram_msg_count = RamMsgCount1, + msg_store_clients = MSCState1 }}; +read_msg(#msg_status { msg = Msg }, _CountDiskToRam, State) -> + {Msg, State}. remove(AckRequired, MsgStatus = #msg_status { seq_id = SeqId, @@ -1375,7 +1371,8 @@ msg_indices_written_to_disk(Callback, MsgIdSet) -> %%---------------------------------------------------------------------------- publish_alpha(#msg_status { msg = undefined } = MsgStatus, State) -> - read_msg(MsgStatus, State); + {Msg, State1} = read_msg(MsgStatus, true, State), + {MsgStatus#msg_status { msg = Msg }, State1}; publish_alpha(MsgStatus, #vqstate {ram_msg_count = RamMsgCount } = State) -> {MsgStatus, State #vqstate { ram_msg_count = RamMsgCount + 1 }}. -- cgit v1.2.1 From 2957576dddea3ca4a344c6003f1c559c3dfeb9a1 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 3 Jan 2013 13:02:06 +0000 Subject: oops; put exception handling back in --- src/rabbit_reader.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 840f430e..86859687 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -612,7 +612,7 @@ process_frame(Frame, Channel, State) -> put(ChKey, {ChPid, NewAState}), post_process_frame(Frame, ChPid, control_throttle(State)); {error, Reason} -> - {error, Reason} + handle_exception(State, Channel, Reason) end. post_process_frame({method, 'channel.close_ok', _}, ChPid, State) -> -- cgit v1.2.1 From 4aeac66d004c6bfd7775424921d78697ad320410 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 3 Jan 2013 13:32:01 +0000 Subject: Explain --- src/credit_flow.erl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/credit_flow.erl b/src/credit_flow.erl index ec9b2c36..8f1d4d00 100644 --- a/src/credit_flow.erl +++ b/src/credit_flow.erl @@ -56,6 +56,9 @@ %% closure creation a HOF would introduce -define(UPDATE(Key, Default, Var, Expr), begin + %% We delibarately allow Var to escape from the case here + %% to be used in Expr. Any temporary var we introduced + %% would also escape, and might conflict. case get(Key) of undefined -> Var = Default; Var -> ok -- cgit v1.2.1 From 67677c12a0e1d805bb326e127f2a19981b0a6ff5 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 3 Jan 2013 13:36:06 +0000 Subject: Typo --- src/credit_flow.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/credit_flow.erl b/src/credit_flow.erl index 8f1d4d00..102c353f 100644 --- a/src/credit_flow.erl +++ b/src/credit_flow.erl @@ -56,7 +56,7 @@ %% closure creation a HOF would introduce -define(UPDATE(Key, Default, Var, Expr), begin - %% We delibarately allow Var to escape from the case here + %% We deliberately allow Var to escape from the case here %% to be used in Expr. Any temporary var we introduced %% would also escape, and might conflict. case get(Key) of -- cgit v1.2.1 From c6f09ee2749801d133350a8e048896b82a860083 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 3 Jan 2013 16:49:46 +0000 Subject: Explain why --- src/rabbit_mirror_queue_sync.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rabbit_mirror_queue_sync.erl b/src/rabbit_mirror_queue_sync.erl index f9502219..c90a141f 100644 --- a/src/rabbit_mirror_queue_sync.erl +++ b/src/rabbit_mirror_queue_sync.erl @@ -219,6 +219,7 @@ slave_sync_loop(Args = {Ref, MRef, Syncer, BQ, UpdateRamDuration, Parent}, slave_sync_loop(Args, TRef, BQS1); {'EXIT', Parent, Reason} -> {stop, Reason, {TRef, BQS}}; + %% If the master throws an exception {'$gen_cast', {gm, {delete_and_terminate, Reason}}} -> BQS1 = BQ:delete_and_terminate(Reason, BQS), {stop, Reason, {TRef, BQS1}} -- cgit v1.2.1 From f713c839e78b451f060a1ea784d0f7b3b2e360d2 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 3 Jan 2013 16:51:09 +0000 Subject: Consistency with the real slave. --- src/rabbit_mirror_queue_sync.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_mirror_queue_sync.erl b/src/rabbit_mirror_queue_sync.erl index c90a141f..040f3c9b 100644 --- a/src/rabbit_mirror_queue_sync.erl +++ b/src/rabbit_mirror_queue_sync.erl @@ -221,6 +221,6 @@ slave_sync_loop(Args = {Ref, MRef, Syncer, BQ, UpdateRamDuration, Parent}, {stop, Reason, {TRef, BQS}}; %% If the master throws an exception {'$gen_cast', {gm, {delete_and_terminate, Reason}}} -> - BQS1 = BQ:delete_and_terminate(Reason, BQS), - {stop, Reason, {TRef, BQS1}} + BQ:delete_and_terminate(Reason, BQS), + {stop, Reason, {TRef, undefined}} end. -- cgit v1.2.1 From 32b0db675b3b32ed643ca4d551d1bad318379deb Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 3 Jan 2013 17:03:59 +0000 Subject: API consistency. --- src/rabbit_amqqueue.erl | 11 +++-------- src/rabbit_control_main.erl | 9 +++++---- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 3169948b..35b4fadf 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -31,7 +31,7 @@ -export([notify_down_all/2, limit_all/3]). -export([on_node_down/1]). -export([update/2, store_queue/1, policy_changed/2]). --export([start_mirroring/1, stop_mirroring/1, sync/2]). +-export([start_mirroring/1, stop_mirroring/1, sync_mirrors/1]). %% internal -export([internal_declare/2, internal_delete/1, run_backing_queue/3, @@ -173,7 +173,7 @@ (rabbit_types:amqqueue(), rabbit_types:amqqueue()) -> 'ok'). -spec(start_mirroring/1 :: (pid()) -> 'ok'). -spec(stop_mirroring/1 :: (pid()) -> 'ok'). --spec(sync/2 :: (binary(), rabbit_types:vhost()) -> +-spec(sync_mirrors/1 :: (pid()) -> 'ok' | rabbit_types:error('pending_acks' | 'not_mirrored')). -endif. @@ -592,12 +592,7 @@ set_maximum_since_use(QPid, Age) -> start_mirroring(QPid) -> ok = delegate_cast(QPid, start_mirroring). stop_mirroring(QPid) -> ok = delegate_cast(QPid, stop_mirroring). -sync(QNameBin, VHostBin) -> - QName = rabbit_misc:r(VHostBin, queue, QNameBin), - case lookup(QName) of - {ok, #amqqueue{pid = QPid}} -> delegate_call(QPid, sync_mirrors); - E -> E - end. +sync_mirrors(QPid) -> delegate_call(QPid, sync_mirrors). on_node_down(Node) -> rabbit_misc:execute_mnesia_tx_with_tail( diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index 819435ee..24528e32 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -281,11 +281,12 @@ action(forget_cluster_node, Node, [ClusterNodeS], Opts, Inform) -> rpc_call(Node, rabbit_mnesia, forget_cluster_node, [ClusterNode, RemoveWhenOffline]); -action(sync_queue, Node, [Queue], Opts, Inform) -> +action(sync_queue, Node, [Q], Opts, Inform) -> VHost = proplists:get_value(?VHOST_OPT, Opts), - Inform("Synchronising queue \"~s\" in vhost \"~s\"", [Queue, VHost]), - rpc_call(Node, rabbit_amqqueue, sync, - [list_to_binary(Queue), list_to_binary(VHost)]); + Inform("Synchronising queue \"~s\" in vhost \"~s\"", [Q, VHost]), + rpc_call(Node, rabbit_amqqueue, with, + [rabbit_misc:r(list_to_binary(VHost), queue, list_to_binary(Q)), + fun(#amqqueue{pid = P}) -> rabbit_amqqueue:sync_mirrors(P) end]); action(wait, Node, [PidFile], _Opts, Inform) -> Inform("Waiting for ~p", [Node]), -- cgit v1.2.1 From 514e84e5a865b03b3ddda30ea3a0f09c5ff9ae94 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 3 Jan 2013 17:15:33 +0000 Subject: Don't use a closure for the usual cluster upgrade reasons. --- src/rabbit_control_main.erl | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index 24528e32..70ca6177 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -17,7 +17,7 @@ -module(rabbit_control_main). -include("rabbit.hrl"). --export([start/0, stop/0, action/5]). +-export([start/0, stop/0, action/5, sync_queue/1]). -define(RPC_TIMEOUT, infinity). -define(EXTERNAL_CHECK_INTERVAL, 1000). @@ -284,9 +284,8 @@ action(forget_cluster_node, Node, [ClusterNodeS], Opts, Inform) -> action(sync_queue, Node, [Q], Opts, Inform) -> VHost = proplists:get_value(?VHOST_OPT, Opts), Inform("Synchronising queue \"~s\" in vhost \"~s\"", [Q, VHost]), - rpc_call(Node, rabbit_amqqueue, with, - [rabbit_misc:r(list_to_binary(VHost), queue, list_to_binary(Q)), - fun(#amqqueue{pid = P}) -> rabbit_amqqueue:sync_mirrors(P) end]); + rpc_call(Node, rabbit_control_main, sync_queue, + [rabbit_misc:r(list_to_binary(VHost), queue, list_to_binary(Q))]); action(wait, Node, [PidFile], _Opts, Inform) -> Inform("Waiting for ~p", [Node]), @@ -521,6 +520,10 @@ action(eval, Node, [Expr], _Opts, _Inform) -> format_parse_error({_Line, Mod, Err}) -> lists:flatten(Mod:format_error(Err)). +sync_queue(Q) -> + rabbit_amqqueue:with( + Q, fun(#amqqueue{pid = QPid}) -> rabbit_amqqueue:sync_mirrors(QPid) end). + %%---------------------------------------------------------------------------- wait_for_application(Node, PidFile, Application, Inform) -> -- cgit v1.2.1 From 123584aed5eb4fd71fe6090e525c339843081630 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 3 Jan 2013 17:38:51 +0000 Subject: Specs. --- src/credit_flow.erl | 2 +- src/rabbit_mirror_queue_master.erl | 2 ++ src/rabbit_mirror_queue_sync.erl | 19 +++++++++++++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/credit_flow.erl b/src/credit_flow.erl index ba99811f..c2bec7c7 100644 --- a/src/credit_flow.erl +++ b/src/credit_flow.erl @@ -37,7 +37,7 @@ -ifdef(use_specs). --opaque(bump_msg() :: {pid(), non_neg_integer()}). +-type(bump_msg() :: {pid(), non_neg_integer()}). -type(credit_spec() :: {non_neg_integer(), non_neg_integer()}). -spec(send/1 :: (pid()) -> 'ok'). diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index c9b6269b..70df62e2 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -70,6 +70,8 @@ -spec(init_with_existing_bq/3 :: (rabbit_types:amqqueue(), atom(), any()) -> master_state()). -spec(stop_mirroring/1 :: (master_state()) -> {atom(), any()}). +-spec(sync_mirrors/1 :: (master_state()) -> + {'ok', master_state()} | {stop, any(), master_state()}). -endif. diff --git a/src/rabbit_mirror_queue_sync.erl b/src/rabbit_mirror_queue_sync.erl index 040f3c9b..ac03ca8d 100644 --- a/src/rabbit_mirror_queue_sync.erl +++ b/src/rabbit_mirror_queue_sync.erl @@ -52,6 +52,25 @@ %% || || -- sync_complete --> || %% || (Dies) || +-ifdef(use_specs). + +-type(log_fun() :: fun ((string(), [any()]) -> 'ok')). +-type(bq() :: atom()). +-type(bqs() :: any()). + +-spec(master_prepare/3 :: (reference(), log_fun(), [pid()]) -> pid()). +-spec(master_go/5 :: (pid(), reference(), log_fun(), bq(), bqs()) -> + {'already_synced', bqs()} | {'ok', bqs()} | + {'shutdown', any(), bqs()} | + {'sync_died', any(), bqs()}). +-spec(slave/7 :: (non_neg_integer(), reference(), timer:tref(), pid(), + bq(), bqs(), fun((bq(), bqs()) -> {timer:tref(), bqs()})) -> + 'denied' | + {'ok' | 'failed', {timer:tref(), bqs()}} | + {'stop', any(), {timer:tref(), bqs()}}). + +-endif. + %% --------------------------------------------------------------------------- %% Master -- cgit v1.2.1 From 673ee6e88793eb90c0c86d40ecf317ea0e4fd2e6 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 3 Jan 2013 21:10:51 +0000 Subject: bring up to date with 'default' --- src/rabbit_amqqueue.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 71dc8257..fbe146e8 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -600,7 +600,7 @@ set_maximum_since_use(QPid, Age) -> start_mirroring(QPid) -> ok = delegate:cast(QPid, start_mirroring). stop_mirroring(QPid) -> ok = delegate:cast(QPid, stop_mirroring). -sync_mirrors(QPid) -> delegate_call(QPid, sync_mirrors). +sync_mirrors(QPid) -> delegate:call(QPid, sync_mirrors). on_node_down(Node) -> rabbit_misc:execute_mnesia_tx_with_tail( -- cgit v1.2.1 From 4d137f9a7b05d2a994e8e1a8d0da2f7ee9d12b65 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 3 Jan 2013 21:54:35 +0000 Subject: fix bug found by dialyzer --- src/rabbit_amqqueue_process.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 326065d1..6b065b96 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1151,8 +1151,8 @@ handle_call(sync_mirrors, _From, S = fun(BQSN) -> State#q{backing_queue_state = BQSN} end, case BQ:depth(BQS) - BQ:len(BQS) of 0 -> case rabbit_mirror_queue_master:sync_mirrors(BQS) of - {shutdown, Reason, BQS1} -> {stop, Reason, S(BQS1)}; - {Result, BQS1} -> reply(Result, S(BQS1)) + {ok, BQS1} -> reply(ok, S(BQS1)); + {stop, Reason, BQS1} -> {stop, Reason, S(BQS1)} end; _ -> reply({error, pending_acks}, State) end; -- cgit v1.2.1 From 192ff326d82e601c0ebc1ebcdcc0d4411fda08eb Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 3 Jan 2013 22:05:17 +0000 Subject: make credit waiting less brittle We were relying on running out of credit for all slaves *simultaneously*, which requires in-depth knowledge of the credit flow logic and that no other credit-requiring messages are sent to a slave prior to this. Fortunately, since we are running in a fresh separate process we can simply handle *any* credit bumping message and *any* DOWN message. As a bonus we can revert to making the type of the bump msg opaque. --- src/credit_flow.erl | 2 +- src/rabbit_mirror_queue_sync.erl | 21 ++++++++++----------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/credit_flow.erl b/src/credit_flow.erl index dff339fc..102c353f 100644 --- a/src/credit_flow.erl +++ b/src/credit_flow.erl @@ -37,7 +37,7 @@ -ifdef(use_specs). --type(bump_msg() :: {pid(), non_neg_integer()}). +-opaque(bump_msg() :: {pid(), non_neg_integer()}). -type(credit_spec() :: {non_neg_integer(), non_neg_integer()}). -spec(send/1 :: (pid()) -> 'ok'). diff --git a/src/rabbit_mirror_queue_sync.erl b/src/rabbit_mirror_queue_sync.erl index ac03ca8d..8c561d1c 100644 --- a/src/rabbit_mirror_queue_sync.erl +++ b/src/rabbit_mirror_queue_sync.erl @@ -148,7 +148,7 @@ syncer_loop({Ref, MPid} = Args, SPidsMRefs) -> MPid ! {next, Ref}, receive {msg, Ref, Msg, MsgProps} -> - SPidsMRefs1 = wait_for_credit(SPidsMRefs, Ref), + SPidsMRefs1 = wait_for_credit(SPidsMRefs), [begin credit_flow:send(SPid), SPid ! {sync_msg, Ref, Msg, MsgProps} @@ -158,10 +158,16 @@ syncer_loop({Ref, MPid} = Args, SPidsMRefs) -> SPidsMRefs end. -wait_for_credit(SPidsMRefs, Ref) -> +wait_for_credit(SPidsMRefs) -> case credit_flow:blocked() of - true -> wait_for_credit(foreach_slave(SPidsMRefs, Ref, - fun sync_receive_credit/3), Ref); + true -> receive + {bump_credit, Msg} -> + credit_flow:handle_bump_msg(Msg), + wait_for_credit(SPidsMRefs); + {'DOWN', MRef, _, SPid, _} -> + credit_flow:peer_down(SPid), + wait_for_credit(lists:delete({SPid, MRef}, SPidsMRefs)) + end; false -> SPidsMRefs end. @@ -176,13 +182,6 @@ sync_receive_ready(SPid, MRef, Ref) -> {'DOWN', MRef, _, SPid, _} -> ignore end. -sync_receive_credit(SPid, MRef, _Ref) -> - receive - {bump_credit, {SPid, _} = Msg} -> credit_flow:handle_bump_msg(Msg), - SPid; - {'DOWN', MRef, _, SPid, _} -> credit_flow:peer_down(SPid), - ignore - end. sync_send_complete(SPid, _MRef, Ref) -> SPid ! {sync_complete, Ref}. -- cgit v1.2.1 From 08c59a9170c1858d009ccecd089b6f1b2d25cd20 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 3 Jan 2013 22:29:56 +0000 Subject: simplify syncer we don't need to track monitors --- src/rabbit_mirror_queue_sync.erl | 56 +++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 33 deletions(-) diff --git a/src/rabbit_mirror_queue_sync.erl b/src/rabbit_mirror_queue_sync.erl index 8c561d1c..88f4639f 100644 --- a/src/rabbit_mirror_queue_sync.erl +++ b/src/rabbit_mirror_queue_sync.erl @@ -131,61 +131,51 @@ master_done({Syncer, Ref, _Log, Parent}, BQS) -> %% Syncer syncer(Ref, Log, MPid, SPids) -> - SPidsMRefs = [{SPid, erlang:monitor(process, SPid)} || SPid <- SPids], + [erlang:monitor(process, SPid) || SPid <- SPids], %% We wait for a reply from the slaves so that we know they are in %% a receive block and will thus receive messages we send to them %% *without* those messages ending up in their gen_server2 pqueue. - case foreach_slave(SPidsMRefs, Ref, fun sync_receive_ready/3) of - [] -> Log("all slaves already synced", []); - SPidsMRefs1 -> MPid ! {ready, self()}, - Log("~p to sync", [[rabbit_misc:pid_to_string(S) || - {S, _} <- SPidsMRefs1]]), - SPidsMRefs2 = syncer_loop({Ref, MPid}, SPidsMRefs1), - foreach_slave(SPidsMRefs2, Ref, fun sync_send_complete/3) + case [SPid || SPid <- SPids, + receive + {sync_ready, Ref, SPid} -> true; + {sync_deny, Ref, SPid} -> false; + {'DOWN', _, process, SPid, _} -> false + end] of + [] -> Log("all slaves already synced", []); + SPids1 -> MPid ! {ready, self()}, + Log("~p to sync", [[rabbit_misc:pid_to_string(SPid) || + SPid <- SPids1]]), + SPids2 = syncer_loop(Ref, MPid, SPids1), + [SPid ! {sync_complete, Ref} || SPid <- SPids2] end. -syncer_loop({Ref, MPid} = Args, SPidsMRefs) -> +syncer_loop(Ref, MPid, SPids) -> MPid ! {next, Ref}, receive {msg, Ref, Msg, MsgProps} -> - SPidsMRefs1 = wait_for_credit(SPidsMRefs), + SPids1 = wait_for_credit(SPids), [begin credit_flow:send(SPid), SPid ! {sync_msg, Ref, Msg, MsgProps} - end || {SPid, _} <- SPidsMRefs1], - syncer_loop(Args, SPidsMRefs1); + end || SPid <- SPids1], + syncer_loop(Ref, MPid, SPids1); {done, Ref} -> - SPidsMRefs + SPids end. -wait_for_credit(SPidsMRefs) -> +wait_for_credit(SPids) -> case credit_flow:blocked() of true -> receive {bump_credit, Msg} -> credit_flow:handle_bump_msg(Msg), - wait_for_credit(SPidsMRefs); - {'DOWN', MRef, _, SPid, _} -> + wait_for_credit(SPids); + {'DOWN', _, _, SPid, _} -> credit_flow:peer_down(SPid), - wait_for_credit(lists:delete({SPid, MRef}, SPidsMRefs)) + wait_for_credit(lists:delete(SPid, SPids)) end; - false -> SPidsMRefs + false -> SPids end. -foreach_slave(SPidsMRefs, Ref, Fun) -> - [{SPid, MRef} || {SPid, MRef} <- SPidsMRefs, - Fun(SPid, MRef, Ref) =/= ignore]. - -sync_receive_ready(SPid, MRef, Ref) -> - receive - {sync_ready, Ref, SPid} -> SPid; - {sync_deny, Ref, SPid} -> ignore; - {'DOWN', MRef, _, SPid, _} -> ignore - end. - - -sync_send_complete(SPid, _MRef, Ref) -> - SPid ! {sync_complete, Ref}. - %% Syncer %% --------------------------------------------------------------------------- %% Slave -- cgit v1.2.1 From ca53fb64918bbf0f32765644654ec7d0001a86cf Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 3 Jan 2013 22:37:00 +0000 Subject: cosmetic --- src/rabbit_mirror_queue_sync.erl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/rabbit_mirror_queue_sync.erl b/src/rabbit_mirror_queue_sync.erl index 88f4639f..bb69a664 100644 --- a/src/rabbit_mirror_queue_sync.erl +++ b/src/rabbit_mirror_queue_sync.erl @@ -145,8 +145,7 @@ syncer(Ref, Log, MPid, SPids) -> SPids1 -> MPid ! {ready, self()}, Log("~p to sync", [[rabbit_misc:pid_to_string(SPid) || SPid <- SPids1]]), - SPids2 = syncer_loop(Ref, MPid, SPids1), - [SPid ! {sync_complete, Ref} || SPid <- SPids2] + syncer_loop(Ref, MPid, SPids1) end. syncer_loop(Ref, MPid, SPids) -> @@ -160,7 +159,7 @@ syncer_loop(Ref, MPid, SPids) -> end || SPid <- SPids1], syncer_loop(Ref, MPid, SPids1); {done, Ref} -> - SPids + [SPid ! {sync_complete, Ref} || SPid <- SPids] end. wait_for_credit(SPids) -> -- cgit v1.2.1 From f6f0d2fe572ae7697d91139396d634864cecdf04 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 4 Jan 2013 02:21:40 +0000 Subject: cosmetic(ish) --- src/rabbit_mirror_queue_sync.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_mirror_queue_sync.erl b/src/rabbit_mirror_queue_sync.erl index bb69a664..10a74cc9 100644 --- a/src/rabbit_mirror_queue_sync.erl +++ b/src/rabbit_mirror_queue_sync.erl @@ -168,7 +168,7 @@ wait_for_credit(SPids) -> {bump_credit, Msg} -> credit_flow:handle_bump_msg(Msg), wait_for_credit(SPids); - {'DOWN', _, _, SPid, _} -> + {'DOWN', _, process, SPid, _} -> credit_flow:peer_down(SPid), wait_for_credit(lists:delete(SPid, SPids)) end; -- cgit v1.2.1 From dd03620c775e937693c946c73a73c7b0836a6137 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 4 Jan 2013 02:35:08 +0000 Subject: correct and/or confusion --- src/rabbit_control_main.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index 669a0787..2b7061e8 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -168,7 +168,7 @@ start() -> {'EXIT', {badarg, _}} -> print_error("invalid parameter: ~p", [Args]), usage(); - {error, {Problem, Reason}} when is_atom(Problem); is_binary(Reason) -> + {error, {Problem, Reason}} when is_atom(Problem), is_binary(Reason) -> %% We handle this common case specially to avoid ~p since %% that has i18n issues print_error("~s: ~s", [Problem, Reason]), -- cgit v1.2.1 From e1220e0133aa10d98e8af0e4318b4885e74a9757 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Fri, 4 Jan 2013 12:16:12 +0000 Subject: Remove confirm_all --- src/dtree.erl | 24 +----------------------- src/rabbit_amqqueue_process.erl | 1 - src/rabbit_channel.erl | 9 ++------- src/rabbit_misc.erl | 5 +---- 4 files changed, 4 insertions(+), 35 deletions(-) diff --git a/src/dtree.erl b/src/dtree.erl index c59243bb..ca2d30cf 100644 --- a/src/dtree.erl +++ b/src/dtree.erl @@ -32,7 +32,7 @@ -module(dtree). --export([empty/0, insert/4, take/3, take/2, take_all/2, take_prim/2, +-export([empty/0, insert/4, take/3, take/2, take_all/2, is_defined/2, is_empty/1, smallest/1, size/1]). %%---------------------------------------------------------------------------- @@ -53,7 +53,6 @@ -spec(take/3 :: ([pk()], sk(), ?MODULE()) -> {[kv()], ?MODULE()}). -spec(take/2 :: (sk(), ?MODULE()) -> {[kv()], ?MODULE()}). -spec(take_all/2 :: (sk(), ?MODULE()) -> {[kv()], ?MODULE()}). --spec(take_prim/2 :: (pk(), ?MODULE()) -> {[kv()], ?MODULE()}). -spec(is_defined/2 :: (sk(), ?MODULE()) -> boolean()). -spec(is_empty/1 :: (?MODULE()) -> boolean()). -spec(smallest/1 :: (?MODULE()) -> kv()). @@ -121,13 +120,6 @@ take_all(SK, {P, S}) -> {KVs, {P1, prune(SKS, PKS, S)}} end. -%% Drop the entry with the given primary key -take_prim(PK, {P, S} = DTree) -> - case gb_trees:lookup(PK, P) of - none -> {[], DTree}; - {value, {SKS, V}} -> {[{PK, V}], take_prim2(PK, SKS, DTree)} - end. - is_defined(SK, {_P, S}) -> gb_trees:is_defined(SK, S). is_empty({P, _S}) -> gb_trees:is_empty(P). @@ -157,20 +149,6 @@ take_all2(PKS, P) -> gb_trees:delete(PK, P0)} end, {[], gb_sets:empty(), P}, PKS). -take_prim2(PK, SKS, {P, S}) -> - {gb_trees:delete(PK, P), - rabbit_misc:gb_trees_fold( - fun (SK0, PKS, S1) -> - case gb_sets:is_member(SK0, SKS) of - false -> S1; - true -> PKS1 = gb_sets:delete(PK, PKS), - case gb_sets:is_empty(PKS1) of - true -> gb_trees:delete(SK0, S1); - false -> gb_trees:update(SK0, PKS1, S1) - end - end - end, S, S)}. - prune(SKS, PKS, S) -> gb_sets:fold(fun (SK0, S0) -> PKS1 = gb_trees:get(SK0, S0), diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index f588c024..09f01109 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -595,7 +595,6 @@ publish_max(#delivery{message = Message, BQ:publish(Message, Props, Delivered, SenderPid, BQS); {true, true} -> (dead_letter_fun(maxdepth))([{Message, undefined}]), - rabbit_misc:confirm_all(SenderPid, MsgSeqNo), nopub; {true, false} -> {{Msg, _IsDelivered, AckTag}, BQS1} = BQ:fetch(true, BQS), diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 885452ce..1af60de8 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -228,9 +228,8 @@ prioritise_call(Msg, _From, _State) -> prioritise_cast(Msg, _State) -> case Msg of - {confirm, _MsgSeqNos, _QPid} -> 5; - {confirm_all, _MsgSeqNo, _QPid} -> 5; - _ -> 0 + {confirm, _MsgSeqNos, _QPid} -> 5; + _ -> 0 end. prioritise_info(Msg, _State) -> @@ -557,10 +556,6 @@ confirm(MsgSeqNos, QPid, State = #ch{unconfirmed = UC}) -> {MXs, UC1} = dtree:take(MsgSeqNos, QPid, UC), record_confirms(MXs, State#ch{unconfirmed = UC1}). -confirm_all(MsgSeqNo, State = #ch{unconfirmed = UC}) -> - {MXs, UC1} = dtree:take_prim(MsgSeqNo, UC), - record_confirms(MXs, State#ch{unconfirmed = UC1}). - handle_method(#'channel.open'{}, _, State = #ch{state = starting}) -> {reply, #'channel.open_ok'{}, State#ch{state = running}}; diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index c6c8676f..edaa7198 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -28,7 +28,7 @@ -export([enable_cover/0, report_cover/0]). -export([enable_cover/1, report_cover/1]). -export([start_cover/1]). --export([confirm_to_sender/2, confirm_all/2]). +-export([confirm_to_sender/2]). -export([throw_on_error/2, with_exit_handler/2, is_abnormal_exit/1, filter_exit_map/2]). -export([with_user/2, with_user_and_vhost/3]). @@ -427,9 +427,6 @@ report_coverage_percentage(File, Cov, NotCov, Mod) -> confirm_to_sender(Pid, MsgSeqNos) -> gen_server2:cast(Pid, {confirm, MsgSeqNos, self()}). -confirm_all(Pid, MsgSeqNo) -> - gen_server2:cast(Pid, {confirm_all, MsgSeqNo, self()}). - %% @doc Halts the emulator returning the given status code to the os. %% On Windows this function will block indefinitely so as to give the io %% subsystem time to flush stdout completely. -- cgit v1.2.1 From d5b173e4611ca319ce8f0d65fdce0e84b879d919 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Fri, 4 Jan 2013 12:33:11 +0000 Subject: Rename depth to length --- src/rabbit_amqqueue.erl | 4 ++-- src/rabbit_amqqueue_process.erl | 22 +++++++++++----------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index dacf4f0a..9d6dcd15 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -390,7 +390,7 @@ check_declare_arguments(QueueName, Args) -> {<<"x-message-ttl">>, fun check_message_ttl_arg/2}, {<<"x-dead-letter-exchange">>, fun check_string_arg/2}, {<<"x-dead-letter-routing-key">>, fun check_dlxrk_arg/2}, - {<<"x-maxdepth">>, fun check_maxdepth_arg/2}], + {<<"x-max-length">>, fun check_max_length_arg/2}], [case rabbit_misc:table_lookup(Args, Key) of undefined -> ok; TypeVal -> case Fun(TypeVal, Args) of @@ -413,7 +413,7 @@ check_int_arg({Type, _}, _) -> false -> {error, {unacceptable_type, Type}} end. -check_maxdepth_arg({Type, Val}, Args) -> +check_max_length_arg({Type, Val}, Args) -> case check_int_arg({Type, Val}, Args) of ok when Val > 0 -> ok; ok -> {error, {value_not_positive, Val}}; diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 09f01109..6eb40886 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -55,7 +55,7 @@ queue_monitors, dlx, dlx_routing_key, - max_depth + max_length }). -record(consumer, {tag, ack_required}). @@ -135,7 +135,7 @@ init(Q) -> senders = pmon:new(), dlx = undefined, dlx_routing_key = undefined, - max_depth = undefined, + max_length = undefined, publish_seqno = 1, unconfirmed = dtree:empty(), delayed_stop = undefined, @@ -161,7 +161,7 @@ init_with_backing_queue_state(Q = #amqqueue{exclusive_owner = Owner}, BQ, BQS, rate_timer_ref = RateTRef, expiry_timer_ref = undefined, ttl = undefined, - max_depth = undefined, + max_length = undefined, senders = Senders, publish_seqno = 1, unconfirmed = dtree:empty(), @@ -262,7 +262,7 @@ process_args(State = #q{q = #amqqueue{arguments = Arguments}}) -> {<<"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-maxdepth">>, fun init_maxdepth/2}]). + {<<"x-max-length">>, fun init_max_length/2}]). init_expires(Expires, State) -> ensure_expiry_timer(State#q{expires = Expires}). @@ -274,8 +274,8 @@ init_dlx(DLX, State = #q{q = #amqqueue{name = QName}}) -> init_dlx_routing_key(RoutingKey, State) -> State#q{dlx_routing_key = RoutingKey}. -init_maxdepth(MaxDepth, State) -> - State#q{max_depth = MaxDepth}. +init_max_length(MaxLen, State) -> + State#q{max_length = MaxLen}. terminate_shutdown(Fun, State) -> State1 = #q{backing_queue_state = BQS} = @@ -582,23 +582,23 @@ publish_max(#delivery{message = Message, sender = SenderPid}, Props, Delivered, #q{backing_queue = BQ, backing_queue_state = BQS, - max_depth = undefined}) -> + max_length = undefined}) -> BQ:publish(Message, Props, Delivered, SenderPid, BQS); publish_max(#delivery{message = Message, msg_seq_no = MsgSeqNo, sender = SenderPid}, Props, Delivered, #q{backing_queue = BQ, backing_queue_state = BQS, - max_depth = MaxDepth}) -> - case {BQ:depth(BQS) >= MaxDepth, BQ:len(BQS) =:= 0} of + max_length = MaxLen}) -> + case {BQ:depth(BQS) >= MaxLen, BQ:len(BQS) =:= 0} of {false, _} -> BQ:publish(Message, Props, Delivered, SenderPid, BQS); {true, true} -> - (dead_letter_fun(maxdepth))([{Message, undefined}]), + (dead_letter_fun(maxlen))([{Message, undefined}]), nopub; {true, false} -> {{Msg, _IsDelivered, AckTag}, BQS1} = BQ:fetch(true, BQS), - (dead_letter_fun(maxdepth))([{Msg, AckTag}]), + (dead_letter_fun(maxlen))([{Msg, AckTag}]), BQ:publish(Message, Props, Delivered, SenderPid, BQS1) end. -- cgit v1.2.1 From 93cad02186b4c0ac27a02090a9e0c719ac6dfc8d Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Fri, 4 Jan 2013 15:42:55 +0000 Subject: Ignore queue depth for maxlen --- src/rabbit_amqqueue.erl | 6 +++--- src/rabbit_amqqueue_process.erl | 25 +++++++++---------------- 2 files changed, 12 insertions(+), 19 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 9d6dcd15..92a9f4b3 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -415,9 +415,9 @@ check_int_arg({Type, _}, _) -> check_max_length_arg({Type, Val}, Args) -> case check_int_arg({Type, Val}, Args) of - ok when Val > 0 -> ok; - ok -> {error, {value_not_positive, Val}}; - Error -> Error + ok when Val >= 0 -> ok; + ok -> {error, {value_negative, Val}}; + Error -> Error end. check_expires_arg({Type, Val}, Args) -> diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 6eb40886..a4a30021 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -571,11 +571,9 @@ deliver_or_enqueue(Delivery = #delivery{message = Message}, {false, State2 = #q{ttl = 0, dlx = undefined}} -> discard(Delivery, State2); {false, State2} -> - case publish_max(Delivery, Props, Delivered, State2) of - nopub -> State2; - BQS1 -> ensure_ttl_timer(Props#message_properties.expiry, - State2#q{backing_queue_state = BQS1}) - end + BQS1 = publish_max(Delivery, Props, Delivered, State2), + ensure_ttl_timer(Props#message_properties.expiry, + State2#q{backing_queue_state = BQS1}) end. publish_max(#delivery{message = Message, @@ -590,16 +588,11 @@ publish_max(#delivery{message = Message, Props, Delivered, #q{backing_queue = BQ, backing_queue_state = BQS, max_length = MaxLen}) -> - case {BQ:depth(BQS) >= MaxLen, BQ:len(BQS) =:= 0} of - {false, _} -> - BQ:publish(Message, Props, Delivered, SenderPid, BQS); - {true, true} -> - (dead_letter_fun(maxlen))([{Message, undefined}]), - nopub; - {true, false} -> - {{Msg, _IsDelivered, AckTag}, BQS1} = BQ:fetch(true, BQS), - (dead_letter_fun(maxlen))([{Msg, AckTag}]), - BQ:publish(Message, Props, Delivered, SenderPid, BQS1) + case BQ:len(BQS) >= MaxLen of + true -> {{Msg, _IsDelivered, AckTag}, BQS1} = BQ:fetch(true, BQS), + (dead_letter_fun(maxlen))([{Msg, AckTag}]), + BQ:publish(Message, Props, Delivered, SenderPid, BQS1); + false -> BQ:publish(Message, Props, Delivered, SenderPid, BQS) end. requeue_and_run(AckTags, State = #q{backing_queue = BQ, @@ -833,7 +826,7 @@ cleanup_after_confirm(AckTags, State = #q{delayed_stop = DS, unconfirmed = UC, backing_queue = BQ, backing_queue_state = BQS}) -> - {_Guids, BQS1} = BQ:ack([Ack || Ack <- AckTags, Ack /= undefined], BQS), + {_Guids, BQS1} = BQ:ack(AckTags, BQS), State1 = State#q{backing_queue_state = BQS1}, case dtree:is_empty(UC) andalso DS =/= undefined of true -> case DS of -- cgit v1.2.1 From 0af18258f54c8d7a57d64e005ee2fda9bce97b8f Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 4 Jan 2013 16:10:42 +0000 Subject: tweak logging --- src/rabbit_mirror_queue_sync.erl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rabbit_mirror_queue_sync.erl b/src/rabbit_mirror_queue_sync.erl index 10a74cc9..508d46e9 100644 --- a/src/rabbit_mirror_queue_sync.erl +++ b/src/rabbit_mirror_queue_sync.erl @@ -143,8 +143,7 @@ syncer(Ref, Log, MPid, SPids) -> end] of [] -> Log("all slaves already synced", []); SPids1 -> MPid ! {ready, self()}, - Log("~p to sync", [[rabbit_misc:pid_to_string(SPid) || - SPid <- SPids1]]), + Log("mirrors ~p to sync", [[node(SPid) || SPid <- SPids1]]), syncer_loop(Ref, MPid, SPids1) end. -- cgit v1.2.1 From 6532a3ca4f8ec7b51e3810c4c2220aaeea681935 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 4 Jan 2013 18:38:12 +0000 Subject: some more control flow abstraction --- src/rabbit_amqqueue_process.erl | 47 ++++++++++++++++++----------------------- 1 file changed, 21 insertions(+), 26 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 66e48024..0bef1e4b 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -711,28 +711,27 @@ calculate_msg_expiry(#basic_message{content = Content}, TTL) -> T -> now_micros() + T * 1000 end. -drop_expired_msgs(State = #q{dlx = DLX, - backing_queue_state = BQS, +drop_expired_msgs(State = #q{backing_queue_state = BQS, backing_queue = BQ }) -> Now = now_micros(), ExpirePred = fun (#message_properties{expiry = Exp}) -> Now >= Exp end, {Props, State1} = - case DLX of - undefined -> {Next, BQS1} = BQ:dropwhile(ExpirePred, BQS), - {Next, State#q{backing_queue_state = BQS1}}; - _ -> case rabbit_exchange:lookup(DLX) of - {ok, X} -> - dead_letter_expired_msgs(ExpirePred, X, State); - {error, not_found} -> - {Next, BQS1} = BQ:dropwhile(ExpirePred, BQS), - {Next, State#q{backing_queue_state = BQS1}} - end - end, + with_dlx( + State#q.dlx, + fun (X) -> dead_letter_expired_msgs(ExpirePred, X, State) end, + fun () -> {Next, BQS1} = BQ:dropwhile(ExpirePred, BQS), + {Next, State#q{backing_queue_state = BQS1}} end), ensure_ttl_timer(case Props of undefined -> undefined; #message_properties{expiry = Exp} -> Exp end, State1). +with_dlx(undefined, _With, Without) -> Without(); +with_dlx(DLX, With, Without) -> case rabbit_exchange:lookup(DLX) of + {ok, X} -> With(X); + {error, not_found} -> Without() + end. + dead_letter_expired_msgs(ExpirePred, X, State = #q{backing_queue = BQ}) -> dead_letter_msgs(fun (DLFun, Acc, BQS1) -> BQ:fetchwhile(ExpirePred, DLFun, Acc, BQS1) @@ -1221,19 +1220,15 @@ handle_cast({ack, AckTags, ChPid}, State) -> handle_cast({reject, AckTags, true, ChPid}, State) -> noreply(requeue(AckTags, ChPid, State)); -handle_cast({reject, AckTags, false, ChPid}, State = #q{dlx = undefined}) -> - noreply(ack(AckTags, ChPid, State)); - -handle_cast({reject, AckTags, false, ChPid}, State = #q{dlx = DLX}) -> - noreply(case rabbit_exchange:lookup(DLX) of - {ok, X} -> subtract_acks( - ChPid, AckTags, State, - fun (State1) -> - dead_letter_rejected_msgs( - AckTags, X, State1) - end); - {error, not_found} -> ack(AckTags, ChPid, State) - end); +handle_cast({reject, AckTags, false, ChPid}, State) -> + noreply(with_dlx( + State#q.dlx, + fun (X) -> subtract_acks(ChPid, AckTags, State, + fun (State1) -> + dead_letter_rejected_msgs( + AckTags, X, State1) + end) end, + fun () -> ack(AckTags, ChPid, State) end)); handle_cast(delete_immediately, State) -> stop(State); -- cgit v1.2.1 From dc87918e5f1160d7f7c72b55ab484720072b26af Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 4 Jan 2013 19:44:45 +0000 Subject: refactor: disentangle head dropping and publishing --- src/rabbit_amqqueue_process.erl | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index a4a30021..77514383 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -560,7 +560,7 @@ attempt_delivery(Delivery = #delivery{sender = SenderPid, message = Message}, {false, State#q{backing_queue_state = BQS1}} end. -deliver_or_enqueue(Delivery = #delivery{message = Message}, +deliver_or_enqueue(Delivery = #delivery{message = Message, sender = SenderPid}, Delivered, State) -> {Confirm, State1} = send_or_record_confirm(Delivery, State), Props = message_properties(Message, Confirm, State), @@ -571,28 +571,23 @@ deliver_or_enqueue(Delivery = #delivery{message = Message}, {false, State2 = #q{ttl = 0, dlx = undefined}} -> discard(Delivery, State2); {false, State2} -> - BQS1 = publish_max(Delivery, Props, Delivered, State2), + State3 = #q{backing_queue = BQ, backing_queue_state = BQS} = + maybe_drop_head(State2), + BQS1 = BQ:publish(Message, Props, Delivered, SenderPid, BQS), ensure_ttl_timer(Props#message_properties.expiry, - State2#q{backing_queue_state = BQS1}) + State3#q{backing_queue_state = BQS1}) end. -publish_max(#delivery{message = Message, - sender = SenderPid}, - Props, Delivered, #q{backing_queue = BQ, - backing_queue_state = BQS, - max_length = undefined}) -> - BQ:publish(Message, Props, Delivered, SenderPid, BQS); -publish_max(#delivery{message = Message, - msg_seq_no = MsgSeqNo, - sender = SenderPid}, - Props, Delivered, #q{backing_queue = BQ, - backing_queue_state = BQS, - max_length = MaxLen}) -> +maybe_drop_head(State = #q{max_length = undefined}) -> + State; +maybe_drop_head(State = #q{max_length = MaxLen, + backing_queue = BQ, + backing_queue_state = BQS}) -> case BQ:len(BQS) >= MaxLen of true -> {{Msg, _IsDelivered, AckTag}, BQS1} = BQ:fetch(true, BQS), (dead_letter_fun(maxlen))([{Msg, AckTag}]), - BQ:publish(Message, Props, Delivered, SenderPid, BQS1); - false -> BQ:publish(Message, Props, Delivered, SenderPid, BQS) + State#q{backing_queue_state = BQS1}; + false -> State end. requeue_and_run(AckTags, State = #q{backing_queue = BQ, -- cgit v1.2.1 From feddbf3ec5fdb0a112cbd770bed20ea9cd3e02fc Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 4 Jan 2013 19:59:55 +0000 Subject: refactor queue initialisation - get rid of all the 'undefined' setting, since that's the default - extract commonality of state initialisation --- src/rabbit_amqqueue_process.erl | 50 +++++++++++++---------------------------- 1 file changed, 16 insertions(+), 34 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 6b065b96..35f0b816 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -120,26 +120,7 @@ info_keys() -> ?INFO_KEYS. init(Q) -> process_flag(trap_exit, true), - State = #q{q = Q#amqqueue{pid = self()}, - exclusive_consumer = none, - has_had_consumers = false, - backing_queue = undefined, - backing_queue_state = undefined, - active_consumers = queue:new(), - expires = undefined, - sync_timer_ref = undefined, - rate_timer_ref = undefined, - expiry_timer_ref = undefined, - ttl = undefined, - senders = pmon:new(), - dlx = undefined, - dlx_routing_key = undefined, - publish_seqno = 1, - unconfirmed = dtree:empty(), - delayed_stop = undefined, - queue_monitors = pmon:new(), - msg_id_to_channel = gb_trees:empty()}, - {ok, rabbit_event:init_stats_timer(State, #q.stats_timer), hibernate, + {ok, init_state(Q#amqqueue{pid = self()}), hibernate, {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}. init_with_backing_queue_state(Q = #amqqueue{exclusive_owner = Owner}, BQ, BQS, @@ -148,27 +129,28 @@ init_with_backing_queue_state(Q = #amqqueue{exclusive_owner = Owner}, BQ, BQS, none -> ok; _ -> erlang:monitor(process, Owner) end, + State = init_state(Q), + State1 = State#q{backing_queue = BQ, + backing_queue_state = BQS, + rate_timer_ref = RateTRef, + 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). + +init_state(Q) -> State = #q{q = Q, exclusive_consumer = none, has_had_consumers = false, - backing_queue = BQ, - backing_queue_state = BQS, active_consumers = queue:new(), - expires = undefined, - sync_timer_ref = undefined, - rate_timer_ref = RateTRef, - expiry_timer_ref = undefined, - ttl = undefined, - senders = Senders, + senders = pmon:new(), publish_seqno = 1, unconfirmed = dtree:empty(), - delayed_stop = undefined, queue_monitors = pmon:new(), - msg_id_to_channel = MTC}, - State1 = process_args(rabbit_event:init_stats_timer(State, #q.stats_timer)), - lists:foldl(fun (Delivery, StateN) -> - deliver_or_enqueue(Delivery, true, StateN) - end, State1, Deliveries). + msg_id_to_channel = gb_trees:empty()}, + rabbit_event:init_stats_timer(State, #q.stats_timer). terminate(shutdown = R, State = #q{backing_queue = BQ}) -> terminate_shutdown(fun (BQS) -> BQ:terminate(R, BQS) end, State); -- cgit v1.2.1 From 8f3985f1cd22af9bcf76db38e0a4b2c3a3505955 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 4 Jan 2013 20:05:51 +0000 Subject: cosmetic --- src/rabbit_amqqueue_process.erl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 004180db..f56df9d9 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -254,8 +254,7 @@ init_dlx(DLX, State = #q{q = #amqqueue{name = QName}}) -> init_dlx_routing_key(RoutingKey, State) -> State#q{dlx_routing_key = RoutingKey}. -init_max_length(MaxLen, State) -> - State#q{max_length = MaxLen}. +init_max_length(MaxLen, State) -> State#q{max_length = MaxLen}. terminate_shutdown(Fun, State) -> State1 = #q{backing_queue_state = BQS} = -- cgit v1.2.1 From d175a33ce7ab8a303f2fc1266c9479c23615c7ac Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 4 Jan 2013 21:15:57 +0000 Subject: assert queue dlx arg equivalence and restructure code to make future omissions less likely --- src/rabbit_amqqueue.erl | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 173f7648..f1e46fa2 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -380,14 +380,10 @@ 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, [<<"x-expires">>, <<"x-message-ttl">>]). + rabbit_misc:assert_args_equivalence(Args, RequiredArgs, QueueName, + [Key || {Key, _Fun} <- args()]). check_declare_arguments(QueueName, Args) -> - Checks = [{<<"x-expires">>, fun check_expires_arg/2}, - {<<"x-message-ttl">>, fun check_message_ttl_arg/2}, - {<<"x-dead-letter-exchange">>, fun check_string_arg/2}, - {<<"x-dead-letter-routing-key">>, fun check_dlxrk_arg/2}], [case rabbit_misc:table_lookup(Args, Key) of undefined -> ok; TypeVal -> case Fun(TypeVal, Args) of @@ -398,9 +394,15 @@ check_declare_arguments(QueueName, Args) -> [Key, rabbit_misc:rs(QueueName), Error]) end - end || {Key, Fun} <- Checks], + end || {Key, Fun} <- args()], ok. +args() -> + [{<<"x-expires">>, fun check_expires_arg/2}, + {<<"x-message-ttl">>, fun check_message_ttl_arg/2}, + {<<"x-dead-letter-exchange">>, fun check_string_arg/2}, + {<<"x-dead-letter-routing-key">>, fun check_dlxrk_arg/2}]. + check_string_arg({longstr, _}, _Args) -> ok; check_string_arg({Type, _}, _Args) -> {error, {unacceptable_type, Type}}. -- cgit v1.2.1 From 7f1f617a1f058fc893f6ed593208532e60ef06bf Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 5 Jan 2013 15:24:51 +0000 Subject: optimisation: shrink gen_server2 state slightly for a (very) modest performance gain, and slightly neater code --- src/gen_server2.erl | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/src/gen_server2.erl b/src/gen_server2.erl index 78bbbe06..de43cf5c 100644 --- a/src/gen_server2.erl +++ b/src/gen_server2.erl @@ -196,8 +196,7 @@ %% State record -record(gs2_state, {parent, name, state, mod, time, - timeout_state, queue, debug, prioritise_call, - prioritise_cast, prioritise_info}). + timeout_state, queue, debug, prioritisers}). -ifdef(use_specs). @@ -638,17 +637,17 @@ adjust_timeout_state(SleptAt, AwokeAt, {backoff, CurrentTO, MinimumTO, {backoff, CurrentTO1, MinimumTO, DesiredHibPeriod, RandomState1}. in({'$gen_cast', Msg} = Input, - GS2State = #gs2_state { prioritise_cast = PC }) -> - in(Input, PC(Msg, GS2State), GS2State); + GS2State = #gs2_state { prioritisers = {_, F, _} }) -> + in(Input, F(Msg, GS2State), GS2State); in({'$gen_call', From, Msg} = Input, - GS2State = #gs2_state { prioritise_call = PC }) -> - in(Input, PC(Msg, From, GS2State), GS2State); + GS2State = #gs2_state { prioritisers = {F, _, _} }) -> + in(Input, F(Msg, From, GS2State), GS2State); in({'EXIT', Parent, _R} = Input, GS2State = #gs2_state { parent = Parent }) -> in(Input, infinity, GS2State); in({system, _From, _Req} = Input, GS2State) -> in(Input, infinity, GS2State); -in(Input, GS2State = #gs2_state { prioritise_info = PI }) -> - in(Input, PI(Input, GS2State), GS2State). +in(Input, GS2State = #gs2_state { prioritisers = {_, _, F} }) -> + in(Input, F(Input, GS2State), GS2State). in(Input, Priority, GS2State = #gs2_state { queue = Queue }) -> GS2State # gs2_state { queue = priority_queue:in(Input, Priority, Queue) }. @@ -1165,16 +1164,13 @@ whereis_name(Name) -> end. find_prioritisers(GS2State = #gs2_state { mod = Mod }) -> - PrioriCall = function_exported_or_default( - Mod, 'prioritise_call', 3, - fun (_Msg, _From, _State) -> 0 end), - PrioriCast = function_exported_or_default(Mod, 'prioritise_cast', 2, - fun (_Msg, _State) -> 0 end), - PrioriInfo = function_exported_or_default(Mod, 'prioritise_info', 2, - fun (_Msg, _State) -> 0 end), - GS2State #gs2_state { prioritise_call = PrioriCall, - prioritise_cast = PrioriCast, - prioritise_info = PrioriInfo }. + PCall = function_exported_or_default(Mod, 'prioritise_call', 3, + fun (_Msg, _From, _State) -> 0 end), + PCast = function_exported_or_default(Mod, 'prioritise_cast', 2, + fun (_Msg, _State) -> 0 end), + PInfo = function_exported_or_default(Mod, 'prioritise_info', 2, + fun (_Msg, _State) -> 0 end), + GS2State #gs2_state { prioritisers = {PCall, PCast, PInfo} }. function_exported_or_default(Mod, Fun, Arity, Default) -> case erlang:function_exported(Mod, Fun, Arity) of -- cgit v1.2.1 From c0c20029324ba398fe07e05ec74e4fcdb5894b1e Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 5 Jan 2013 16:14:20 +0000 Subject: refactor gen_server2 debug handling - more uniformity - less code duplication - less closure creation -> performance increase --- src/gen_server2.erl | 60 ++++++++++++++++++++--------------------------------- 1 file changed, 22 insertions(+), 38 deletions(-) diff --git a/src/gen_server2.erl b/src/gen_server2.erl index de43cf5c..dc55948b 100644 --- a/src/gen_server2.erl +++ b/src/gen_server2.erl @@ -863,13 +863,19 @@ dispatch(Info, Mod, State) -> common_reply(_Name, From, Reply, _NState, [] = _Debug) -> reply(From, Reply), []; -common_reply(Name, From, Reply, NState, Debug) -> - reply(Name, From, Reply, NState, Debug). +common_reply(Name, {To, Tag} = From, Reply, NState, Debug) -> + reply(From, Reply), + sys:handle_debug(Debug, fun print_event/3, Name, {out, Reply, To, NState}). + +common_noreply(_Name, _NState, [] = _Debug) -> + []; +common_noreply(Name, NState, Debug) -> + sys:handle_debug(Debug, fun print_event/3, Name, {noreply, NState}). -common_debug([] = _Debug, _Func, _Info, _Event) -> +common_become(_Name, _Mod, _NState, [] = _Debug) -> []; -common_debug(Debug, Func, Info, Event) -> - sys:handle_debug(Debug, Func, Info, Event). +common_become(Name, Mod, NState, Debug) -> + sys:handle_debug(Debug, fun print_event/3, Name, {become, Mod, NState}). handle_msg({'$gen_call', From, Msg}, GS2State = #gs2_state { mod = Mod, state = State, @@ -886,23 +892,11 @@ handle_msg({'$gen_call', From, Msg}, GS2State = #gs2_state { mod = Mod, loop(GS2State #gs2_state { state = NState, time = Time1, debug = Debug1}); - {noreply, NState} -> - Debug1 = common_debug(Debug, fun print_event/3, Name, - {noreply, NState}), - loop(GS2State #gs2_state {state = NState, - time = infinity, - debug = Debug1}); - {noreply, NState, Time1} -> - Debug1 = common_debug(Debug, fun print_event/3, Name, - {noreply, NState}), - loop(GS2State #gs2_state {state = NState, - time = Time1, - debug = Debug1}); {stop, Reason, Reply, NState} -> {'EXIT', R} = (catch terminate(Reason, Msg, GS2State #gs2_state { state = NState })), - reply(Name, From, Reply, NState, Debug), + common_reply(Name, From, Reply, NState, Debug), exit(R); Other -> handle_common_reply(Other, Msg, GS2State) @@ -915,28 +909,24 @@ handle_common_reply(Reply, Msg, GS2State = #gs2_state { name = Name, debug = Debug}) -> case Reply of {noreply, NState} -> - Debug1 = common_debug(Debug, fun print_event/3, Name, - {noreply, NState}), - loop(GS2State #gs2_state { state = NState, - time = infinity, - debug = Debug1 }); + Debug1 = common_noreply(Name, NState, Debug), + loop(GS2State #gs2_state {state = NState, + time = infinity, + debug = Debug1}); {noreply, NState, Time1} -> - Debug1 = common_debug(Debug, fun print_event/3, Name, - {noreply, NState}), - loop(GS2State #gs2_state { state = NState, - time = Time1, - debug = Debug1 }); + Debug1 = common_noreply(Name, NState, Debug), + loop(GS2State #gs2_state {state = NState, + time = Time1, + debug = Debug1}); {become, Mod, NState} -> - Debug1 = common_debug(Debug, fun print_event/3, Name, - {become, Mod, NState}), + Debug1 = common_become(Name, Mod, NState, Debug), loop(find_prioritisers( GS2State #gs2_state { mod = Mod, state = NState, time = infinity, debug = Debug1 })); {become, Mod, NState, Time1} -> - Debug1 = common_debug(Debug, fun print_event/3, Name, - {become, Mod, NState}), + Debug1 = common_become(Name, Mod, NState, Debug), loop(find_prioritisers( GS2State #gs2_state { mod = Mod, state = NState, @@ -956,12 +946,6 @@ handle_common_termination(Reply, Msg, GS2State) -> terminate({bad_return_value, Reply}, Msg, GS2State) end. -reply(Name, {To, Tag}, Reply, State, Debug) -> - reply({To, Tag}, Reply), - sys:handle_debug( - Debug, fun print_event/3, Name, {out, Reply, To, State}). - - %%----------------------------------------------------------------- %% Callback functions for system messages handling. %%----------------------------------------------------------------- -- cgit v1.2.1 From 24fd11646a0fdf8741b527254893a1e35a87a3bf Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 5 Jan 2013 17:04:20 +0000 Subject: optimisation: improve performance of permission cache Don't update the cache when the permission is already in it. This saves list munching and a 'put' at the expense of no longer being strictly LRU, but that only affects pathological cases. --- src/rabbit_channel.erl | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 1af60de8..11a117ee 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -432,15 +432,13 @@ check_resource_access(User, Resource, Perm) -> undefined -> []; Other -> Other end, - CacheTail = - case lists:member(V, Cache) of - true -> lists:delete(V, Cache); - false -> ok = rabbit_access_control:check_resource_access( - User, Resource, Perm), - lists:sublist(Cache, ?MAX_PERMISSION_CACHE_SIZE - 1) - end, - put(permission_cache, [V | CacheTail]), - ok. + case lists:member(V, Cache) of + true -> ok; + false -> ok = rabbit_access_control:check_resource_access( + User, Resource, Perm), + CacheTail = lists:sublist(Cache, ?MAX_PERMISSION_CACHE_SIZE-1), + put(permission_cache, [V | CacheTail]) + end. clear_permission_cache() -> erase(permission_cache), -- cgit v1.2.1 From 0949f2f599c5183c4beb979b6804936dd29525ce Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 5 Jan 2013 23:47:50 +0000 Subject: optimise rabbit_amqqueue:qpids/1 common case --- src/rabbit_amqqueue.erl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index fbe146e8..1ec89c63 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -678,6 +678,8 @@ deliver(Qs, Delivery, _Flow) -> R -> {routed, [QPid || {QPid, ok} <- R]} end. +qpids([]) -> {[], []}; %% optimisation +qpids([#amqqueue{pid = QPid, slave_pids = SPids}]) -> {[QPid], SPids}; %% opt qpids(Qs) -> {MPids, SPids} = lists:foldl(fun (#amqqueue{pid = QPid, slave_pids = SPids}, {MPidAcc, SPidAcc}) -> -- cgit v1.2.1 From 115369202c1a49b3030e091a545c775149b818da Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sun, 6 Jan 2013 00:35:11 +0000 Subject: common-case optimisations for delegate:invoke[_no_result] --- src/delegate.erl | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/delegate.erl b/src/delegate.erl index 9222c34c..96b8ba31 100644 --- a/src/delegate.erl +++ b/src/delegate.erl @@ -62,6 +62,13 @@ invoke(Pid, Fun) when is_pid(Pid) -> erlang:raise(Class, Reason, StackTrace) end; +invoke([], _Fun) -> %% optimisation + {[], []}; +invoke([Pid], Fun) when node(Pid) =:= node() -> %% optimisation + case safe_invoke(Pid, Fun) of + {ok, _, Result} -> {[{Pid, Result}], []}; + {error, _, Error} -> {[], [{Pid, Error}]} + end; invoke(Pids, Fun) when is_list(Pids) -> {LocalPids, Grouped} = group_pids_by_node(Pids), %% The use of multi_call is only safe because the timeout is @@ -90,6 +97,11 @@ invoke_no_result(Pid, Fun) when is_pid(Pid) andalso node(Pid) =:= node() -> invoke_no_result(Pid, Fun) when is_pid(Pid) -> invoke_no_result([Pid], Fun); +invoke_no_result([], _Fun) -> %% optimisation + ok; +invoke_no_result([Pid], Fun) when node(Pid) =:= node() -> %% optimisation + safe_invoke(Pid, Fun), %% must not die + ok; invoke_no_result(Pids, Fun) when is_list(Pids) -> {LocalPids, Grouped} = group_pids_by_node(Pids), case orddict:fetch_keys(Grouped) of -- cgit v1.2.1 From f2474ad8809cd83b71741eef718138d2479b745e Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sun, 6 Jan 2013 00:44:35 +0000 Subject: optimise "route to no queues" path --- src/rabbit_channel.erl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 11a117ee..b9f8d1bb 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -1327,6 +1327,10 @@ notify_limiter(Limiter, Acked) -> end end. +deliver_to_queues({#delivery{message = #basic_message{exchange_name = XName}}, + []}, State) -> %% optimisation + ?INCR_STATS([{exchange_stats, XName, 1}], publish, State), + State; deliver_to_queues({Delivery = #delivery{message = Message = #basic_message{ exchange_name = XName}, msg_seq_no = MsgSeqNo}, -- cgit v1.2.1 From 53d2a03f0f5dc5a442672292aa0e49f48ca5e560 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sun, 6 Jan 2013 01:31:31 +0000 Subject: optimise "no confirms" case of rabbit_amqqueue_process:discard --- src/rabbit_amqqueue_process.erl | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 35f0b816..d7cd9fb1 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -498,11 +498,14 @@ send_or_record_confirm(#delivery{sender = SenderPid, rabbit_misc:confirm_to_sender(SenderPid, [MsgSeqNo]), {immediately, State}. -discard(#delivery{sender = SenderPid, message = #basic_message{id = MsgId}}, - State) -> - %% fake an 'eventual' confirm from BQ; noop if not needed +discard(#delivery{sender = SenderPid, + msg_seq_no = MsgSeqNo, + message = #basic_message{id = MsgId}}, State) -> State1 = #q{backing_queue = BQ, backing_queue_state = BQS} = - confirm_messages([MsgId], State), + case MsgSeqNo of + undefined -> State; + _ -> confirm_messages([MsgId], State) + end, BQS1 = BQ:discard(MsgId, SenderPid, BQS), State1#q{backing_queue_state = BQS1}. -- cgit v1.2.1 From a045f82f44f70dddc74f025812300ed104688f5b Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sun, 6 Jan 2013 03:55:13 +0000 Subject: cosmetic --- src/rabbit_channel.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index b9f8d1bb..37354f93 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -1320,7 +1320,7 @@ notify_limiter(Limiter, Acked) -> case rabbit_limiter:is_enabled(Limiter) of false -> ok; true -> case lists:foldl(fun ({_, none, _}, Acc) -> Acc; - ({_, _, _}, Acc) -> Acc + 1 + ({_, _, _}, Acc) -> Acc + 1 end, 0, Acked) of 0 -> ok; Count -> rabbit_limiter:ack(Limiter, Count) -- cgit v1.2.1 From 0b7c1a6c6ee008f836efb7d40a48a1df9d850fac Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sun, 6 Jan 2013 04:43:14 +0000 Subject: restrict previous optimisation, for better workingness --- src/rabbit_channel.erl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 37354f93..aaa463f1 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -1327,7 +1327,9 @@ notify_limiter(Limiter, Acked) -> end end. -deliver_to_queues({#delivery{message = #basic_message{exchange_name = XName}}, +deliver_to_queues({#delivery{message = #basic_message{exchange_name = XName}, + msg_seq_no = undefined, + mandatory = false}, []}, State) -> %% optimisation ?INCR_STATS([{exchange_stats, XName, 1}], publish, State), State; -- cgit v1.2.1 From 2683e219af2c91432a0b5453d978b175c02fea5c Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sun, 6 Jan 2013 04:48:18 +0000 Subject: optimise rabbit_channel:ack/2 - moving the ?INCR_STATS call inside the fold_per_queue fun reduces consing when stats are disabled - since this was the only call to fold_per_queue where we cared about the result, and we no longer do, we can switch the 'fold' to 'foreach' --- src/rabbit_channel.erl | 55 ++++++++++++++++++++++++-------------------------- 1 file changed, 26 insertions(+), 29 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index aaa463f1..68625dbf 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -800,14 +800,12 @@ handle_method(#'basic.recover_async'{requeue = true}, limiter = Limiter}) -> OkFun = fun () -> ok end, UAMQL = queue:to_list(UAMQ), - ok = fold_per_queue( - fun (QPid, MsgIds, ok) -> - rabbit_misc:with_exit_handler( - OkFun, fun () -> - rabbit_amqqueue:requeue( - QPid, MsgIds, self()) - end) - end, ok, UAMQL), + foreach_per_queue( + fun (QPid, MsgIds) -> + rabbit_misc:with_exit_handler( + OkFun, + fun () -> rabbit_amqqueue:requeue(QPid, MsgIds, self()) end) + end, UAMQL), ok = notify_limiter(Limiter, UAMQL), %% No answer required - basic.recover is the newer, synchronous %% variant of this method @@ -1215,10 +1213,10 @@ reject(DeliveryTag, Requeue, Multiple, end}. reject(Requeue, Acked, Limiter) -> - ok = fold_per_queue( - fun (QPid, MsgIds, ok) -> - rabbit_amqqueue:reject(QPid, MsgIds, Requeue, self()) - end, ok, Acked), + foreach_per_queue( + fun (QPid, MsgIds) -> + rabbit_amqqueue:reject(QPid, MsgIds, Requeue, self()) + end, Acked), ok = notify_limiter(Limiter, Acked). record_sent(ConsumerTag, AckRequired, @@ -1267,17 +1265,16 @@ collect_acks(ToAcc, PrefixAcc, Q, DeliveryTag, Multiple) -> end. ack(Acked, State = #ch{queue_names = QNames}) -> - Incs = fold_per_queue( - fun (QPid, MsgIds, L) -> - ok = rabbit_amqqueue:ack(QPid, MsgIds, self()), - case dict:find(QPid, QNames) of - {ok, QName} -> Count = length(MsgIds), - [{queue_stats, QName, Count} | L]; - error -> L - end - end, [], Acked), - ok = notify_limiter(State#ch.limiter, Acked), - ?INCR_STATS(Incs, ack, State). + foreach_per_queue( + fun (QPid, MsgIds) -> + ok = rabbit_amqqueue:ack(QPid, MsgIds, self()), + ?INCR_STATS(case dict:find(QPid, QNames) of + {ok, QName} -> Count = length(MsgIds), + [{queue_stats, QName, Count}]; + error -> [] + end, ack, State) + end, Acked), + ok = notify_limiter(State#ch.limiter, Acked). new_tx() -> #tx{msgs = queue:new(), acks = [], nacks = []}. @@ -1289,15 +1286,15 @@ notify_queues(State = #ch{consumer_mapping = Consumers, sets:union(sets:from_list(consumer_queues(Consumers)), DQ)), {rabbit_amqqueue:notify_down_all(QPids, self()), State#ch{state = closing}}. -fold_per_queue(_F, Acc, []) -> - Acc; -fold_per_queue(F, Acc, [{_DTag, _CTag, {QPid, MsgId}}]) -> %% common case - F(QPid, [MsgId], Acc); -fold_per_queue(F, Acc, UAL) -> +foreach_per_queue(_F, []) -> + ok; +foreach_per_queue(F, [{_DTag, _CTag, {QPid, MsgId}}]) -> %% common case + F(QPid, [MsgId]); +foreach_per_queue(F, UAL) -> T = lists:foldl(fun ({_DTag, _CTag, {QPid, MsgId}}, T) -> rabbit_misc:gb_trees_cons(QPid, MsgId, T) end, gb_trees:empty(), UAL), - rabbit_misc:gb_trees_fold(F, Acc, T). + rabbit_misc:gb_trees_foreach(F, T). enable_limiter(State = #ch{unacked_message_q = UAMQ, limiter = Limiter}) -> -- cgit v1.2.1 From 716d91ed4d1935c966126a7636195c2ab57770eb Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sun, 6 Jan 2013 05:33:26 +0000 Subject: optimise ack collection for the common case of ack'ing/reject'ing the oldest tag --- src/rabbit_channel.erl | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 68625dbf..2686d76d 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -1245,19 +1245,25 @@ record_sent(ConsumerTag, AckRequired, collect_acks(Q, 0, true) -> {queue:to_list(Q), queue:new()}; collect_acks(Q, DeliveryTag, Multiple) -> - collect_acks([], queue:new(), Q, DeliveryTag, Multiple). + collect_acks([], [], Q, DeliveryTag, Multiple). collect_acks(ToAcc, PrefixAcc, Q, DeliveryTag, Multiple) -> case queue:out(Q) of {{value, UnackedMsg = {CurrentDeliveryTag, _ConsumerTag, _Msg}}, QTail} -> if CurrentDeliveryTag == DeliveryTag -> - {[UnackedMsg | ToAcc], queue:join(PrefixAcc, QTail)}; + {[UnackedMsg | ToAcc], + case PrefixAcc of + [] -> QTail; + _ -> queue:join( + queue:from_list(lists:reverse(PrefixAcc)), + QTail) + end}; Multiple -> collect_acks([UnackedMsg | ToAcc], PrefixAcc, QTail, DeliveryTag, Multiple); true -> - collect_acks(ToAcc, queue:in(UnackedMsg, PrefixAcc), + collect_acks(ToAcc, [UnackedMsg | PrefixAcc], QTail, DeliveryTag, Multiple) end; {empty, _} -> -- cgit v1.2.1 From 18aa3cb89836a29a91704a11b62a32ca0e43ef0d Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 7 Jan 2013 10:52:55 +0000 Subject: record pending acks in a queue rather than set in queue process Just as we do in the channel; this is more efficient for the typical ack-in-order access pattern. We depend on tags being passed to the queue process in order when ack'ing/rejecting. This requires some slightly fiddly code in the channel. --- src/rabbit_amqqueue_process.erl | 30 ++++++++++++++++-------- src/rabbit_channel.erl | 51 ++++++++++++++++++++++++++--------------- 2 files changed, 53 insertions(+), 28 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index d7cd9fb1..4341c3d6 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -352,7 +352,7 @@ ch_record(ChPid) -> undefined -> MonitorRef = erlang:monitor(process, ChPid), C = #cr{ch_pid = ChPid, monitor_ref = MonitorRef, - acktags = sets:new(), + acktags = queue:new(), consumer_count = 0, blocked_consumers = queue:new(), is_limit_active = false, @@ -366,9 +366,9 @@ ch_record(ChPid) -> update_ch_record(C = #cr{consumer_count = ConsumerCount, acktags = ChAckTags, unsent_message_count = UnsentMessageCount}) -> - case {sets:size(ChAckTags), ConsumerCount, UnsentMessageCount} of - {0, 0, 0} -> ok = erase_ch_record(C); - _ -> ok = store_ch_record(C) + case {queue:is_empty(ChAckTags), ConsumerCount, UnsentMessageCount} of + {true, 0, 0} -> ok = erase_ch_record(C); + _ -> ok = store_ch_record(C) end, C. @@ -451,7 +451,7 @@ deliver_msg_to_consumer(DeliverFun, rabbit_channel:deliver(ChPid, ConsumerTag, AckRequired, {QName, self(), AckTag, IsDelivered, Message}), ChAckTags1 = case AckRequired of - true -> sets:add_element(AckTag, ChAckTags); + true -> queue:in(AckTag, ChAckTags); false -> ChAckTags end, update_ch_record(C#cr{acktags = ChAckTags1, @@ -638,7 +638,7 @@ handle_ch_down(DownPid, State = #q{exclusive_consumer = Holder, senders = Senders1}, case should_auto_delete(State1) of true -> {stop, State1}; - false -> {ok, requeue_and_run(sets:to_list(ChAckTags), + false -> {ok, requeue_and_run(queue:to_list(ChAckTags), ensure_expiry_timer(State1))} end end. @@ -677,11 +677,21 @@ subtract_acks(ChPid, AckTags, State, Fun) -> not_found -> State; C = #cr{acktags = ChAckTags} -> - update_ch_record(C#cr{acktags = lists:foldl(fun sets:del_element/2, - ChAckTags, AckTags)}), + update_ch_record( + C#cr{acktags = subtract_acks(AckTags, [], ChAckTags)}), Fun(State) end. +subtract_acks([], [], AckQ) -> + AckQ; +subtract_acks([], Prefix, AckQ) -> + queue:join(queue:from_list(lists:reverse(Prefix)), AckQ); +subtract_acks([T | TL] = AckTags, Prefix, AckQ) -> + case queue:out(AckQ) of + {{value, T}, QTail} -> subtract_acks(TL, Prefix, QTail); + {{value, AT}, QTail} -> subtract_acks(AckTags, [AT | Prefix], QTail) + end. + message_properties(Message, Confirm, #q{ttl = TTL}) -> #message_properties{expiry = calculate_msg_expiry(Message, TTL), needs_confirming = Confirm == eventually}. @@ -886,7 +896,7 @@ i(exclusive_consumer_tag, #q{exclusive_consumer = {_ChPid, ConsumerTag}}) -> i(messages_ready, #q{backing_queue_state = BQS, backing_queue = BQ}) -> BQ:len(BQS); i(messages_unacknowledged, _) -> - lists:sum([sets:size(C#cr.acktags) || C <- all_ch_record()]); + lists:sum([queue:len(C#cr.acktags) || C <- all_ch_record()]); i(messages, State) -> lists:sum([i(Item, State) || Item <- [messages_ready, messages_unacknowledged]]); @@ -1042,7 +1052,7 @@ handle_call({basic_get, ChPid, NoAck}, _From, State3 = #q{backing_queue = BQ, backing_queue_state = BQS} = case AckRequired of true -> C = #cr{acktags = ChAckTags} = ch_record(ChPid), - ChAckTags1 = sets:add_element(AckTag, ChAckTags), + ChAckTags1 = queue:in(AckTag, ChAckTags), update_ch_record(C#cr{acktags = ChAckTags1}), State2; false -> State2 diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 2686d76d..831058db 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -40,7 +40,14 @@ queue_collector_pid, stats_timer, confirm_enabled, publish_seqno, unconfirmed, confirmed, capabilities, trace_state}). --record(tx, {msgs, acks, nacks}). + +-record(tx, {msgs, acks}). %% (1) +%% (1) acks looks s.t. like this: +%% [{true,[[6,7,8],[5]]},{ack,[[4],[1,2,3]]}, ...] +%% +%% Each element is a pair consisting of a tag and a list of lists of +%% ack'ed/reject'ed msg ids. The tag is one of 'ack' (to ack), 'true' +%% (reject w requeue), 'false' (reject w/o requeue). -define(MAX_PERMISSION_CACHE_SIZE, 12). @@ -647,7 +654,8 @@ handle_method(#'basic.ack'{delivery_tag = DeliveryTag, case Tx of none -> ack(Acked, State1), State1; - #tx{acks = Acks} -> State1#ch{tx = Tx#tx{acks = Acked ++ Acks}} + #tx{acks = Acks} -> Acks1 = ack_cons(ack, Acked, Acks), + State1#ch{tx = Tx#tx{acks = Acks1}} end}; handle_method(#'basic.get'{queue = QueueNameBin, @@ -1032,24 +1040,23 @@ handle_method(#'tx.select'{}, _, State) -> handle_method(#'tx.commit'{}, _, #ch{tx = none}) -> precondition_failed("channel is not transactional"); -handle_method(#'tx.commit'{}, _, State = #ch{tx = #tx{msgs = Msgs, - acks = Acks, - nacks = Nacks}, +handle_method(#'tx.commit'{}, _, State = #ch{tx = #tx{msgs = Msgs, + acks = Acks}, limiter = Limiter}) -> State1 = rabbit_misc:queue_fold(fun deliver_to_queues/2, State, Msgs), - ack(Acks, State1), lists:foreach( - fun({Requeue, Acked}) -> reject(Requeue, Acked, Limiter) end, Nacks), + fun ({ack, A}) -> ack(append_reverse(A), State1); + ({Requeue, A}) -> reject(Requeue, append_reverse(A), Limiter) + end, lists:reverse(Acks)), {noreply, maybe_complete_tx(State1#ch{tx = committing})}; handle_method(#'tx.rollback'{}, _, #ch{tx = none}) -> precondition_failed("channel is not transactional"); handle_method(#'tx.rollback'{}, _, State = #ch{unacked_message_q = UAMQ, - tx = #tx{acks = Acks, - nacks = Nacks}}) -> - NacksL = lists:append([L || {_, L} <- Nacks]), - UAMQ1 = queue:from_list(lists:usort(Acks ++ NacksL ++ queue:to_list(UAMQ))), + tx = #tx{acks = Acks}}) -> + AcksL = append_reverse([append_reverse(L) || {_, L} <- Acks]), + UAMQ1 = queue:from_list(lists:usort(AcksL ++ queue:to_list(UAMQ))), {reply, #'tx.rollback_ok'{}, State#ch{unacked_message_q = UAMQ1, tx = new_tx()}}; @@ -1206,10 +1213,10 @@ reject(DeliveryTag, Requeue, Multiple, State1 = State#ch{unacked_message_q = Remaining}, {noreply, case Tx of - none -> reject(Requeue, Acked, State1#ch.limiter), - State1; - #tx{nacks = Nacks} -> Nacks1 = [{Requeue, Acked} | Nacks], - State1#ch{tx = Tx#tx{nacks = Nacks1}} + none -> reject(Requeue, Acked, State1#ch.limiter), + State1; + #tx{acks = Acks} -> Acks1 = ack_cons(Requeue, Acked, Acks), + State1#ch{tx = Tx#tx{acks = Acks1}} end}. reject(Requeue, Acked, Limiter) -> @@ -1252,7 +1259,10 @@ collect_acks(ToAcc, PrefixAcc, Q, DeliveryTag, Multiple) -> {{value, UnackedMsg = {CurrentDeliveryTag, _ConsumerTag, _Msg}}, QTail} -> if CurrentDeliveryTag == DeliveryTag -> - {[UnackedMsg | ToAcc], + {case ToAcc of + [] -> [UnackedMsg]; + _ -> lists:reverse([UnackedMsg | ToAcc]) + end, case PrefixAcc of [] -> QTail; _ -> queue:join( @@ -1282,7 +1292,7 @@ ack(Acked, State = #ch{queue_names = QNames}) -> end, Acked), ok = notify_limiter(State#ch.limiter, Acked). -new_tx() -> #tx{msgs = queue:new(), acks = [], nacks = []}. +new_tx() -> #tx{msgs = queue:new(), acks = []}. notify_queues(State = #ch{state = closing}) -> {ok, State}; @@ -1299,7 +1309,7 @@ foreach_per_queue(F, [{_DTag, _CTag, {QPid, MsgId}}]) -> %% common case foreach_per_queue(F, UAL) -> T = lists:foldl(fun ({_DTag, _CTag, {QPid, MsgId}}, T) -> rabbit_misc:gb_trees_cons(QPid, MsgId, T) - end, gb_trees:empty(), UAL), + end, gb_trees:empty(), lists:reverse(UAL)), rabbit_misc:gb_trees_foreach(F, T). enable_limiter(State = #ch{unacked_message_q = UAMQ, @@ -1440,6 +1450,11 @@ coalesce_and_send(MsgSeqNos, MkMsgFun, WriterPid, MkMsgFun(SeqNo, false)) || SeqNo <- Ss], State. +append_reverse(L) -> lists:append(lists:reverse(L)). + +ack_cons(Tag, Acked, [{Tag, Acks} | L]) -> [{Tag, [Acked | Acks]} | L]; +ack_cons(Tag, Acked, Acks) -> [{Tag, [Acked]} | Acks]. + maybe_complete_tx(State = #ch{tx = #tx{}}) -> State; maybe_complete_tx(State = #ch{unconfirmed = UC}) -> -- cgit v1.2.1 From 0860b908377d89725fc366095e6c273eae2d691a Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 7 Jan 2013 11:35:28 +0000 Subject: simplify & document ordering --- src/rabbit_channel.erl | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 831058db..cccd09dd 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -43,11 +43,13 @@ -record(tx, {msgs, acks}). %% (1) %% (1) acks looks s.t. like this: -%% [{true,[[6,7,8],[5]]},{ack,[[4],[1,2,3]]}, ...] +%% [{false,[5,4]},{true,[3]},{ack,[2,1]}, ...] %% -%% Each element is a pair consisting of a tag and a list of lists of +%% Each element is a pair consisting of a tag and a list of %% ack'ed/reject'ed msg ids. The tag is one of 'ack' (to ack), 'true' -%% (reject w requeue), 'false' (reject w/o requeue). +%% (reject w requeue), 'false' (reject w/o requeue). The msg ids, as +%% well as the list overall, are in "most-recent (generally youngest) +%% ack first" order. -define(MAX_PERMISSION_CACHE_SIZE, 12). @@ -813,7 +815,7 @@ handle_method(#'basic.recover_async'{requeue = true}, rabbit_misc:with_exit_handler( OkFun, fun () -> rabbit_amqqueue:requeue(QPid, MsgIds, self()) end) - end, UAMQL), + end, lists:reverse(UAMQL)), ok = notify_limiter(Limiter, UAMQL), %% No answer required - basic.recover is the newer, synchronous %% variant of this method @@ -1045,8 +1047,8 @@ handle_method(#'tx.commit'{}, _, State = #ch{tx = #tx{msgs = Msgs, limiter = Limiter}) -> State1 = rabbit_misc:queue_fold(fun deliver_to_queues/2, State, Msgs), lists:foreach( - fun ({ack, A}) -> ack(append_reverse(A), State1); - ({Requeue, A}) -> reject(Requeue, append_reverse(A), Limiter) + fun ({ack, A}) -> ack(lists:reverse(A), State1); + ({Requeue, A}) -> reject(Requeue, lists:reverse(A), Limiter) end, lists:reverse(Acks)), {noreply, maybe_complete_tx(State1#ch{tx = committing})}; @@ -1055,7 +1057,7 @@ handle_method(#'tx.rollback'{}, _, #ch{tx = none}) -> handle_method(#'tx.rollback'{}, _, State = #ch{unacked_message_q = UAMQ, tx = #tx{acks = Acks}}) -> - AcksL = append_reverse([append_reverse(L) || {_, L} <- Acks]), + AcksL = lists:append(lists:reverse([lists:reverse(L) || {_, L} <- Acks])), UAMQ1 = queue:from_list(lists:usort(AcksL ++ queue:to_list(UAMQ))), {reply, #'tx.rollback_ok'{}, State#ch{unacked_message_q = UAMQ1, tx = new_tx()}}; @@ -1219,6 +1221,7 @@ reject(DeliveryTag, Requeue, Multiple, State1#ch{tx = Tx#tx{acks = Acks1}} end}. +%% NB: Acked is in youngest-first order reject(Requeue, Acked, Limiter) -> foreach_per_queue( fun (QPid, MsgIds) -> @@ -1249,8 +1252,9 @@ record_sent(ConsumerTag, AckRequired, end, State#ch{unacked_message_q = UAMQ1, next_tag = DeliveryTag + 1}. +%% NB: returns acks in youngest-first order collect_acks(Q, 0, true) -> - {queue:to_list(Q), queue:new()}; + {lists:reverse(queue:to_list(Q)), queue:new()}; collect_acks(Q, DeliveryTag, Multiple) -> collect_acks([], [], Q, DeliveryTag, Multiple). @@ -1259,10 +1263,7 @@ collect_acks(ToAcc, PrefixAcc, Q, DeliveryTag, Multiple) -> {{value, UnackedMsg = {CurrentDeliveryTag, _ConsumerTag, _Msg}}, QTail} -> if CurrentDeliveryTag == DeliveryTag -> - {case ToAcc of - [] -> [UnackedMsg]; - _ -> lists:reverse([UnackedMsg | ToAcc]) - end, + {[UnackedMsg | ToAcc], case PrefixAcc of [] -> QTail; _ -> queue:join( @@ -1280,6 +1281,7 @@ collect_acks(ToAcc, PrefixAcc, Q, DeliveryTag, Multiple) -> precondition_failed("unknown delivery tag ~w", [DeliveryTag]) end. +%% NB: Acked is in youngest-first order ack(Acked, State = #ch{queue_names = QNames}) -> foreach_per_queue( fun (QPid, MsgIds) -> @@ -1306,10 +1308,12 @@ foreach_per_queue(_F, []) -> ok; foreach_per_queue(F, [{_DTag, _CTag, {QPid, MsgId}}]) -> %% common case F(QPid, [MsgId]); +%% NB: UAL should be in youngest-first order; the tree values will +%% then be in oldest-first order foreach_per_queue(F, UAL) -> T = lists:foldl(fun ({_DTag, _CTag, {QPid, MsgId}}, T) -> rabbit_misc:gb_trees_cons(QPid, MsgId, T) - end, gb_trees:empty(), lists:reverse(UAL)), + end, gb_trees:empty(), UAL), rabbit_misc:gb_trees_foreach(F, T). enable_limiter(State = #ch{unacked_message_q = UAMQ, @@ -1450,10 +1454,8 @@ coalesce_and_send(MsgSeqNos, MkMsgFun, WriterPid, MkMsgFun(SeqNo, false)) || SeqNo <- Ss], State. -append_reverse(L) -> lists:append(lists:reverse(L)). - -ack_cons(Tag, Acked, [{Tag, Acks} | L]) -> [{Tag, [Acked | Acks]} | L]; -ack_cons(Tag, Acked, Acks) -> [{Tag, [Acked]} | Acks]. +ack_cons(Tag, Acked, [{Tag, Acks} | L]) -> [{Tag, Acked ++ Acks} | L]; +ack_cons(Tag, Acked, Acks) -> [{Tag, Acked} | Acks]. maybe_complete_tx(State = #ch{tx = #tx{}}) -> State; -- cgit v1.2.1 From cebfaf63a18858987bd19b0d2dbfa11642392c69 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 7 Jan 2013 11:53:54 +0000 Subject: get rid of #tx{} and fix uncommitted_acks info item --- src/rabbit_channel.erl | 77 +++++++++++++++++++++++++------------------------- 1 file changed, 38 insertions(+), 39 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index cccd09dd..df056a6e 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -40,17 +40,6 @@ queue_collector_pid, stats_timer, confirm_enabled, publish_seqno, unconfirmed, confirmed, capabilities, trace_state}). - --record(tx, {msgs, acks}). %% (1) -%% (1) acks looks s.t. like this: -%% [{false,[5,4]},{true,[3]},{ack,[2,1]}, ...] -%% -%% Each element is a pair consisting of a tag and a list of -%% ack'ed/reject'ed msg ids. The tag is one of 'ack' (to ack), 'true' -%% (reject w requeue), 'false' (reject w/o requeue). The msg ids, as -%% well as the list overall, are in "most-recent (generally youngest) -%% ack first" order. - -define(MAX_PERMISSION_CACHE_SIZE, 12). -define(STATISTICS_KEYS, @@ -631,12 +620,11 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, Delivery = rabbit_basic:delivery(Mandatory, Message, MsgSeqNo), QNames = rabbit_exchange:route(Exchange, Delivery), DQ = {Delivery, QNames}, - {noreply, - case Tx of - none -> deliver_to_queues(DQ, State1); - #tx{msgs = Msgs} -> Msgs1 = queue:in(DQ, Msgs), - State1#ch{tx = Tx#tx{msgs = Msgs1}} - end}; + {noreply, case Tx of + none -> deliver_to_queues(DQ, State1); + {Msgs, Acks} -> Msgs1 = queue:in(DQ, Msgs), + State1#ch{tx = {Msgs1, Acks}} + end}; {error, Reason} -> precondition_failed("invalid message: ~p", [Reason]) end; @@ -652,13 +640,12 @@ handle_method(#'basic.ack'{delivery_tag = DeliveryTag, _, State = #ch{unacked_message_q = UAMQ, tx = Tx}) -> {Acked, Remaining} = collect_acks(UAMQ, DeliveryTag, Multiple), State1 = State#ch{unacked_message_q = Remaining}, - {noreply, - case Tx of - none -> ack(Acked, State1), - State1; - #tx{acks = Acks} -> Acks1 = ack_cons(ack, Acked, Acks), - State1#ch{tx = Tx#tx{acks = Acks1}} - end}; + {noreply, case Tx of + none -> ack(Acked, State1), + State1; + {Msgs, Acks} -> Acks1 = ack_cons(ack, Acked, Acks), + State1#ch{tx = {Msgs, Acks1}} + end}; handle_method(#'basic.get'{queue = QueueNameBin, no_ack = NoAck}, @@ -1042,8 +1029,7 @@ handle_method(#'tx.select'{}, _, State) -> handle_method(#'tx.commit'{}, _, #ch{tx = none}) -> precondition_failed("channel is not transactional"); -handle_method(#'tx.commit'{}, _, State = #ch{tx = #tx{msgs = Msgs, - acks = Acks}, +handle_method(#'tx.commit'{}, _, State = #ch{tx = {Msgs, Acks}, limiter = Limiter}) -> State1 = rabbit_misc:queue_fold(fun deliver_to_queues/2, State, Msgs), lists:foreach( @@ -1056,13 +1042,13 @@ handle_method(#'tx.rollback'{}, _, #ch{tx = none}) -> precondition_failed("channel is not transactional"); handle_method(#'tx.rollback'{}, _, State = #ch{unacked_message_q = UAMQ, - tx = #tx{acks = Acks}}) -> + tx = {_Msgs, Acks}}) -> AcksL = lists:append(lists:reverse([lists:reverse(L) || {_, L} <- Acks])), UAMQ1 = queue:from_list(lists:usort(AcksL ++ queue:to_list(UAMQ))), {reply, #'tx.rollback_ok'{}, State#ch{unacked_message_q = UAMQ1, tx = new_tx()}}; -handle_method(#'confirm.select'{}, _, #ch{tx = #tx{}}) -> +handle_method(#'confirm.select'{}, _, #ch{tx = {_, _}}) -> precondition_failed("cannot switch from tx to confirm mode"); handle_method(#'confirm.select'{nowait = NoWait}, _, State) -> @@ -1213,13 +1199,12 @@ reject(DeliveryTag, Requeue, Multiple, State = #ch{unacked_message_q = UAMQ, tx = Tx}) -> {Acked, Remaining} = collect_acks(UAMQ, DeliveryTag, Multiple), State1 = State#ch{unacked_message_q = Remaining}, - {noreply, - case Tx of - none -> reject(Requeue, Acked, State1#ch.limiter), - State1; - #tx{acks = Acks} -> Acks1 = ack_cons(Requeue, Acked, Acks), - State1#ch{tx = Tx#tx{acks = Acks1}} - end}. + {noreply, case Tx of + none -> reject(Requeue, Acked, State1#ch.limiter), + State1; + {Msgs, Acks} -> Acks1 = ack_cons(Requeue, Acked, Acks), + State1#ch{tx = {Msgs, Acks1}} + end}. %% NB: Acked is in youngest-first order reject(Requeue, Acked, Limiter) -> @@ -1294,7 +1279,19 @@ ack(Acked, State = #ch{queue_names = QNames}) -> end, Acked), ok = notify_limiter(State#ch.limiter, Acked). -new_tx() -> #tx{msgs = queue:new(), acks = []}. +%% {Msgs, Acks} +%% +%% Msgs is a queue. +%% +%% Acks looks s.t. like this: +%% [{false,[5,4]},{true,[3]},{ack,[2,1]}, ...] +%% +%% Each element is a pair consisting of a tag and a list of +%% ack'ed/reject'ed msg ids. The tag is one of 'ack' (to ack), 'true' +%% (reject w requeue), 'false' (reject w/o requeue). The msg ids, as +%% well as the list overall, are in "most-recent (generally youngest) +%% ack first" order. +new_tx() -> {queue:new(), []}. notify_queues(State = #ch{state = closing}) -> {ok, State}; @@ -1457,7 +1454,9 @@ coalesce_and_send(MsgSeqNos, MkMsgFun, ack_cons(Tag, Acked, [{Tag, Acks} | L]) -> [{Tag, Acked ++ Acks} | L]; ack_cons(Tag, Acked, Acks) -> [{Tag, Acked} | Acks]. -maybe_complete_tx(State = #ch{tx = #tx{}}) -> +ack_len(Acks) -> lists:sum([length(L) || {ack, L} <- Acks]). + +maybe_complete_tx(State = #ch{tx = {_, _}}) -> State; maybe_complete_tx(State = #ch{unconfirmed = UC}) -> case dtree:is_empty(UC) of @@ -1489,9 +1488,9 @@ i(name, State) -> name(State); i(consumer_count, #ch{consumer_mapping = CM}) -> dict:size(CM); i(messages_unconfirmed, #ch{unconfirmed = UC}) -> dtree:size(UC); i(messages_unacknowledged, #ch{unacked_message_q = UAMQ}) -> queue:len(UAMQ); -i(messages_uncommitted, #ch{tx = #tx{msgs = Msgs}}) -> queue:len(Msgs); +i(messages_uncommitted, #ch{tx = {Msgs, _Acks}}) -> queue:len(Msgs); i(messages_uncommitted, #ch{}) -> 0; -i(acks_uncommitted, #ch{tx = #tx{acks = Acks}}) -> length(Acks); +i(acks_uncommitted, #ch{tx = {_Msgs, Acks}}) -> ack_len(Acks); i(acks_uncommitted, #ch{}) -> 0; i(prefetch_count, #ch{limiter = Limiter}) -> rabbit_limiter:get_limit(Limiter); -- cgit v1.2.1 From 259f60e0bbec5388c3834fe18ff49cf82d7cf575 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 7 Jan 2013 11:58:54 +0000 Subject: oops --- src/rabbit_channel.erl | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index df056a6e..88e3dfc5 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -1032,10 +1032,9 @@ handle_method(#'tx.commit'{}, _, #ch{tx = none}) -> handle_method(#'tx.commit'{}, _, State = #ch{tx = {Msgs, Acks}, limiter = Limiter}) -> State1 = rabbit_misc:queue_fold(fun deliver_to_queues/2, State, Msgs), - lists:foreach( - fun ({ack, A}) -> ack(lists:reverse(A), State1); - ({Requeue, A}) -> reject(Requeue, lists:reverse(A), Limiter) - end, lists:reverse(Acks)), + lists:foreach(fun ({ack, A}) -> ack(A, State1); + ({Requeue, A}) -> reject(Requeue, A, Limiter) + end, lists:reverse(Acks)), {noreply, maybe_complete_tx(State1#ch{tx = committing})}; handle_method(#'tx.rollback'{}, _, #ch{tx = none}) -> -- cgit v1.2.1 From 461310b1d1a7205b9fcaa50ff41a2dd32e51b869 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 7 Jan 2013 12:39:21 +0000 Subject: Idempotence --- src/rabbit_amqqueue_process.erl | 2 +- src/rabbit_control_main.erl | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 64d55684..bb1a5f86 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1166,7 +1166,7 @@ handle_call(sync_mirrors, _From, State) -> %% By definition if we get this message here we do not have to do anything. handle_call(cancel_sync_mirrors, _From, State) -> - reply({error, not_syncing}, State); + reply({ok, not_syncing}, State); handle_call(force_event_refresh, _From, State = #q{exclusive_consumer = Exclusive}) -> diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index 9f48877c..0f1620bf 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -161,6 +161,12 @@ start() -> false -> io:format("...done.~n") end, rabbit_misc:quit(0); + {ok, Info} -> + case Quiet of + true -> ok; + false -> io:format("...done (~p).~n", [Info]) + end, + rabbit_misc:quit(0); {'EXIT', {function_clause, [{?MODULE, action, _} | _]}} -> %% < R15 PrintInvalidCommandError(), usage(); -- cgit v1.2.1 From 07de428feb957283231fc94eb13e9d722ccccf2d Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 7 Jan 2013 15:13:44 +0000 Subject: tweak: make use of rabbit_misc:rs/1 --- src/rabbit_control_main.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index 5bde996e..6ccd5011 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -283,9 +283,9 @@ action(forget_cluster_node, Node, [ClusterNodeS], Opts, Inform) -> action(sync_queue, Node, [Q], Opts, Inform) -> VHost = proplists:get_value(?VHOST_OPT, Opts), - Inform("Synchronising queue \"~s\" in vhost \"~s\"", [Q, VHost]), - rpc_call(Node, rabbit_control_main, sync_queue, - [rabbit_misc:r(list_to_binary(VHost), queue, list_to_binary(Q))]); + QName = rabbit_misc:r(list_to_binary(VHost), queue, list_to_binary(Q)), + Inform("Synchronising ~s", [rabbit_misc:rs(QName)]), + rpc_call(Node, rabbit_control_main, sync_queue, [QName]); action(wait, Node, [PidFile], _Opts, Inform) -> Inform("Waiting for ~p", [Node]), -- cgit v1.2.1 From f3a2cac4dad2c9e8cdbc3576e8b4e5efa1029471 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 7 Jan 2013 15:16:20 +0000 Subject: tweak: make use of rabbit_misc:rs/1 --- src/rabbit_control_main.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index 8cd9f83b..fc9c41a4 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -296,9 +296,9 @@ action(sync_queue, Node, [Q], Opts, Inform) -> action(cancel_sync_queue, Node, [Q], Opts, Inform) -> VHost = proplists:get_value(?VHOST_OPT, Opts), - Inform("Stopping synchronising queue ~s in ~s", [Q, VHost]), - rpc_call(Node, rabbit_control_main, cancel_sync_queue, - [rabbit_misc:r(list_to_binary(VHost), queue, list_to_binary(Q))]); + QName = rabbit_misc:r(list_to_binary(VHost), queue, list_to_binary(Q)), + Inform("Stopping synchronising ~s", [rabbit_misc:rs(QName)]), + rpc_call(Node, rabbit_control_main, cancel_sync_queue, [QName]); action(wait, Node, [PidFile], _Opts, Inform) -> Inform("Waiting for ~p", [Node]), -- cgit v1.2.1 From 89a5c13172f05437e9f4fef0ee4e271b2e6dde28 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 7 Jan 2013 17:37:40 +0000 Subject: Resurrect --- src/rabbit_limiter.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index f102c3b9..62bcecf3 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -346,3 +346,4 @@ notify_queues(State = #lim{ch_pid = ChPid, queues = Queues}) -> ok end, State#lim{queues = NewQueues}. + -- cgit v1.2.1 From 7c0751538285d150f4ebbf2d6536f2b09b2ec26d Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 7 Jan 2013 22:19:33 +0000 Subject: get rid of vq's ram_ack_index and instead track ram and disk acks in separate gb_trees, which is more efficient. --- src/rabbit_variable_queue.erl | 131 +++++++++++++++++++++++------------------- 1 file changed, 72 insertions(+), 59 deletions(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 37ca6de0..59acd194 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -255,8 +255,8 @@ q3, q4, next_seq_id, - pending_ack, - ram_ack_index, + ram_pending_ack, + disk_pending_ack, index_state, msg_store_clients, durable, @@ -348,8 +348,8 @@ q3 :: ?QUEUE:?QUEUE(), q4 :: ?QUEUE:?QUEUE(), next_seq_id :: seq_id(), - pending_ack :: gb_tree(), - ram_ack_index :: gb_set(), + ram_pending_ack :: gb_tree(), + disk_pending_ack :: gb_tree(), index_state :: any(), msg_store_clients :: 'undefined' | {{any(), binary()}, {any(), binary()}}, @@ -670,12 +670,11 @@ requeue(AckTags, #vqstate { delta = Delta, ackfold(MsgFun, Acc, State, AckTags) -> {AccN, StateN} = - lists:foldl( - fun(SeqId, {Acc0, State0 = #vqstate{ pending_ack = PA }}) -> - MsgStatus = gb_trees:get(SeqId, PA), - {Msg, State1} = read_msg(MsgStatus, false, State0), - {MsgFun(Msg, SeqId, Acc0), State1} - end, {Acc, State}, AckTags), + lists:foldl(fun(SeqId, {Acc0, State0}) -> + MsgStatus = lookup_pending_ack(SeqId, State0), + {Msg, State1} = read_msg(MsgStatus, false, State0), + {MsgFun(Msg, SeqId, Acc0), State1} + end, {Acc, State}, AckTags), {AccN, a(StateN)}. fold(Fun, Acc, #vqstate { q1 = Q1, @@ -702,8 +701,8 @@ len(#vqstate { len = Len }) -> Len. is_empty(State) -> 0 == len(State). -depth(State = #vqstate { pending_ack = Ack }) -> - len(State) + gb_trees:size(Ack). +depth(State = #vqstate { ram_pending_ack = RPA, disk_pending_ack = DPA }) -> + len(State) + gb_trees:size(RPA) + gb_trees:size(DPA). set_ram_duration_target( DurationTarget, State = #vqstate { @@ -740,7 +739,7 @@ ram_duration(State = #vqstate { ack_out_counter = AckOutCount, ram_msg_count = RamMsgCount, ram_msg_count_prev = RamMsgCountPrev, - ram_ack_index = RamAckIndex, + ram_pending_ack = RPA, ram_ack_count_prev = RamAckCountPrev }) -> Now = now(), {AvgEgressRate, Egress1} = update_rate(Now, Timestamp, OutCount, Egress), @@ -751,7 +750,7 @@ ram_duration(State = #vqstate { {AvgAckIngressRate, AckIngress1} = update_rate(Now, AckTimestamp, AckInCount, AckIngress), - RamAckCount = gb_sets:size(RamAckIndex), + RamAckCount = gb_trees:size(RPA), Duration = %% msgs+acks / (msgs+acks/sec) == sec case (AvgEgressRate == 0 andalso AvgIngressRate == 0 andalso @@ -811,8 +810,8 @@ handle_pre_hibernate(State = #vqstate { index_state = IndexState }) -> status(#vqstate { q1 = Q1, q2 = Q2, delta = Delta, q3 = Q3, q4 = Q4, len = Len, - pending_ack = PA, - ram_ack_index = RAI, + ram_pending_ack = RPA, + disk_pending_ack = DPA, target_ram_count = TargetRamCount, ram_msg_count = RamMsgCount, next_seq_id = NextSeqId, @@ -827,10 +826,10 @@ status(#vqstate { {q3 , ?QUEUE:len(Q3)}, {q4 , ?QUEUE:len(Q4)}, {len , Len}, - {pending_acks , gb_trees:size(PA)}, + {pending_acks , gb_trees:size(RPA) + gb_trees:size(DPA)}, {target_ram_count , TargetRamCount}, {ram_msg_count , RamMsgCount}, - {ram_ack_count , gb_sets:size(RAI)}, + {ram_ack_count , gb_trees:size(RPA)}, {next_seq_id , NextSeqId}, {persistent_count , PersistentCount}, {avg_ingress_rate , AvgIngressRate}, @@ -962,7 +961,7 @@ maybe_write_delivered(false, _SeqId, IndexState) -> maybe_write_delivered(true, SeqId, IndexState) -> rabbit_queue_index:deliver([SeqId], IndexState). -betas_from_index_entries(List, TransientThreshold, PA, IndexState) -> +betas_from_index_entries(List, TransientThreshold, RPA, DPA, IndexState) -> {Filtered, Delivers, Acks} = lists:foldr( fun ({MsgId, SeqId, MsgProps, IsPersistent, IsDelivered}, @@ -971,7 +970,8 @@ betas_from_index_entries(List, TransientThreshold, PA, IndexState) -> true -> {Filtered1, cons_if(not IsDelivered, SeqId, Delivers1), [SeqId | Acks1]}; - false -> case gb_trees:is_defined(SeqId, PA) of + false -> case (gb_trees:is_defined(SeqId, RPA) orelse + gb_trees:is_defined(SeqId, DPA)) of false -> {?QUEUE:in_r( m(#msg_status { @@ -1033,8 +1033,8 @@ init(IsDurable, IndexState, DeltaCount, Terms, AsyncCallback, q3 = ?QUEUE:new(), q4 = ?QUEUE:new(), next_seq_id = NextSeqId, - pending_ack = gb_trees:empty(), - ram_ack_index = gb_sets:empty(), + ram_pending_ack = gb_trees:empty(), + disk_pending_ack = gb_trees:empty(), index_state = IndexState1, msg_store_clients = {PersistentClient, TransientClient}, durable = IsDurable, @@ -1248,33 +1248,47 @@ maybe_write_to_disk(ForceMsg, ForceIndex, MsgStatus, %%---------------------------------------------------------------------------- record_pending_ack(#msg_status { seq_id = SeqId, msg = Msg } = MsgStatus, - State = #vqstate { pending_ack = PA, - ram_ack_index = RAI, - ack_in_counter = AckInCount}) -> - RAI1 = case Msg of - undefined -> RAI; - _ -> gb_sets:insert(SeqId, RAI) - end, - State #vqstate { pending_ack = gb_trees:insert(SeqId, MsgStatus, PA), - ram_ack_index = RAI1, - ack_in_counter = AckInCount + 1}. + State = #vqstate { ram_pending_ack = RPA, + disk_pending_ack = DPA, + ack_in_counter = AckInCount}) -> + {RPA1, DPA1} = + case Msg of + undefined -> {RPA, gb_trees:insert(SeqId, MsgStatus, DPA)}; + _ -> {gb_trees:insert(SeqId, MsgStatus, RPA), DPA} + end, + State #vqstate { ram_pending_ack = RPA1, + disk_pending_ack = DPA1, + ack_in_counter = AckInCount + 1}. + +lookup_pending_ack(SeqId, #vqstate { ram_pending_ack = RPA, + disk_pending_ack = DPA }) -> + case gb_trees:lookup(SeqId, RPA) of + {value, V} -> V; + none -> gb_trees:get(SeqId, DPA) + end. -remove_pending_ack(SeqId, State = #vqstate { pending_ack = PA, - ram_ack_index = RAI }) -> - {gb_trees:get(SeqId, PA), - State #vqstate { pending_ack = gb_trees:delete(SeqId, PA), - ram_ack_index = gb_sets:delete_any(SeqId, RAI) }}. +remove_pending_ack(SeqId, State = #vqstate { ram_pending_ack = RPA, + disk_pending_ack = DPA }) -> + case gb_trees:lookup(SeqId, RPA) of + {value, V} -> RPA1 = gb_trees:delete(SeqId, RPA), + {V, State #vqstate { ram_pending_ack = RPA1 }}; + none -> DPA1 = gb_trees:delete(SeqId, DPA), + {gb_trees:get(SeqId, DPA), + State #vqstate { disk_pending_ack = DPA1 }} + end. purge_pending_ack(KeepPersistent, - State = #vqstate { pending_ack = PA, + State = #vqstate { ram_pending_ack = RPA, + disk_pending_ack = DPA, index_state = IndexState, msg_store_clients = MSCState }) -> + F = fun (_SeqId, MsgStatus, Acc) -> accumulate_ack(MsgStatus, Acc) end, {IndexOnDiskSeqIds, MsgIdsByStore, _AllMsgIds} = - rabbit_misc:gb_trees_fold(fun (_SeqId, MsgStatus, Acc) -> - accumulate_ack(MsgStatus, Acc) - end, accumulate_ack_init(), PA), - State1 = State #vqstate { pending_ack = gb_trees:empty(), - ram_ack_index = gb_sets:empty() }, + rabbit_misc:gb_trees_fold( + F, rabbit_misc:gb_trees_fold(F, accumulate_ack_init(), RPA), DPA), + State1 = State #vqstate { ram_pending_ack = gb_trees:empty(), + disk_pending_ack = gb_trees:empty() }, + case KeepPersistent of true -> case orddict:find(false, MsgIdsByStore) of error -> State1; @@ -1500,7 +1514,7 @@ reduce_memory_use(_AlphaBetaFun, _BetaDeltaFun, _AckFun, {false, State}; reduce_memory_use(AlphaBetaFun, BetaDeltaFun, AckFun, State = #vqstate { - ram_ack_index = RamAckIndex, + ram_pending_ack = RPA, ram_msg_count = RamMsgCount, target_ram_count = TargetRamCount, rates = #rates { avg_ingress = AvgIngress, @@ -1510,8 +1524,7 @@ reduce_memory_use(AlphaBetaFun, BetaDeltaFun, AckFun, }) -> {Reduce, State1 = #vqstate { q2 = Q2, q3 = Q3 }} = - case chunk_size(RamMsgCount + gb_sets:size(RamAckIndex), - TargetRamCount) of + case chunk_size(RamMsgCount + gb_trees:size(RPA), TargetRamCount) of 0 -> {false, State}; %% Reduce memory of pending acks and alphas. The order is %% determined based on which is growing faster. Whichever @@ -1536,20 +1549,19 @@ reduce_memory_use(AlphaBetaFun, BetaDeltaFun, AckFun, limit_ram_acks(0, State) -> {0, State}; -limit_ram_acks(Quota, State = #vqstate { pending_ack = PA, - ram_ack_index = RAI }) -> - case gb_sets:is_empty(RAI) of +limit_ram_acks(Quota, State = #vqstate { ram_pending_ack = RPA, + disk_pending_ack = DPA }) -> + case gb_trees:is_empty(RPA) of true -> {Quota, State}; false -> - {SeqId, RAI1} = gb_sets:take_largest(RAI), - MsgStatus = gb_trees:get(SeqId, PA), + {SeqId, MsgStatus, RPA1} = gb_trees:take_largest(RPA), {MsgStatus1, State1} = maybe_write_to_disk(true, false, MsgStatus, State), - PA1 = gb_trees:update(SeqId, m(trim_msg_status(MsgStatus1)), PA), + DPA1 = gb_trees:insert(SeqId, m(trim_msg_status(MsgStatus1)), DPA), limit_ram_acks(Quota - 1, - State1 #vqstate { pending_ack = PA1, - ram_ack_index = RAI1 }) + State1 #vqstate { ram_pending_ack = RPA1, + disk_pending_ack = DPA1 }) end. reduce_memory_use(State) -> @@ -1617,7 +1629,8 @@ maybe_deltas_to_betas(State = #vqstate { delta = Delta, q3 = Q3, index_state = IndexState, - pending_ack = PA, + ram_pending_ack = RPA, + disk_pending_ack = DPA, transient_threshold = TransientThreshold }) -> #delta { start_seq_id = DeltaSeqId, count = DeltaCount, @@ -1625,10 +1638,10 @@ maybe_deltas_to_betas(State = #vqstate { DeltaSeqId1 = lists:min([rabbit_queue_index:next_segment_boundary(DeltaSeqId), DeltaSeqIdEnd]), - {List, IndexState1} = - rabbit_queue_index:read(DeltaSeqId, DeltaSeqId1, IndexState), - {Q3a, IndexState2} = - betas_from_index_entries(List, TransientThreshold, PA, IndexState1), + {List, IndexState1} = rabbit_queue_index:read(DeltaSeqId, DeltaSeqId1, + IndexState), + {Q3a, IndexState2} = betas_from_index_entries(List, TransientThreshold, + RPA, DPA, IndexState1), State1 = State #vqstate { index_state = IndexState2 }, case ?QUEUE:len(Q3a) of 0 -> -- cgit v1.2.1 From 547d0e785e915ee18ca379a610a66097d8f10010 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 7 Jan 2013 23:05:17 +0000 Subject: optimise vq:reduce_memory_use/4 invocations by suppressing the call, and thus the closure creations, when target_ram_count == infinity --- src/rabbit_variable_queue.erl | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 37ca6de0..6ac7feaa 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -783,19 +783,24 @@ ram_duration(State = #vqstate { ram_msg_count_prev = RamMsgCount, ram_ack_count_prev = RamAckCount }}. -needs_timeout(State = #vqstate { index_state = IndexState }) -> +needs_timeout(State = #vqstate { index_state = IndexState, + target_ram_count = TargetRamCount }) -> case must_sync_index(State) of true -> timed; false -> case rabbit_queue_index:needs_sync(IndexState) of true -> idle; - false -> case reduce_memory_use( - fun (_Quota, State1) -> {0, State1} end, - fun (_Quota, State1) -> State1 end, - fun (_Quota, State1) -> {0, State1} end, - State) of - {true, _State} -> idle; - {false, _State} -> false + false -> case TargetRamCount of + infinity -> false; + _ -> case + reduce_memory_use( + fun (_Quota, State1) -> {0, State1} end, + fun (_Quota, State1) -> State1 end, + fun (_Quota, State1) -> {0, State1} end, + State) of + {true, _State} -> idle; + {false, _State} -> false + end end end end. @@ -1495,9 +1500,6 @@ delta_fold( Fun, {cont, Acc}, DeltaSeqId, DeltaSeqIdEnd, %% one segment's worth of messages in q3 - and thus would risk %% perpetually reporting the need for a conversion when no such %% conversion is needed. That in turn could cause an infinite loop. -reduce_memory_use(_AlphaBetaFun, _BetaDeltaFun, _AckFun, - State = #vqstate {target_ram_count = infinity}) -> - {false, State}; reduce_memory_use(AlphaBetaFun, BetaDeltaFun, AckFun, State = #vqstate { ram_ack_index = RamAckIndex, @@ -1552,6 +1554,8 @@ limit_ram_acks(Quota, State = #vqstate { pending_ack = PA, ram_ack_index = RAI1 }) end. +reduce_memory_use(State = #vqstate { target_ram_count = infinity }) -> + State; reduce_memory_use(State) -> {_, State1} = reduce_memory_use(fun push_alphas_to_betas/2, fun push_betas_to_deltas/2, -- cgit v1.2.1 From 47b8892946f50f66fe15f4d8db24bcd4acad074f Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 8 Jan 2013 14:39:48 +0000 Subject: Stub for 1.0 support. --- src/rabbit_connection_sup.erl | 9 ++++++++- src/rabbit_reader.erl | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/src/rabbit_connection_sup.erl b/src/rabbit_connection_sup.erl index 12a532b6..f4f3c72f 100644 --- a/src/rabbit_connection_sup.erl +++ b/src/rabbit_connection_sup.erl @@ -42,10 +42,17 @@ start_link() -> SupPid, {collector, {rabbit_queue_collector, start_link, []}, intrinsic, ?MAX_WAIT, worker, [rabbit_queue_collector]}), + %% Note that rabbit_amqp1_0_session_sup_sup despite the name can + %% mimic rabbit_channel_sup_sup when we handle a 0-9-1 connection + %% and the 1.0 plugin is loaded. + ChannelSupSupModule = case code:is_loaded(rabbit_amqp1_0_session_sup_sup) of + false -> rabbit_channel_sup_sup; + _ -> rabbit_amqp1_0_session_sup_sup + end, {ok, ChannelSupSupPid} = supervisor2:start_child( SupPid, - {channel_sup_sup, {rabbit_channel_sup_sup, start_link, []}, + {channel_sup_sup, {ChannelSupSupModule, start_link, []}, intrinsic, infinity, supervisor, [rabbit_channel_sup_sup]}), {ok, ReaderPid} = supervisor2:start_child( diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 13e8feff..f140bd23 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -689,6 +689,15 @@ handle_input(handshake, <<"AMQP", 1, 1, 8, 0>>, State) -> handle_input(handshake, <<"AMQP", 1, 1, 9, 1>>, State) -> start_connection({8, 0, 0}, rabbit_framing_amqp_0_8, State); +%% ... and finally, the 1.0 spec is crystal clear! Note that the +%% FIXME TLS uses a different protocol number, and would go here. +handle_input(handshake, <<"AMQP", 0, 1, 0, 0>>, State) -> + become_1_0(amqp, [0, 1, 0, 0], State); + +%% 3 stands for "SASL" +handle_input(handshake, <<"AMQP", 3, 1, 0, 0>>, State) -> + become_1_0(sasl, [0, 3, 0, 0], State); + handle_input(handshake, <<"AMQP", A, B, C, D>>, #v1{sock = Sock}) -> refuse_connection(Sock, {bad_version, A, B, C, D}); @@ -981,3 +990,28 @@ cert_info(F, #v1{sock = Sock}) -> emit_stats(State) -> rabbit_event:notify(connection_stats, infos(?STATISTICS_KEYS, State)), rabbit_event:reset_stats_timer(State, #v1.stats_timer). + +%% 1.0 stub + +become_1_0(Mode, HandshakeBytes, State = #v1{sock = Sock}) -> + case code:is_loaded(rabbit_amqp1_0_reader) of + false -> refuse_connection( + Sock, list_to_tuple([bad_version | HandshakeBytes])); + _ -> apply0(rabbit_amqp1_0_reader, become, + [Mode, pack_for_1_0(State)]) + end. + +%% Fool xref. Simply using apply(M, F, A) with constants is not enough. +apply0(M, F, A) -> apply(M, F, A). + +pack_for_1_0(#v1{parent = Parent, + sock = Sock, + recv_len = RecvLen, + pending_recv = PendingRecv, + queue_collector = QueueCollector, + channel_sup_sup_pid = ChannelSupSupPid, + start_heartbeat_fun = SHF, + buf = Buf, + buf_len = BufLen}) -> + {Parent, Sock, RecvLen, PendingRecv, QueueCollector, + ChannelSupSupPid, SHF, Buf, BufLen}. -- cgit v1.2.1 From 01a0f6109f9e8ef9f29379755f7212d86f1508c1 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 8 Jan 2013 15:12:21 +0000 Subject: Fix docs and specs. --- docs/rabbitmqctl.1.xml | 2 +- src/rabbit_amqqueue.erl | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/rabbitmqctl.1.xml b/docs/rabbitmqctl.1.xml index 31921769..c7069aed 100644 --- a/docs/rabbitmqctl.1.xml +++ b/docs/rabbitmqctl.1.xml @@ -1162,7 +1162,7 @@ status The status of the queue. Normally - 'running', but may be different if the queue is + 'running', but may be "{syncing, MsgCount}" if the queue is synchronising. diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 33c2cd62..2477b891 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -176,8 +176,7 @@ -spec(stop_mirroring/1 :: (pid()) -> 'ok'). -spec(sync_mirrors/1 :: (pid()) -> 'ok' | rabbit_types:error('pending_acks' | 'not_mirrored')). --spec(cancel_sync_mirrors/1 :: (pid()) -> - 'ok' | rabbit_types:error('not_mirrored')). +-spec(cancel_sync_mirrors/1 :: (pid()) -> 'ok' | {'ok', 'not_syncing'}). -endif. -- cgit v1.2.1 From 3c9cd7c346a546a717bf7b06f8f92f722263c216 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 8 Jan 2013 16:28:13 +0000 Subject: copy change from rabbit_amqqueue_process to slave for consistency --- src/rabbit_mirror_queue_slave.erl | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 1ba1420f..21b4407d 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -239,8 +239,11 @@ handle_info(update_ram_duration, DesiredDuration = rabbit_memory_monitor:report_ram_duration(self(), RamDuration), BQS2 = BQ:set_ram_duration_target(DesiredDuration, BQS1), - noreply(State #state { rate_timer_ref = just_measured, - backing_queue_state = BQS2 }); + %% Don't call noreply/1, we don't want to set timers + {State1, Timeout} = next_state(State #state { + rate_timer_ref = undefined, + backing_queue_state = BQS2 }), + {noreply, State1, Timeout}; handle_info(sync_timeout, State) -> noreply(backing_queue_timeout( @@ -542,17 +545,16 @@ promote_me(From, #state { q = Q = #amqqueue { name = QName }, noreply(State) -> {NewState, Timeout} = next_state(State), - {noreply, NewState, Timeout}. + {noreply, ensure_rate_timer(NewState), Timeout}. reply(Reply, State) -> {NewState, Timeout} = next_state(State), - {reply, Reply, NewState, Timeout}. + {reply, Reply, ensure_rate_timer(NewState), Timeout}. next_state(State = #state{backing_queue = BQ, backing_queue_state = BQS}) -> {MsgIds, BQS1} = BQ:drain_confirmed(BQS), - State1 = ensure_rate_timer( - confirm_messages(MsgIds, State #state { - backing_queue_state = BQS1 })), + State1 = confirm_messages(MsgIds, + State #state { backing_queue_state = BQS1 }), case BQ:needs_timeout(BQS1) of false -> {stop_sync_timer(State1), hibernate }; idle -> {stop_sync_timer(State1), ?SYNC_INTERVAL}; -- cgit v1.2.1 From 4baa89636d8f36cb8a1991f4a9059fca16242f83 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 8 Jan 2013 16:48:04 +0000 Subject: tidy up --- src/rabbit_mirror_queue_slave.erl | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 21b4407d..5fbda9c5 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -580,15 +580,11 @@ ensure_rate_timer(State = #state { rate_timer_ref = undefined }) -> TRef = erlang:send_after(?RAM_DURATION_UPDATE_INTERVAL, self(), update_ram_duration), State #state { rate_timer_ref = TRef }; -ensure_rate_timer(State = #state { rate_timer_ref = just_measured }) -> - State #state { rate_timer_ref = undefined }; ensure_rate_timer(State) -> State. stop_rate_timer(State = #state { rate_timer_ref = undefined }) -> State; -stop_rate_timer(State = #state { rate_timer_ref = just_measured }) -> - State #state { rate_timer_ref = undefined }; stop_rate_timer(State = #state { rate_timer_ref = TRef }) -> erlang:cancel_timer(TRef), State #state { rate_timer_ref = undefined }. -- cgit v1.2.1 From 0e929121c541ca467d9a30cc48b981b5f61ca997 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 8 Jan 2013 18:04:47 +0000 Subject: rationalise timer maintenance - introduce a couple of helper functions and use them wherever we can - pay attention to the result of timer cancellation, to prevent duplicate timer creation - stop all timers when a queue terminates. More out of politeness than necessity. --- src/rabbit_amqqueue_process.erl | 86 +++++++++++++++++---------------------- src/rabbit_mirror_queue_slave.erl | 28 ++++--------- src/rabbit_misc.erl | 19 +++++++++ src/rabbit_msg_store.erl | 13 +++--- 4 files changed, 70 insertions(+), 76 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 536ed48a..2293d001 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -254,7 +254,11 @@ init_dlx_routing_key(RoutingKey, State) -> terminate_shutdown(Fun, State) -> State1 = #q{backing_queue_state = BQS} = - stop_sync_timer(stop_rate_timer(State)), + lists:foldl(fun (F, S) -> F(S) end, State, + [fun stop_sync_timer/1, + fun stop_rate_timer/1, + fun stop_expiry_timer/1, + fun stop_ttl_timer/1]), case BQS of undefined -> State1; _ -> ok = rabbit_memory_monitor:deregister(self()), @@ -289,36 +293,18 @@ backing_queue_module(Q) -> true -> rabbit_mirror_queue_master end. -ensure_sync_timer(State = #q{sync_timer_ref = undefined}) -> - TRef = erlang:send_after(?SYNC_INTERVAL, self(), sync_timeout), - State#q{sync_timer_ref = TRef}; ensure_sync_timer(State) -> - State. + rabbit_misc:ensure_timer(State, #q.sync_timer_ref, + ?SYNC_INTERVAL, sync_timeout). -stop_sync_timer(State = #q{sync_timer_ref = undefined}) -> - State; -stop_sync_timer(State = #q{sync_timer_ref = TRef}) -> - erlang:cancel_timer(TRef), - State#q{sync_timer_ref = undefined}. - -ensure_rate_timer(State = #q{rate_timer_ref = undefined}) -> - TRef = erlang:send_after( - ?RAM_DURATION_UPDATE_INTERVAL, self(), update_ram_duration), - State#q{rate_timer_ref = TRef}; -ensure_rate_timer(State) -> - State. +stop_sync_timer(State) -> rabbit_misc:stop_timer(State, #q.sync_timer_ref). -stop_rate_timer(State = #q{rate_timer_ref = undefined}) -> - State; -stop_rate_timer(State = #q{rate_timer_ref = TRef}) -> - erlang:cancel_timer(TRef), - State#q{rate_timer_ref = undefined}. +ensure_rate_timer(State) -> + rabbit_misc:ensure_timer(State, #q.rate_timer_ref, + ?RAM_DURATION_UPDATE_INTERVAL, + update_ram_duration). -stop_expiry_timer(State = #q{expiry_timer_ref = undefined}) -> - State; -stop_expiry_timer(State = #q{expiry_timer_ref = TRef}) -> - erlang:cancel_timer(TRef), - State#q{expiry_timer_ref = undefined}. +stop_rate_timer(State) -> rabbit_misc:stop_timer(State, #q.rate_timer_ref). %% We wish to expire only when there are no consumers *and* the expiry %% hasn't been refreshed (by queue.declare or basic.get) for the @@ -328,11 +314,34 @@ ensure_expiry_timer(State = #q{expires = undefined}) -> ensure_expiry_timer(State = #q{expires = Expires}) -> case is_unused(State) of true -> NewState = stop_expiry_timer(State), - TRef = erlang:send_after(Expires, self(), maybe_expire), - NewState#q{expiry_timer_ref = TRef}; + rabbit_misc:ensure_timer(NewState, #q.expiry_timer_ref, + Expires, maybe_expire); false -> State end. +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}) -> + 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), + State#q{ttl_timer_ref = TRef, ttl_timer_expiry = Expiry}; +ensure_ttl_timer(Expiry, State = #q{ttl_timer_ref = TRef, + ttl_timer_expiry = TExpiry}) + when Expiry + 1000 < TExpiry -> + case erlang:cancel_timer(TRef) of + false -> State; + _ -> ensure_ttl_timer(Expiry, State#q{ttl_timer_ref = undefined}) + end; +ensure_ttl_timer(_Expiry, State) -> + State. + +stop_ttl_timer(State) -> rabbit_misc:stop_timer(State, #q.ttl_timer_ref). + ensure_stats_timer(State) -> rabbit_event:ensure_stats_timer(State, #q.stats_timer, emit_stats). @@ -764,25 +773,6 @@ dead_letter_msgs(Fun, Reason, X, State = #q{dlx_routing_key = RK, queue_monitors = QMons1, backing_queue_state = BQS2}}. -ensure_ttl_timer(undefined, State) -> - State; -ensure_ttl_timer(Expiry, State = #q{ttl_timer_ref = undefined}) -> - 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), - State#q{ttl_timer_ref = TRef, ttl_timer_expiry = Expiry}; -ensure_ttl_timer(Expiry, State = #q{ttl_timer_ref = TRef, - ttl_timer_expiry = TExpiry}) - when Expiry + 1000 < TExpiry -> - case erlang:cancel_timer(TRef) of - false -> State; - _ -> ensure_ttl_timer(Expiry, State#q{ttl_timer_ref = undefined}) - end; -ensure_ttl_timer(_Expiry, State) -> - State. - dead_letter_publish(Msg, Reason, X, RK, MsgSeqNo, QName) -> DLMsg = make_dead_letter_msg(Msg, Reason, X#exchange.name, RK, QName), Delivery = rabbit_basic:delivery(false, DLMsg, MsgSeqNo), diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index feddf45a..9f12b34e 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -589,30 +589,18 @@ next_state(State = #state{backing_queue = BQ, backing_queue_state = BQS}) -> backing_queue_timeout(State = #state { backing_queue = BQ }) -> run_backing_queue(BQ, fun (M, BQS) -> M:timeout(BQS) end, State). -ensure_sync_timer(State = #state { sync_timer_ref = undefined }) -> - TRef = erlang:send_after(?SYNC_INTERVAL, self(), sync_timeout), - State #state { sync_timer_ref = TRef }; ensure_sync_timer(State) -> - State. + rabbit_misc:ensure_timer(State, #state.sync_timer_ref, + ?SYNC_INTERVAL, sync_timeout). + +stop_sync_timer(State) -> rabbit_misc:stop_timer(State, #state.sync_timer_ref). -stop_sync_timer(State = #state { sync_timer_ref = undefined }) -> - State; -stop_sync_timer(State = #state { sync_timer_ref = TRef }) -> - erlang:cancel_timer(TRef), - State #state { sync_timer_ref = undefined }. - -ensure_rate_timer(State = #state { rate_timer_ref = undefined }) -> - TRef = erlang:send_after(?RAM_DURATION_UPDATE_INTERVAL, - self(), update_ram_duration), - State #state { rate_timer_ref = TRef }; ensure_rate_timer(State) -> - State. + rabbit_misc:ensure_timer(State, #state.rate_timer_ref, + ?RAM_DURATION_UPDATE_INTERVAL, + update_ram_duration). -stop_rate_timer(State = #state { rate_timer_ref = undefined }) -> - State; -stop_rate_timer(State = #state { rate_timer_ref = TRef }) -> - erlang:cancel_timer(TRef), - State #state { rate_timer_ref = undefined }. +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) }. diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index ce3e3802..73a4c922 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -67,6 +67,7 @@ -export([check_expiry/1]). -export([base64url/1]). -export([interval_operation/4]). +-export([ensure_timer/4, stop_timer/2]). -export([get_parent/0]). %% Horrible macro to use in guards @@ -242,6 +243,8 @@ -spec(interval_operation/4 :: ({atom(), atom(), any()}, float(), non_neg_integer(), non_neg_integer()) -> {any(), non_neg_integer()}). +-spec(ensure_timer/4 :: (A, non_neg_integer(), non_neg_integer(), any()) -> A). +-spec(stop_timer/2 :: (A, non_neg_integer()) -> A). -spec(get_parent/0 :: () -> pid()). -endif. @@ -1047,6 +1050,22 @@ interval_operation({M, F, A}, MaxRatio, IdealInterval, LastInterval) -> round(LastInterval / 1.5)]) end}. +ensure_timer(State, Idx, After, Msg) -> + case element(Idx, State) of + undefined -> TRef = erlang:send_after(After, self(), Msg), + setelement(Idx, State, TRef); + _ -> State + end. + +stop_timer(State, Idx) -> + case element(Idx, State) of + undefined -> State; + TRef -> case erlang:cancel_timer(TRef) of + false -> State; + _ -> setelement(Idx, State, undefined) + end + end. + %% ------------------------------------------------------------------------- %% Begin copypasta from gen_server2.erl diff --git a/src/rabbit_msg_store.erl b/src/rabbit_msg_store.erl index c2e55022..485a3256 100644 --- a/src/rabbit_msg_store.erl +++ b/src/rabbit_msg_store.erl @@ -943,15 +943,12 @@ next_state(State = #msstate { cref_to_msg_ids = CTM }) -> _ -> {State, 0} end. -start_sync_timer(State = #msstate { sync_timer_ref = undefined }) -> - TRef = erlang:send_after(?SYNC_INTERVAL, self(), sync), - State #msstate { sync_timer_ref = TRef }. +start_sync_timer(State) -> + rabbit_misc:ensure_timer(State, #msstate.sync_timer_ref, + ?SYNC_INTERVAL, sync). -stop_sync_timer(State = #msstate { sync_timer_ref = undefined }) -> - State; -stop_sync_timer(State = #msstate { sync_timer_ref = TRef }) -> - erlang:cancel_timer(TRef), - State #msstate { sync_timer_ref = undefined }. +stop_sync_timer(State) -> + rabbit_misc:stop_timer(State, #msstate.sync_timer_ref). internal_sync(State = #msstate { current_file_handle = CurHdl, cref_to_msg_ids = CTM }) -> -- cgit v1.2.1 From 534051bae26aa38e7770351546173034c9c2d7d6 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Wed, 9 Jan 2013 11:10:56 +0000 Subject: Update dead-lettering due to queue length limit --- src/rabbit_amqqueue_process.erl | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 2506ff91..81db5491 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -563,10 +563,18 @@ maybe_drop_head(State = #q{max_length = MaxLen, backing_queue = BQ, backing_queue_state = BQS}) -> case BQ:len(BQS) >= MaxLen of - true -> {{Msg, _IsDelivered, AckTag}, BQS1} = BQ:fetch(true, BQS), - (dead_letter_fun(maxlen))([{Msg, AckTag}]), - State#q{backing_queue_state = BQS1}; - false -> State + true -> + with_dlx(State#q.dlx, + fun (X) -> + {ok, State1} = dead_letter_maxlen_msgs(X, State), + State1 + end, + fun () -> + {_, BQS1} = BQ:drop(false, BQS), + State#q{backing_queue_state = BQS1} + end); + false -> + State end. requeue_and_run(AckTags, State = #q{backing_queue = BQ, @@ -746,6 +754,12 @@ dead_letter_rejected_msgs(AckTags, X, State = #q{backing_queue = BQ}) -> end, rejected, X, State), State1. +dead_letter_maxlen_msgs(X, State = #q{backing_queue = BQ}) -> + dead_letter_msgs(fun (DLFun, Acc, BQS1) -> + {{Msg, _, AckTag}, BQS2} = BQ:fetch(true, BQS1), + {ok, DLFun(Msg, AckTag, Acc), BQS2} + end, maxlen, X, State). + dead_letter_msgs(Fun, Reason, X, State = #q{dlx_routing_key = RK, publish_seqno = SeqNo0, unconfirmed = UC0, -- cgit v1.2.1 From 253bc8f774078ef98bfc55f2373c948c08d7124d Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Wed, 9 Jan 2013 11:15:16 +0000 Subject: Add queue max length equivalence condition --- src/rabbit_amqqueue.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index a9f2f390..205a61f5 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -385,7 +385,8 @@ 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, [<<"x-expires">>, <<"x-message-ttl">>]). + Args, RequiredArgs, QueueName, + [<<"x-expires">>, <<"x-message-ttl">>, <<"x-max-length">>]). check_declare_arguments(QueueName, Args) -> Checks = [{<<"x-expires">>, fun check_expires_arg/2}, -- cgit v1.2.1 From 06b3e6229030c357da8b1fc2cf816e1cf3e7e050 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 9 Jan 2013 14:52:41 +0000 Subject: I added that clause early in the history of this bug and it's always been wrong. The semantics of that line are just "set is_limit_active to false if the limiter is disabled", is_blocked (which only deals with the channel.flow case anyway) has nothing to do with it. --- src/rabbit_amqqueue_process.erl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 08a1ca70..87b93d17 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1249,8 +1249,7 @@ handle_cast({limit, ChPid, Limiter}, State) -> true -> ok = rabbit_limiter:register(Limiter, self()); false -> ok end, - Limited = OldLimited andalso rabbit_limiter:is_enabled(Limiter) - andalso rabbit_limiter:is_blocked(Limiter), + Limited = OldLimited andalso rabbit_limiter:is_enabled(Limiter), C#cr{limiter = Limiter, is_limit_active = Limited} end)); -- cgit v1.2.1 From ecd7fb6e097f72060fcae8c49f5903fd8e569676 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 9 Jan 2013 16:09:26 +0000 Subject: tiny refactor --- src/rabbit_amqqueue_process.erl | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 904eb6d0..589e8289 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -569,18 +569,14 @@ maybe_drop_head(State = #q{max_length = MaxLen, backing_queue = BQ, backing_queue_state = BQS}) -> case BQ:len(BQS) >= MaxLen of - true -> - with_dlx(State#q.dlx, - fun (X) -> - {ok, State1} = dead_letter_maxlen_msgs(X, State), - State1 - end, - fun () -> - {_, BQS1} = BQ:drop(false, BQS), - State#q{backing_queue_state = BQS1} - end); - false -> - State + true -> with_dlx( + State#q.dlx, + fun (X) -> dead_letter_maxlen_msgs(X, State) end, + fun () -> + {_, BQS1} = BQ:drop(false, BQS), + State#q{backing_queue_state = BQS1} + end); + false -> State end. requeue_and_run(AckTags, State = #q{backing_queue = BQ, @@ -771,10 +767,13 @@ dead_letter_rejected_msgs(AckTags, X, State = #q{backing_queue = BQ}) -> State1. dead_letter_maxlen_msgs(X, State = #q{backing_queue = BQ}) -> - dead_letter_msgs(fun (DLFun, Acc, BQS1) -> - {{Msg, _, AckTag}, BQS2} = BQ:fetch(true, BQS1), - {ok, DLFun(Msg, AckTag, Acc), BQS2} - end, maxlen, X, State). + {ok State1} = + dead_letter_msgs( + fun (DLFun, Acc, BQS1) -> + {{Msg, _, AckTag}, BQS2} = BQ:fetch(true, BQS1), + {ok, DLFun(Msg, AckTag, Acc), BQS2} + end, maxlen, X, State), + State1. dead_letter_msgs(Fun, Reason, X, State = #q{dlx_routing_key = RK, publish_seqno = SeqNo0, -- 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(-) 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 e2393df71b70a19895cd27cde9ead044930c83fa Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 10 Jan 2013 15:34:49 +0000 Subject: cosmetic rename --- src/rabbit_alarm.erl | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/rabbit_alarm.erl b/src/rabbit_alarm.erl index d7d4d82a..f813eab8 100644 --- a/src/rabbit_alarm.erl +++ b/src/rabbit_alarm.erl @@ -67,9 +67,8 @@ start() -> stop() -> ok. -register(Pid, HighMemMFA) -> - gen_event:call(?SERVER, ?MODULE, {register, Pid, HighMemMFA}, - infinity). +register(Pid, AlertMFA) -> + gen_event:call(?SERVER, ?MODULE, {register, Pid, AlertMFA}, infinity). set_alarm(Alarm) -> gen_event:notify(?SERVER, {set_alarm, Alarm}). clear_alarm(Alarm) -> gen_event:notify(?SERVER, {clear_alarm, Alarm}). @@ -94,9 +93,9 @@ init([]) -> alarmed_nodes = dict:new(), alarms = []}}. -handle_call({register, Pid, HighMemMFA}, State) -> +handle_call({register, Pid, AlertMFA}, State) -> {ok, 0 < dict:size(State#alarms.alarmed_nodes), - internal_register(Pid, HighMemMFA, State)}; + internal_register(Pid, AlertMFA, State)}; handle_call(get_alarms, State = #alarms{alarms = Alarms}) -> {ok, Alarms, State}; @@ -121,8 +120,8 @@ handle_event({node_up, Node}, State) -> handle_event({node_down, Node}, State) -> {ok, maybe_alert(fun dict_unappend_all/3, Node, [], State)}; -handle_event({register, Pid, HighMemMFA}, State) -> - {ok, internal_register(Pid, HighMemMFA, State)}; +handle_event({register, Pid, AlertMFA}, State) -> + {ok, internal_register(Pid, AlertMFA, State)}; handle_event(_Event, State) -> {ok, State}. @@ -198,14 +197,14 @@ alert(Alertees, Source, Alert, NodeComparator) -> end end, ok, Alertees). -internal_register(Pid, {M, F, A} = HighMemMFA, +internal_register(Pid, {M, F, A} = AlertMFA, State = #alarms{alertees = Alertees}) -> _MRef = erlang:monitor(process, Pid), case dict:find(node(), State#alarms.alarmed_nodes) of {ok, Sources} -> [apply(M, F, A ++ [Pid, R, true]) || R <- Sources]; error -> ok end, - NewAlertees = dict:store(Pid, HighMemMFA, Alertees), + NewAlertees = dict:store(Pid, AlertMFA, Alertees), State#alarms{alertees = NewAlertees}. handle_set_alarm({{resource_limit, Source, Node}, []}, State) -> -- cgit v1.2.1 From a09fb4cb5e720029bd1be47bae67a6103d885929 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 10 Jan 2013 15:54:35 +0000 Subject: consumer_mapping changed since this branch was last worked on. --- src/rabbit_channel.erl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 6c2c37f9..a8df19ff 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -1090,11 +1090,11 @@ handle_method(#'basic.credit'{consumer_tag = CTag, %% want that? Because at least then it's consistent with the credit value %% we return. And Available is always going to be racy. Available = case dict:find(CTag, Consumers) of - {ok, {Q, _}} -> case rabbit_amqqueue:stat(Q) of - {ok, Len, _} -> Len; - _ -> -1 - end; - error -> -1 %% TODO these -1s smell very iffy! + {ok, Q} -> case rabbit_amqqueue:stat(Q) of + {ok, Len, _} -> Len; + _ -> -1 + end; + error -> -1 %% TODO these -1s smell very iffy! end, Limiter1 = case rabbit_limiter:is_enabled(Limiter) of true -> Limiter; -- cgit v1.2.1 From d65b067aa9759edd4faab55aad4aaca53690b075 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 10 Jan 2013 18:01:16 +0000 Subject: Second attempt at moving credit into the queue. This time we pretend the limiter is still doing the work. While testing this I note that the credit calculation is crazy when testing with Proton. But it was just as bad before, so let's commit. --- src/rabbit_amqqueue.erl | 5 + src/rabbit_amqqueue_process.erl | 30 ++++-- src/rabbit_channel.erl | 42 +++----- src/rabbit_limiter.erl | 227 ++++++++++++++++++++-------------------- 4 files changed, 155 insertions(+), 149 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 94150f1c..a337c722 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -32,6 +32,7 @@ -export([on_node_down/1]). -export([update/2, store_queue/1, policy_changed/2]). -export([start_mirroring/1, stop_mirroring/1, sync_mirrors/1]). +-export([inform_limiter/3]). %% internal -export([internal_declare/2, internal_delete/1, run_backing_queue/3, @@ -175,6 +176,7 @@ -spec(stop_mirroring/1 :: (pid()) -> 'ok'). -spec(sync_mirrors/1 :: (pid()) -> 'ok' | rabbit_types:error('pending_acks' | 'not_mirrored')). +-spec(inform_limiter/3 :: (pid(), pid(), any()) -> 'ok'). -endif. @@ -604,6 +606,9 @@ stop_mirroring(QPid) -> ok = delegate:cast(QPid, stop_mirroring). sync_mirrors(QPid) -> delegate:call(QPid, sync_mirrors). +inform_limiter(ChPid, QPid, Msg) -> + delegate:cast(QPid, {inform_limiter, ChPid, Msg}). + on_node_down(Node) -> rabbit_misc:execute_mnesia_tx_with_tail( fun () -> QsDels = diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 87b93d17..2ec54c7b 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -430,19 +430,25 @@ deliver_msg_to_consumer(DeliverFun, E = {ChPid, Consumer}, case is_ch_blocked(C) of true -> block_consumer(C, E), {false, State}; - false -> case rabbit_limiter:can_send(C#cr.limiter, self(), - Consumer#consumer.ack_required, - Consumer#consumer.tag, - BQ:len(BQS)) of - false -> block_consumer(C#cr{is_limit_active = true}, E), + false -> #cr{limiter = Limiter, ch_pid = ChPid} = C, + {CanSend, Lim2} = + rabbit_limiter:can_send( + Limiter, ChPid, self(), Consumer#consumer.ack_required, + Consumer#consumer.tag, BQ:len(BQS)), + case CanSend of + false -> block_consumer(C#cr{is_limit_active = true, + limiter = Lim2}, E), {false, State}; - true -> AC1 = queue:in(E, State#q.active_consumers), + true -> update_ch_record(C#cr{limiter = Lim2}), %%[0] + AC1 = queue:in(E, State#q.active_consumers), deliver_msg_to_consumer( DeliverFun, Consumer, C, State#q{active_consumers = AC1}) end end. +%% [0] TODO is this a hotspot in the case where the limiter has not changed? + deliver_msg_to_consumer(DeliverFun, #consumer{tag = ConsumerTag, ack_required = AckRequired}, @@ -1250,7 +1256,9 @@ handle_cast({limit, ChPid, Limiter}, State) -> false -> ok end, Limited = OldLimited andalso rabbit_limiter:is_enabled(Limiter), - C#cr{limiter = Limiter, is_limit_active = Limited} + C#cr{limiter = rabbit_limiter:copy_queue_state( + OldLimiter, Limiter), + is_limit_active = Limited} end)); handle_cast({flush, ChPid}, State) -> @@ -1308,6 +1316,14 @@ handle_cast(stop_mirroring, State = #q{backing_queue = BQ, noreply(State#q{backing_queue = BQ1, backing_queue_state = BQS1}); +handle_cast({inform_limiter, ChPid, Msg}, + State = #q{backing_queue = BQ, + backing_queue_state = BQS}) -> + C = #cr{limiter = Limiter} = ch_record(ChPid), + Limiter2 = rabbit_limiter:inform(Limiter, ChPid, BQ:len(BQS), Msg), + update_ch_record(C#cr{limiter = Limiter2}), + noreply(State); + handle_cast(wake_up, State) -> noreply(State). diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index a8df19ff..6d00fdb2 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -1080,35 +1080,19 @@ handle_method(#'channel.flow'{active = false}, _, end; handle_method(#'basic.credit'{consumer_tag = CTag, - credit = Credit, - count = Count, - drain = Drain}, _, - State = #ch{limiter = Limiter, - consumer_mapping = Consumers}) -> - %% We get Available first because it's likely that as soon as we set - %% the credit msgs will get consumed and it'll be out of date. Why do we - %% want that? Because at least then it's consistent with the credit value - %% we return. And Available is always going to be racy. - Available = case dict:find(CTag, Consumers) of - {ok, Q} -> case rabbit_amqqueue:stat(Q) of - {ok, Len, _} -> Len; - _ -> -1 - end; - error -> -1 %% TODO these -1s smell very iffy! - end, - Limiter1 = case rabbit_limiter:is_enabled(Limiter) of - true -> Limiter; - false -> enable_limiter(State) - end, - Limiter3 = - case rabbit_limiter:set_credit( - Limiter1, CTag, Credit, Count, Drain) of - ok -> Limiter1; - {disabled, Limiter2} -> ok = limit_queues(Limiter2, State), - Limiter2 - end, - State1 = State#ch{limiter = Limiter3}, - return_ok(State1, false, #'basic.credit_ok'{available = Available}); + credit = Credit, + count = Count, + drain = Drain} = M, _, + State = #ch{consumer_mapping = Consumers}) -> + %%io:format(" ~p~n", [M]), + case dict:find(CTag, Consumers) of + {ok, Q} -> ok = rabbit_amqqueue:inform_limiter( + self(), Q#amqqueue.pid, + {basic_credit, CTag, Credit, Count, Drain}), + {noreply, State}; + error -> rabbit_misc:protocol_error( + not_allowed, "unknown consumer tag '~s'", [CTag]) + end; handle_method(_MethodRecord, _Content, _State) -> rabbit_misc:protocol_error( diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index 5f1bc07c..6e3a228b 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -24,15 +24,15 @@ -export([start_link/0, make_token/0, make_token/1, is_enabled/1, enable/2, disable/1]). --export([limit/2, can_send/5, ack/2, register/2, unregister/2]). +-export([limit/2, can_send/6, ack/2, register/2, unregister/2]). -export([get_limit/1, block/1, unblock/1, is_blocked/1]). --export([set_credit/5]). +-export([inform/4]). -import(rabbit_misc, [serial_add/2, serial_diff/2]). %%---------------------------------------------------------------------------- --record(token, {pid, enabled}). +-record(token, {pid, enabled, q_state}). -ifdef(use_specs). @@ -47,8 +47,9 @@ -spec(enable/2 :: (token(), non_neg_integer()) -> token()). -spec(disable/1 :: (token()) -> token()). -spec(limit/2 :: (token(), non_neg_integer()) -> 'ok' | {'disabled', token()}). --spec(can_send/5 :: (token(), pid(), boolean(), - rabbit_types:ctag(), non_neg_integer()) -> boolean()). +%% TODO +%% -spec(can_send/5 :: (token(), pid(), boolean(), +%% rabbit_types:ctag(), non_neg_integer()) -> boolean()). -spec(ack/2 :: (token(), non_neg_integer()) -> 'ok'). -spec(register/2 :: (token(), pid()) -> 'ok'). -spec(unregister/2 :: (token(), pid()) -> 'ok'). @@ -56,10 +57,10 @@ -spec(block/1 :: (token()) -> 'ok'). -spec(unblock/1 :: (token()) -> 'ok' | {'disabled', token()}). -spec(is_blocked/1 :: (token()) -> boolean()). --spec(set_credit/5 :: (token(), rabbit_types:ctag(), - non_neg_integer(), - non_neg_integer(), boolean()) -> 'ok'). - +%% -spec(set_credit/5 :: (token(), rabbit_types:ctag(), +%% non_neg_integer(), +%% non_neg_integer(), boolean()) -> 'ok'). +-spec(inform/4 :: (token(), pid(), non_neg_integer(), any()) -> token()). -endif. %%---------------------------------------------------------------------------- @@ -67,7 +68,6 @@ -record(lim, {prefetch_count = 0, ch_pid, blocked = false, - credits = dict:new(), queues = orddict:new(), % QPid -> {MonitorRef, Notify} volume = 0}). @@ -80,7 +80,8 @@ start_link() -> gen_server2:start_link(?MODULE, [], []). make_token() -> make_token(undefined). -make_token(Pid) -> #token{pid = Pid, enabled = false}. +make_token(Pid) -> #token{pid = Pid, enabled = false, + q_state = dict:new()}. is_enabled(#token{enabled = Enabled}) -> Enabled. @@ -97,19 +98,22 @@ limit(Limiter, PrefetchCount) -> %% breaching a limit. Note that we don't use maybe_call here in order %% to avoid always going through with_exit_handler/2, even when the %% limiter is disabled. -can_send(#token{pid = Pid, enabled = true}, QPid, AckRequired, CTag, Len) -> +can_send(#token{pid = Pid, enabled = true, q_state = QState} = Token, + ChPid, QPid, AckRequired, CTag, Len) -> rabbit_misc:with_exit_handler( - fun () -> true end, + fun () -> {true, Token} end, fun () -> - gen_server2:call(Pid, {can_send, QPid, AckRequired, CTag, Len}, - infinity) + CanLim = gen_server2:call(Pid, {can_send, QPid, AckRequired}, + infinity), + {CanQ, NewQState} = can_send_q(CTag, Len, ChPid, QState), + {CanLim andalso CanQ, Token#token{q_state = NewQState}} end); -can_send(_, _, _, _, _) -> - true. +can_send(Token, _, _, _, _, _) -> + {true, Token}. %% Let the limiter know that the channel has received some acks from a %% consumer -ack(Limiter, CTag) -> maybe_cast(Limiter, {ack, CTag}). +ack(Limiter, Count) -> maybe_cast(Limiter, {ack, Count}). register(Limiter, QPid) -> maybe_cast(Limiter, {register, QPid}). @@ -126,12 +130,82 @@ block(Limiter) -> unblock(Limiter) -> maybe_call(Limiter, {unblock, Limiter}, ok). -set_credit(Limiter, CTag, Credit, Count, Drain) -> - maybe_call(Limiter, {set_credit, CTag, Credit, Count, Drain, Limiter}, ok). - is_blocked(Limiter) -> maybe_call(Limiter, is_blocked, false). +inform(Limiter = #token{q_state = Credits}, + ChPid, Len, {basic_credit, CTag, Credit, Count, Drain}) -> + Credits2 = reset_credit(CTag, Len, ChPid, Credit, Count, Drain, Credits), + Limiter#token{q_state = Credits2}. + +%%---------------------------------------------------------------------------- +%% Queue-local code +%%---------------------------------------------------------------------------- + +%% We want to do all the AMQP 1.0-ish link level credit calculations in the +%% queue (to do them elsewhere introduces a ton of races). However, it's a big +%% chunk of code that is conceptually very linked to the limiter concept. So +%% we get the queue to hold a bit of state for us (#token.q_state), and +%% maintain a fiction that the limiter is making the decisions... + +can_send_q(CTag, Len, ChPid, Credits) -> + case dict:find(CTag, Credits) of + {ok, #credit{credit = 0}} -> exit(bang), {false, Credits}; + {ok, Cred} -> Credits2 = + decr_credit( + CTag, Len, ChPid, Cred, Credits), + {true, Credits2}; + _ -> {true, Credits} + end. + +decr_credit(CTag, Len, ChPid, Cred, Credits) -> + #credit{credit = Credit, count = Count, drain = Drain} = Cred, + {NewCredit, NewCount} = + case {Credit, Len, Drain} of + {1, _, _} -> {0, serial_add(Count, 1)}; + {_, 1, true} -> %% Drain, so advance til credit = 0 + NewCount0 = serial_add(Count, (Credit - 1)), + send_drained(ChPid, CTag, NewCount0), + {0, NewCount0}; %% Magic reduction to 0 + {_, _, _} -> {Credit - 1, serial_add(Count, 1)} + end, + update_credit(CTag, NewCredit, NewCount, Drain, Credits). + +send_drained(ChPid, CTag, Count) -> + rabbit_channel:send_command(ChPid, + #'basic.credit_state'{consumer_tag = CTag, + credit = 0, + count = Count, + available = 0, + drain = true}). + +%% Assert the credit state. The count may not match ours, in which +%% case we must rebase the credit. +%% TODO Edge case: if the queue has nothing in it, and drain is set, +%% we want to send a basic.credit back. +reset_credit(CTag, Len, ChPid, Credit0, Count0, Drain, Credits) -> + Count = + case dict:find(CTag, Credits) of + {ok, #credit{ count = LocalCount }} -> + LocalCount; + _ -> Count0 + end, + %% Our credit may have been reduced while messages are in flight, + %% so we bottom out at 0. + Credit = erlang:max(0, serial_diff(serial_add(Count0, Credit0), Count)), + rabbit_channel:send_command(ChPid, + #'basic.credit_ok'{available = Len}), + update_credit(CTag, Credit, Count, Drain, Credits). + +%% Store the credit +update_credit(CTag, -1, _Count, _Drain, Credits) -> + dict:erase(CTag, Credits); + +update_credit(CTag, Credit, Count, Drain, Credits) -> + dict:store(CTag, #credit{credit = Credit, + count = Count, + drain = Drain}, Credits). + %%---------------------------------------------------------------------------- %% gen_server callbacks %%---------------------------------------------------------------------------- @@ -142,26 +216,23 @@ init([]) -> prioritise_call(get_limit, _From, _State) -> 9; prioritise_call(_Msg, _From, _State) -> 0. -handle_call({can_send, QPid, _AckRequired, _CTag, _Len}, _From, +handle_call({can_send, QPid, _AckRequired}, _From, State = #lim{blocked = true}) -> {reply, false, limit_queue(QPid, State)}; -handle_call({can_send, QPid, AckRequired, CTag, Len}, _From, +handle_call({can_send, QPid, AckRequired}, _From, State = #lim{volume = Volume}) -> - case limit_reached(CTag, State) of + case limit_reached(State) of true -> {reply, false, limit_queue(QPid, State)}; - false -> {reply, true, - decr_credit(CTag, Len, - State#lim{volume = if AckRequired -> Volume + 1; - true -> Volume - end})} + false -> {reply, true, State#lim{volume = if AckRequired -> Volume + 1; + true -> Volume + end}} end; handle_call(get_limit, _From, State = #lim{prefetch_count = PrefetchCount}) -> {reply, PrefetchCount, State}; handle_call({limit, PrefetchCount, Token}, _From, State) -> - case maybe_notify(irrelevant, - State, State#lim{prefetch_count = PrefetchCount}) of + case maybe_notify(State, State#lim{prefetch_count = PrefetchCount}) of {cont, State1} -> {reply, ok, State1}; {stop, State1} -> @@ -171,17 +242,8 @@ handle_call({limit, PrefetchCount, Token}, _From, State) -> handle_call(block, _From, State) -> {reply, ok, State#lim{blocked = true}}; -handle_call({set_credit, CTag, Credit, Count, Drain, Token}, _From, State) -> - case maybe_notify(CTag, State, - reset_credit(CTag, Credit, Count, Drain, State)) of - {cont, State1} -> - {reply, ok, State1}; - {stop, State1} -> - {reply, {disabled, Token#token{enabled = false}}, State1} - end; - handle_call({unblock, Token}, _From, State) -> - case maybe_notify(irrelevant, State, State#lim{blocked = false}) of + case maybe_notify(State, State#lim{blocked = false}) of {cont, State1} -> {reply, ok, State1}; {stop, State1} -> @@ -197,11 +259,11 @@ handle_call({enable, Token, Channel, Volume}, _From, State) -> handle_call({disable, Token}, _From, State) -> {reply, Token#token{enabled = false}, State}. -handle_cast({ack, CTag}, State = #lim{volume = Volume}) -> +handle_cast({ack, Count}, State = #lim{volume = Volume}) -> NewVolume = if Volume == 0 -> 0; - true -> Volume - 1 + true -> Volume - Count end, - {cont, State1} = maybe_notify(CTag, State, State#lim{volume = NewVolume}), + {cont, State1} = maybe_notify(State, State#lim{volume = NewVolume}), {noreply, State1}; handle_cast({register, QPid}, State) -> @@ -223,14 +285,13 @@ code_change(_, State, _) -> %% Internal plumbing %%---------------------------------------------------------------------------- -maybe_notify(CTag, OldState, NewState) -> - case (limit_reached(CTag, OldState) orelse blocked(OldState)) andalso - not (limit_reached(CTag, NewState) orelse blocked(NewState)) of +maybe_notify(OldState, NewState) -> + case (limit_reached(OldState) orelse blocked(OldState)) andalso + not (limit_reached(NewState) orelse blocked(NewState)) of true -> NewState1 = notify_queues(NewState), - {case {NewState1#lim.prefetch_count, - dict:size(NewState1#lim.credits)} of - {0, 0} -> stop; - _ -> cont + {case NewState1#lim.prefetch_count of + 0 -> stop; + _ -> cont end, NewState1}; false -> {cont, NewState} end. @@ -245,67 +306,8 @@ maybe_cast(#token{pid = Pid, enabled = true}, Cast) -> maybe_cast(_, _Call) -> ok. -limit_reached(irrelevant, _) -> - false; -limit_reached(CTag, #lim{prefetch_count = Limit, volume = Volume, - credits = Credits}) -> - case dict:find(CTag, Credits) of - {ok, #credit{ credit = 0 }} -> true; - _ -> false - end orelse (Limit =/= 0 andalso Volume >= Limit). - -decr_credit(CTag, Len, State = #lim{ credits = Credits, - ch_pid = ChPid } ) -> - case dict:find(CTag, Credits) of - {ok, #credit{ credit = Credit, count = Count, drain = Drain }} -> - {NewCredit, NewCount} = - case {Credit, Len, Drain} of - {1, _, _} -> {0, serial_add(Count, 1)}; - {_, 1, true} -> - %% Drain, so advance til credit = 0 - NewCount0 = serial_add(Count, (Credit - 1)), - send_drained(ChPid, CTag, NewCount0), - {0, NewCount0}; %% Magic reduction to 0 - {_, _, _} -> {Credit - 1, serial_add(Count, 1)} - end, - update_credit(CTag, NewCredit, NewCount, Drain, State); - error -> - State - end. - -send_drained(ChPid, CTag, Count) -> - rabbit_channel:send_command(ChPid, - #'basic.credit_state'{consumer_tag = CTag, - credit = 0, - count = Count, - available = 0, - drain = true}). - -%% Assert the credit state. The count may not match ours, in which -%% case we must rebase the credit. -%% TODO Edge case: if the queue has nothing in it, and drain is set, -%% we want to send a basic.credit back. -reset_credit(CTag, Credit0, Count0, Drain, State = #lim{credits = Credits}) -> - Count = - case dict:find(CTag, Credits) of - {ok, #credit{ count = LocalCount }} -> - LocalCount; - _ -> Count0 - end, - %% Our credit may have been reduced while messages are in flight, - %% so we bottom out at 0. - Credit = erlang:max(0, serial_diff(serial_add(Count0, Credit0), Count)), - update_credit(CTag, Credit, Count, Drain, State). - -%% Store the credit -update_credit(CTag, -1, _Count, _Drain, State = #lim{credits = Credits}) -> - State#lim{credits = dict:erase(CTag, Credits)}; - -update_credit(CTag, Credit, Count, Drain, State = #lim{credits = Credits}) -> - State#lim{credits = dict:store(CTag, - #credit{credit = Credit, - count = Count, - drain = Drain}, Credits)}. +limit_reached(#lim{prefetch_count = Limit, volume = Volume}) -> + Limit =/= 0 andalso Volume >= Limit. blocked(#lim{blocked = Blocked}) -> Blocked. @@ -347,4 +349,3 @@ notify_queues(State = #lim{ch_pid = ChPid, queues = Queues}) -> ok end, State#lim{queues = NewQueues}. - -- cgit v1.2.1 From 4b286c55a5275b75c6e43cebe71ccc78e70ba9f5 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 11 Jan 2013 18:01:16 +0000 Subject: Right. There were a lot of bugs. This fixes most of them. One major confusion was that both the adapter and the credit impl were trying to normalise against remote count, now it's just the adapter. Also when we get credit we need to unblock. Also there were various things that assumed our local credit could not go negative - well it can and we just need to wait for it to be positive again. --- src/rabbit_amqqueue_process.erl | 31 +++++++++++++------ src/rabbit_channel.erl | 3 +- src/rabbit_limiter.erl | 67 ++++++++++++++++++++--------------------- 3 files changed, 55 insertions(+), 46 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 2ec54c7b..f48005ef 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -439,16 +439,13 @@ deliver_msg_to_consumer(DeliverFun, E = {ChPid, Consumer}, false -> block_consumer(C#cr{is_limit_active = true, limiter = Lim2}, E), {false, State}; - true -> update_ch_record(C#cr{limiter = Lim2}), %%[0] - AC1 = queue:in(E, State#q.active_consumers), + true -> AC1 = queue:in(E, State#q.active_consumers), deliver_msg_to_consumer( - DeliverFun, Consumer, C, + DeliverFun, Consumer, C#cr{limiter = Lim2}, State#q{active_consumers = AC1}) end end. -%% [0] TODO is this a hotspot in the case where the limiter has not changed? - deliver_msg_to_consumer(DeliverFun, #consumer{tag = ConsumerTag, ack_required = AckRequired}, @@ -1317,12 +1314,26 @@ handle_cast(stop_mirroring, State = #q{backing_queue = BQ, backing_queue_state = BQS1}); handle_cast({inform_limiter, ChPid, Msg}, - State = #q{backing_queue = BQ, + State = #q{active_consumers = AC, + backing_queue = BQ, backing_queue_state = BQS}) -> - C = #cr{limiter = Limiter} = ch_record(ChPid), - Limiter2 = rabbit_limiter:inform(Limiter, ChPid, BQ:len(BQS), Msg), - update_ch_record(C#cr{limiter = Limiter2}), - noreply(State); + C = #cr{limiter = Limiter, + blocked_consumers = Blocked} = ch_record(ChPid), + {Unblock, Limiter2} = + rabbit_limiter:inform(Limiter, ChPid, BQ:len(BQS), Msg), + NewBlocked = queue:filter(fun({_ChPid, #consumer{tag = CTag}}) -> + not lists:member(CTag, Unblock) + end, Blocked), + NewUnblocked = queue:filter(fun({_ChPid, #consumer{tag = CTag}}) -> + lists:member(CTag, Unblock) + end, Blocked), + %% TODO can this whole thing be replaced by possibly_unblock? + %% TODO that is_limit_active = false thing is wrong - but we do + %% not allow for per-consumer blocking! + update_ch_record(C#cr{limiter = Limiter2, blocked_consumers = NewBlocked, + is_limit_active = false}), + AC1 = queue:join(NewUnblocked, AC), + noreply(run_message_queue(State#q{active_consumers = AC1})); handle_cast(wake_up, State) -> noreply(State). diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 6d00fdb2..c3a5b16d 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -1082,9 +1082,8 @@ handle_method(#'channel.flow'{active = false}, _, handle_method(#'basic.credit'{consumer_tag = CTag, credit = Credit, count = Count, - drain = Drain} = M, _, + drain = Drain}, _, State = #ch{consumer_mapping = Consumers}) -> - %%io:format(" ~p~n", [M]), case dict:find(CTag, Consumers) of {ok, Q} -> ok = rabbit_amqqueue:inform_limiter( self(), Q#amqqueue.pid, diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index 6e3a228b..f031db51 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -60,7 +60,7 @@ %% -spec(set_credit/5 :: (token(), rabbit_types:ctag(), %% non_neg_integer(), %% non_neg_integer(), boolean()) -> 'ok'). --spec(inform/4 :: (token(), pid(), non_neg_integer(), any()) -> token()). +%%-spec(inform/4 :: (token(), pid(), non_neg_integer(), any()) -> token()). -endif. %%---------------------------------------------------------------------------- @@ -135,8 +135,9 @@ is_blocked(Limiter) -> inform(Limiter = #token{q_state = Credits}, ChPid, Len, {basic_credit, CTag, Credit, Count, Drain}) -> - Credits2 = reset_credit(CTag, Len, ChPid, Credit, Count, Drain, Credits), - Limiter#token{q_state = Credits2}. + {Unblock, Credits2} = + update_credit(CTag, Len, ChPid, Credit, Count, Drain, Credits), + {Unblock, Limiter#token{q_state = Credits2}}. %%---------------------------------------------------------------------------- %% Queue-local code @@ -150,26 +151,26 @@ inform(Limiter = #token{q_state = Credits}, can_send_q(CTag, Len, ChPid, Credits) -> case dict:find(CTag, Credits) of - {ok, #credit{credit = 0}} -> exit(bang), {false, Credits}; - {ok, Cred} -> Credits2 = - decr_credit( - CTag, Len, ChPid, Cred, Credits), - {true, Credits2}; - _ -> {true, Credits} + {ok, #credit{credit = C} = Cred} -> + if C > 0 -> Credits2 = decr_credit(CTag, Len, ChPid, Cred, Credits), + {true, Credits2}; + true -> {false, Credits} + end; + _ -> + {true, Credits} end. decr_credit(CTag, Len, ChPid, Cred, Credits) -> #credit{credit = Credit, count = Count, drain = Drain} = Cred, {NewCredit, NewCount} = - case {Credit, Len, Drain} of - {1, _, _} -> {0, serial_add(Count, 1)}; - {_, 1, true} -> %% Drain, so advance til credit = 0 - NewCount0 = serial_add(Count, (Credit - 1)), - send_drained(ChPid, CTag, NewCount0), - {0, NewCount0}; %% Magic reduction to 0 - {_, _, _} -> {Credit - 1, serial_add(Count, 1)} + case {Len, Drain} of + {1, true} -> %% Drain, so advance til credit = 0 + NewCount0 = serial_add(Count, (Credit - 1)), + send_drained(ChPid, CTag, NewCount0), + {0, NewCount0}; %% Magic reduction to 0 + {_, _} -> {Credit - 1, serial_add(Count, 1)} end, - update_credit(CTag, NewCredit, NewCount, Drain, Credits). + write_credit(CTag, NewCredit, NewCount, Drain, Credits). send_drained(ChPid, CTag, Count) -> rabbit_channel:send_command(ChPid, @@ -179,29 +180,27 @@ send_drained(ChPid, CTag, Count) -> available = 0, drain = true}). -%% Assert the credit state. The count may not match ours, in which -%% case we must rebase the credit. +%% Update the credit state. %% TODO Edge case: if the queue has nothing in it, and drain is set, %% we want to send a basic.credit back. -reset_credit(CTag, Len, ChPid, Credit0, Count0, Drain, Credits) -> +update_credit(CTag, Len, ChPid, Credit, Count0, Drain, Credits) -> Count = case dict:find(CTag, Credits) of - {ok, #credit{ count = LocalCount }} -> - LocalCount; - _ -> Count0 + %% Use our count if we can, more accurate + {ok, #credit{ count = LocalCount }} -> LocalCount; + %% But if this is new, take it from the adapter + _ -> Count0 end, - %% Our credit may have been reduced while messages are in flight, - %% so we bottom out at 0. - Credit = erlang:max(0, serial_diff(serial_add(Count0, Credit0), Count)), - rabbit_channel:send_command(ChPid, - #'basic.credit_ok'{available = Len}), - update_credit(CTag, Credit, Count, Drain, Credits). - -%% Store the credit -update_credit(CTag, -1, _Count, _Drain, Credits) -> - dict:erase(CTag, Credits); + rabbit_channel:send_command(ChPid, #'basic.credit_ok'{available = Len}), + NewCredits = write_credit(CTag, Credit, Count, Drain, Credits), + case Credit > 0 of + true -> {[CTag], NewCredits}; + false -> {[], NewCredits} + end. -update_credit(CTag, Credit, Count, Drain, Credits) -> +%% TODO currently we leak when a single session creates and destroys +%% lot of links. +write_credit(CTag, Credit, Count, Drain, Credits) -> dict:store(CTag, #credit{credit = Credit, count = Count, drain = Drain}, Credits). -- cgit v1.2.1 From 05d71de3832a386a0b843570a787e7b53e719088 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 11 Jan 2013 19:39:34 +0000 Subject: implement vq:fold in terms of an iterator --- src/rabbit_variable_queue.erl | 100 ++++++++++++++++++++++-------------------- 1 file changed, 52 insertions(+), 48 deletions(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 90ee3439..427bd03c 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -677,25 +677,7 @@ ackfold(MsgFun, Acc, State, AckTags) -> end, {Acc, State}, AckTags), {AccN, a(StateN)}. -fold(Fun, Acc, #vqstate { q1 = Q1, - q2 = Q2, - delta = #delta { start_seq_id = DeltaSeqId, - end_seq_id = DeltaSeqIdEnd }, - q3 = Q3, - q4 = Q4 } = State) -> - QFun = fun(MsgStatus, {Acc0, State0}) -> - {Msg, State1} = read_msg(MsgStatus, false, State0), - {StopGo, AccNext} = - Fun(Msg, MsgStatus#msg_status.msg_props, Acc0), - {StopGo, {AccNext, State1}} - end, - {Cont1, {Acc1, State1}} = qfoldl(QFun, {cont, {Acc, State }}, Q4), - {Cont2, {Acc2, State2}} = qfoldl(QFun, {Cont1, {Acc1, State1}}, Q3), - {Cont3, {Acc3, State3}} = delta_fold(Fun, {Cont2, Acc2}, - DeltaSeqId, DeltaSeqIdEnd, State2), - {Cont4, {Acc4, State4}} = qfoldl(QFun, {Cont3, {Acc3, State3}}, Q2), - {_, {Acc5, State5}} = qfoldl(QFun, {Cont4, {Acc4, State4}}, Q1), - {Acc5, State5}. +fold(Fun, Acc, State) -> ifold(Fun, Acc, iterator(State)). len(#vqstate { len = Len }) -> Len. @@ -1386,7 +1368,7 @@ msg_indices_written_to_disk(Callback, MsgIdSet) -> end). %%---------------------------------------------------------------------------- -%% Internal plumbing for requeue and fold +%% Internal plumbing for requeue %%---------------------------------------------------------------------------- publish_alpha(#msg_status { msg = undefined } = MsgStatus, State) -> @@ -1456,40 +1438,62 @@ beta_limit(Q) -> delta_limit(?BLANK_DELTA_PATTERN(_X)) -> undefined; delta_limit(#delta { start_seq_id = StartSeqId }) -> StartSeqId. -qfoldl(_Fun, {stop, _Acc} = A, _Q) -> A; -qfoldl( Fun, {cont, Acc} = A, Q) -> - case ?QUEUE:out(Q) of - {empty, _Q} -> A; - {{value, V}, Q1} -> qfoldl(Fun, Fun(V, Acc), Q1) - end. +%%---------------------------------------------------------------------------- +%% Iterator +%%---------------------------------------------------------------------------- -lfoldl(_Fun, {stop, _Acc} = A, _L) -> A; -lfoldl(_Fun, {cont, _Acc} = A, []) -> A; -lfoldl( Fun, {cont, Acc}, [H | T]) -> lfoldl(Fun, Fun(H, Acc), T). - -delta_fold(_Fun, {stop, Acc}, _DeltaSeqId, _DeltaSeqIdEnd, State) -> - {stop, {Acc, State}}; -delta_fold(_Fun, {cont, Acc}, DeltaSeqIdEnd, DeltaSeqIdEnd, State) -> - {cont, {Acc, State}}; -delta_fold( Fun, {cont, Acc}, DeltaSeqId, DeltaSeqIdEnd, - #vqstate { index_state = IndexState, - msg_store_clients = MSCState } = State) -> +iterator(State = #vqstate{q4 = Q4}) -> {q4, Q4, State}. + +next({q4, _, State} = It) -> next(It, q3, State#vqstate.q3); +next({q3, _, State} = It) -> next(It, delta, State#vqstate.delta); +next({delta, _, State} = It) -> next(It, q2, State#vqstate.q2); +next({q2, _, State} = It) -> next(It, q1, State#vqstate.q1); +next({q1, _, State} = It) -> next(It, done, State); +next({done, _, State}) -> {empty, State}. + +next({delta, #delta{start_seq_id = DeltaSeqId, end_seq_id = DeltaSeqId}, State}, + NextKey, Next) -> + next({NextKey, Next, State}); +next({delta, Delta = #delta{start_seq_id = DeltaSeqId, + end_seq_id = DeltaSeqIdEnd}, + State = #vqstate{index_state = IndexState}}, NextKey, Next) -> DeltaSeqId1 = lists:min( [rabbit_queue_index:next_segment_boundary(DeltaSeqId), DeltaSeqIdEnd]), {List, IndexState1} = rabbit_queue_index:read(DeltaSeqId, DeltaSeqId1, IndexState), - {StopCont, {Acc1, MSCState1}} = - lfoldl(fun ({MsgId, _SeqId, MsgProps, IsPersistent, _IsDelivered}, - {Acc0, MSCState0}) -> - {{ok, Msg = #basic_message {}}, MSCState1} = - msg_store_read(MSCState0, IsPersistent, MsgId), - {StopCont, AccNext} = Fun(Msg, MsgProps, Acc0), - {StopCont, {AccNext, MSCState1}} - end, {cont, {Acc, MSCState}}, List), - delta_fold(Fun, {StopCont, Acc1}, DeltaSeqId1, DeltaSeqIdEnd, - State #vqstate { index_state = IndexState1, - msg_store_clients = MSCState1 }). + next({delta, {Delta#delta{start_seq_id = DeltaSeqId1}, List}, + State#vqstate{index_state = IndexState1}}, NextKey, Next); +next({delta, {Delta, []}, State}, NextKey, Next) -> + next({delta, Delta, State}, NextKey, Next); +next({delta, {Delta, [M | Rest]}, + State = #vqstate{msg_store_clients = MSCState}}, _NextKey, _Next) -> + {MsgId, _SeqId, MsgProps, IsPersistent, _IsDelivered} = M, + {{ok, Msg = #basic_message {}}, MSCState1} = + msg_store_read(MSCState, IsPersistent, MsgId), + State1 = State#vqstate{msg_store_clients = MSCState1}, + {value, Msg, MsgProps, {delta, {Delta, Rest}, State1}}; +next({Key, Q, State}, NextKey, Next) -> + case ?QUEUE:out(Q) of + {empty, _Q} -> + next({NextKey, Next, State}); + {{value, MsgStatus}, QN} -> + {Msg, State1} = read_msg(MsgStatus, false, State), + {value, Msg, MsgStatus#msg_status.msg_props, {Key, QN, State1}} + end. + +done({_, _, State}) -> State. + +ifold(Fun, Acc, It) -> + case next(It) of + {value, Msg, MsgProps, Next} -> + case Fun(Msg, MsgProps, Acc) of + {stop, Acc1} -> {Acc1, done(Next)}; + {cont, Acc1} -> ifold(Fun, Acc1, Next) + end; + {empty, Done} -> + {Acc, Done} + end. %%---------------------------------------------------------------------------- %% Phase changes -- cgit v1.2.1 From 0c84e4f85fa92a384646af16ec9087696c65c139 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 12 Jan 2013 10:18:28 +0000 Subject: unmodalise vq:read_msg --- src/rabbit_variable_queue.erl | 54 +++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 90ee3439..285dfcd7 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -527,7 +527,6 @@ publish(Msg = #basic_message { is_persistent = IsPersistent, id = MsgId }, in_counter = InCount, persistent_count = PCount, durable = IsDurable, - ram_msg_count = RamMsgCount, unconfirmed = UC }) -> IsPersistent1 = IsDurable andalso IsPersistent, MsgStatus = msg_status(IsPersistent1, IsDelivered, SeqId, Msg, MsgProps), @@ -538,12 +537,12 @@ publish(Msg = #basic_message { is_persistent = IsPersistent, id = MsgId }, end, PCount1 = PCount + one_if(IsPersistent1), UC1 = gb_sets_maybe_insert(NeedsConfirming, MsgId, UC), - a(reduce_memory_use(State2 #vqstate { next_seq_id = SeqId + 1, - len = Len + 1, - in_counter = InCount + 1, - persistent_count = PCount1, - ram_msg_count = RamMsgCount + 1, - unconfirmed = UC1 })). + a(reduce_memory_use( + inc_ram_msg_count(State2 #vqstate { next_seq_id = SeqId + 1, + len = Len + 1, + in_counter = InCount + 1, + persistent_count = PCount1, + unconfirmed = UC1 }))). publish_delivered(Msg = #basic_message { is_persistent = IsPersistent, id = MsgId }, @@ -596,7 +595,7 @@ fetchwhile(Pred, Fun, Acc, State) -> {undefined, Acc, a(State1)}; {{value, MsgStatus = #msg_status { msg_props = MsgProps }}, State1} -> case Pred(MsgProps) of - true -> {Msg, State2} = read_msg(MsgStatus, false, State1), + true -> {Msg, State2} = read_msg(MsgStatus, State1), {AckTag, State3} = remove(true, MsgStatus, State2), fetchwhile(Pred, Fun, Fun(Msg, AckTag, Acc), State3); false -> {MsgProps, Acc, a(in_r(MsgStatus, State1))} @@ -610,7 +609,7 @@ fetch(AckRequired, State) -> {{value, MsgStatus}, State1} -> %% it is possible that the message wasn't read from disk %% at this point, so read it in. - {Msg, State2} = read_msg(MsgStatus, false, State1), + {Msg, State2} = read_msg(MsgStatus, State1), {AckTag, State3} = remove(AckRequired, MsgStatus, State2), {{Msg, MsgStatus#msg_status.is_delivered, AckTag}, a(State3)} end. @@ -672,7 +671,7 @@ ackfold(MsgFun, Acc, State, AckTags) -> {AccN, StateN} = lists:foldl(fun(SeqId, {Acc0, State0}) -> MsgStatus = lookup_pending_ack(SeqId, State0), - {Msg, State1} = read_msg(MsgStatus, false, State0), + {Msg, State1} = read_msg(MsgStatus, State0), {MsgFun(Msg, SeqId, Acc0), State1} end, {Acc, State}, AckTags), {AccN, a(StateN)}. @@ -684,7 +683,7 @@ fold(Fun, Acc, #vqstate { q1 = Q1, q3 = Q3, q4 = Q4 } = State) -> QFun = fun(MsgStatus, {Acc0, State0}) -> - {Msg, State1} = read_msg(MsgStatus, false, State0), + {Msg, State1} = read_msg(MsgStatus, State0), {StopGo, AccNext} = Fun(Msg, MsgStatus#msg_status.msg_props, Acc0), {StopGo, {AccNext, State1}} @@ -1078,9 +1077,10 @@ in_r(MsgStatus = #msg_status { msg = undefined }, case ?QUEUE:is_empty(Q4) of true -> State #vqstate { q3 = ?QUEUE:in_r(MsgStatus, Q3) }; false -> {Msg, State1 = #vqstate { q4 = Q4a }} = - read_msg(MsgStatus, true, State), - State1 #vqstate { q4 = ?QUEUE:in_r(MsgStatus#msg_status { - msg = Msg }, Q4a) } + read_msg(MsgStatus, State), + inc_ram_msg_count( + State1 #vqstate { q4 = ?QUEUE:in_r(MsgStatus#msg_status { + msg = Msg }, Q4a) }) end; in_r(MsgStatus, State = #vqstate { q4 = Q4 }) -> State #vqstate { q4 = ?QUEUE:in_r(MsgStatus, Q4) }. @@ -1096,19 +1096,19 @@ queue_out(State = #vqstate { q4 = Q4 }) -> {{value, MsgStatus}, State #vqstate { q4 = Q4a }} end. -read_msg(#msg_status { msg = undefined, - msg_id = MsgId, - is_persistent = IsPersistent }, - CountDiskToRam, State = #vqstate { ram_msg_count = RamMsgCount, - msg_store_clients = MSCState}) -> +read_msg(#msg_status{msg = undefined, + msg_id = MsgId, + is_persistent = IsPersistent}, + State = #vqstate{msg_store_clients = MSCState}) -> {{ok, Msg = #basic_message {}}, MSCState1} = msg_store_read(MSCState, IsPersistent, MsgId), - RamMsgCount1 = RamMsgCount + one_if(CountDiskToRam), - {Msg, State #vqstate { ram_msg_count = RamMsgCount1, - msg_store_clients = MSCState1 }}; -read_msg(#msg_status { msg = Msg }, _CountDiskToRam, State) -> + {Msg, State #vqstate {msg_store_clients = MSCState1}}; +read_msg(#msg_status{msg = Msg}, State) -> {Msg, State}. +inc_ram_msg_count(State = #vqstate{ram_msg_count = RamMsgCount}) -> + State#vqstate{ram_msg_count = RamMsgCount + 1}. + remove(AckRequired, MsgStatus = #msg_status { seq_id = SeqId, msg_id = MsgId, @@ -1390,10 +1390,10 @@ msg_indices_written_to_disk(Callback, MsgIdSet) -> %%---------------------------------------------------------------------------- publish_alpha(#msg_status { msg = undefined } = MsgStatus, State) -> - {Msg, State1} = read_msg(MsgStatus, true, State), - {MsgStatus#msg_status { msg = Msg }, State1}; -publish_alpha(MsgStatus, #vqstate {ram_msg_count = RamMsgCount } = State) -> - {MsgStatus, State #vqstate { ram_msg_count = RamMsgCount + 1 }}. + {Msg, State1} = read_msg(MsgStatus, State), + {MsgStatus#msg_status { msg = Msg }, inc_ram_msg_count(State1)}; +publish_alpha(MsgStatus, State) -> + {MsgStatus, inc_ram_msg_count(State)}. publish_beta(MsgStatus, State) -> {#msg_status { msg = Msg} = MsgStatus1, -- cgit v1.2.1 From 6a5738c6025ee65865d1fc358758fb01b6be82e4 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 12 Jan 2013 10:18:45 +0000 Subject: cosmetic --- src/rabbit_variable_queue.erl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 285dfcd7..5dc46f1b 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -1122,7 +1122,7 @@ remove(AckRequired, MsgStatus = #msg_status { index_state = IndexState, msg_store_clients = MSCState, len = Len, - persistent_count = PCount }) -> + persistent_count = PCount}) -> %% 1. Mark it delivered if necessary IndexState1 = maybe_write_delivered( IndexOnDisk andalso not IsDelivered, @@ -1151,11 +1151,11 @@ remove(AckRequired, MsgStatus = #msg_status { PCount1 = PCount - one_if(IsPersistent andalso not AckRequired), RamMsgCount1 = RamMsgCount - one_if(Msg =/= undefined), - {AckTag, State1 #vqstate { ram_msg_count = RamMsgCount1, - out_counter = OutCount + 1, - index_state = IndexState2, - len = Len - 1, - persistent_count = PCount1 }}. + {AckTag, State1 #vqstate {ram_msg_count = RamMsgCount1, + out_counter = OutCount + 1, + index_state = IndexState2, + len = Len - 1, + persistent_count = PCount1}}. purge_betas_and_deltas(LensByStore, State = #vqstate { q3 = Q3, -- cgit v1.2.1 From 02f75e388797b9efb9b1535667e60904f72ab9ed Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 12 Jan 2013 11:35:24 +0000 Subject: pass State to iterator We want to be able to zip this iterator with other iterators that also manipulate the vqstate. Hence we must pass the State explicitly rather than keeping it opaque inside the iterator state. Also, some refactoring on read_msg. --- src/rabbit_variable_queue.erl | 94 ++++++++++++++++++++----------------------- 1 file changed, 43 insertions(+), 51 deletions(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index ac6a50af..8cb5da0b 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -676,7 +676,7 @@ ackfold(MsgFun, Acc, State, AckTags) -> end, {Acc, State}, AckTags), {AccN, a(StateN)}. -fold(Fun, Acc, State) -> ifold(Fun, Acc, iterator(State)). +fold(Fun, Acc, State) -> ifold(Fun, Acc, iterator(State), State). len(#vqstate { len = Len }) -> Len. @@ -1080,14 +1080,16 @@ queue_out(State = #vqstate { q4 = Q4 }) -> read_msg(#msg_status{msg = undefined, msg_id = MsgId, - is_persistent = IsPersistent}, - State = #vqstate{msg_store_clients = MSCState}) -> - {{ok, Msg = #basic_message {}}, MSCState1} = - msg_store_read(MSCState, IsPersistent, MsgId), - {Msg, State #vqstate {msg_store_clients = MSCState1}}; + is_persistent = IsPersistent}, State) -> + read_msg(MsgId, IsPersistent, State); read_msg(#msg_status{msg = Msg}, State) -> {Msg, State}. +read_msg(MsgId, IsPersistent, State = #vqstate{msg_store_clients = MSCState}) -> + {{ok, Msg = #basic_message {}}, MSCState1} = + msg_store_read(MSCState, IsPersistent, MsgId), + {Msg, State #vqstate {msg_store_clients = MSCState1}}. + inc_ram_msg_count(State = #vqstate{ram_msg_count = RamMsgCount}) -> State#vqstate{ram_msg_count = RamMsgCount + 1}. @@ -1442,57 +1444,47 @@ delta_limit(#delta { start_seq_id = StartSeqId }) -> StartSeqId. %% Iterator %%---------------------------------------------------------------------------- -iterator(State = #vqstate{q4 = Q4}) -> {q4, Q4, State}. - -next({q4, _, State} = It) -> next(It, q3, State#vqstate.q3); -next({q3, _, State} = It) -> next(It, delta, State#vqstate.delta); -next({delta, _, State} = It) -> next(It, q2, State#vqstate.q2); -next({q2, _, State} = It) -> next(It, q1, State#vqstate.q1); -next({q1, _, State} = It) -> next(It, done, State); -next({done, _, State}) -> {empty, State}. - -next({delta, #delta{start_seq_id = DeltaSeqId, end_seq_id = DeltaSeqId}, State}, - NextKey, Next) -> - next({NextKey, Next, State}); -next({delta, Delta = #delta{start_seq_id = DeltaSeqId, - end_seq_id = DeltaSeqIdEnd}, - State = #vqstate{index_state = IndexState}}, NextKey, Next) -> - DeltaSeqId1 = lists:min( - [rabbit_queue_index:next_segment_boundary(DeltaSeqId), - DeltaSeqIdEnd]), - {List, IndexState1} = rabbit_queue_index:read(DeltaSeqId, DeltaSeqId1, - IndexState), - next({delta, {Delta#delta{start_seq_id = DeltaSeqId1}, List}, - State#vqstate{index_state = IndexState1}}, NextKey, Next); -next({delta, {Delta, []}, State}, NextKey, Next) -> - next({delta, Delta, State}, NextKey, Next); -next({delta, {Delta, [M | Rest]}, - State = #vqstate{msg_store_clients = MSCState}}, _NextKey, _Next) -> +iterator(State) -> istate(start, State). + +istate(start, State) -> {q4, State#vqstate.q4}; +istate(q4, State) -> {q3, State#vqstate.q3}; +istate(q3, State) -> {delta, State#vqstate.delta}; +istate(delta, State) -> {q2, State#vqstate.q2}; +istate(q2, State) -> {q1, State#vqstate.q1}; +istate(q1, _State) -> done. + +next(done, State) -> {empty, State}; +next({delta, #delta{start_seq_id = SeqId, end_seq_id = SeqId}}, State) -> + next(istate(delta, State), State); +next({delta, Delta = #delta{start_seq_id = SeqId, end_seq_id = SeqIdEnd}}, + State = #vqstate{index_state = IndexState}) -> + SeqIdB = rabbit_queue_index:next_segment_boundary(SeqId), + SeqId1 = lists:min([SeqIdB, SeqIdEnd]), + {List, IndexState1} = rabbit_queue_index:read(SeqId, SeqId1, IndexState), + next({delta, Delta#delta{start_seq_id = SeqId1}, List}, + State#vqstate{index_state = IndexState1}); +next({delta, Delta, []}, State) -> next({delta, Delta}, State); +next({delta, Delta, [M | Rest]}, State) -> {MsgId, _SeqId, MsgProps, IsPersistent, _IsDelivered} = M, - {{ok, Msg = #basic_message {}}, MSCState1} = - msg_store_read(MSCState, IsPersistent, MsgId), - State1 = State#vqstate{msg_store_clients = MSCState1}, - {value, Msg, MsgProps, {delta, {Delta, Rest}, State1}}; -next({Key, Q, State}, NextKey, Next) -> + {Msg, State1} = read_msg(MsgId, IsPersistent, State), + {value, Msg, MsgProps, {delta, Delta, Rest}, State1}; +next({Key, Q}, State) -> case ?QUEUE:out(Q) of - {empty, _Q} -> - next({NextKey, Next, State}); - {{value, MsgStatus}, QN} -> - {Msg, State1} = read_msg(MsgStatus, State), - {value, Msg, MsgStatus#msg_status.msg_props, {Key, QN, State1}} + {empty, _Q} -> next(istate(Key, State), State); + {{value, MsgStatus}, QN} -> {Msg, State1} = read_msg(MsgStatus, State), + MsgProps = MsgStatus#msg_status.msg_props, + {value, Msg, MsgProps, {Key, QN}, State1} end. -done({_, _, State}) -> State. - -ifold(Fun, Acc, It) -> - case next(It) of - {value, Msg, MsgProps, Next} -> +ifold(Fun, Acc, It, State) -> + case next(It, State) of + {value, Msg, MsgProps, Next, State1} -> case Fun(Msg, MsgProps, Acc) of - {stop, Acc1} -> {Acc1, done(Next)}; - {cont, Acc1} -> ifold(Fun, Acc1, Next) + {stop, Acc1} -> {Acc1, State1}; + {cont, Acc1} -> ifold(Fun, Acc1, Next, State1) end; - {empty, Done} -> - {Acc, Done} + {empty, State1} -> + {Acc, State1} end. %%---------------------------------------------------------------------------- -- cgit v1.2.1 From 58872acdc0cc36fa2c47a9559fd087edd581ce15 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 12 Jan 2013 16:15:06 +0000 Subject: extract a vq helper fun for constructing a msg_status --- src/rabbit_variable_queue.erl | 45 +++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 5dc46f1b..dc32902f 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -897,11 +897,25 @@ gb_sets_maybe_insert(false, _Val, Set) -> Set; gb_sets_maybe_insert(true, Val, Set) -> gb_sets:add(Val, Set). msg_status(IsPersistent, IsDelivered, SeqId, - Msg = #basic_message { id = MsgId }, MsgProps) -> - #msg_status { seq_id = SeqId, msg_id = MsgId, msg = Msg, - is_persistent = IsPersistent, is_delivered = IsDelivered, - msg_on_disk = false, index_on_disk = false, - msg_props = MsgProps }. + Msg = #basic_message {id = MsgId}, MsgProps) -> + #msg_status{seq_id = SeqId, + msg_id = MsgId, + msg = Msg, + is_persistent = IsPersistent, + is_delivered = IsDelivered, + msg_on_disk = false, + index_on_disk = false, + msg_props = MsgProps}. + +beta_msg_status({MsgId, SeqId, MsgProps, IsPersistent, IsDelivered}) -> + #msg_status{seq_id = SeqId, + msg_id = MsgId, + msg = undefined, + is_persistent = IsPersistent, + is_delivered = IsDelivered, + msg_on_disk = true, + index_on_disk = true, + msg_props = MsgProps}. trim_msg_status(MsgStatus) -> MsgStatus #msg_status { msg = undefined }. @@ -968,7 +982,7 @@ maybe_write_delivered(true, SeqId, IndexState) -> betas_from_index_entries(List, TransientThreshold, RPA, DPA, IndexState) -> {Filtered, Delivers, Acks} = lists:foldr( - fun ({MsgId, SeqId, MsgProps, IsPersistent, IsDelivered}, + fun ({_MsgId, SeqId, _MsgProps, IsPersistent, IsDelivered} = M, {Filtered1, Delivers1, Acks1} = Acc) -> case SeqId < TransientThreshold andalso not IsPersistent of true -> {Filtered1, @@ -976,21 +990,10 @@ betas_from_index_entries(List, TransientThreshold, RPA, DPA, IndexState) -> [SeqId | Acks1]}; false -> case (gb_trees:is_defined(SeqId, RPA) orelse gb_trees:is_defined(SeqId, DPA)) of - false -> - {?QUEUE:in_r( - m(#msg_status { - seq_id = SeqId, - msg_id = MsgId, - msg = undefined, - is_persistent = IsPersistent, - is_delivered = IsDelivered, - msg_on_disk = true, - index_on_disk = true, - msg_props = MsgProps - }), Filtered1), - Delivers1, Acks1}; - true -> - Acc + false -> {?QUEUE:in_r(m(beta_msg_status(M)), + Filtered1), + Delivers1, Acks1}; + true -> Acc end end end, {?QUEUE:new(), [], []}, List), -- cgit v1.2.1 From 3f887c3ac7bb0c4f94bae9860b157906ed40a950 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 12 Jan 2013 16:18:48 +0000 Subject: return MsgStatus only from iterator leaving the message reading to the fold --- src/rabbit_variable_queue.erl | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index baeb4721..347964f4 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -1468,23 +1468,20 @@ next({delta, Delta = #delta{start_seq_id = SeqId, end_seq_id = SeqIdEnd}}, State#vqstate{index_state = IndexState1}); next({delta, Delta, []}, State) -> next({delta, Delta}, State); next({delta, Delta, [M | Rest]}, State) -> - {MsgId, _SeqId, MsgProps, IsPersistent, _IsDelivered} = M, - {Msg, State1} = read_msg(MsgId, IsPersistent, State), - {value, Msg, MsgProps, {delta, Delta, Rest}, State1}; + {value, beta_msg_status(M), {delta, Delta, Rest}, State}; next({Key, Q}, State) -> case ?QUEUE:out(Q) of {empty, _Q} -> next(istate(Key, State), State); - {{value, MsgStatus}, QN} -> {Msg, State1} = read_msg(MsgStatus, State), - MsgProps = MsgStatus#msg_status.msg_props, - {value, Msg, MsgProps, {Key, QN}, State1} + {{value, MsgStatus}, QN} -> {value, MsgStatus, {Key, QN}, State} end. ifold(Fun, Acc, It, State) -> case next(It, State) of - {value, Msg, MsgProps, Next, State1} -> - case Fun(Msg, MsgProps, Acc) of - {stop, Acc1} -> {Acc1, State1}; - {cont, Acc1} -> ifold(Fun, Acc1, Next, State1) + {value, MsgStatus, Next, State1} -> + {Msg, State2} = read_msg(MsgStatus, State1), + case Fun(Msg, MsgStatus#msg_status.msg_props, Acc) of + {stop, Acc1} -> {Acc1, State2}; + {cont, Acc1} -> ifold(Fun, Acc1, Next, State2) end; {empty, State1} -> {Acc, State1} -- cgit v1.2.1 From 304ee315b59815ec5e20bcb8b25d31f2e6b18b84 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 12 Jan 2013 23:41:19 +0000 Subject: only pass the minimum necessary state to 'next' i.e. the IndexState. The remainder of the State is encapsulated inside the iterator state. Technically we only need q{1-4} and delta, but it's simpler and more obvious to just pass a read-only State around. --- src/rabbit_variable_queue.erl | 49 +++++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 347964f4..dcaaa5ed 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -1449,42 +1449,45 @@ delta_limit(#delta { start_seq_id = StartSeqId }) -> StartSeqId. iterator(State) -> istate(start, State). -istate(start, State) -> {q4, State#vqstate.q4}; -istate(q4, State) -> {q3, State#vqstate.q3}; -istate(q3, State) -> {delta, State#vqstate.delta}; -istate(delta, State) -> {q2, State#vqstate.q2}; -istate(q2, State) -> {q1, State#vqstate.q1}; +istate(start, State) -> {q4, State#vqstate.q4, State}; +istate(q4, State) -> {q3, State#vqstate.q3, State}; +istate(q3, State) -> {delta, State#vqstate.delta, State}; +istate(delta, State) -> {q2, State#vqstate.q2, State}; +istate(q2, State) -> {q1, State#vqstate.q1, State}; istate(q1, _State) -> done. -next(done, State) -> {empty, State}; -next({delta, #delta{start_seq_id = SeqId, end_seq_id = SeqId}}, State) -> - next(istate(delta, State), State); -next({delta, Delta = #delta{start_seq_id = SeqId, end_seq_id = SeqIdEnd}}, - State = #vqstate{index_state = IndexState}) -> +next(done, IndexState) -> {empty, IndexState}; +next({delta, #delta{start_seq_id = SeqId, + end_seq_id = SeqId}, State}, IndexState) -> + next(istate(delta, State), IndexState); +next({delta, #delta{start_seq_id = SeqId, + end_seq_id = SeqIdEnd} = Delta, State}, IndexState) -> SeqIdB = rabbit_queue_index:next_segment_boundary(SeqId), SeqId1 = lists:min([SeqIdB, SeqIdEnd]), {List, IndexState1} = rabbit_queue_index:read(SeqId, SeqId1, IndexState), - next({delta, Delta#delta{start_seq_id = SeqId1}, List}, - State#vqstate{index_state = IndexState1}); -next({delta, Delta, []}, State) -> next({delta, Delta}, State); -next({delta, Delta, [M | Rest]}, State) -> - {value, beta_msg_status(M), {delta, Delta, Rest}, State}; -next({Key, Q}, State) -> + next({delta, Delta#delta{start_seq_id = SeqId1}, List, State}, IndexState1); +next({delta, Delta, [], State}, IndexState) -> + next({delta, Delta, State}, IndexState); +next({delta, Delta, [M | Rest], State}, IndexState) -> + {value, beta_msg_status(M), {delta, Delta, Rest, State}, IndexState}; +next({Key, Q, State}, IndexState) -> case ?QUEUE:out(Q) of - {empty, _Q} -> next(istate(Key, State), State); - {{value, MsgStatus}, QN} -> {value, MsgStatus, {Key, QN}, State} + {empty, _Q} -> next(istate(Key, State), IndexState); + {{value, MsgStatus}, QN} -> {value, MsgStatus, {Key, QN, State}, + IndexState} end. -ifold(Fun, Acc, It, State) -> - case next(It, State) of - {value, MsgStatus, Next, State1} -> +ifold(Fun, Acc, It, State = #vqstate{index_state = IndexState}) -> + case next(It, IndexState) of + {value, MsgStatus, Next, IndexState1} -> + State1 = State#vqstate{index_state = IndexState1}, {Msg, State2} = read_msg(MsgStatus, State1), case Fun(Msg, MsgStatus#msg_status.msg_props, Acc) of {stop, Acc1} -> {Acc1, State2}; {cont, Acc1} -> ifold(Fun, Acc1, Next, State2) end; - {empty, State1} -> - {Acc, State1} + {empty, IndexState1} -> + {Acc, State#vqstate{index_state = IndexState1}} end. %%---------------------------------------------------------------------------- -- cgit v1.2.1 From b9616756ea1fdd4dbb8a51376dd3aa531d7b4b70 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 12 Jan 2013 23:46:19 +0000 Subject: oops; nuke unused var --- src/gen_server2.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gen_server2.erl b/src/gen_server2.erl index dc55948b..20de79c2 100644 --- a/src/gen_server2.erl +++ b/src/gen_server2.erl @@ -863,7 +863,7 @@ dispatch(Info, Mod, State) -> common_reply(_Name, From, Reply, _NState, [] = _Debug) -> reply(From, Reply), []; -common_reply(Name, {To, Tag} = From, Reply, NState, Debug) -> +common_reply(Name, {To, _Tag} = From, Reply, NState, Debug) -> reply(From, Reply), sys:handle_debug(Debug, fun print_event/3, Name, {out, Reply, To, NState}). -- cgit v1.2.1 From f55da0d65a5851e4249ac0afee70ff200bff8aeb Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sun, 13 Jan 2013 10:21:13 +0000 Subject: cosmetic --- src/rabbit_variable_queue.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index dcaaa5ed..d3147999 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -1473,8 +1473,8 @@ next({delta, Delta, [M | Rest], State}, IndexState) -> next({Key, Q, State}, IndexState) -> case ?QUEUE:out(Q) of {empty, _Q} -> next(istate(Key, State), IndexState); - {{value, MsgStatus}, QN} -> {value, MsgStatus, {Key, QN, State}, - IndexState} + {{value, MsgStatus}, QN} -> Next = {Key, QN, State}, + {value, MsgStatus, Next, IndexState} end. ifold(Fun, Acc, It, State = #vqstate{index_state = IndexState}) -> -- cgit v1.2.1 From 7141d5bfc377e29d6c6b8f69762b6bb865a6b414 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sun, 13 Jan 2013 10:49:08 +0000 Subject: include pending_acks in 'fold' we implement this as a zipper over three iterators --- src/rabbit_variable_queue.erl | 59 +++++++++++++++++++++++++++++++++---------- 1 file changed, 46 insertions(+), 13 deletions(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index d3147999..185da19c 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -676,7 +676,19 @@ ackfold(MsgFun, Acc, State, AckTags) -> end, {Acc, State}, AckTags), {AccN, a(StateN)}. -fold(Fun, Acc, State) -> ifold(Fun, Acc, iterator(State), State). +fold(Fun, Acc, State = #vqstate{index_state = IndexState}) -> + {Its, IndexState1} = + lists:foldl(fun (It, {Its, IndexState2}) -> + case next(It, IndexState2) of + {empty, IndexState3} -> + {Its, IndexState3}; + {value, MsgStatus, It1, IndexState3} -> + {[{MsgStatus, It1} | Its], IndexState3} + end + end, {[], IndexState}, [msg_iterator(State), + disk_ack_iterator(State), + ram_ack_iterator(State)]), + ifold(Fun, Acc, Its, State#vqstate{index_state = IndexState1}). len(#vqstate { len = Len }) -> Len. @@ -1447,7 +1459,13 @@ delta_limit(#delta { start_seq_id = StartSeqId }) -> StartSeqId. %% Iterator %%---------------------------------------------------------------------------- -iterator(State) -> istate(start, State). +ram_ack_iterator(State) -> + {ack, gb_trees:iterator(State#vqstate.ram_pending_ack)}. + +disk_ack_iterator(State) -> + {ack, gb_trees:iterator(State#vqstate.disk_pending_ack)}. + +msg_iterator(State) -> istate(start, State). istate(start, State) -> {q4, State#vqstate.q4, State}; istate(q4, State) -> {q3, State#vqstate.q3, State}; @@ -1456,6 +1474,11 @@ istate(delta, State) -> {q2, State#vqstate.q2, State}; istate(q2, State) -> {q1, State#vqstate.q1, State}; istate(q1, _State) -> done. +next({ack, It}, IndexState) -> + case gb_trees:next(It) of + none -> {empty, IndexState}; + {_SeqId, MsgStatus, It1} -> {value, MsgStatus, {ack, It1}, IndexState} + end; next(done, IndexState) -> {empty, IndexState}; next({delta, #delta{start_seq_id = SeqId, end_seq_id = SeqId}, State}, IndexState) -> @@ -1477,17 +1500,27 @@ next({Key, Q, State}, IndexState) -> {value, MsgStatus, Next, IndexState} end. -ifold(Fun, Acc, It, State = #vqstate{index_state = IndexState}) -> - case next(It, IndexState) of - {value, MsgStatus, Next, IndexState1} -> - State1 = State#vqstate{index_state = IndexState1}, - {Msg, State2} = read_msg(MsgStatus, State1), - case Fun(Msg, MsgStatus#msg_status.msg_props, Acc) of - {stop, Acc1} -> {Acc1, State2}; - {cont, Acc1} -> ifold(Fun, Acc1, Next, State2) - end; - {empty, IndexState1} -> - {Acc, State#vqstate{index_state = IndexState1}} +ifold(_Fun, Acc, [], State) -> + {Acc, State}; +ifold(Fun, Acc, Its, State) -> + [{MsgStatus, It} | Rest] = lists:sort( + fun ({MsgStatus1, _}, {MsgStatus2, _}) -> + MsgStatus1#msg_status.seq_id < + MsgStatus2#msg_status.seq_id + end, Its), + {Msg, State1} = read_msg(MsgStatus, State), + case Fun(Msg, MsgStatus#msg_status.msg_props, Acc) of + {stop, Acc1} -> + {Acc1, State}; + {cont, Acc1} -> + case next(It, State1#vqstate.index_state) of + {empty, IndexState1} -> + ifold(Fun, Acc1, Rest, + State1#vqstate{index_state = IndexState1}); + {value, MsgStatus1, It1, IndexState1} -> + ifold(Fun, Acc1, [{MsgStatus1, It1} | Rest], + State1#vqstate{index_state = IndexState1}) + end end. %%---------------------------------------------------------------------------- -- cgit v1.2.1 From 63db60fd705c15fe66530fe68fedfb80c9eaaaa0 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sun, 13 Jan 2013 10:59:59 +0000 Subject: refactor --- src/rabbit_variable_queue.erl | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 185da19c..6f77b867 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -677,17 +677,10 @@ ackfold(MsgFun, Acc, State, AckTags) -> {AccN, a(StateN)}. fold(Fun, Acc, State = #vqstate{index_state = IndexState}) -> - {Its, IndexState1} = - lists:foldl(fun (It, {Its, IndexState2}) -> - case next(It, IndexState2) of - {empty, IndexState3} -> - {Its, IndexState3}; - {value, MsgStatus, It1, IndexState3} -> - {[{MsgStatus, It1} | Its], IndexState3} - end - end, {[], IndexState}, [msg_iterator(State), - disk_ack_iterator(State), - ram_ack_iterator(State)]), + {Its, IndexState1} = lists:foldl(fun inext/2, {[], IndexState}, + [msg_iterator(State), + disk_ack_iterator(State), + ram_ack_iterator(State)]), ifold(Fun, Acc, Its, State#vqstate{index_state = IndexState1}). len(#vqstate { len = Len }) -> Len. @@ -1500,6 +1493,14 @@ next({Key, Q, State}, IndexState) -> {value, MsgStatus, Next, IndexState} end. +inext(It, {Its, IndexState}) -> + case next(It, IndexState) of + {empty, IndexState1} -> + {Its, IndexState1}; + {value, MsgStatus1, It1, IndexState1} -> + {[{MsgStatus1, It1} | Its], IndexState1} + end. + ifold(_Fun, Acc, [], State) -> {Acc, State}; ifold(Fun, Acc, Its, State) -> @@ -1513,14 +1514,8 @@ ifold(Fun, Acc, Its, State) -> {stop, Acc1} -> {Acc1, State}; {cont, Acc1} -> - case next(It, State1#vqstate.index_state) of - {empty, IndexState1} -> - ifold(Fun, Acc1, Rest, - State1#vqstate{index_state = IndexState1}); - {value, MsgStatus1, It1, IndexState1} -> - ifold(Fun, Acc1, [{MsgStatus1, It1} | Rest], - State1#vqstate{index_state = IndexState1}) - end + {Its1, IndexState1} = inext(It, {Rest, State1#vqstate.index_state}), + ifold(Fun, Acc1, Its1, State1#vqstate{index_state = IndexState1}) end. %%---------------------------------------------------------------------------- -- cgit v1.2.1 From bd61fc79a05b5d1facf4ccecfabdcbce04ed1b23 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 14 Jan 2013 11:25:16 +0000 Subject: improve vq:fold test by placing some messages in q1 --- src/rabbit_tests.erl | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 09ed3d08..45d5a09e 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -2227,10 +2227,10 @@ variable_queue_publish(IsPersistent, Count, VQ) -> variable_queue_publish(IsPersistent, Count, fun (_N, P) -> P end, VQ). variable_queue_publish(IsPersistent, Count, PropFun, VQ) -> - variable_queue_publish(IsPersistent, Count, PropFun, + variable_queue_publish(IsPersistent, 1, Count, PropFun, fun (_N) -> <<>> end, VQ). -variable_queue_publish(IsPersistent, Count, PropFun, PayloadFun, VQ) -> +variable_queue_publish(IsPersistent, Start, Count, PropFun, PayloadFun, VQ) -> lists:foldl( fun (N, VQN) -> rabbit_variable_queue:publish( @@ -2242,7 +2242,7 @@ variable_queue_publish(IsPersistent, Count, PropFun, PayloadFun, VQ) -> end}, PayloadFun(N)), PropFun(N, #message_properties{}), false, self(), VQN) - end, VQ, lists:seq(1, Count)). + end, VQ, lists:seq(Start, Start + Count - 1)). variable_queue_fetch(Count, IsPersistent, IsDelivered, Len, VQ) -> lists:foldl(fun (N, {VQN, AckTagsAcc}) -> @@ -2327,13 +2327,21 @@ test_variable_queue() -> passed. test_variable_queue_fold(VQ0) -> - Count = rabbit_queue_index:next_segment_boundary(0) * 2 + 64, + JustOverTwoSegs = rabbit_queue_index:next_segment_boundary(0) * 2 + 64, VQ1 = rabbit_variable_queue:set_ram_duration_target(0, VQ0), VQ2 = variable_queue_publish( - true, Count, fun (_, P) -> P end, fun erlang:term_to_binary/1, VQ1), + true, 1, JustOverTwoSegs, + fun (_, P) -> P end, fun erlang:term_to_binary/1, VQ1), + VQ3 = rabbit_variable_queue:set_ram_duration_target(infinity, VQ2), + VQ4 = variable_queue_publish( + true, JustOverTwoSegs + 1, 64, + fun (_, P) -> P end, fun erlang:term_to_binary/1, VQ3), + [false = V == 0 || {K, V} <- rabbit_variable_queue:status(VQ4), + lists:member(K, [q1, delta, q3])], %% precondition + Count = JustOverTwoSegs + 64, lists:foldl( - fun (Cut, VQ3) -> test_variable_queue_fold(Cut, Count, VQ3) end, - VQ2, [0, 1, 2, Count div 2, Count - 1, Count, Count + 1, Count * 2]). + fun (Cut, VQ5) -> test_variable_queue_fold(Cut, Count, VQ5) end, + VQ4, [0, 1, 2, Count div 2, Count - 1, Count, Count + 1, Count * 2]). test_variable_queue_fold(Cut, Count, VQ0) -> {Acc, VQ1} = rabbit_variable_queue:fold( @@ -2426,7 +2434,7 @@ test_dropfetchwhile(VQ0) -> %% add messages with sequential expiry VQ1 = variable_queue_publish( - false, Count, + false, 1, Count, fun (N, Props) -> Props#message_properties{expiry = N} end, fun erlang:term_to_binary/1, VQ0), -- cgit v1.2.1 From d3a12bdc757e87e3205d60e1bb4a58a94f3454ec Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 14 Jan 2013 13:35:05 +0000 Subject: improve assertion in vq:fold test --- src/rabbit_tests.erl | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 45d5a09e..af8e2f9b 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -2336,8 +2336,12 @@ test_variable_queue_fold(VQ0) -> VQ4 = variable_queue_publish( true, JustOverTwoSegs + 1, 64, fun (_, P) -> P end, fun erlang:term_to_binary/1, VQ3), - [false = V == 0 || {K, V} <- rabbit_variable_queue:status(VQ4), - lists:member(K, [q1, delta, q3])], %% precondition + [false = case V of + {delta, _, 0, _} -> true; + 0 -> true; + _ -> false + end || {K, V} <- rabbit_variable_queue:status(VQ4), + lists:member(K, [q1, delta, q3])], %% precondition Count = JustOverTwoSegs + 64, lists:foldl( fun (Cut, VQ5) -> test_variable_queue_fold(Cut, Count, VQ5) end, -- cgit v1.2.1 From 1781208aff667a53475afc805882210dd3013dc4 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 14 Jan 2013 15:42:00 +0000 Subject: Improve workingness further. We now should be handling multiple blocks for different reasons correctly. --- src/rabbit_amqqueue_process.erl | 121 +++++++++++++++++++++------------------- src/rabbit_limiter.erl | 22 ++++---- 2 files changed, 76 insertions(+), 67 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index f48005ef..8878de9c 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -64,9 +64,18 @@ monitor_ref, acktags, consumer_count, + %% Queue of {ChPid, #consumer{}} for consumers which have + %% been blocked for any reason blocked_consumers, + %% List of consumer tags which have individually been + %% blocked by the limiter. + blocked_ctags, + %% The limiter itself limiter, + %% Has the limiter imposed a channel-wide block, either + %% because of qos or channel flow? is_limit_active, + %% Internal flow control for queue -> writer unsent_message_count}). %%---------------------------------------------------------------------------- @@ -355,6 +364,7 @@ ch_record(ChPid) -> acktags = queue:new(), consumer_count = 0, blocked_consumers = queue:new(), + blocked_ctags = [], is_limit_active = false, limiter = rabbit_limiter:make_token(), unsent_message_count = 0}, @@ -402,13 +412,6 @@ block_consumer(C = #cr{blocked_consumers = Blocked}, QEntry) -> is_ch_blocked(#cr{unsent_message_count = Count, is_limit_active = Limited}) -> Limited orelse Count >= ?UNSENT_MESSAGE_LIMIT. -ch_record_state_transition(OldCR, NewCR) -> - case {is_ch_blocked(OldCR), is_ch_blocked(NewCR)} of - {true, false} -> unblock; - {false, true} -> block; - {_, _} -> ok - end. - deliver_msgs_to_consumers(_DeliverFun, true, State) -> {true, State}; deliver_msgs_to_consumers(DeliverFun, false, @@ -423,27 +426,38 @@ deliver_msgs_to_consumers(DeliverFun, false, deliver_msgs_to_consumers(DeliverFun, Stop, State1) end. -deliver_msg_to_consumer(DeliverFun, E = {ChPid, Consumer}, +deliver_msg_to_consumer(DeliverFun, + E = {ChPid, + Consumer = #consumer{tag = CTag, + ack_required = AckReq}}, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> C = ch_record(ChPid), case is_ch_blocked(C) of - true -> block_consumer(C, E), - {false, State}; - false -> #cr{limiter = Limiter, ch_pid = ChPid} = C, - {CanSend, Lim2} = - rabbit_limiter:can_send( - Limiter, ChPid, self(), Consumer#consumer.ack_required, - Consumer#consumer.tag, BQ:len(BQS)), - case CanSend of - false -> block_consumer(C#cr{is_limit_active = true, - limiter = Lim2}, E), - {false, State}; - true -> AC1 = queue:in(E, State#q.active_consumers), - deliver_msg_to_consumer( - DeliverFun, Consumer, C#cr{limiter = Lim2}, - State#q{active_consumers = AC1}) - end + true -> + block_consumer(C, E), + {false, State}; + false -> + #cr{limiter = Limiter, ch_pid = ChPid, blocked_ctags = BCTags} = C, + case rabbit_limiter:can_cons_send( + Limiter, ChPid, CTag, BQ:len(BQS)) of + {false, Lim2} -> + %% TODO unify with first case? + block_consumer(C#cr{limiter = Lim2, + blocked_ctags = [CTag | BCTags]}, E), + {false, State}; + {true, Lim2} -> + case rabbit_limiter:can_ch_send(Limiter, self(), AckReq) of + false -> + block_consumer(C#cr{is_limit_active = true}, E), + {false, State}; + true -> + AC1 = queue:in(E, State#q.active_consumers), + deliver_msg_to_consumer( + DeliverFun, Consumer, C#cr{limiter = Lim2}, + State#q{active_consumers = AC1}) + end + end end. deliver_msg_to_consumer(DeliverFun, @@ -601,16 +615,20 @@ possibly_unblock(State, ChPid, Update) -> not_found -> State; C -> - C1 = Update(C), - case ch_record_state_transition(C, C1) of - ok -> update_ch_record(C1), - State; - unblock -> #cr{blocked_consumers = Consumers} = C1, - update_ch_record( - C1#cr{blocked_consumers = queue:new()}), - AC1 = queue:join(State#q.active_consumers, - Consumers), - run_message_queue(State#q{active_consumers = AC1}) + C1 = #cr{blocked_ctags = BCTags1} = Update(C), + {Blocked, Unblocked} = + lists:partition( + fun({_ChPid, #consumer{tag = CTag}}) -> + is_ch_blocked(C1) orelse lists:member(CTag, BCTags1) + end, queue:to_list(C1#cr.blocked_consumers)), + case Unblocked of + [] -> update_ch_record(C1), + State; + _ -> update_ch_record( + C1#cr{blocked_consumers = queue:from_list(Blocked)}), + AC1 = queue:join(State#q.active_consumers, + queue:from_list(Unblocked)), + run_message_queue(State#q{active_consumers = AC1}) end end. @@ -662,8 +680,6 @@ check_exclusive_access(none, true, State) -> consumer_count() -> consumer_count(fun (_) -> false end). -active_consumer_count() -> consumer_count(fun is_ch_blocked/1). - consumer_count(Exclude) -> lists:sum([Count || C = #cr{consumer_count = Count} <- all_ch_record(), not Exclude(C)]). @@ -909,8 +925,8 @@ i(messages, State) -> messages_unacknowledged]]); i(consumers, _) -> consumer_count(); -i(active_consumers, _) -> - active_consumer_count(); +i(active_consumers, #q{active_consumers = ActiveConsumers}) -> + queue:len(ActiveConsumers); i(memory, _) -> {memory, M} = process_info(self(), memory), M; @@ -1124,9 +1140,10 @@ handle_call({basic_cancel, ChPid, ConsumerTag, OkMsg}, From, end; handle_call(stat, _From, State) -> - State1 = #q{backing_queue = BQ, backing_queue_state = BQS} = + State1 = #q{active_consumers = AC, + backing_queue = BQ, backing_queue_state = BQS} = drop_expired_msgs(ensure_expiry_timer(State)), - reply({ok, BQ:len(BQS), active_consumer_count()}, State1); + reply({ok, BQ:len(BQS), queue:len(AC)}, State1); handle_call({delete, IfUnused, IfEmpty}, From, State = #q{backing_queue_state = BQS, backing_queue = BQ}) -> @@ -1314,26 +1331,16 @@ handle_cast(stop_mirroring, State = #q{backing_queue = BQ, backing_queue_state = BQS1}); handle_cast({inform_limiter, ChPid, Msg}, - State = #q{active_consumers = AC, - backing_queue = BQ, + State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> - C = #cr{limiter = Limiter, - blocked_consumers = Blocked} = ch_record(ChPid), + #cr{limiter = Limiter, + blocked_ctags = BCTags} = ch_record(ChPid), {Unblock, Limiter2} = rabbit_limiter:inform(Limiter, ChPid, BQ:len(BQS), Msg), - NewBlocked = queue:filter(fun({_ChPid, #consumer{tag = CTag}}) -> - not lists:member(CTag, Unblock) - end, Blocked), - NewUnblocked = queue:filter(fun({_ChPid, #consumer{tag = CTag}}) -> - lists:member(CTag, Unblock) - end, Blocked), - %% TODO can this whole thing be replaced by possibly_unblock? - %% TODO that is_limit_active = false thing is wrong - but we do - %% not allow for per-consumer blocking! - update_ch_record(C#cr{limiter = Limiter2, blocked_consumers = NewBlocked, - is_limit_active = false}), - AC1 = queue:join(NewUnblocked, AC), - noreply(run_message_queue(State#q{active_consumers = AC1})); + noreply(possibly_unblock( + State, ChPid, + fun(C) -> C#cr{blocked_ctags = BCTags -- Unblock, + limiter = Limiter2} end)); handle_cast(wake_up, State) -> noreply(State). diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index f031db51..9da1bc6f 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -24,7 +24,8 @@ -export([start_link/0, make_token/0, make_token/1, is_enabled/1, enable/2, disable/1]). --export([limit/2, can_send/6, ack/2, register/2, unregister/2]). +-export([limit/2, can_ch_send/3, can_cons_send/4, + ack/2, register/2, unregister/2]). -export([get_limit/1, block/1, unblock/1, is_blocked/1]). -export([inform/4]). @@ -47,6 +48,7 @@ -spec(enable/2 :: (token(), non_neg_integer()) -> token()). -spec(disable/1 :: (token()) -> token()). -spec(limit/2 :: (token(), non_neg_integer()) -> 'ok' | {'disabled', token()}). +-spec(can_ch_send/3 :: (token(), pid(), boolean()) -> boolean()). %% TODO %% -spec(can_send/5 :: (token(), pid(), boolean(), %% rabbit_types:ctag(), non_neg_integer()) -> boolean()). @@ -98,18 +100,18 @@ limit(Limiter, PrefetchCount) -> %% breaching a limit. Note that we don't use maybe_call here in order %% to avoid always going through with_exit_handler/2, even when the %% limiter is disabled. -can_send(#token{pid = Pid, enabled = true, q_state = QState} = Token, - ChPid, QPid, AckRequired, CTag, Len) -> +can_ch_send(#token{pid = Pid, enabled = true}, QPid, AckRequired) -> rabbit_misc:with_exit_handler( - fun () -> {true, Token} end, + fun () -> true end, fun () -> - CanLim = gen_server2:call(Pid, {can_send, QPid, AckRequired}, - infinity), - {CanQ, NewQState} = can_send_q(CTag, Len, ChPid, QState), - {CanLim andalso CanQ, Token#token{q_state = NewQState}} + gen_server2:call(Pid, {can_send, QPid, AckRequired}, infinity) end); -can_send(Token, _, _, _, _, _) -> - {true, Token}. +can_ch_send(_, _, _) -> + true. + +can_cons_send(#token{q_state = QState} = Token, ChPid, CTag, Len) -> + {CanQ, NewQState} = can_send_q(CTag, Len, ChPid, QState), + {CanQ, Token#token{q_state = NewQState}}. %% Let the limiter know that the channel has received some acks from a %% consumer -- cgit v1.2.1 From e4502d77d2aec095f9983a1cb03d0ec7472612f1 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 14 Jan 2013 15:50:45 +0000 Subject: No. --- src/rabbit_amqqueue_process.erl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 13292091..f33ce920 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -445,7 +445,6 @@ deliver_msg_to_consumer(DeliverFun, case rabbit_limiter:can_cons_send( Limiter, ChPid, CTag, BQ:len(BQS)) of {false, Lim2} -> - %% TODO unify with first case? block_consumer(C#cr{limiter = Lim2, blocked_ctags = [CTag | BCTags]}, E), {false, State}; -- cgit v1.2.1 From 903efe324ad46fa0978c0ed759eb03d83bb26542 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 14 Jan 2013 16:00:38 +0000 Subject: TODO-- --- src/rabbit_amqqueue_process.erl | 6 ++++-- src/rabbit_limiter.erl | 7 ++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index f33ce920..47bc1641 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1152,10 +1152,12 @@ handle_call({basic_cancel, ChPid, ConsumerTag, OkMsg}, From, case lookup_ch(ChPid) of not_found -> reply(ok, State); - C = #cr{blocked_consumers = Blocked} -> + C = #cr{limiter = Limiter, blocked_consumers = Blocked} -> emit_consumer_deleted(ChPid, ConsumerTag, qname(State)), + Limiter1 = rabbit_limiter:forget_consumer(Limiter, ConsumerTag), Blocked1 = remove_consumer(ChPid, ConsumerTag, Blocked), - update_consumer_count(C#cr{blocked_consumers = Blocked1}, -1), + update_consumer_count(C#cr{limiter = Limiter1, + blocked_consumers = Blocked1}, -1), State1 = State#q{ exclusive_consumer = case Holder of {ChPid, ConsumerTag} -> none; diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index 9da1bc6f..39879063 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -27,7 +27,7 @@ -export([limit/2, can_ch_send/3, can_cons_send/4, ack/2, register/2, unregister/2]). -export([get_limit/1, block/1, unblock/1, is_blocked/1]). --export([inform/4]). +-export([inform/4, forget_consumer/2]). -import(rabbit_misc, [serial_add/2, serial_diff/2]). @@ -141,6 +141,9 @@ inform(Limiter = #token{q_state = Credits}, update_credit(CTag, Len, ChPid, Credit, Count, Drain, Credits), {Unblock, Limiter#token{q_state = Credits2}}. +forget_consumer(Limiter = #token{q_state = Credits}, CTag) -> + Limiter#token{q_state = dict:erase(CTag, Credits)}. + %%---------------------------------------------------------------------------- %% Queue-local code %%---------------------------------------------------------------------------- @@ -200,8 +203,6 @@ update_credit(CTag, Len, ChPid, Credit, Count0, Drain, Credits) -> false -> {[], NewCredits} end. -%% TODO currently we leak when a single session creates and destroys -%% lot of links. write_credit(CTag, Credit, Count, Drain, Credits) -> dict:store(CTag, #credit{credit = Credit, count = Count, -- cgit v1.2.1 From 57663f28556439ab5ef9a15a35685732d92ea63a Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 14 Jan 2013 17:02:50 +0000 Subject: Allow setting credit for a consumer tag that does not yet exist, since the adapter wants to do that. We leak if you set credit for a ctag and then don't consume... but the adapter never does that. --- src/rabbit_channel.erl | 31 +++++++++++++++++++++++-------- src/rabbit_limiter.erl | 13 ++++++++----- 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index c3a5b16d..2bc2db83 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -38,7 +38,7 @@ queue_names, queue_monitors, consumer_mapping, blocking, queue_consumers, delivering_queues, queue_collector_pid, stats_timer, confirm_enabled, publish_seqno, - unconfirmed, confirmed, capabilities, trace_state}). + unconfirmed, confirmed, capabilities, trace_state, credit_map}). -define(MAX_PERMISSION_CACHE_SIZE, 12). @@ -209,7 +209,8 @@ init([Channel, ReaderPid, WriterPid, ConnPid, ConnName, Protocol, User, VHost, unconfirmed = dtree:empty(), confirmed = [], capabilities = Capabilities, - trace_state = rabbit_trace:init(VHost)}, + trace_state = rabbit_trace:init(VHost), + credit_map = dict:new()}, State1 = rabbit_event:init_stats_timer(State, #ch.stats_timer), rabbit_event:notify(channel_created, infos(?CREATION_EVENT_KEYS, State1)), rabbit_event:if_enabled(State1, #ch.stats_timer, @@ -684,7 +685,8 @@ handle_method(#'basic.consume'{queue = QueueNameBin, nowait = NoWait}, _, State = #ch{conn_pid = ConnPid, limiter = Limiter, - consumer_mapping = ConsumerMapping}) -> + consumer_mapping = ConsumerMapping, + credit_map = CreditMap}) -> case dict:find(ConsumerTag, ConsumerMapping) of error -> QueueName = expand_queue_name_shortcut(QueueNameBin, State), @@ -702,6 +704,15 @@ handle_method(#'basic.consume'{queue = QueueNameBin, case rabbit_amqqueue:with_exclusive_access_or_die( QueueName, ConnPid, fun (Q) -> + case dict:find(ActualConsumerTag, CreditMap) of + {ok, {Credit, Count, Drain}} -> + ok = rabbit_amqqueue:inform_limiter( + self(), Q#amqqueue.pid, + {basic_credit, ActualConsumerTag, + Credit, Count, Drain, false}); + error -> + ok + end, {rabbit_amqqueue:basic_consume( Q, NoAck, self(), Limiter, ActualConsumerTag, ExclusiveConsume, @@ -711,9 +722,11 @@ handle_method(#'basic.consume'{queue = QueueNameBin, end) of {ok, Q = #amqqueue{pid = QPid, name = QName}} -> CM1 = dict:store(ActualConsumerTag, Q, ConsumerMapping), + CrM1 = dict:erase(ActualConsumerTag, CreditMap), State1 = monitor_delivering_queue( NoAck, QPid, QName, - State#ch{consumer_mapping = CM1}), + State#ch{consumer_mapping = CM1, + credit_map = CrM1}), {noreply, case NoWait of true -> consumer_monitor(ActualConsumerTag, State1); @@ -1083,14 +1096,16 @@ handle_method(#'basic.credit'{consumer_tag = CTag, credit = Credit, count = Count, drain = Drain}, _, - State = #ch{consumer_mapping = Consumers}) -> + State = #ch{consumer_mapping = Consumers, + credit_map = CMap}) -> case dict:find(CTag, Consumers) of {ok, Q} -> ok = rabbit_amqqueue:inform_limiter( self(), Q#amqqueue.pid, - {basic_credit, CTag, Credit, Count, Drain}), + {basic_credit, CTag, Credit, Count, Drain, true}), {noreply, State}; - error -> rabbit_misc:protocol_error( - not_allowed, "unknown consumer tag '~s'", [CTag]) + error -> CMap2 = dict:store(CTag, {Credit, Count, Drain}, CMap), + {reply, #'basic.credit_ok'{available = 0}, + State#ch{credit_map = CMap2}} end; handle_method(_MethodRecord, _Content, _State) -> diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index 39879063..900fbec3 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -136,9 +136,13 @@ is_blocked(Limiter) -> maybe_call(Limiter, is_blocked, false). inform(Limiter = #token{q_state = Credits}, - ChPid, Len, {basic_credit, CTag, Credit, Count, Drain}) -> - {Unblock, Credits2} = - update_credit(CTag, Len, ChPid, Credit, Count, Drain, Credits), + ChPid, Len, {basic_credit, CTag, Credit, Count, Drain, Reply}) -> + {Unblock, Credits2} = update_credit(CTag, Credit, Count, Drain, Credits), + case Reply of + true -> rabbit_channel:send_command( + ChPid, #'basic.credit_ok'{available = Len}); + false -> ok + end, {Unblock, Limiter#token{q_state = Credits2}}. forget_consumer(Limiter = #token{q_state = Credits}, CTag) -> @@ -188,7 +192,7 @@ send_drained(ChPid, CTag, Count) -> %% Update the credit state. %% TODO Edge case: if the queue has nothing in it, and drain is set, %% we want to send a basic.credit back. -update_credit(CTag, Len, ChPid, Credit, Count0, Drain, Credits) -> +update_credit(CTag, Credit, Count0, Drain, Credits) -> Count = case dict:find(CTag, Credits) of %% Use our count if we can, more accurate @@ -196,7 +200,6 @@ update_credit(CTag, Len, ChPid, Credit, Count0, Drain, Credits) -> %% But if this is new, take it from the adapter _ -> Count0 end, - rabbit_channel:send_command(ChPid, #'basic.credit_ok'{available = Len}), NewCredits = write_credit(CTag, Credit, Count, Drain, Credits), case Credit > 0 of true -> {[CTag], NewCredits}; -- cgit v1.2.1 From 1a6d9d04133365dfb25313f63266d7afef87b5f3 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 15 Jan 2013 13:05:54 +0000 Subject: much more thorough testing of vq:requeue improving code coverage --- src/rabbit_tests.erl | 83 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 62 insertions(+), 21 deletions(-) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index af8e2f9b..b6969d06 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -2362,31 +2362,72 @@ test_variable_queue_fold(Cut, Count, VQ0) -> msg2int(#basic_message{content = #content{ payload_fragments_rev = P}}) -> binary_to_term(list_to_binary(lists:reverse(P))). -test_variable_queue_requeue(VQ0) -> - Interval = 50, - Count = rabbit_queue_index:next_segment_boundary(0) + 2 * Interval, +ack_subset(AckSeqs, Interval, Rem) -> + lists:filter(fun ({_Ack, N}) -> (N + Rem) rem Interval == 0 end, AckSeqs). + +requeue_one_by_one(Acks, VQ) -> + lists:foldl(fun (AckTag, VQN) -> + {_MsgId, VQM} = rabbit_variable_queue:requeue( + [AckTag], VQN), + VQM + end, VQ, Acks). + +%% Create a vq with messages in q1, delta, and q3, and holes (in the +%% form of pending acks) in the latter two. +variable_queue_with_holes(VQ0) -> + Interval = 64, + Count = rabbit_queue_index:next_segment_boundary(0)*2 + 2 * Interval, Seq = lists:seq(1, Count), VQ1 = rabbit_variable_queue:set_ram_duration_target(0, VQ0), - VQ2 = variable_queue_publish(false, Count, VQ1), - {VQ3, Acks} = variable_queue_fetch(Count, false, false, Count, VQ2), - Subset = lists:foldl(fun ({Ack, N}, Acc) when N rem Interval == 0 -> - [Ack | Acc]; - (_, Acc) -> - Acc - end, [], lists:zip(Acks, Seq)), - {_MsgIds, VQ4} = rabbit_variable_queue:requeue(Acks -- Subset, VQ3), - VQ5 = lists:foldl(fun (AckTag, VQN) -> - {_MsgId, VQM} = rabbit_variable_queue:requeue( - [AckTag], VQN), - VQM - end, VQ4, Subset), - VQ6 = lists:foldl(fun (AckTag, VQa) -> - {{#basic_message{}, true, AckTag}, VQb} = + VQ2 = variable_queue_publish( + false, 1, Count, + fun (_, P) -> P end, fun erlang:term_to_binary/1, VQ1), + {VQ3, AcksR} = variable_queue_fetch(Count, false, false, Count, VQ2), + Acks = lists:reverse(AcksR), + AckSeqs = lists:zip(Acks, Seq), + [{Subset1, _Seq1}, {Subset2, _Seq2}, {Subset3, Seq3}] = + [lists:unzip(ack_subset(AckSeqs, Interval, I)) || I <- [0, 1, 2]], + %% we requeue in three phases in order to exercise requeuing logic + %% in various vq states + {_MsgIds, VQ4} = rabbit_variable_queue:requeue( + Acks -- (Subset1 ++ Subset2 ++ Subset3), VQ3), + VQ5 = requeue_one_by_one(Subset1, VQ4), + %% by now we have some messages (and holes) in delt + VQ6 = requeue_one_by_one(Subset2, VQ5), + VQ7 = rabbit_variable_queue:set_ram_duration_target(infinity, VQ6), + %% add the q1 tail + VQ8 = variable_queue_publish( + true, Count + 1, 64, + fun (_, P) -> P end, fun erlang:term_to_binary/1, VQ7), + %% assertions + [false = case V of + {delta, _, 0, _} -> true; + 0 -> true; + _ -> false + end || {K, V} <- rabbit_variable_queue:status(VQ8), + lists:member(K, [q1, delta, q3])], + Depth = Count + 64, + Depth = rabbit_variable_queue:depth(VQ8), + Len = Depth - length(Subset3), + Len = rabbit_variable_queue:len(VQ8), + {Len, (Seq -- Seq3), lists:seq(Count + 1, Count + 64), VQ8}. + +test_variable_queue_requeue(VQ0) -> + {_, RequeuedMsgs, FreshMsgs, VQ1} = variable_queue_with_holes(VQ0), + Msgs = + lists:zip(RequeuedMsgs, + lists:duplicate(length(RequeuedMsgs), true)) ++ + lists:zip(FreshMsgs, + lists:duplicate(length(FreshMsgs), false)), + VQ2 = lists:foldl(fun ({I, Requeued}, VQa) -> + {{M, MRequeued, _}, VQb} = rabbit_variable_queue:fetch(true, VQa), + Requeued = MRequeued, %% assertion + I = msg2int(M), %% assertion VQb - end, VQ5, lists:reverse(Acks)), - {empty, VQ7} = rabbit_variable_queue:fetch(true, VQ6), - VQ7. + end, VQ1, Msgs), + {empty, VQ3} = rabbit_variable_queue:fetch(true, VQ2), + VQ3. test_variable_queue_ack_limiting(VQ0) -> %% start by sending in a bunch of messages -- cgit v1.2.1 From 58ba91b595d7a65c345b94ec38b0b3538e909f35 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 15 Jan 2013 13:17:38 +0000 Subject: test --- src/rabbit_tests.erl | 33 ++++++++++----------------------- 1 file changed, 10 insertions(+), 23 deletions(-) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index b6969d06..7257827a 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -2327,36 +2327,23 @@ test_variable_queue() -> passed. test_variable_queue_fold(VQ0) -> - JustOverTwoSegs = rabbit_queue_index:next_segment_boundary(0) * 2 + 64, - VQ1 = rabbit_variable_queue:set_ram_duration_target(0, VQ0), - VQ2 = variable_queue_publish( - true, 1, JustOverTwoSegs, - fun (_, P) -> P end, fun erlang:term_to_binary/1, VQ1), - VQ3 = rabbit_variable_queue:set_ram_duration_target(infinity, VQ2), - VQ4 = variable_queue_publish( - true, JustOverTwoSegs + 1, 64, - fun (_, P) -> P end, fun erlang:term_to_binary/1, VQ3), - [false = case V of - {delta, _, 0, _} -> true; - 0 -> true; - _ -> false - end || {K, V} <- rabbit_variable_queue:status(VQ4), - lists:member(K, [q1, delta, q3])], %% precondition - Count = JustOverTwoSegs + 64, + {Count, RequeuedMsgs, FreshMsgs, VQ1} = variable_queue_with_holes(VQ0), + Msgs = RequeuedMsgs ++ FreshMsgs, lists:foldl( - fun (Cut, VQ5) -> test_variable_queue_fold(Cut, Count, VQ5) end, - VQ4, [0, 1, 2, Count div 2, Count - 1, Count, Count + 1, Count * 2]). + fun (Cut, VQ2) -> test_variable_queue_fold(Cut, Msgs, VQ2) end, + VQ1, [0, 1, 2, Count div 2, Count - 1, Count, Count + 1, Count * 2]). -test_variable_queue_fold(Cut, Count, VQ0) -> +test_variable_queue_fold(Cut, Msgs, VQ0) -> {Acc, VQ1} = rabbit_variable_queue:fold( fun (M, _, A) -> - case msg2int(M) =< Cut of - true -> {cont, [M | A]}; + MInt = msg2int(M), + case MInt =< Cut of + true -> {cont, [MInt | A]}; false -> {stop, A} end end, [], VQ0), - true = [N || N <- lists:seq(lists:min([Cut, Count]), 1, -1)] == - [msg2int(M) || M <- Acc], + Expected = lists:takewhile(fun (I) -> I =< Cut end, Msgs), + Expected = lists:reverse(Acc), %% assertion VQ1. msg2int(#basic_message{content = #content{ payload_fragments_rev = P}}) -> -- cgit v1.2.1 From b255ad4ce497b0926c776db2a3bcf09e795a30d8 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 15 Jan 2013 13:23:37 +0000 Subject: filter out pending acks when folding over delta --- src/rabbit_variable_queue.erl | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index dc32902f..8a7045ea 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -1475,7 +1475,9 @@ delta_fold(_Fun, {stop, Acc}, _DeltaSeqId, _DeltaSeqIdEnd, State) -> delta_fold(_Fun, {cont, Acc}, DeltaSeqIdEnd, DeltaSeqIdEnd, State) -> {cont, {Acc, State}}; delta_fold( Fun, {cont, Acc}, DeltaSeqId, DeltaSeqIdEnd, - #vqstate { index_state = IndexState, + #vqstate { ram_pending_ack = RPA, + disk_pending_ack = DPA, + index_state = IndexState, msg_store_clients = MSCState } = State) -> DeltaSeqId1 = lists:min( [rabbit_queue_index:next_segment_boundary(DeltaSeqId), @@ -1483,12 +1485,18 @@ delta_fold( Fun, {cont, Acc}, DeltaSeqId, DeltaSeqIdEnd, {List, IndexState1} = rabbit_queue_index:read(DeltaSeqId, DeltaSeqId1, IndexState), {StopCont, {Acc1, MSCState1}} = - lfoldl(fun ({MsgId, _SeqId, MsgProps, IsPersistent, _IsDelivered}, + lfoldl(fun ({MsgId, SeqId, MsgProps, IsPersistent, _IsDelivered}, {Acc0, MSCState0}) -> - {{ok, Msg = #basic_message {}}, MSCState1} = - msg_store_read(MSCState0, IsPersistent, MsgId), - {StopCont, AccNext} = Fun(Msg, MsgProps, Acc0), - {StopCont, {AccNext, MSCState1}} + case (gb_trees:is_defined(SeqId, RPA) orelse + gb_trees:is_defined(SeqId, DPA)) of + false -> {{ok, Msg = #basic_message{}}, MSCState1} = + msg_store_read(MSCState0, IsPersistent, + MsgId), + {StopCont, AccNext} = + Fun(Msg, MsgProps, Acc0), + {StopCont, {AccNext, MSCState1}}; + true -> {cont, {Acc0, MSCState0}} + end end, {cont, {Acc, MSCState}}, List), delta_fold(Fun, {StopCont, Acc1}, DeltaSeqId1, DeltaSeqIdEnd, State #vqstate { index_state = IndexState1, -- cgit v1.2.1 From f100430c6ccdabd5aea2352526901a13a8fb3150 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 15 Jan 2013 13:28:26 +0000 Subject: Cosmetic --- src/rabbit_amqqueue_process.erl | 12 +++++------- src/rabbit_limiter.erl | 3 +++ src/rabbit_reader.erl | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 47bc1641..66b63386 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1354,14 +1354,12 @@ handle_cast(stop_mirroring, State = #q{backing_queue = BQ, handle_cast({inform_limiter, ChPid, Msg}, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> - #cr{limiter = Limiter, - blocked_ctags = BCTags} = ch_record(ChPid), - {Unblock, Limiter2} = - rabbit_limiter:inform(Limiter, ChPid, BQ:len(BQS), Msg), + #cr{limiter = Lim, + blocked_ctags = BCTags} = ch_record(ChPid), + {Unblock, Lim2} = rabbit_limiter:inform(Lim, ChPid, BQ:len(BQS), Msg), noreply(possibly_unblock( - State, ChPid, - fun(C) -> C#cr{blocked_ctags = BCTags -- Unblock, - limiter = Limiter2} end)); + State, ChPid, fun(C) -> C#cr{blocked_ctags = BCTags -- Unblock, + limiter = Lim2} end)); handle_cast(wake_up, State) -> noreply(State). diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index 900fbec3..3cdb6849 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -72,6 +72,9 @@ blocked = false, queues = orddict:new(), % QPid -> {MonitorRef, Notify} volume = 0}). +%% 'Notify' is a boolean that indicates whether a queue should be +%% notified of a change in the limit or volume that may allow it to +%% deliver more messages via the limiter's channel. -record(credit, {count = 0, credit = 0, drain = false}). diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index f140bd23..fefff6b2 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -690,7 +690,7 @@ handle_input(handshake, <<"AMQP", 1, 1, 9, 1>>, State) -> start_connection({8, 0, 0}, rabbit_framing_amqp_0_8, State); %% ... and finally, the 1.0 spec is crystal clear! Note that the -%% FIXME TLS uses a different protocol number, and would go here. +%% TLS uses a different protocol number, and would go here. handle_input(handshake, <<"AMQP", 0, 1, 0, 0>>, State) -> become_1_0(amqp, [0, 1, 0, 0], State); -- cgit v1.2.1 From f230802d1366c0fce8207f20c45b9b5aae1c4e6b Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 15 Jan 2013 13:40:02 +0000 Subject: Specs. And a function I forgot to write. --- src/rabbit_limiter.erl | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index 3cdb6849..b5ec9f17 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -27,7 +27,7 @@ -export([limit/2, can_ch_send/3, can_cons_send/4, ack/2, register/2, unregister/2]). -export([get_limit/1, block/1, unblock/1, is_blocked/1]). --export([inform/4, forget_consumer/2]). +-export([inform/4, forget_consumer/2, copy_queue_state/2]). -import(rabbit_misc, [serial_add/2, serial_diff/2]). @@ -49,9 +49,8 @@ -spec(disable/1 :: (token()) -> token()). -spec(limit/2 :: (token(), non_neg_integer()) -> 'ok' | {'disabled', token()}). -spec(can_ch_send/3 :: (token(), pid(), boolean()) -> boolean()). -%% TODO -%% -spec(can_send/5 :: (token(), pid(), boolean(), -%% rabbit_types:ctag(), non_neg_integer()) -> boolean()). +-spec(can_cons_send/4 :: (token(), pid(), rabbit_types:ctag(), + non_neg_integer()) -> {boolean(), token()}). -spec(ack/2 :: (token(), non_neg_integer()) -> 'ok'). -spec(register/2 :: (token(), pid()) -> 'ok'). -spec(unregister/2 :: (token(), pid()) -> 'ok'). @@ -59,10 +58,11 @@ -spec(block/1 :: (token()) -> 'ok'). -spec(unblock/1 :: (token()) -> 'ok' | {'disabled', token()}). -spec(is_blocked/1 :: (token()) -> boolean()). -%% -spec(set_credit/5 :: (token(), rabbit_types:ctag(), -%% non_neg_integer(), -%% non_neg_integer(), boolean()) -> 'ok'). -%%-spec(inform/4 :: (token(), pid(), non_neg_integer(), any()) -> token()). +-spec(inform/4 :: (token(), pid(), non_neg_integer(), any()) -> + {[rabbit_types:ctag()], token()}). +-spec(forget_consumer/2 :: (token(), rabbit_types:ctag()) -> token()). +-spec(copy_queue_state/2 :: (token(), token()) -> token()). + -endif. %%---------------------------------------------------------------------------- @@ -151,6 +151,9 @@ inform(Limiter = #token{q_state = Credits}, forget_consumer(Limiter = #token{q_state = Credits}, CTag) -> Limiter#token{q_state = dict:erase(CTag, Credits)}. +copy_queue_state(#token{q_state = Credits}, Token) -> + Token#token{q_state = Credits}. + %%---------------------------------------------------------------------------- %% Queue-local code %%---------------------------------------------------------------------------- -- cgit v1.2.1 From 8a816635bd03a5b9b3c98f771332612c15ab81a3 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 15 Jan 2013 14:00:28 +0000 Subject: Separate out can_cons_send. --- src/rabbit_amqqueue_process.erl | 29 ++++++++++++++--------------- src/rabbit_limiter.erl | 30 ++++++++++++++++-------------- 2 files changed, 30 insertions(+), 29 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 66b63386..fa70765d 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -429,12 +429,7 @@ deliver_msgs_to_consumers(DeliverFun, false, deliver_msgs_to_consumers(DeliverFun, Stop, State1) end. -deliver_msg_to_consumer(DeliverFun, - E = {ChPid, - Consumer = #consumer{tag = CTag, - ack_required = AckReq}}, - State = #q{backing_queue = BQ, - backing_queue_state = BQS}) -> +deliver_msg_to_consumer(DeliverFun, E = {ChPid, Consumer}, State) -> C = ch_record(ChPid), case is_ch_blocked(C) of true -> @@ -442,21 +437,21 @@ deliver_msg_to_consumer(DeliverFun, {false, State}; false -> #cr{limiter = Limiter, ch_pid = ChPid, blocked_ctags = BCTags} = C, - case rabbit_limiter:can_cons_send( - Limiter, ChPid, CTag, BQ:len(BQS)) of - {false, Lim2} -> - block_consumer(C#cr{limiter = Lim2, - blocked_ctags = [CTag | BCTags]}, E), + #consumer{tag = CTag} = Consumer, + case rabbit_limiter:can_cons_send(Limiter, CTag) of + false -> + block_consumer(C#cr{blocked_ctags = [CTag | BCTags]}, E), {false, State}; - {true, Lim2} -> - case rabbit_limiter:can_ch_send(Limiter, self(), AckReq) of + true -> + case rabbit_limiter:can_ch_send( + Limiter, self(), Consumer#consumer.ack_required) of false -> block_consumer(C#cr{is_limit_active = true}, E), {false, State}; true -> AC1 = queue:in(E, State#q.active_consumers), deliver_msg_to_consumer( - DeliverFun, Consumer, C#cr{limiter = Lim2}, + DeliverFun, Consumer, C, State#q{active_consumers = AC1}) end end @@ -467,8 +462,12 @@ deliver_msg_to_consumer(DeliverFun, ack_required = AckRequired}, C = #cr{ch_pid = ChPid, acktags = ChAckTags, + limiter = Limiter, unsent_message_count = Count}, - State = #q{q = #amqqueue{name = QName}}) -> + State = #q{q = #amqqueue{name = QName}, + backing_queue = BQ, + backing_queue_state = BQS}) -> + rabbit_limiter:record_cons_send(Limiter, ChPid, ConsumerTag, BQ:len(BQS)), {{Message, IsDelivered, AckTag}, Stop, State1} = DeliverFun(AckRequired, State), rabbit_channel:deliver(ChPid, ConsumerTag, AckRequired, diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index b5ec9f17..e32f072e 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -24,7 +24,7 @@ -export([start_link/0, make_token/0, make_token/1, is_enabled/1, enable/2, disable/1]). --export([limit/2, can_ch_send/3, can_cons_send/4, +-export([limit/2, can_ch_send/3, can_cons_send/2, record_cons_send/4, ack/2, register/2, unregister/2]). -export([get_limit/1, block/1, unblock/1, is_blocked/1]). -export([inform/4, forget_consumer/2, copy_queue_state/2]). @@ -49,8 +49,7 @@ -spec(disable/1 :: (token()) -> token()). -spec(limit/2 :: (token(), non_neg_integer()) -> 'ok' | {'disabled', token()}). -spec(can_ch_send/3 :: (token(), pid(), boolean()) -> boolean()). --spec(can_cons_send/4 :: (token(), pid(), rabbit_types:ctag(), - non_neg_integer()) -> {boolean(), token()}). +-spec(can_cons_send/2 :: (token(), rabbit_types:ctag()) -> boolean()). -spec(ack/2 :: (token(), non_neg_integer()) -> 'ok'). -spec(register/2 :: (token(), pid()) -> 'ok'). -spec(unregister/2 :: (token(), pid()) -> 'ok'). @@ -112,9 +111,15 @@ can_ch_send(#token{pid = Pid, enabled = true}, QPid, AckRequired) -> can_ch_send(_, _, _) -> true. -can_cons_send(#token{q_state = QState} = Token, ChPid, CTag, Len) -> - {CanQ, NewQState} = can_send_q(CTag, Len, ChPid, QState), - {CanQ, Token#token{q_state = NewQState}}. +can_cons_send(#token{q_state = Credits}, CTag) -> + case dict:find(CTag, Credits) of + {ok, #credit{credit = C}} when C > 0 -> true; + {ok, #credit{}} -> false; + error -> true + end. + +record_cons_send(#token{q_state = QState} = Token, ChPid, CTag, Len) -> + Token#token{q_state = record_send_q(CTag, Len, ChPid, QState)}. %% Let the limiter know that the channel has received some acks from a %% consumer @@ -164,15 +169,12 @@ copy_queue_state(#token{q_state = Credits}, Token) -> %% we get the queue to hold a bit of state for us (#token.q_state), and %% maintain a fiction that the limiter is making the decisions... -can_send_q(CTag, Len, ChPid, Credits) -> +record_send_q(CTag, Len, ChPid, Credits) -> case dict:find(CTag, Credits) of - {ok, #credit{credit = C} = Cred} -> - if C > 0 -> Credits2 = decr_credit(CTag, Len, ChPid, Cred, Credits), - {true, Credits2}; - true -> {false, Credits} - end; - _ -> - {true, Credits} + {ok, #credit{credit = C} = Cred} when C > 0 -> + decr_credit(CTag, Len, ChPid, Cred, Credits); + error -> + Credits end. decr_credit(CTag, Len, ChPid, Cred, Credits) -> -- cgit v1.2.1 From d7fbde0bc3c31f065a0f3c5a9d487b912f878c25 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 15 Jan 2013 14:58:43 +0000 Subject: Fix the case of sending credit state back when changing credit when there are no messages in the queue. --- src/rabbit_limiter.erl | 45 +++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index e32f072e..e2c4fdbe 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -145,7 +145,8 @@ is_blocked(Limiter) -> inform(Limiter = #token{q_state = Credits}, ChPid, Len, {basic_credit, CTag, Credit, Count, Drain, Reply}) -> - {Unblock, Credits2} = update_credit(CTag, Credit, Count, Drain, Credits), + {Unblock, Credits2} = update_credit( + CTag, Len, ChPid, Credit, Count, Drain, Credits), case Reply of true -> rabbit_channel:send_command( ChPid, #'basic.credit_ok'{available = Len}); @@ -179,16 +180,19 @@ record_send_q(CTag, Len, ChPid, Credits) -> decr_credit(CTag, Len, ChPid, Cred, Credits) -> #credit{credit = Credit, count = Count, drain = Drain} = Cred, - {NewCredit, NewCount} = - case {Len, Drain} of - {1, true} -> %% Drain, so advance til credit = 0 - NewCount0 = serial_add(Count, (Credit - 1)), - send_drained(ChPid, CTag, NewCount0), - {0, NewCount0}; %% Magic reduction to 0 - {_, _} -> {Credit - 1, serial_add(Count, 1)} - end, + {NewCredit, NewCount} = maybe_drain(Len - 1, Drain, CTag, ChPid, + Credit - 1, serial_add(Count, 1)), write_credit(CTag, NewCredit, NewCount, Drain, Credits). +maybe_drain(0, true, CTag, ChPid, Credit, Count) -> + %% Drain, so advance til credit = 0 + NewCount = serial_add(Count, Credit - 2), + send_drained(ChPid, CTag, NewCount), + {0, NewCount}; %% Magic reduction to 0 + +maybe_drain(_, _, _, _, Credit, Count) -> + {Credit, Count}. + send_drained(ChPid, CTag, Count) -> rabbit_channel:send_command(ChPid, #'basic.credit_state'{consumer_tag = CTag, @@ -197,19 +201,16 @@ send_drained(ChPid, CTag, Count) -> available = 0, drain = true}). -%% Update the credit state. -%% TODO Edge case: if the queue has nothing in it, and drain is set, -%% we want to send a basic.credit back. -update_credit(CTag, Credit, Count0, Drain, Credits) -> - Count = - case dict:find(CTag, Credits) of - %% Use our count if we can, more accurate - {ok, #credit{ count = LocalCount }} -> LocalCount; - %% But if this is new, take it from the adapter - _ -> Count0 - end, - NewCredits = write_credit(CTag, Credit, Count, Drain, Credits), - case Credit > 0 of +update_credit(CTag, Len, ChPid, Credit, Count0, Drain, Credits) -> + Count = case dict:find(CTag, Credits) of + %% Use our count if we can, more accurate + {ok, #credit{ count = LocalCount }} -> LocalCount; + %% But if this is new, take it from the adapter + _ -> Count0 + end, + {NewCredit, NewCount} = maybe_drain(Len, Drain, CTag, ChPid, Credit, Count), + NewCredits = write_credit(CTag, NewCredit, NewCount, Drain, Credits), + case NewCredit > 0 of true -> {[CTag], NewCredits}; false -> {[], NewCredits} end. -- cgit v1.2.1 From 5c0dab72486ff0f64d8ccc161a2951b20ab96449 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 15 Jan 2013 16:29:36 +0000 Subject: pass 'unacked' flag to BQ:fold fun so it can distinguish between 'ready' messages and those pending ack --- src/rabbit_backing_queue.erl | 2 +- src/rabbit_mirror_queue_sync.erl | 2 +- src/rabbit_tests.erl | 12 +++++++----- src/rabbit_variable_queue.erl | 23 ++++++++++++----------- 4 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/rabbit_backing_queue.erl b/src/rabbit_backing_queue.erl index 99b5946e..eb5c7043 100644 --- a/src/rabbit_backing_queue.erl +++ b/src/rabbit_backing_queue.erl @@ -164,7 +164,7 @@ %% results, leaving the queue undisturbed. -callback fold(fun((rabbit_types:basic_message(), rabbit_types:message_properties(), - A) -> {('stop' | 'cont'), A}), + boolean(), A) -> {('stop' | 'cont'), A}), A, state()) -> {A, state()}. %% How long is my queue? diff --git a/src/rabbit_mirror_queue_sync.erl b/src/rabbit_mirror_queue_sync.erl index f2ab67cd..4d6b1fc9 100644 --- a/src/rabbit_mirror_queue_sync.erl +++ b/src/rabbit_mirror_queue_sync.erl @@ -91,7 +91,7 @@ master_go(Syncer, Ref, Log, HandleInfo, EmitStats, BQ, BQS) -> end. master_go0(Args, BQ, BQS) -> - case BQ:fold(fun (Msg, MsgProps, Acc) -> + case BQ:fold(fun (Msg, MsgProps, false, Acc) -> master_send(Msg, MsgProps, Args, Acc) end, {0, erlang:now()}, BQS) of {{shutdown, Reason}, BQS1} -> {shutdown, Reason, BQS1}; diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 3f7aa9e2..8defedbd 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -2330,14 +2330,16 @@ test_variable_queue_fold(VQ0) -> {Count, PendingMsgs, RequeuedMsgs, FreshMsgs, VQ1} = variable_queue_with_holes(VQ0), Msgs = lists:sort(PendingMsgs ++ RequeuedMsgs ++ FreshMsgs), - lists:foldl( - fun (Cut, VQ2) -> test_variable_queue_fold(Cut, Msgs, VQ2) end, - VQ1, [0, 1, 2, Count div 2, Count - 1, Count, Count + 1, Count * 2]). + lists:foldl(fun (Cut, VQ2) -> + test_variable_queue_fold(Cut, Msgs, PendingMsgs, VQ2) + end, VQ1, [0, 1, 2, Count div 2, + Count - 1, Count, Count + 1, Count * 2]). -test_variable_queue_fold(Cut, Msgs, VQ0) -> +test_variable_queue_fold(Cut, Msgs, PendingMsgs, VQ0) -> {Acc, VQ1} = rabbit_variable_queue:fold( - fun (M, _, A) -> + fun (M, _, Pending, A) -> MInt = msg2int(M), + Pending = lists:member(MInt, PendingMsgs), %% assert case MInt =< Cut of true -> {cont, [MInt | A]}; false -> {stop, A} diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index bbec70b2..c1db522b 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -1470,7 +1470,8 @@ istate(q1, _State) -> done. next({ack, It}, IndexState) -> case gb_trees:next(It) of none -> {empty, IndexState}; - {_SeqId, MsgStatus, It1} -> {value, MsgStatus, {ack, It1}, IndexState} + {_SeqId, MsgStatus, It1} -> Next = {ack, It1}, + {value, MsgStatus, true, Next, IndexState} end; next(done, IndexState) -> {empty, IndexState}; next({delta, #delta{start_seq_id = SeqId, @@ -1488,34 +1489,34 @@ next({delta, Delta, [{_, SeqId, _, _, _} = M | Rest], State}, IndexState) -> case (gb_trees:is_defined(SeqId, State#vqstate.ram_pending_ack) orelse gb_trees:is_defined(SeqId, State#vqstate.disk_pending_ack)) of false -> Next = {delta, Delta, Rest, State}, - {value, beta_msg_status(M), Next, IndexState}; + {value, beta_msg_status(M), false, Next, IndexState}; true -> next({delta, Delta, Rest, State}, IndexState) end; next({Key, Q, State}, IndexState) -> case ?QUEUE:out(Q) of {empty, _Q} -> next(istate(Key, State), IndexState); {{value, MsgStatus}, QN} -> Next = {Key, QN, State}, - {value, MsgStatus, Next, IndexState} + {value, MsgStatus, false, Next, IndexState} end. inext(It, {Its, IndexState}) -> case next(It, IndexState) of {empty, IndexState1} -> {Its, IndexState1}; - {value, MsgStatus1, It1, IndexState1} -> - {[{MsgStatus1, It1} | Its], IndexState1} + {value, MsgStatus1, Unacked, It1, IndexState1} -> + {[{MsgStatus1, Unacked, It1} | Its], IndexState1} end. ifold(_Fun, Acc, [], State) -> {Acc, State}; ifold(Fun, Acc, Its, State) -> - [{MsgStatus, It} | Rest] = lists:sort( - fun ({MsgStatus1, _}, {MsgStatus2, _}) -> - MsgStatus1#msg_status.seq_id < - MsgStatus2#msg_status.seq_id - end, Its), + [{MsgStatus, Unacked, It} | Rest] = + lists:sort(fun ({#msg_status{seq_id = SeqId1}, _, _}, + {#msg_status{seq_id = SeqId2}, _, _}) -> + SeqId1 =< SeqId2 + end, Its), {Msg, State1} = read_msg(MsgStatus, State), - case Fun(Msg, MsgStatus#msg_status.msg_props, Acc) of + case Fun(Msg, MsgStatus#msg_status.msg_props, Unacked, Acc) of {stop, Acc1} -> {Acc1, State}; {cont, Acc1} -> -- cgit v1.2.1 From 4e1cffb31dbedd1ea297d4794766fc67c046cd5d Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 15 Jan 2013 17:40:06 +0000 Subject: fix typo --- src/rabbit_reader.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index fefff6b2..c14951c3 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -696,7 +696,7 @@ handle_input(handshake, <<"AMQP", 0, 1, 0, 0>>, State) -> %% 3 stands for "SASL" handle_input(handshake, <<"AMQP", 3, 1, 0, 0>>, State) -> - become_1_0(sasl, [0, 3, 0, 0], State); + become_1_0(sasl, [3, 1, 0, 0], State); handle_input(handshake, <<"AMQP", A, B, C, D>>, #v1{sock = Sock}) -> refuse_connection(Sock, {bad_version, A, B, C, D}); -- cgit v1.2.1 From ae444cc23f94d00b97f2854d935a60ba66176c05 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 15 Jan 2013 17:43:30 +0000 Subject: simplify bad_version error handling --- src/rabbit_reader.erl | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index c14951c3..0d2ba5d2 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -692,14 +692,14 @@ handle_input(handshake, <<"AMQP", 1, 1, 9, 1>>, State) -> %% ... and finally, the 1.0 spec is crystal clear! Note that the %% TLS uses a different protocol number, and would go here. handle_input(handshake, <<"AMQP", 0, 1, 0, 0>>, State) -> - become_1_0(amqp, [0, 1, 0, 0], State); + become_1_0(amqp, {0, 1, 0, 0}, State); %% 3 stands for "SASL" handle_input(handshake, <<"AMQP", 3, 1, 0, 0>>, State) -> - become_1_0(sasl, [3, 1, 0, 0], State); + become_1_0(sasl, {3, 1, 0, 0}, State); handle_input(handshake, <<"AMQP", A, B, C, D>>, #v1{sock = Sock}) -> - refuse_connection(Sock, {bad_version, A, B, C, D}); + refuse_connection(Sock, {bad_version, {A, B, C, D}}); handle_input(handshake, Other, #v1{sock = Sock}) -> refuse_connection(Sock, {bad_header, Other}); @@ -993,10 +993,9 @@ emit_stats(State) -> %% 1.0 stub -become_1_0(Mode, HandshakeBytes, State = #v1{sock = Sock}) -> +become_1_0(Mode, Version, State = #v1{sock = Sock}) -> case code:is_loaded(rabbit_amqp1_0_reader) of - false -> refuse_connection( - Sock, list_to_tuple([bad_version | HandshakeBytes])); + false -> refuse_connection(Sock, {bad_version, Version}); _ -> apply0(rabbit_amqp1_0_reader, become, [Mode, pack_for_1_0(State)]) end. -- cgit v1.2.1 From 04e567b3ebf3f75c2830d6a147c792326ae2a822 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 15 Jan 2013 18:39:00 +0000 Subject: make check_xref work when plugins dir is empty --- check_xref | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/check_xref b/check_xref index 8f65f3b1..0debd34a 100755 --- a/check_xref +++ b/check_xref @@ -50,6 +50,7 @@ shutdown(Rc, LibDir) -> check(Cwd, PluginsDir, LibDir, Checks) -> {ok, Plugins} = file:list_dir(PluginsDir), ok = file:make_dir(LibDir), + put({?MODULE, third_party}, []), [begin Source = filename:join(PluginsDir, Plugin), Target = filename:join(LibDir, Plugin), @@ -267,14 +268,8 @@ source_file(M) -> store_third_party(App) -> {ok, AppConfig} = application:get_all_key(App), - case get({?MODULE, third_party}) of - undefined -> - put({?MODULE, third_party}, - proplists:get_value(modules, AppConfig)); - Modules -> - put({?MODULE, third_party}, - proplists:get_value(modules, AppConfig) ++ Modules) - end. + AppModules = proplists:get_value(modules, AppConfig), + put({?MODULE, third_party}, AppModules ++ get({?MODULE, third_party})). %% TODO: this ought not to be maintained in such a fashion external_dependency(Path) -> -- cgit v1.2.1 From 0dc5006c8aada96821af5229215c3cb8e4698f2c Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 15 Jan 2013 18:41:10 +0000 Subject: optimising refactor of check_xref --- check_xref | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/check_xref b/check_xref index 0debd34a..ea0102ee 100755 --- a/check_xref +++ b/check_xref @@ -163,7 +163,8 @@ filters() -> filter_chain(FnChain) -> fun(AnalysisResult) -> - lists:foldl(fun(F, false) -> F(cleanup(AnalysisResult)); + Result = cleanup(AnalysisResult), + lists:foldl(fun(F, false) -> F(Result); (_F, true) -> true end, false, FnChain) end. -- cgit v1.2.1 From 444889b386a129691701232631b8e4efb084ee57 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 15 Jan 2013 18:46:52 +0000 Subject: neater xref fooling --- src/rabbit_reader.erl | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 0d2ba5d2..6d4becc0 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -996,13 +996,10 @@ emit_stats(State) -> become_1_0(Mode, Version, State = #v1{sock = Sock}) -> case code:is_loaded(rabbit_amqp1_0_reader) of false -> refuse_connection(Sock, {bad_version, Version}); - _ -> apply0(rabbit_amqp1_0_reader, become, - [Mode, pack_for_1_0(State)]) + _ -> M = rabbit_amqp1_0_reader, %% fool xref + M:become(Mode, pack_for_1_0(State)) end. -%% Fool xref. Simply using apply(M, F, A) with constants is not enough. -apply0(M, F, A) -> apply(M, F, A). - pack_for_1_0(#v1{parent = Parent, sock = Sock, recv_len = RecvLen, -- cgit v1.2.1 From 2ad15707cb8ccf7c192fb619b4bdb5032fd40b9c Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 15 Jan 2013 18:49:13 +0000 Subject: cosmetic --- src/rabbit_amqqueue.erl | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 1715e848..1f2d653e 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -28,12 +28,11 @@ -export([consumers/1, consumers_all/1, consumer_info_keys/0]). -export([basic_get/3, basic_consume/7, basic_cancel/4]). -export([notify_sent/2, notify_sent_queue_down/1, unblock/2, flush_all/2]). --export([notify_down_all/2, limit_all/3]). +-export([notify_down_all/2, limit_all/3, inform_limiter/3]). -export([on_node_down/1]). -export([update/2, store_queue/1, policy_changed/2]). -export([start_mirroring/1, stop_mirroring/1, sync_mirrors/1, cancel_sync_mirrors/1]). --export([inform_limiter/3]). %% internal -export([internal_declare/2, internal_delete/1, run_backing_queue/3, @@ -146,6 +145,7 @@ -spec(notify_down_all/2 :: (qpids(), pid()) -> ok_or_errors()). -spec(limit_all/3 :: (qpids(), pid(), rabbit_limiter:token()) -> ok_or_errors()). +-spec(inform_limiter/3 :: (pid(), pid(), any()) -> 'ok'). -spec(basic_get/3 :: (rabbit_types:amqqueue(), pid(), boolean()) -> {'ok', non_neg_integer(), qmsg()} | 'empty'). -spec(basic_consume/7 :: @@ -178,7 +178,6 @@ -spec(sync_mirrors/1 :: (pid()) -> 'ok' | rabbit_types:error('pending_acks' | 'not_mirrored')). -spec(cancel_sync_mirrors/1 :: (pid()) -> 'ok' | {'ok', 'not_syncing'}). --spec(inform_limiter/3 :: (pid(), pid(), any()) -> 'ok'). -endif. @@ -535,6 +534,9 @@ notify_down_all(QPids, ChPid) -> limit_all(QPids, ChPid, Limiter) -> delegate:cast(QPids, {limit, ChPid, Limiter}). +inform_limiter(ChPid, QPid, Msg) -> + delegate:cast(QPid, {inform_limiter, ChPid, Msg}). + basic_get(#amqqueue{pid = QPid}, ChPid, NoAck) -> delegate:call(QPid, {basic_get, ChPid, NoAck}). @@ -609,9 +611,6 @@ stop_mirroring(QPid) -> ok = delegate:cast(QPid, stop_mirroring). sync_mirrors(QPid) -> delegate:call(QPid, sync_mirrors). cancel_sync_mirrors(QPid) -> delegate:call(QPid, cancel_sync_mirrors). -inform_limiter(ChPid, QPid, Msg) -> - delegate:cast(QPid, {inform_limiter, ChPid, Msg}). - on_node_down(Node) -> rabbit_misc:execute_mnesia_tx_with_tail( fun () -> QsDels = -- cgit v1.2.1 From 097182274da2cd2d5a37ac35f6add7d06963ba76 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 15 Jan 2013 18:50:56 +0000 Subject: API consistency --- src/rabbit_amqqueue.erl | 4 ++-- src/rabbit_channel.erl | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 1f2d653e..788ec558 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -145,7 +145,7 @@ -spec(notify_down_all/2 :: (qpids(), pid()) -> ok_or_errors()). -spec(limit_all/3 :: (qpids(), pid(), rabbit_limiter:token()) -> ok_or_errors()). --spec(inform_limiter/3 :: (pid(), pid(), any()) -> 'ok'). +-spec(inform_limiter/3 :: (rabbit_types:amqqueue(), pid(), any()) -> 'ok'). -spec(basic_get/3 :: (rabbit_types:amqqueue(), pid(), boolean()) -> {'ok', non_neg_integer(), qmsg()} | 'empty'). -spec(basic_consume/7 :: @@ -534,7 +534,7 @@ notify_down_all(QPids, ChPid) -> limit_all(QPids, ChPid, Limiter) -> delegate:cast(QPids, {limit, ChPid, Limiter}). -inform_limiter(ChPid, QPid, Msg) -> +inform_limiter(#amqqueue{pid = QPid}, ChPid, Msg) -> delegate:cast(QPid, {inform_limiter, ChPid, Msg}). basic_get(#amqqueue{pid = QPid}, ChPid, NoAck) -> diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 2bc2db83..8d916703 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -1100,7 +1100,7 @@ handle_method(#'basic.credit'{consumer_tag = CTag, credit_map = CMap}) -> case dict:find(CTag, Consumers) of {ok, Q} -> ok = rabbit_amqqueue:inform_limiter( - self(), Q#amqqueue.pid, + Q, self(), {basic_credit, CTag, Credit, Count, Drain, true}), {noreply, State}; error -> CMap2 = dict:store(CTag, {Credit, Count, Drain}, CMap), -- cgit v1.2.1 From 86f37f1c3cd7102c79880186c7fa5f866f265181 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 15 Jan 2013 20:53:07 +0000 Subject: some more reader connection state abstraction and a slightly more logical (and efficient) handle_frame clause order --- src/rabbit_reader.erl | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 13e8feff..7a28c8a3 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -64,6 +64,10 @@ State#v1.connection_state =:= blocking orelse State#v1.connection_state =:= blocked)). +-define(IS_STOPPING(State), + (State#v1.connection_state =:= closing orelse + State#v1.connection_state =:= closed)). + %%-------------------------------------------------------------------------- -ifdef(use_specs). @@ -323,9 +327,7 @@ handle_other({'DOWN', _MRef, process, ChPid, Reason}, Deb, State) -> handle_other(terminate_connection, _Deb, State) -> State; handle_other(handshake_timeout, Deb, State) - when ?IS_RUNNING(State) orelse - State#v1.connection_state =:= closing orelse - State#v1.connection_state =:= closed -> + when ?IS_RUNNING(State) orelse ?IS_STOPPING(State) -> mainloop(Deb, State); handle_other(handshake_timeout, _Deb, State) -> throw({handshake_timeout, State#v1.callback}); @@ -572,17 +574,13 @@ all_channels() -> [ChPid || {{ch_pid, ChPid}, _ChannelMRef} <- get()]. %%-------------------------------------------------------------------------- handle_frame(Type, 0, Payload, - State = #v1{connection_state = CS, - connection = #connection{protocol = Protocol}}) - when CS =:= closing; CS =:= closed -> + State = #v1{connection = #connection{protocol = Protocol}}) + when ?IS_STOPPING(State) -> case rabbit_command_assembler:analyze_frame(Type, Payload, Protocol) of {method, MethodName, FieldsBin} -> handle_method0(MethodName, FieldsBin, State); _Other -> State end; -handle_frame(_Type, _Channel, _Payload, State = #v1{connection_state = CS}) - when CS =:= closing; CS =:= closed -> - State; handle_frame(Type, 0, Payload, State = #v1{connection = #connection{protocol = Protocol}}) -> case rabbit_command_assembler:analyze_frame(Type, Payload, Protocol) of @@ -600,6 +598,8 @@ handle_frame(Type, Channel, Payload, heartbeat -> unexpected_frame(Type, Channel, Payload, State); Frame -> process_frame(Frame, Channel, State) end; +handle_frame(_Type, _Channel, _Payload, State) when ?IS_STOPPING(State) -> + State; handle_frame(Type, Channel, Payload, State) -> unexpected_frame(Type, Channel, Payload, State). @@ -820,10 +820,9 @@ handle_method0(#'connection.close'{}, State) when ?IS_RUNNING(State) -> lists:foreach(fun rabbit_channel:shutdown/1, all_channels()), maybe_close(State#v1{connection_state = closing}); handle_method0(#'connection.close'{}, - State = #v1{connection_state = CS, - connection = #connection{protocol = Protocol}, + State = #v1{connection = #connection{protocol = Protocol}, sock = Sock}) - when CS =:= closing; CS =:= closed -> + when ?IS_STOPPING(State) -> %% We're already closed or closing, so we don't need to cleanup %% anything. ok = send_on_channel0(Sock, #'connection.close_ok'{}, Protocol), @@ -832,8 +831,7 @@ handle_method0(#'connection.close_ok'{}, State = #v1{connection_state = closed}) -> self() ! terminate_connection, State; -handle_method0(_Method, State = #v1{connection_state = CS}) - when CS =:= closing; CS =:= closed -> +handle_method0(_Method, State) when ?IS_STOPPING(State) -> State; handle_method0(_Method, #v1{connection_state = S}) -> rabbit_misc:protocol_error( -- cgit v1.2.1 From 88c37f00b352a8720190ad33efde29d5db165be3 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 16 Jan 2013 10:55:59 +0000 Subject: ...and do the same thing here. --- src/rabbit_event.erl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/rabbit_event.erl b/src/rabbit_event.erl index 7d91b6fa..2d626ad4 100644 --- a/src/rabbit_event.erl +++ b/src/rabbit_event.erl @@ -112,8 +112,10 @@ stop_stats_timer(C, P) -> case element(P, C) of #state{level = Level, timer = TRef} = State when Level =/= none andalso TRef =/= undefined -> - erlang:cancel_timer(TRef), - setelement(P, C, State#state{timer = undefined}); + case erlang:cancel_timer(TRef) of + false -> C; + _ -> setelement(P, C, State#state{timer = undefined}) + end; #state{} -> C end. -- cgit v1.2.1 From d932b7c913f24436a7c4e7d2b2e5109915cc19cd Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 16 Jan 2013 13:38:01 +0000 Subject: Unbreak --- src/rabbit_channel.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 8d916703..a5a59314 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -707,7 +707,7 @@ handle_method(#'basic.consume'{queue = QueueNameBin, case dict:find(ActualConsumerTag, CreditMap) of {ok, {Credit, Count, Drain}} -> ok = rabbit_amqqueue:inform_limiter( - self(), Q#amqqueue.pid, + Q, self(), {basic_credit, ActualConsumerTag, Credit, Count, Drain, false}); error -> -- cgit v1.2.1 From ffb441fa1f41c4ff6edb98acbe57ecc1753a21fd Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 16 Jan 2013 14:13:03 +0000 Subject: This guard is not needed. --- src/rabbit_limiter.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index e2c4fdbe..57fd0c26 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -172,7 +172,7 @@ copy_queue_state(#token{q_state = Credits}, Token) -> record_send_q(CTag, Len, ChPid, Credits) -> case dict:find(CTag, Credits) of - {ok, #credit{credit = C} = Cred} when C > 0 -> + {ok, Cred} -> decr_credit(CTag, Len, ChPid, Cred, Credits); error -> Credits -- cgit v1.2.1 From c8d5866d31826de791a46d7aee5f9fcdccd929f5 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 16 Jan 2013 17:11:50 +0000 Subject: remove superfluous condition --- src/rabbit_event.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_event.erl b/src/rabbit_event.erl index 2d626ad4..6a0d1892 100644 --- a/src/rabbit_event.erl +++ b/src/rabbit_event.erl @@ -110,8 +110,8 @@ ensure_stats_timer(C, P, Msg) -> stop_stats_timer(C, P) -> case element(P, C) of - #state{level = Level, timer = TRef} = State - when Level =/= none andalso TRef =/= undefined -> + #state{timer = TRef} = State + when TRef =/= undefined -> case erlang:cancel_timer(TRef) of false -> C; _ -> setelement(P, C, State#state{timer = undefined}) -- cgit v1.2.1 From 5db7d0090b706704f92bb1d5c41f99d139a5ca50 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 16 Jan 2013 17:17:06 +0000 Subject: cosmetic --- src/rabbit_event.erl | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/rabbit_event.erl b/src/rabbit_event.erl index 6a0d1892..38d8cd54 100644 --- a/src/rabbit_event.erl +++ b/src/rabbit_event.erl @@ -110,8 +110,7 @@ ensure_stats_timer(C, P, Msg) -> stop_stats_timer(C, P) -> case element(P, C) of - #state{timer = TRef} = State - when TRef =/= undefined -> + #state{timer = TRef} = State when TRef =/= undefined -> case erlang:cancel_timer(TRef) of false -> C; _ -> setelement(P, C, State#state{timer = undefined}) @@ -122,8 +121,7 @@ stop_stats_timer(C, P) -> reset_stats_timer(C, P) -> case element(P, C) of - #state{timer = TRef} = State - when TRef =/= undefined -> + #state{timer = TRef} = State when TRef =/= undefined -> setelement(P, C, State#state{timer = undefined}); #state{} -> C -- cgit v1.2.1 From dd9ff7ba5a9593eaa8204fd07e776d52127e562f Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 16 Jan 2013 17:51:46 +0000 Subject: Prevent explosion if someone passes a list. --- src/rabbit_policy.erl | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/rabbit_policy.erl b/src/rabbit_policy.erl index 2c997f16..fa13c5dd 100644 --- a/src/rabbit_policy.erl +++ b/src/rabbit_policy.erl @@ -218,10 +218,13 @@ validation(_Name, Terms) when is_list(Terms) -> rabbit_registry:lookup_all(policy_validator)), [] = dups(Keys), %% ASSERTION Validators = lists:zipwith(fun (M, K) -> {M, a2b(K)} end, Modules, Keys), - {TermKeys, _} = lists:unzip(Terms), - case dups(TermKeys) of - [] -> validation0(Validators, Terms); - Dup -> {error, "~p duplicate keys not allowed", [Dup]} + case is_proplist(Terms) of + true -> {TermKeys, _} = lists:unzip(Terms), + case dups(TermKeys) of + [] -> validation0(Validators, Terms); + Dup -> {error, "~p duplicate keys not allowed", [Dup]} + end; + false -> {error, "definition must be a dictionary: ~p", [Terms]} end; validation(_Name, Term) -> {error, "parse error while reading policy: ~p", [Term]}. @@ -249,3 +252,5 @@ validation0(Validators, Terms) -> a2b(A) -> list_to_binary(atom_to_list(A)). dups(L) -> L -- lists:usort(L). + +is_proplist(L) -> length(L) =:= length([I || I = {_, _} <- L]). -- cgit v1.2.1 From f90640eda347e7dc1756f5dbe747628816614fc8 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 17 Jan 2013 16:21:19 +0000 Subject: Unwind the stack before taking that one-way trip. --- src/rabbit_reader.erl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 6d4becc0..a1dfeeff 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -245,6 +245,8 @@ start_connection(Parent, ChannelSupSupPid, Collector, StartHeartbeatFun, Deb, handshake, 8)), log(info, "closing AMQP connection ~p (~s)~n", [self(), Name]) catch + throw:{become, M, F, A} -> + apply(M, F, A); Ex -> log(case Ex of connection_closed_abruptly -> warning; _ -> error @@ -996,8 +998,8 @@ emit_stats(State) -> become_1_0(Mode, Version, State = #v1{sock = Sock}) -> case code:is_loaded(rabbit_amqp1_0_reader) of false -> refuse_connection(Sock, {bad_version, Version}); - _ -> M = rabbit_amqp1_0_reader, %% fool xref - M:become(Mode, pack_for_1_0(State)) + _ -> throw({become, rabbit_amqp1_0_reader, become, + [Mode, pack_for_1_0(State)]}) end. pack_for_1_0(#v1{parent = Parent, -- cgit v1.2.1 From efdbf0a822e141a26d1306011a397c2638584ff9 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 17 Jan 2013 16:43:07 +0000 Subject: allow 'become' to return, and become something else --- src/rabbit_reader.erl | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index a1dfeeff..7b867c8e 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -23,7 +23,7 @@ -export([system_continue/3, system_terminate/4, system_code_change/4]). --export([init/4, mainloop/2]). +-export([init/4, mainloop/2, recvloop/2]). -export([conserve_resources/3, server_properties/1]). @@ -240,13 +240,12 @@ start_connection(Parent, ChannelSupSupPid, Collector, StartHeartbeatFun, Deb, last_blocked_at = never}}, try ok = inet_op(fun () -> rabbit_net:tune_buffer_size(ClientSock) end), - recvloop(Deb, switch_callback(rabbit_event:init_stats_timer( - State, #v1.stats_timer), - handshake, 8)), + run({?MODULE, recvloop, + [Deb, switch_callback(rabbit_event:init_stats_timer( + State, #v1.stats_timer), + handshake, 8)]}), log(info, "closing AMQP connection ~p (~s)~n", [self(), Name]) catch - throw:{become, M, F, A} -> - apply(M, F, A); Ex -> log(case Ex of connection_closed_abruptly -> warning; _ -> error @@ -265,6 +264,11 @@ start_connection(Parent, ChannelSupSupPid, Collector, StartHeartbeatFun, Deb, end, done. +run({M, F, A}) -> + try apply(M, F, A) + catch {become, MFA} -> run(MFA) + end. + recvloop(Deb, State = #v1{pending_recv = true}) -> mainloop(Deb, State); recvloop(Deb, State = #v1{connection_state = blocked}) -> @@ -998,8 +1002,8 @@ emit_stats(State) -> become_1_0(Mode, Version, State = #v1{sock = Sock}) -> case code:is_loaded(rabbit_amqp1_0_reader) of false -> refuse_connection(Sock, {bad_version, Version}); - _ -> throw({become, rabbit_amqp1_0_reader, become, - [Mode, pack_for_1_0(State)]}) + _ -> throw({become, {rabbit_amqp1_0_reader, become, + [Mode, pack_for_1_0(State)]}}) end. pack_for_1_0(#v1{parent = Parent, -- cgit v1.2.1 From 31ab9068467652182ed6de357f9e480f6e87e7fd Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 17 Jan 2013 16:46:35 +0000 Subject: Start the chan_sup_sup later, once we have committed to 0-9-1 - one less hack. --- src/rabbit_connection_sup.erl | 14 +------------- src/rabbit_reader.erl | 27 ++++++++++++++++++--------- 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/src/rabbit_connection_sup.erl b/src/rabbit_connection_sup.erl index f4f3c72f..d9a4735c 100644 --- a/src/rabbit_connection_sup.erl +++ b/src/rabbit_connection_sup.erl @@ -42,23 +42,11 @@ start_link() -> SupPid, {collector, {rabbit_queue_collector, start_link, []}, intrinsic, ?MAX_WAIT, worker, [rabbit_queue_collector]}), - %% Note that rabbit_amqp1_0_session_sup_sup despite the name can - %% mimic rabbit_channel_sup_sup when we handle a 0-9-1 connection - %% and the 1.0 plugin is loaded. - ChannelSupSupModule = case code:is_loaded(rabbit_amqp1_0_session_sup_sup) of - false -> rabbit_channel_sup_sup; - _ -> rabbit_amqp1_0_session_sup_sup - end, - {ok, ChannelSupSupPid} = - supervisor2:start_child( - SupPid, - {channel_sup_sup, {ChannelSupSupModule, start_link, []}, - intrinsic, infinity, supervisor, [rabbit_channel_sup_sup]}), {ok, ReaderPid} = supervisor2:start_child( SupPid, {reader, {rabbit_reader, start_link, - [ChannelSupSupPid, Collector, + [SupPid, Collector, rabbit_heartbeat:start_heartbeat_fun(SupPid)]}, intrinsic, ?MAX_WAIT, worker, [rabbit_reader]}), {ok, SupPid, ReaderPid}. diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index a1dfeeff..27ea4d4b 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -37,7 +37,8 @@ -record(v1, {parent, sock, connection, callback, recv_len, pending_recv, connection_state, queue_collector, heartbeater, stats_timer, - channel_sup_sup_pid, start_heartbeat_fun, buf, buf_len, throttle}). + channel_sup_sup_pid, conn_sup_pid, start_heartbeat_fun, + buf, buf_len, throttle}). -record(connection, {name, host, peer_host, port, peer_port, protocol, user, timeout_sec, frame_max, vhost, @@ -105,12 +106,12 @@ start_link(ChannelSupSupPid, Collector, StartHeartbeatFun) -> shutdown(Pid, Explanation) -> gen_server:call(Pid, {shutdown, Explanation}, infinity). -init(Parent, ChannelSupSupPid, Collector, StartHeartbeatFun) -> +init(Parent, ConnSupPid, Collector, StartHeartbeatFun) -> Deb = sys:debug_options([]), receive {go, Sock, SockTransform} -> start_connection( - Parent, ChannelSupSupPid, Collector, StartHeartbeatFun, Deb, Sock, + Parent, ConnSupPid, Collector, StartHeartbeatFun, Deb, Sock, SockTransform) end. @@ -199,7 +200,7 @@ name(Sock) -> socket_ends(Sock) -> socket_op(Sock, fun (S) -> rabbit_net:socket_ends(S, inbound) end). -start_connection(Parent, ChannelSupSupPid, Collector, StartHeartbeatFun, Deb, +start_connection(Parent, ConnSupPid, Collector, StartHeartbeatFun, Deb, Sock, SockTransform) -> process_flag(trap_exit, true), Name = name(Sock), @@ -230,7 +231,8 @@ start_connection(Parent, ChannelSupSupPid, Collector, StartHeartbeatFun, Deb, connection_state = pre_init, queue_collector = Collector, heartbeater = none, - channel_sup_sup_pid = ChannelSupSupPid, + conn_sup_pid = ConnSupPid, + channel_sup_sup_pid = none, start_heartbeat_fun = StartHeartbeatFun, buf = [], buf_len = 0, @@ -714,7 +716,13 @@ handle_input(Callback, Data, _State) -> %% are similar enough that clients will be happy with either. start_connection({ProtocolMajor, ProtocolMinor, _ProtocolRevision}, Protocol, - State = #v1{sock = Sock, connection = Connection}) -> + State = #v1{sock = Sock, connection = Connection, + conn_sup_pid = ConnSupPid}) -> + {ok, ChannelSupSupPid} = + supervisor2:start_child( + ConnSupPid, + {channel_sup_sup, {rabbit_channel_sup_sup, start_link, []}, + intrinsic, infinity, supervisor, [rabbit_channel_sup_sup]}), Start = #'connection.start'{ version_major = ProtocolMajor, version_minor = ProtocolMinor, @@ -725,6 +733,7 @@ start_connection({ProtocolMajor, ProtocolMinor, _ProtocolRevision}, switch_callback(State#v1{connection = Connection#connection{ timeout_sec = ?NORMAL_TIMEOUT, protocol = Protocol}, + channel_sup_sup_pid = ChannelSupSupPid, connection_state = starting}, frame_header, 7). @@ -1007,9 +1016,9 @@ pack_for_1_0(#v1{parent = Parent, recv_len = RecvLen, pending_recv = PendingRecv, queue_collector = QueueCollector, - channel_sup_sup_pid = ChannelSupSupPid, + conn_sup_pid = ConnSupPid, start_heartbeat_fun = SHF, buf = Buf, buf_len = BufLen}) -> - {Parent, Sock, RecvLen, PendingRecv, QueueCollector, - ChannelSupSupPid, SHF, Buf, BufLen}. + {Parent, Sock, RecvLen, PendingRecv, QueueCollector, ConnSupPid, SHF, + Buf, BufLen}. -- cgit v1.2.1 From b235064ef4642a8e3c69a7a04b41cba609355f7c Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 17 Jan 2013 17:04:09 +0000 Subject: delay starting of channel_sup_sup as much as possible which makes abandoned connection attempts less costly --- src/rabbit_reader.erl | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index b411f927..e1462163 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -37,7 +37,7 @@ -record(v1, {parent, sock, connection, callback, recv_len, pending_recv, connection_state, queue_collector, heartbeater, stats_timer, - channel_sup_sup_pid, conn_sup_pid, start_heartbeat_fun, + conn_sup_pid, channel_sup_sup_pid, start_heartbeat_fun, buf, buf_len, throttle}). -record(connection, {name, host, peer_host, port, peer_port, @@ -720,13 +720,7 @@ handle_input(Callback, Data, _State) -> %% are similar enough that clients will be happy with either. start_connection({ProtocolMajor, ProtocolMinor, _ProtocolRevision}, Protocol, - State = #v1{sock = Sock, connection = Connection, - conn_sup_pid = ConnSupPid}) -> - {ok, ChannelSupSupPid} = - supervisor2:start_child( - ConnSupPid, - {channel_sup_sup, {rabbit_channel_sup_sup, start_link, []}, - intrinsic, infinity, supervisor, [rabbit_channel_sup_sup]}), + State = #v1{sock = Sock, connection = Connection}) -> Start = #'connection.start'{ version_major = ProtocolMajor, version_minor = ProtocolMinor, @@ -737,7 +731,6 @@ start_connection({ProtocolMajor, ProtocolMinor, _ProtocolRevision}, switch_callback(State#v1{connection = Connection#connection{ timeout_sec = ?NORMAL_TIMEOUT, protocol = Protocol}, - channel_sup_sup_pid = ChannelSupSupPid, connection_state = starting}, frame_header, 7). @@ -823,17 +816,24 @@ handle_method0(#'connection.open'{virtual_host = VHostPath}, connection = Connection = #connection{ user = User, protocol = Protocol}, + conn_sup_pid = ConnSupPid, sock = Sock, throttle = Throttle}) -> ok = rabbit_access_control:check_vhost_access(User, 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}, + {ok, ChannelSupSupPid} = + supervisor2:start_child( + ConnSupPid, + {channel_sup_sup, {rabbit_channel_sup_sup, start_link, []}, + intrinsic, infinity, supervisor, [rabbit_channel_sup_sup]}), State1 = control_throttle( - State#v1{connection_state = running, - connection = NewConnection, - throttle = Throttle#throttle{ - conserve_resources = Conserve}}), + State#v1{connection_state = running, + connection = NewConnection, + channel_sup_sup_pid = ChannelSupSupPid, + throttle = Throttle1}), rabbit_event:notify(connection_created, [{type, network} | infos(?CREATION_EVENT_KEYS, State1)]), -- cgit v1.2.1 From a7fb2ee05bc227af8de4a85ef2f217ce32e6bacb Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 17 Jan 2013 17:16:46 +0000 Subject: Stick "real" 0-9-1 connections in an ETS table. --- include/rabbit.hrl | 2 ++ src/rabbit_networking.erl | 13 ++++--------- src/rabbit_reader.erl | 2 ++ 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/include/rabbit.hrl b/include/rabbit.hrl index 7385b4b3..397a3df6 100644 --- a/include/rabbit.hrl +++ b/include/rabbit.hrl @@ -109,3 +109,5 @@ -define(INVALID_HEADERS_KEY, <<"x-invalid-headers">>). -define(ROUTING_HEADERS, [<<"CC">>, <<"BCC">>]). -define(DELETED_HEADER, <<"BCC">>). + +-define(CONNECTION_TABLE, rabbit_connection). diff --git a/src/rabbit_networking.erl b/src/rabbit_networking.erl index 31eeef73..52163354 100644 --- a/src/rabbit_networking.erl +++ b/src/rabbit_networking.erl @@ -117,6 +117,9 @@ %%---------------------------------------------------------------------------- boot() -> + %% This exists to disambiguate 0-x connections from 1.0 ones + %% (since they are both children of the same supervisor). + ets:new(?CONNECTION_TABLE, [public, named_table]), ok = start(), ok = boot_tcp(), ok = boot_ssl(). @@ -299,15 +302,7 @@ connections() -> rabbit_networking, connections_local, []). connections_local() -> - [Reader || - {_, ConnSup, supervisor, _} - <- supervisor:which_children(rabbit_tcp_client_sup), - Reader <- [try - rabbit_connection_sup:reader(ConnSup) - catch exit:{noproc, _} -> - noproc - end], - Reader =/= noproc]. + [P || {P} <- ets:tab2list(?CONNECTION_TABLE)]. connection_info_keys() -> rabbit_reader:info_keys(). diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index e1462163..fae65b1d 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -265,6 +265,7 @@ start_connection(Parent, ConnSupPid, Collector, StartHeartbeatFun, Deb, %% the socket. However, to keep the file_handle_cache %% accounting as accurate as possible we ought to close the %% socket w/o delay before termination. + ets:delete(?CONNECTION_TABLE, self()), rabbit_net:fast_close(ClientSock), rabbit_event:notify(connection_closed, [{pid, self()}]) end, @@ -721,6 +722,7 @@ handle_input(Callback, Data, _State) -> start_connection({ProtocolMajor, ProtocolMinor, _ProtocolRevision}, Protocol, State = #v1{sock = Sock, connection = Connection}) -> + ets:insert(?CONNECTION_TABLE, {self()}), Start = #'connection.start'{ version_major = ProtocolMajor, version_minor = ProtocolMinor, -- cgit v1.2.1 From b3d4e8a9b7c99ef3d5d64cf8b71fe7e91b3e19eb Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 17 Jan 2013 17:32:28 +0000 Subject: cosmetic --- src/rabbit_reader.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index fae65b1d..f5ddf1b0 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -265,8 +265,8 @@ start_connection(Parent, ConnSupPid, Collector, StartHeartbeatFun, Deb, %% the socket. However, to keep the file_handle_cache %% accounting as accurate as possible we ought to close the %% socket w/o delay before termination. - ets:delete(?CONNECTION_TABLE, self()), rabbit_net:fast_close(ClientSock), + ets:delete(?CONNECTION_TABLE, self()), rabbit_event:notify(connection_closed, [{pid, self()}]) end, done. -- cgit v1.2.1 From 8d6369705ad1ff872fdb1896d05fb28c67e43d8a Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 17 Jan 2013 17:35:25 +0000 Subject: cosmetic --- src/rabbit_networking.erl | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/rabbit_networking.erl b/src/rabbit_networking.erl index 52163354..e864140d 100644 --- a/src/rabbit_networking.erl +++ b/src/rabbit_networking.erl @@ -117,8 +117,6 @@ %%---------------------------------------------------------------------------- boot() -> - %% This exists to disambiguate 0-x connections from 1.0 ones - %% (since they are both children of the same supervisor). ets:new(?CONNECTION_TABLE, [public, named_table]), ok = start(), ok = boot_tcp(), @@ -301,8 +299,7 @@ connections() -> rabbit_misc:append_rpc_all_nodes(rabbit_mnesia:cluster_nodes(running), rabbit_networking, connections_local, []). -connections_local() -> - [P || {P} <- ets:tab2list(?CONNECTION_TABLE)]. +connections_local() -> [P || {P} <- ets:tab2list(?CONNECTION_TABLE)]. connection_info_keys() -> rabbit_reader:info_keys(). -- cgit v1.2.1 From 672289cefb5532d74bb24924aebd14f4ac7afad8 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 17 Jan 2013 17:45:03 +0000 Subject: refactor: encapsulate rabbit_connection table --- include/rabbit.hrl | 2 -- src/rabbit_networking.erl | 11 ++++++++++- src/rabbit_reader.erl | 4 ++-- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/include/rabbit.hrl b/include/rabbit.hrl index 397a3df6..7385b4b3 100644 --- a/include/rabbit.hrl +++ b/include/rabbit.hrl @@ -109,5 +109,3 @@ -define(INVALID_HEADERS_KEY, <<"x-invalid-headers">>). -define(ROUTING_HEADERS, [<<"CC">>, <<"BCC">>]). -define(DELETED_HEADER, <<"BCC">>). - --define(CONNECTION_TABLE, rabbit_connection). diff --git a/src/rabbit_networking.erl b/src/rabbit_networking.erl index e864140d..ee430fb4 100644 --- a/src/rabbit_networking.erl +++ b/src/rabbit_networking.erl @@ -18,7 +18,8 @@ -export([boot/0, start/0, start_tcp_listener/1, start_ssl_listener/2, stop_tcp_listener/1, on_node_down/1, active_listeners/0, - node_listeners/1, connections/0, connection_info_keys/0, + node_listeners/1, register_connection/1, unregister_connection/1, + connections/0, connection_info_keys/0, connection_info/1, connection_info/2, connection_info_all/0, connection_info_all/1, close_connection/2, force_connection_event_refresh/0, tcp_host/1]). @@ -40,6 +41,8 @@ -define(FIRST_TEST_BIND_PORT, 10000). +-define(CONNECTION_TABLE, rabbit_connection). + %%---------------------------------------------------------------------------- -ifdef(use_specs). @@ -65,6 +68,8 @@ -spec(stop_tcp_listener/1 :: (listener_config()) -> 'ok'). -spec(active_listeners/0 :: () -> [rabbit_types:listener()]). -spec(node_listeners/1 :: (node()) -> [rabbit_types:listener()]). +-spec(register_connection/1 :: (pid()) -> ok). +-spec(unregister_connection/1 :: (pid()) -> ok). -spec(connections/0 :: () -> [rabbit_types:connection()]). -spec(connections_local/0 :: () -> [rabbit_types:connection()]). -spec(connection_info_keys/0 :: () -> rabbit_types:info_keys()). @@ -295,6 +300,10 @@ start_client(Sock) -> start_ssl_client(SslOpts, Sock) -> start_client(Sock, ssl_transform_fun(SslOpts)). +register_connection(Pid) -> ets:insert(?CONNECTION_TABLE, {Pid}), ok. + +unregister_connection(Pid) -> ets:delete(?CONNECTION_TABLE, Pid), ok. + connections() -> rabbit_misc:append_rpc_all_nodes(rabbit_mnesia:cluster_nodes(running), rabbit_networking, connections_local, []). diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index f5ddf1b0..13459350 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -266,7 +266,7 @@ start_connection(Parent, ConnSupPid, Collector, StartHeartbeatFun, Deb, %% accounting as accurate as possible we ought to close the %% socket w/o delay before termination. rabbit_net:fast_close(ClientSock), - ets:delete(?CONNECTION_TABLE, self()), + rabbit_networking:unregister_connection(self()), rabbit_event:notify(connection_closed, [{pid, self()}]) end, done. @@ -722,7 +722,7 @@ handle_input(Callback, Data, _State) -> start_connection({ProtocolMajor, ProtocolMinor, _ProtocolRevision}, Protocol, State = #v1{sock = Sock, connection = Connection}) -> - ets:insert(?CONNECTION_TABLE, {self()}), + rabbit_networking:register_connection(self()), Start = #'connection.start'{ version_major = ProtocolMajor, version_minor = ProtocolMinor, -- cgit v1.2.1 From 8ea1d6208a55a5e93e70515388a66f34f8948337 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 17 Jan 2013 18:47:34 +0000 Subject: simplifying refactor on rabbit_mnesia:discover_cluster --- src/rabbit_mnesia.erl | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 6a442fec..d5efffa5 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -601,19 +601,16 @@ discover_cluster(Nodes) when is_list(Nodes) -> lists:foldl(fun (_, {ok, Res}) -> {ok, Res}; (Node, {error, _}) -> discover_cluster(Node) end, {error, no_nodes_provided}, Nodes); +discover_cluster(Node) when Node == node() -> + {error, {cannot_discover_cluster, "Cannot cluster node with itself"}}; discover_cluster(Node) -> OfflineError = {error, {cannot_discover_cluster, "The nodes provided are either offline or not running"}}, - case node() of - Node -> {error, {cannot_discover_cluster, - "Cannot cluster node with itself"}}; - _ -> case rpc:call(Node, - rabbit_mnesia, cluster_status_from_mnesia, []) of - {badrpc, _Reason} -> OfflineError; - {error, mnesia_not_running} -> OfflineError; - {ok, Res} -> {ok, Res} - end + case rpc:call(Node, rabbit_mnesia, cluster_status_from_mnesia, []) of + {badrpc, _Reason} -> OfflineError; + {error, mnesia_not_running} -> OfflineError; + {ok, Res} -> {ok, Res} end. schema_ok_or_move() -> -- cgit v1.2.1 From ab97f127fb502a5c64d289397720cf620da0e766 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 18 Jan 2013 11:53:05 +0000 Subject: Don't io:format the HiPE compilation warning. --- src/rabbit.erl | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index ef01bd88..7ba8a686 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -265,12 +265,21 @@ maybe_hipe_compile() -> {ok, Want} = application:get_env(rabbit, hipe_compile), Can = code:which(hipe) =/= non_existing, case {Want, Can} of - {true, true} -> hipe_compile(); - {true, false} -> io:format("Not HiPE compiling: HiPE not found in " - "this Erlang installation.~n"); - {false, _} -> ok + {true, true} -> hipe_compile(), + true; + {true, false} -> false; + {false, _} -> true end. +warn_if_hipe_compilation_failed(true) -> + ok; +warn_if_hipe_compilation_failed(false) -> + error_logger:warning_msg( + "Not HiPE compiling: HiPE not found in this Erlang installation.~n"). + +%% HiPE compilation happens before we have log handlers and can take a +%% long time, so make an exception to our no-stdout policy and display +%% progress via stdout. hipe_compile() -> Count = length(?HIPE_WORTHY), io:format("~nHiPE compiling: |~s|~n |", @@ -320,8 +329,9 @@ start() -> boot() -> start_it(fun() -> ok = ensure_application_loaded(), - maybe_hipe_compile(), + Success = maybe_hipe_compile(), ok = ensure_working_log_handlers(), + warn_if_hipe_compilation_failed(Success), rabbit_node_monitor:prepare_cluster_status_files(), ok = rabbit_upgrade:maybe_upgrade_mnesia(), %% It's important that the consistency check happens after -- cgit v1.2.1 From 70639db909ee3f0b514d8296ad934387a25eba9c Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 18 Jan 2013 12:18:05 +0000 Subject: Remove that. --- include/rabbit.hrl | 1 - src/rabbit.erl | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/include/rabbit.hrl b/include/rabbit.hrl index 7385b4b3..78763045 100644 --- a/include/rabbit.hrl +++ b/include/rabbit.hrl @@ -88,7 +88,6 @@ -define(COPYRIGHT_MESSAGE, "Copyright (C) 2007-2012 VMware, Inc."). -define(INFORMATION_MESSAGE, "Licensed under the MPL. See http://www.rabbitmq.com/"). --define(PROTOCOL_VERSION, "AMQP 0-9-1 / 0-9 / 0-8"). -define(ERTS_MINIMUM, "5.6.3"). %% EMPTY_FRAME_SIZE, 8 = 1 + 2 + 4 + 1 diff --git a/src/rabbit.erl b/src/rabbit.erl index 7ba8a686..d9601ad1 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -740,8 +740,8 @@ log_banner() -> end, Banner = iolist_to_binary( rabbit_misc:format( - "~s ~s~n~s~n~s~n~s~n", - [Product, Version, ?PROTOCOL_VERSION, ?COPYRIGHT_MESSAGE, + "~s ~s~n~s~n~s~n", + [Product, Version, ?COPYRIGHT_MESSAGE, ?INFORMATION_MESSAGE]) ++ [case S of {"config file(s)" = K, []} -> -- cgit v1.2.1 From 44782048888c7818ec06c07311578775a42a4aa2 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 18 Jan 2013 12:21:59 +0000 Subject: These should go to the log, they have no excuse. --- src/rabbit_plugins.erl | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/rabbit_plugins.erl b/src/rabbit_plugins.erl index 9f94af7d..d2f36590 100644 --- a/src/rabbit_plugins.erl +++ b/src/rabbit_plugins.erl @@ -64,8 +64,8 @@ list(PluginsDir) -> [plugin_info(PluginsDir, Plug) || Plug <- EZs ++ FreeApps]), case Problems of [] -> ok; - _ -> io:format("Warning: Problem reading some plugins: ~p~n", - [Problems]) + _ -> error_logger:warning_msg( + "Problem reading some plugins: ~p~n", [Problems]) end, Plugins. @@ -112,8 +112,9 @@ prepare_plugins(EnabledFile, PluginsDistDir, ExpandDir) -> case Enabled -- plugin_names(ToUnpackPlugins) of [] -> ok; - Missing -> io:format("Warning: the following enabled plugins were " - "not found: ~p~n", [Missing]) + Missing -> error_logger:warning_msg( + "The following enabled plugins were not found: ~p~n", + [Missing]) end, %% Eliminate the contents of the destination directory -- cgit v1.2.1 From 54dde58d3128423a73eecf77117e417cb2ba663e Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 18 Jan 2013 12:32:50 +0000 Subject: Not sure that's worth special-casing --- src/rabbit.erl | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index d9601ad1..dac0c4dd 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -691,12 +691,6 @@ force_event_refresh() -> %%--------------------------------------------------------------------------- %% misc -log_broker_started([]) -> - rabbit_misc:with_local_io( - fun() -> - error_logger:info_msg("Server startup complete~n", []), - io:format("~nBroker running~n") - end); log_broker_started(Plugins) -> rabbit_misc:with_local_io( fun() -> -- cgit v1.2.1 From ceb5f7fd0f030b5c92e6642f9ff0caeb18cbcc68 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 18 Jan 2013 12:34:27 +0000 Subject: Formatting tweaks --- src/rabbit.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index dac0c4dd..1900f794 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -695,7 +695,7 @@ log_broker_started(Plugins) -> rabbit_misc:with_local_io( fun() -> error_logger:info_msg( - "Server startup complete; plugins are:~n~n~p~n", [Plugins]), + "Server startup complete; plugins are: ~p~n", [Plugins]), io:format("~nBroker running with ~p plugins.~n", [length(Plugins)]) end). @@ -745,7 +745,7 @@ log_banner() -> {K, V} -> Format(K, V) end || S <- Settings]), - error_logger:info_msg("~s~n", [Banner]). + error_logger:info_msg("~s", [Banner]). home_dir() -> case init:get_argument(home) of -- cgit v1.2.1 From ad61bbf463646ff7f74ec80dc0feebff3fd24dad Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 18 Jan 2013 13:59:14 +0000 Subject: various reader related changes for AMQP 1.0 - mechanism for the reader to 'become' a different reader. - become the 1.0 reader if an AMQP 1.0 header is presented by a client and the reader is present. That way we can support 1.0 on the same port as 0-{8,9,9-1}. - defer starting of the channel_sup_sup and do that in the reader. This allows the AMQP 1.0 reader to start its own versio of the sup. It also makes aborted connections less costly. - track connections in an ets table rather than implicitly via the supervisor. That way AMQP 1.0 connections can exclude themselves, since they are already tracked via their direct connections. --- src/rabbit_connection_sup.erl | 7 +--- src/rabbit_networking.erl | 23 ++++++------- src/rabbit_reader.erl | 75 +++++++++++++++++++++++++++++++++++-------- 3 files changed, 74 insertions(+), 31 deletions(-) diff --git a/src/rabbit_connection_sup.erl b/src/rabbit_connection_sup.erl index 12a532b6..d9a4735c 100644 --- a/src/rabbit_connection_sup.erl +++ b/src/rabbit_connection_sup.erl @@ -42,16 +42,11 @@ start_link() -> SupPid, {collector, {rabbit_queue_collector, start_link, []}, intrinsic, ?MAX_WAIT, worker, [rabbit_queue_collector]}), - {ok, ChannelSupSupPid} = - supervisor2:start_child( - SupPid, - {channel_sup_sup, {rabbit_channel_sup_sup, start_link, []}, - intrinsic, infinity, supervisor, [rabbit_channel_sup_sup]}), {ok, ReaderPid} = supervisor2:start_child( SupPid, {reader, {rabbit_reader, start_link, - [ChannelSupSupPid, Collector, + [SupPid, Collector, rabbit_heartbeat:start_heartbeat_fun(SupPid)]}, intrinsic, ?MAX_WAIT, worker, [rabbit_reader]}), {ok, SupPid, ReaderPid}. diff --git a/src/rabbit_networking.erl b/src/rabbit_networking.erl index 31eeef73..ee430fb4 100644 --- a/src/rabbit_networking.erl +++ b/src/rabbit_networking.erl @@ -18,7 +18,8 @@ -export([boot/0, start/0, start_tcp_listener/1, start_ssl_listener/2, stop_tcp_listener/1, on_node_down/1, active_listeners/0, - node_listeners/1, connections/0, connection_info_keys/0, + node_listeners/1, register_connection/1, unregister_connection/1, + connections/0, connection_info_keys/0, connection_info/1, connection_info/2, connection_info_all/0, connection_info_all/1, close_connection/2, force_connection_event_refresh/0, tcp_host/1]). @@ -40,6 +41,8 @@ -define(FIRST_TEST_BIND_PORT, 10000). +-define(CONNECTION_TABLE, rabbit_connection). + %%---------------------------------------------------------------------------- -ifdef(use_specs). @@ -65,6 +68,8 @@ -spec(stop_tcp_listener/1 :: (listener_config()) -> 'ok'). -spec(active_listeners/0 :: () -> [rabbit_types:listener()]). -spec(node_listeners/1 :: (node()) -> [rabbit_types:listener()]). +-spec(register_connection/1 :: (pid()) -> ok). +-spec(unregister_connection/1 :: (pid()) -> ok). -spec(connections/0 :: () -> [rabbit_types:connection()]). -spec(connections_local/0 :: () -> [rabbit_types:connection()]). -spec(connection_info_keys/0 :: () -> rabbit_types:info_keys()). @@ -117,6 +122,7 @@ %%---------------------------------------------------------------------------- boot() -> + ets:new(?CONNECTION_TABLE, [public, named_table]), ok = start(), ok = boot_tcp(), ok = boot_ssl(). @@ -294,20 +300,15 @@ start_client(Sock) -> start_ssl_client(SslOpts, Sock) -> start_client(Sock, ssl_transform_fun(SslOpts)). +register_connection(Pid) -> ets:insert(?CONNECTION_TABLE, {Pid}), ok. + +unregister_connection(Pid) -> ets:delete(?CONNECTION_TABLE, Pid), ok. + connections() -> rabbit_misc:append_rpc_all_nodes(rabbit_mnesia:cluster_nodes(running), rabbit_networking, connections_local, []). -connections_local() -> - [Reader || - {_, ConnSup, supervisor, _} - <- supervisor:which_children(rabbit_tcp_client_sup), - Reader <- [try - rabbit_connection_sup:reader(ConnSup) - catch exit:{noproc, _} -> - noproc - end], - Reader =/= noproc]. +connections_local() -> [P || {P} <- ets:tab2list(?CONNECTION_TABLE)]. connection_info_keys() -> rabbit_reader:info_keys(). diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 7a28c8a3..13459350 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -23,7 +23,7 @@ -export([system_continue/3, system_terminate/4, system_code_change/4]). --export([init/4, mainloop/2]). +-export([init/4, mainloop/2, recvloop/2]). -export([conserve_resources/3, server_properties/1]). @@ -37,7 +37,8 @@ -record(v1, {parent, sock, connection, callback, recv_len, pending_recv, connection_state, queue_collector, heartbeater, stats_timer, - channel_sup_sup_pid, start_heartbeat_fun, buf, buf_len, throttle}). + conn_sup_pid, channel_sup_sup_pid, start_heartbeat_fun, + buf, buf_len, throttle}). -record(connection, {name, host, peer_host, port, peer_port, protocol, user, timeout_sec, frame_max, vhost, @@ -109,12 +110,12 @@ start_link(ChannelSupSupPid, Collector, StartHeartbeatFun) -> shutdown(Pid, Explanation) -> gen_server:call(Pid, {shutdown, Explanation}, infinity). -init(Parent, ChannelSupSupPid, Collector, StartHeartbeatFun) -> +init(Parent, ConnSupPid, Collector, StartHeartbeatFun) -> Deb = sys:debug_options([]), receive {go, Sock, SockTransform} -> start_connection( - Parent, ChannelSupSupPid, Collector, StartHeartbeatFun, Deb, Sock, + Parent, ConnSupPid, Collector, StartHeartbeatFun, Deb, Sock, SockTransform) end. @@ -203,7 +204,7 @@ name(Sock) -> socket_ends(Sock) -> socket_op(Sock, fun (S) -> rabbit_net:socket_ends(S, inbound) end). -start_connection(Parent, ChannelSupSupPid, Collector, StartHeartbeatFun, Deb, +start_connection(Parent, ConnSupPid, Collector, StartHeartbeatFun, Deb, Sock, SockTransform) -> process_flag(trap_exit, true), Name = name(Sock), @@ -234,7 +235,8 @@ start_connection(Parent, ChannelSupSupPid, Collector, StartHeartbeatFun, Deb, connection_state = pre_init, queue_collector = Collector, heartbeater = none, - channel_sup_sup_pid = ChannelSupSupPid, + conn_sup_pid = ConnSupPid, + channel_sup_sup_pid = none, start_heartbeat_fun = StartHeartbeatFun, buf = [], buf_len = 0, @@ -244,9 +246,10 @@ start_connection(Parent, ChannelSupSupPid, Collector, StartHeartbeatFun, Deb, last_blocked_at = never}}, try ok = inet_op(fun () -> rabbit_net:tune_buffer_size(ClientSock) end), - recvloop(Deb, switch_callback(rabbit_event:init_stats_timer( - State, #v1.stats_timer), - handshake, 8)), + run({?MODULE, recvloop, + [Deb, switch_callback(rabbit_event:init_stats_timer( + State, #v1.stats_timer), + handshake, 8)]}), log(info, "closing AMQP connection ~p (~s)~n", [self(), Name]) catch Ex -> log(case Ex of @@ -263,10 +266,16 @@ start_connection(Parent, ChannelSupSupPid, Collector, StartHeartbeatFun, Deb, %% accounting as accurate as possible we ought to close the %% socket w/o delay before termination. rabbit_net:fast_close(ClientSock), + rabbit_networking:unregister_connection(self()), rabbit_event:notify(connection_closed, [{pid, self()}]) end, done. +run({M, F, A}) -> + try apply(M, F, A) + catch {become, MFA} -> run(MFA) + end. + recvloop(Deb, State = #v1{pending_recv = true}) -> mainloop(Deb, State); recvloop(Deb, State = #v1{connection_state = blocked}) -> @@ -689,8 +698,17 @@ handle_input(handshake, <<"AMQP", 1, 1, 8, 0>>, State) -> handle_input(handshake, <<"AMQP", 1, 1, 9, 1>>, State) -> start_connection({8, 0, 0}, rabbit_framing_amqp_0_8, State); +%% ... and finally, the 1.0 spec is crystal clear! Note that the +%% TLS uses a different protocol number, and would go here. +handle_input(handshake, <<"AMQP", 0, 1, 0, 0>>, State) -> + become_1_0(amqp, {0, 1, 0, 0}, State); + +%% 3 stands for "SASL" +handle_input(handshake, <<"AMQP", 3, 1, 0, 0>>, State) -> + become_1_0(sasl, {3, 1, 0, 0}, State); + handle_input(handshake, <<"AMQP", A, B, C, D>>, #v1{sock = Sock}) -> - refuse_connection(Sock, {bad_version, A, B, C, D}); + refuse_connection(Sock, {bad_version, {A, B, C, D}}); handle_input(handshake, Other, #v1{sock = Sock}) -> refuse_connection(Sock, {bad_header, Other}); @@ -704,6 +722,7 @@ handle_input(Callback, Data, _State) -> start_connection({ProtocolMajor, ProtocolMinor, _ProtocolRevision}, Protocol, State = #v1{sock = Sock, connection = Connection}) -> + rabbit_networking:register_connection(self()), Start = #'connection.start'{ version_major = ProtocolMajor, version_minor = ProtocolMinor, @@ -799,17 +818,24 @@ handle_method0(#'connection.open'{virtual_host = VHostPath}, connection = Connection = #connection{ user = User, protocol = Protocol}, + conn_sup_pid = ConnSupPid, sock = Sock, throttle = Throttle}) -> ok = rabbit_access_control:check_vhost_access(User, 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}, + {ok, ChannelSupSupPid} = + supervisor2:start_child( + ConnSupPid, + {channel_sup_sup, {rabbit_channel_sup_sup, start_link, []}, + intrinsic, infinity, supervisor, [rabbit_channel_sup_sup]}), State1 = control_throttle( - State#v1{connection_state = running, - connection = NewConnection, - throttle = Throttle#throttle{ - conserve_resources = Conserve}}), + State#v1{connection_state = running, + connection = NewConnection, + channel_sup_sup_pid = ChannelSupSupPid, + throttle = Throttle1}), rabbit_event:notify(connection_created, [{type, network} | infos(?CREATION_EVENT_KEYS, State1)]), @@ -979,3 +1005,24 @@ cert_info(F, #v1{sock = Sock}) -> emit_stats(State) -> rabbit_event:notify(connection_stats, infos(?STATISTICS_KEYS, State)), rabbit_event:reset_stats_timer(State, #v1.stats_timer). + +%% 1.0 stub + +become_1_0(Mode, Version, State = #v1{sock = Sock}) -> + case code:is_loaded(rabbit_amqp1_0_reader) of + false -> refuse_connection(Sock, {bad_version, Version}); + _ -> throw({become, {rabbit_amqp1_0_reader, become, + [Mode, pack_for_1_0(State)]}}) + end. + +pack_for_1_0(#v1{parent = Parent, + sock = Sock, + recv_len = RecvLen, + pending_recv = PendingRecv, + queue_collector = QueueCollector, + conn_sup_pid = ConnSupPid, + start_heartbeat_fun = SHF, + buf = Buf, + buf_len = BufLen}) -> + {Parent, Sock, RecvLen, PendingRecv, QueueCollector, ConnSupPid, SHF, + Buf, BufLen}. -- cgit v1.2.1 From d5679358424983a04b70e7e605f4c311fcc0395e Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 18 Jan 2013 14:04:08 +0000 Subject: fix test connections only show up in 'list_connections' after the protocol header has been sent --- src/rabbit_tests.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 7257827a..b845360e 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -1123,7 +1123,8 @@ test_server_status() -> [L || L = #listener{node = N} <- rabbit_networking:active_listeners(), N =:= node()], - {ok, _C} = gen_tcp:connect(H, P, []), + {ok, C} = gen_tcp:connect(H, P, []), + gen_tcp:send(C, <<"AMQP", 0, 0, 9, 1>>), timer:sleep(100), ok = info_action(list_connections, rabbit_networking:connection_info_keys(), false), -- cgit v1.2.1 From 96e34b42f50beff14f918f0f570155e2b10fc56a Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 19 Jan 2013 14:17:31 +0000 Subject: cosmetic --- src/rabbit_backing_queue.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_backing_queue.erl b/src/rabbit_backing_queue.erl index 99b5946e..9a3c67f9 100644 --- a/src/rabbit_backing_queue.erl +++ b/src/rabbit_backing_queue.erl @@ -71,8 +71,8 @@ %% content. -callback delete_and_terminate(any(), state()) -> state(). -%% Remove all messages in the queue, but not messages which have been -%% fetched and are pending acks. +%% Remove all 'fetchable' messages from the queue, i.e. all messages +%% except those that have been fetched already and are pending acks. -callback purge(state()) -> {purged_msg_count(), state()}. %% Publish a message. -- cgit v1.2.1 From 3bfb99cd754170f93d64851a0dfc05c7b6decc51 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 19 Jan 2013 14:19:43 +0000 Subject: tiny refactor on variable_queue_with_holes --- src/rabbit_tests.erl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index b845360e..13454d31 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -2328,7 +2328,8 @@ test_variable_queue() -> passed. test_variable_queue_fold(VQ0) -> - {Count, RequeuedMsgs, FreshMsgs, VQ1} = variable_queue_with_holes(VQ0), + {RequeuedMsgs, FreshMsgs, VQ1} = variable_queue_with_holes(VQ0), + Count = rabbit_variable_queue:len(VQ1), Msgs = RequeuedMsgs ++ FreshMsgs, lists:foldl( fun (Cut, VQ2) -> test_variable_queue_fold(Cut, Msgs, VQ2) end, @@ -2398,10 +2399,10 @@ variable_queue_with_holes(VQ0) -> Depth = rabbit_variable_queue:depth(VQ8), Len = Depth - length(Subset3), Len = rabbit_variable_queue:len(VQ8), - {Len, (Seq -- Seq3), lists:seq(Count + 1, Count + 64), VQ8}. + {(Seq -- Seq3), lists:seq(Count + 1, Count + 64), VQ8}. test_variable_queue_requeue(VQ0) -> - {_, RequeuedMsgs, FreshMsgs, VQ1} = variable_queue_with_holes(VQ0), + {RequeuedMsgs, FreshMsgs, VQ1} = variable_queue_with_holes(VQ0), Msgs = lists:zip(RequeuedMsgs, lists:duplicate(length(RequeuedMsgs), true)) ++ -- cgit v1.2.1 From ee6e1d0f90be6b2515471d7ee9e0e3989897e8c2 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 19 Jan 2013 14:20:51 +0000 Subject: add BQ:purge_acks/1 --- src/rabbit_backing_queue.erl | 6 +++++- src/rabbit_mirror_queue_master.erl | 4 +++- src/rabbit_variable_queue.erl | 4 +++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/rabbit_backing_queue.erl b/src/rabbit_backing_queue.erl index 9a3c67f9..2b43c8ba 100644 --- a/src/rabbit_backing_queue.erl +++ b/src/rabbit_backing_queue.erl @@ -75,6 +75,10 @@ %% except those that have been fetched already and are pending acks. -callback purge(state()) -> {purged_msg_count(), state()}. +%% Remove all messages in the queue which have been fetched and are +%% pending acks. +-callback purge_acks(state()) -> state(). + %% Publish a message. -callback publish(rabbit_types:basic_message(), rabbit_types:message_properties(), boolean(), pid(), @@ -226,7 +230,7 @@ behaviour_info(callbacks) -> [{start, 1}, {stop, 0}, {init, 3}, {terminate, 2}, - {delete_and_terminate, 2}, {purge, 1}, {publish, 5}, + {delete_and_terminate, 2}, {purge, 1}, {purge_acks, 1}, {publish, 5}, {publish_delivered, 4}, {discard, 3}, {drain_confirmed, 1}, {dropwhile, 2}, {fetchwhile, 4}, {fetch, 2}, {ack, 2}, {requeue, 2}, {ackfold, 4}, {fold, 3}, {len, 1}, diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index b5f72cad..c704804e 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -17,7 +17,7 @@ -module(rabbit_mirror_queue_master). -export([init/3, terminate/2, delete_and_terminate/2, - purge/1, publish/5, publish_delivered/4, + purge/1, purge_acks/1, publish/5, publish_delivered/4, discard/3, fetch/2, drop/2, ack/2, requeue/2, ackfold/4, fold/3, len/1, is_empty/1, depth/1, drain_confirmed/1, dropwhile/2, fetchwhile/4, set_ram_duration_target/2, ram_duration/1, @@ -198,6 +198,8 @@ purge(State = #state { gm = GM, {Count, BQS1} = BQ:purge(BQS), {Count, State #state { backing_queue_state = BQS1 }}. +purge_acks(_State) -> exit({not_implemented, {?MODULE, purge_acks}}). + publish(Msg = #basic_message { id = MsgId }, MsgProps, IsDelivered, ChPid, State = #state { gm = GM, seen_status = SS, diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 8a7045ea..7e09e5e3 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -16,7 +16,7 @@ -module(rabbit_variable_queue). --export([init/3, terminate/2, delete_and_terminate/2, purge/1, +-export([init/3, terminate/2, delete_and_terminate/2, purge/1, purge_acks/1, publish/5, publish_delivered/4, discard/3, drain_confirmed/1, dropwhile/2, fetchwhile/4, fetch/2, drop/2, ack/2, requeue/2, ackfold/4, fold/3, len/1, @@ -519,6 +519,8 @@ purge(State = #vqstate { q4 = Q4, ram_msg_count = 0, persistent_count = PCount1 })}. +purge_acks(State) -> a(purge_pending_ack(false, State)). + publish(Msg = #basic_message { is_persistent = IsPersistent, id = MsgId }, MsgProps = #message_properties { needs_confirming = NeedsConfirming }, IsDelivered, _ChPid, State = #vqstate { q1 = Q1, q3 = Q3, q4 = Q4, -- cgit v1.2.1 From a4891ce1d6006c6f36c8408d96028b7b3ee35be9 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 19 Jan 2013 14:43:56 +0000 Subject: add a test --- src/rabbit_tests.erl | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 13454d31..7bd8d541 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -2323,6 +2323,7 @@ test_variable_queue() -> fun test_dropwhile_varying_ram_duration/1, fun test_fetchwhile_varying_ram_duration/1, fun test_variable_queue_ack_limiting/1, + fun test_variable_queue_purge/1, fun test_variable_queue_requeue/1, fun test_variable_queue_fold/1]], passed. @@ -2418,6 +2419,21 @@ test_variable_queue_requeue(VQ0) -> {empty, VQ3} = rabbit_variable_queue:fetch(true, VQ2), VQ3. +test_variable_queue_purge(VQ0) -> + LenDepth = fun (VQ) -> + {rabbit_variable_queue:len(VQ), + rabbit_variable_queue:depth(VQ)} + end, + VQ1 = variable_queue_publish(false, 10, VQ0), + {VQ2, Acks} = variable_queue_fetch(6, false, false, 10, VQ1), + {4, VQ3} = rabbit_variable_queue:purge(VQ2), + {0, 6} = LenDepth(VQ3), + {_, VQ4} = rabbit_variable_queue:requeue(lists:sublist(Acks, 2), VQ3), + {2, 6} = LenDepth(VQ4), + VQ5 = rabbit_variable_queue:purge_acks(VQ4), + {2, 2} = LenDepth(VQ5), + VQ5. + test_variable_queue_ack_limiting(VQ0) -> %% start by sending in a bunch of messages Len = 1024, -- cgit v1.2.1 From 788524d54c276e721258814d74b51325556248b7 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 19 Jan 2013 14:56:58 +0000 Subject: cosmetic --- src/rabbit.erl | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 7b8348fc..0f3c52ca 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -552,13 +552,11 @@ boot_error(Reason, Stacktrace) -> Args = [Reason, log_location(kernel), log_location(sasl)], boot_error(Reason, Fmt, Args, Stacktrace). +boot_error(Reason, Fmt, Args, not_available) -> + basic_boot_error(Reason, Fmt, Args); boot_error(Reason, Fmt, Args, Stacktrace) -> - case Stacktrace of - not_available -> basic_boot_error(Reason, Fmt, Args); - _ -> basic_boot_error(Reason, Fmt ++ - "Stack trace:~n ~p~n~n", - Args ++ [Stacktrace]) - end. + basic_boot_error(Reason, Fmt ++ "Stack trace:~n ~p~n~n", + Args ++ [Stacktrace]). basic_boot_error(Reason, Format, Args) -> io:format("~n~nBOOT FAILED~n===========~n~n" ++ Format, Args), -- cgit v1.2.1 From 374d44968ec18d1cc4ad51568d999ca59c839b80 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 19 Jan 2013 15:46:43 +0000 Subject: eliminate "Function X has no local return" dialyzer errors --- src/rabbit.erl | 7 +++++++ src/rabbit_channel.erl | 6 ++++++ src/rabbit_exchange_type_invalid.erl | 4 ++++ src/rabbit_reader.erl | 7 ++++++- 4 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 0f3c52ca..16694105 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -533,6 +533,9 @@ sort_boot_steps(UnsortedSteps) -> end]) end. +-ifdef(use_specs). +-spec(boot_error/2 :: (term(), not_available | [tuple()]) -> no_return()). +-endif. boot_error(Term={error, {timeout_waiting_for_tables, _}}, _Stacktrace) -> AllNodes = rabbit_mnesia:cluster_nodes(all), {Err, Nodes} = @@ -552,6 +555,10 @@ boot_error(Reason, Stacktrace) -> Args = [Reason, log_location(kernel), log_location(sasl)], boot_error(Reason, Fmt, Args, Stacktrace). +-ifdef(use_specs). +-spec(boot_error/4 :: (term(), string(), [any()], not_available | [tuple()]) + -> no_return()). +-endif. boot_error(Reason, Fmt, Args, not_available) -> basic_boot_error(Reason, Fmt, Args); boot_error(Reason, Fmt, Args, Stacktrace) -> diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 88e3dfc5..2b89be8f 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -412,8 +412,14 @@ handle_exception(Reason, State = #ch{protocol = Protocol, {stop, normal, State1} end. +-ifdef(use_specs). +-spec(precondition_failed/1 :: (string()) -> no_return()). +-endif. precondition_failed(Format) -> precondition_failed(Format, []). +-ifdef(use_specs). +-spec(precondition_failed/2 :: (string(), [any()]) -> no_return()). +-endif. precondition_failed(Format, Params) -> rabbit_misc:protocol_error(precondition_failed, Format, Params). diff --git a/src/rabbit_exchange_type_invalid.erl b/src/rabbit_exchange_type_invalid.erl index 101fe434..c5d781c2 100644 --- a/src/rabbit_exchange_type_invalid.erl +++ b/src/rabbit_exchange_type_invalid.erl @@ -31,6 +31,10 @@ description() -> serialise_events() -> false. +-ifdef(use_specs). +-spec(route/2 :: (rabbit_types:exchange(), rabbit_types:delivery()) + -> no_return()). +-endif. route(#exchange{name = Name, type = Type}, _) -> rabbit_misc:protocol_error( precondition_failed, diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 13459350..ae832749 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -1007,7 +1007,12 @@ emit_stats(State) -> rabbit_event:reset_stats_timer(State, #v1.stats_timer). %% 1.0 stub - +-ifdef(use_specs). +-spec(become_1_0/3 :: ('amqp' | 'sasl', + {non_neg_integer(), non_neg_integer(), + non_neg_integer(), non_neg_integer()}, + #v1{}) -> no_return()). +-endif. become_1_0(Mode, Version, State = #v1{sock = Sock}) -> case code:is_loaded(rabbit_amqp1_0_reader) of false -> refuse_connection(Sock, {bad_version, Version}); -- cgit v1.2.1 From 1e9492f35c5d13f8a49be16c4649cddd95bd32b7 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 19 Jan 2013 17:03:56 +0000 Subject: add xmerl to plt so we get fewer 'Unknown functions' in dialyzer mochijson2 depends on it --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c63e3dfd..bf33b931 100644 --- a/Makefile +++ b/Makefile @@ -162,7 +162,7 @@ $(BASIC_PLT): $(BEAM_TARGETS) else \ dialyzer --output_plt $@ --build_plt \ --apps erts kernel stdlib compiler sasl os_mon mnesia tools \ - public_key crypto ssl; \ + public_key crypto ssl xmerl; \ fi clean: -- cgit v1.2.1 From 005788d47882dade23b7c3b605bcafde4107222d Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 19 Jan 2013 20:16:22 +0000 Subject: eager sync of messages pending ack --- docs/rabbitmqctl.1.xml | 3 +-- src/rabbit_amqqueue.erl | 3 +-- src/rabbit_amqqueue_process.erl | 12 ++++-------- src/rabbit_mirror_queue_slave.erl | 3 +-- src/rabbit_mirror_queue_sync.erl | 26 ++++++++++++++++---------- 5 files changed, 23 insertions(+), 24 deletions(-) diff --git a/docs/rabbitmqctl.1.xml b/docs/rabbitmqctl.1.xml index c7069aed..bbd2fe5b 100644 --- a/docs/rabbitmqctl.1.xml +++ b/docs/rabbitmqctl.1.xml @@ -465,8 +465,7 @@ synchronise itself. The queue will block while synchronisation takes place (all publishers to and consumers from the queue will block). The queue must be - mirrored, and must not have any pending unacknowledged - messages for this command to succeed. + mirrored for this command to succeed. Note that unsynchronised queues from which messages are diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 2477b891..21b6bb92 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -174,8 +174,7 @@ (rabbit_types:amqqueue(), rabbit_types:amqqueue()) -> 'ok'). -spec(start_mirroring/1 :: (pid()) -> 'ok'). -spec(stop_mirroring/1 :: (pid()) -> 'ok'). --spec(sync_mirrors/1 :: (pid()) -> - 'ok' | rabbit_types:error('pending_acks' | 'not_mirrored')). +-spec(sync_mirrors/1 :: (pid()) -> 'ok' | rabbit_types:error('not_mirrored')). -spec(cancel_sync_mirrors/1 :: (pid()) -> 'ok' | {'ok', 'not_syncing'}). -endif. diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 0a07a005..2795e317 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1163,7 +1163,7 @@ handle_call({requeue, AckTags, ChPid}, From, State) -> noreply(requeue(AckTags, ChPid, State)); handle_call(sync_mirrors, _From, - State = #q{backing_queue = rabbit_mirror_queue_master = BQ, + State = #q{backing_queue = rabbit_mirror_queue_master, backing_queue_state = BQS}) -> S = fun(BQSN) -> State#q{backing_queue_state = BQSN} end, HandleInfo = fun (Status) -> @@ -1179,13 +1179,9 @@ handle_call(sync_mirrors, _From, State, #q.stats_timer, fun() -> emit_stats(State#q{status = Status}) end) end, - case BQ:depth(BQS) - BQ:len(BQS) of - 0 -> case rabbit_mirror_queue_master:sync_mirrors( - HandleInfo, EmitStats, BQS) of - {ok, BQS1} -> reply(ok, S(BQS1)); - {stop, Reason, BQS1} -> {stop, Reason, S(BQS1)} - end; - _ -> reply({error, pending_acks}, State) + case rabbit_mirror_queue_master:sync_mirrors(HandleInfo, EmitStats, BQS) of + {ok, BQS1} -> reply(ok, S(BQS1)); + {stop, Reason, BQS1} -> {stop, Reason, S(BQS1)} end; handle_call(sync_mirrors, _From, State) -> diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 9f12b34e..b63fccc9 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -230,7 +230,6 @@ handle_cast({sync_start, Ref, Syncer}, S = fun({TRefN, BQSN}) -> State1#state{depth_delta = undefined, rate_timer_ref = TRefN, backing_queue_state = BQSN} end, - %% [0] We can only sync when there are no pending acks case rabbit_mirror_queue_sync:slave( DD, Ref, TRef, Syncer, BQ, BQS, fun (BQN, BQSN) -> @@ -240,7 +239,7 @@ handle_cast({sync_start, Ref, Syncer}, {TRefN, BQSN1} end) of denied -> noreply(State1); - {ok, Res} -> noreply(set_delta(0, S(Res))); %% [0] + {ok, Res} -> noreply(set_delta(0, S(Res))); {failed, Res} -> noreply(S(Res)); {stop, Reason, Res} -> {stop, Reason, S(Res)} end; diff --git a/src/rabbit_mirror_queue_sync.erl b/src/rabbit_mirror_queue_sync.erl index 4d6b1fc9..b023823e 100644 --- a/src/rabbit_mirror_queue_sync.erl +++ b/src/rabbit_mirror_queue_sync.erl @@ -91,16 +91,16 @@ master_go(Syncer, Ref, Log, HandleInfo, EmitStats, BQ, BQS) -> end. master_go0(Args, BQ, BQS) -> - case BQ:fold(fun (Msg, MsgProps, false, Acc) -> - master_send(Msg, MsgProps, Args, Acc) + case BQ:fold(fun (Msg, MsgProps, Unacked, Acc) -> + master_send(Msg, MsgProps, Unacked, Args, Acc) end, {0, erlang:now()}, BQS) of {{shutdown, Reason}, BQS1} -> {shutdown, Reason, BQS1}; {{sync_died, Reason}, BQS1} -> {sync_died, Reason, BQS1}; {_, BQS1} -> master_done(Args, BQS1) end. -master_send(Msg, MsgProps, {Syncer, Ref, Log, HandleInfo, EmitStats, Parent}, - {I, Last}) -> +master_send(Msg, MsgProps, Unacked, + {Syncer, Ref, Log, HandleInfo, EmitStats, Parent}, {I, Last}) -> T = case timer:now_diff(erlang:now(), Last) > ?SYNC_PROGRESS_INTERVAL of true -> EmitStats({syncing, I}), Log("~p messages", [I]), @@ -119,7 +119,7 @@ master_send(Msg, MsgProps, {Syncer, Ref, Log, HandleInfo, EmitStats, Parent}, cancel_sync_mirrors} -> stop_syncer(Syncer, {cancel, Ref}), gen_server2:reply(From, ok), {stop, cancelled}; - {next, Ref} -> Syncer ! {msg, Ref, Msg, MsgProps}, + {next, Ref} -> Syncer ! {msg, Ref, Msg, MsgProps, Unacked}, {cont, {I + 1, T}}; {'EXIT', Parent, Reason} -> {stop, {shutdown, Reason}}; {'EXIT', Syncer, Reason} -> {stop, {sync_died, Reason}} @@ -164,11 +164,11 @@ syncer(Ref, Log, MPid, SPids) -> syncer_loop(Ref, MPid, SPids) -> MPid ! {next, Ref}, receive - {msg, Ref, Msg, MsgProps} -> + {msg, Ref, Msg, MsgProps, Unacked} -> SPids1 = wait_for_credit(SPids), [begin credit_flow:send(SPid), - SPid ! {sync_msg, Ref, Msg, MsgProps} + SPid ! {sync_msg, Ref, Msg, MsgProps, Unacked} end || SPid <- SPids1], syncer_loop(Ref, MPid, SPids1); {cancel, Ref} -> @@ -204,7 +204,7 @@ slave(0, Ref, _TRef, Syncer, _BQ, _BQS, _UpdateRamDuration) -> slave(_DD, Ref, TRef, Syncer, BQ, BQS, UpdateRamDuration) -> MRef = erlang:monitor(process, Syncer), Syncer ! {sync_ready, Ref, self()}, - {_MsgCount, BQS1} = BQ:purge(BQS), + {_MsgCount, BQS1} = BQ:purge(BQ:purge_acks(BQS)), slave_sync_loop({Ref, MRef, Syncer, BQ, UpdateRamDuration, rabbit_misc:get_parent()}, TRef, BQS1). @@ -237,10 +237,16 @@ slave_sync_loop(Args = {Ref, MRef, Syncer, BQ, UpdateRamDuration, Parent}, update_ram_duration -> {TRef1, BQS1} = UpdateRamDuration(BQ, BQS), slave_sync_loop(Args, TRef1, BQS1); - {sync_msg, Ref, Msg, Props} -> + {sync_msg, Ref, Msg, Props, Unacked} -> credit_flow:ack(Syncer), Props1 = Props#message_properties{needs_confirming = false}, - BQS1 = BQ:publish(Msg, Props1, true, none, BQS), + BQS1 = case Unacked of + false -> BQ:publish(Msg, Props1, true, none, BQS); + true -> {_AckTag, BQS2} = BQ:publish_delivered( + Msg, Props1, none, BQS), + %% TODO do something w AckTag + BQS2 + end, slave_sync_loop(Args, TRef, BQS1); {'EXIT', Parent, Reason} -> {stop, Reason, {TRef, BQS}}; -- cgit v1.2.1 From f27c502034c9e5218e280c4a39da88562b466f51 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 19 Jan 2013 21:32:01 +0000 Subject: populate slave's msg_id_ack with sync'ed messages pending ack --- src/rabbit_mirror_queue_slave.erl | 9 +++++--- src/rabbit_mirror_queue_sync.erl | 45 +++++++++++++++++++++------------------ 2 files changed, 30 insertions(+), 24 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index b63fccc9..cd2a8042 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -227,9 +227,12 @@ handle_cast({sync_start, Ref, Syncer}, backing_queue = BQ, backing_queue_state = BQS }) -> State1 = #state{rate_timer_ref = TRef} = ensure_rate_timer(State), - S = fun({TRefN, BQSN}) -> State1#state{depth_delta = undefined, - rate_timer_ref = TRefN, - backing_queue_state = BQSN} end, + S = fun({MA, TRefN, BQSN}) -> + State1#state{depth_delta = undefined, + msg_id_ack = dict:from_list(MA), + rate_timer_ref = TRefN, + backing_queue_state = BQSN} + end, case rabbit_mirror_queue_sync:slave( DD, Ref, TRef, Syncer, BQ, BQS, fun (BQN, BQSN) -> diff --git a/src/rabbit_mirror_queue_sync.erl b/src/rabbit_mirror_queue_sync.erl index b023823e..b8cfe4a9 100644 --- a/src/rabbit_mirror_queue_sync.erl +++ b/src/rabbit_mirror_queue_sync.erl @@ -57,6 +57,9 @@ -type(log_fun() :: fun ((string(), [any()]) -> 'ok')). -type(bq() :: atom()). -type(bqs() :: any()). +-type(ack() :: any()). +-type(slave_sync_state() :: {[{rabbit_types:msg_id(), ack()}], timer:tref(), + bqs()}). -spec(master_prepare/3 :: (reference(), log_fun(), [pid()]) -> pid()). -spec(master_go/7 :: (pid(), reference(), log_fun(), @@ -69,8 +72,8 @@ -spec(slave/7 :: (non_neg_integer(), reference(), timer:tref(), pid(), bq(), bqs(), fun((bq(), bqs()) -> {timer:tref(), bqs()})) -> 'denied' | - {'ok' | 'failed', {timer:tref(), bqs()}} | - {'stop', any(), {timer:tref(), bqs()}}). + {'ok' | 'failed', slave_sync_state()} | + {'stop', any(), slave_sync_state()}). -endif. @@ -206,10 +209,10 @@ slave(_DD, Ref, TRef, Syncer, BQ, BQS, UpdateRamDuration) -> Syncer ! {sync_ready, Ref, self()}, {_MsgCount, BQS1} = BQ:purge(BQ:purge_acks(BQS)), slave_sync_loop({Ref, MRef, Syncer, BQ, UpdateRamDuration, - rabbit_misc:get_parent()}, TRef, BQS1). + rabbit_misc:get_parent()}, {[], TRef, BQS1}). slave_sync_loop(Args = {Ref, MRef, Syncer, BQ, UpdateRamDuration, Parent}, - TRef, BQS) -> + State = {MA, TRef, BQS}) -> receive {'DOWN', MRef, process, Syncer, _Reason} -> %% If the master dies half way we are not in the usual @@ -218,40 +221,40 @@ slave_sync_loop(Args = {Ref, MRef, Syncer, BQ, UpdateRamDuration, Parent}, %% sync with a newly promoted master, or even just receive %% messages from it, we have a hole in the middle. So the %% only thing to do here is purge. - {_MsgCount, BQS1} = BQ:purge(BQS), + {_MsgCount, BQS1} = BQ:purge(BQ:purge_acks(BQS)), credit_flow:peer_down(Syncer), - {failed, {TRef, BQS1}}; + {failed, {[], TRef, BQS1}}; {bump_credit, Msg} -> credit_flow:handle_bump_msg(Msg), - slave_sync_loop(Args, TRef, BQS); + slave_sync_loop(Args, State); {sync_complete, Ref} -> erlang:demonitor(MRef, [flush]), credit_flow:peer_down(Syncer), - {ok, {TRef, BQS}}; + {ok, State}; {'$gen_cast', {set_maximum_since_use, Age}} -> ok = file_handle_cache:set_maximum_since_use(Age), - slave_sync_loop(Args, TRef, BQS); + slave_sync_loop(Args, State); {'$gen_cast', {set_ram_duration_target, Duration}} -> BQS1 = BQ:set_ram_duration_target(Duration, BQS), - slave_sync_loop(Args, TRef, BQS1); + slave_sync_loop(Args, {MA, TRef, BQS1}); update_ram_duration -> {TRef1, BQS1} = UpdateRamDuration(BQ, BQS), - slave_sync_loop(Args, TRef1, BQS1); + slave_sync_loop(Args, {MA, TRef1, BQS1}); {sync_msg, Ref, Msg, Props, Unacked} -> credit_flow:ack(Syncer), Props1 = Props#message_properties{needs_confirming = false}, - BQS1 = case Unacked of - false -> BQ:publish(Msg, Props1, true, none, BQS); - true -> {_AckTag, BQS2} = BQ:publish_delivered( - Msg, Props1, none, BQS), - %% TODO do something w AckTag - BQS2 - end, - slave_sync_loop(Args, TRef, BQS1); + {MA1, BQS1} = + case Unacked of + false -> {MA, BQ:publish(Msg, Props1, true, none, BQS)}; + true -> {AckTag, BQS2} = BQ:publish_delivered( + Msg, Props1, none, BQS), + {[{Msg#basic_message.id, AckTag} | MA], BQS2} + end, + slave_sync_loop(Args, {MA1, TRef, BQS1}); {'EXIT', Parent, Reason} -> - {stop, Reason, {TRef, BQS}}; + {stop, Reason, State}; %% If the master throws an exception {'$gen_cast', {gm, {delete_and_terminate, Reason}}} -> BQ:delete_and_terminate(Reason, BQS), - {stop, Reason, {TRef, undefined}} + {stop, Reason, {[], TRef, undefined}} end. -- cgit v1.2.1 From 488057258e8bd53a62348bd82ae0c70c268638ad Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sun, 20 Jan 2013 13:20:53 +0000 Subject: cosmetic: move spec of internal function and make it more precise --- src/rabbit_mirror_queue_slave.erl | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 9f12b34e..867aa2ed 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -37,18 +37,10 @@ -include("rabbit.hrl"). -%%---------------------------------------------------------------------------- - -include("gm_specs.hrl"). --ifdef(use_specs). -%% Shut dialyzer up --spec(promote_me/2 :: (_, _) -> no_return()). --endif. - %%---------------------------------------------------------------------------- - -define(CREATION_EVENT_KEYS, [pid, name, @@ -79,6 +71,8 @@ depth_delta }). +%%---------------------------------------------------------------------------- + start_link(Q) -> gen_server2:start_link(?MODULE, Q, []). set_maximum_since_use(QPid, Age) -> @@ -469,6 +463,9 @@ confirm_messages(MsgIds, State = #state { msg_id_status = MS }) -> handle_process_result({ok, State}) -> noreply(State); handle_process_result({stop, State}) -> {stop, normal, State}. +-ifdef(use_specs). +-spec(promote_me/2 :: ({pid(), term()}, #state{}) -> no_return()). +-endif. promote_me(From, #state { q = Q = #amqqueue { name = QName }, gm = GM, backing_queue = BQ, -- cgit v1.2.1 From 154510f6dc1ae3146b21b02d2c07a7a9d3ff8183 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 21 Jan 2013 13:06:59 +0000 Subject: USe pg_local rather than an ets table. --- src/rabbit_networking.erl | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/rabbit_networking.erl b/src/rabbit_networking.erl index ee430fb4..080e0987 100644 --- a/src/rabbit_networking.erl +++ b/src/rabbit_networking.erl @@ -41,8 +41,6 @@ -define(FIRST_TEST_BIND_PORT, 10000). --define(CONNECTION_TABLE, rabbit_connection). - %%---------------------------------------------------------------------------- -ifdef(use_specs). @@ -122,7 +120,6 @@ %%---------------------------------------------------------------------------- boot() -> - ets:new(?CONNECTION_TABLE, [public, named_table]), ok = start(), ok = boot_tcp(), ok = boot_ssl(). @@ -300,15 +297,15 @@ start_client(Sock) -> start_ssl_client(SslOpts, Sock) -> start_client(Sock, ssl_transform_fun(SslOpts)). -register_connection(Pid) -> ets:insert(?CONNECTION_TABLE, {Pid}), ok. +register_connection(Pid) -> pg_local:join(rabbit_connections, Pid). -unregister_connection(Pid) -> ets:delete(?CONNECTION_TABLE, Pid), ok. +unregister_connection(Pid) -> pg_local:leave(rabbit_connections, Pid). connections() -> rabbit_misc:append_rpc_all_nodes(rabbit_mnesia:cluster_nodes(running), rabbit_networking, connections_local, []). -connections_local() -> [P || {P} <- ets:tab2list(?CONNECTION_TABLE)]. +connections_local() -> pg_local:get_members(rabbit_connections). connection_info_keys() -> rabbit_reader:info_keys(). -- cgit v1.2.1 From 62c4b3d9963ea78a5c36a3981958c8e6988f0756 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 21 Jan 2013 14:45:51 +0000 Subject: get th channel to flush the writer when the former is asked to terminate by the reader --- src/rabbit_channel.erl | 3 ++- src/rabbit_writer.erl | 24 +++++++++++++++++------- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index b97af6d8..2b9cffd4 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -280,7 +280,8 @@ handle_cast(ready_for_close, State = #ch{state = closing, ok = rabbit_writer:send_command_sync(WriterPid, #'channel.close_ok'{}), {stop, normal, State}; -handle_cast(terminate, State) -> +handle_cast(terminate, State = #ch{writer_pid = WriterPid}) -> + ok = rabbit_writer:flush(WriterPid), {stop, normal, State}; handle_cast({command, #'basic.consume_ok'{consumer_tag = ConsumerTag} = Msg}, diff --git a/src/rabbit_writer.erl b/src/rabbit_writer.erl index a7ea3d99..059d3839 100644 --- a/src/rabbit_writer.erl +++ b/src/rabbit_writer.erl @@ -21,7 +21,8 @@ -export([start/5, start_link/5, start/6, start_link/6]). -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_and_notify/4, send_command_and_notify/5, + flush/1]). -export([internal_send_command/4, internal_send_command/6]). %% internal @@ -69,6 +70,7 @@ (pid(), pid(), 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(), rabbit_framing:amqp_method_record(), rabbit_types:protocol()) @@ -130,7 +132,7 @@ mainloop1(State) -> receive Message -> ?MODULE:mainloop1(handle_message(Message, State)) after 0 -> - ?MODULE:mainloop1(flush(State)) + ?MODULE:mainloop1(internal_flush(State)) end. handle_message({send_command, MethodRecord}, State) -> @@ -138,12 +140,18 @@ handle_message({send_command, MethodRecord}, State) -> handle_message({send_command, MethodRecord, Content}, State) -> internal_send_command_async(MethodRecord, Content, State); handle_message({'$gen_call', From, {send_command_sync, MethodRecord}}, State) -> - State1 = flush(internal_send_command_async(MethodRecord, State)), + State1 = internal_flush( + internal_send_command_async(MethodRecord, State)), gen_server:reply(From, ok), State1; handle_message({'$gen_call', From, {send_command_sync, MethodRecord, Content}}, State) -> - State1 = flush(internal_send_command_async(MethodRecord, Content, State)), + State1 = internal_flush( + internal_send_command_async(MethodRecord, Content, State)), + gen_server:reply(From, ok), + State1; +handle_message({'$gen_call', From, flush}, State) -> + State1 = internal_flush(State), gen_server:reply(From, ok), State1; handle_message({send_command_and_notify, QPid, ChPid, MethodRecord}, State) -> @@ -192,6 +200,8 @@ send_command_and_notify(W, Q, ChPid, MethodRecord, Content) -> W ! {send_command_and_notify, Q, ChPid, MethodRecord, Content}, ok. +flush(W) -> call(W, flush). + %%--------------------------------------------------------------------------- call(Pid, Msg) -> @@ -251,13 +261,13 @@ internal_send_command_async(MethodRecord, Content, maybe_flush(State = #wstate{pending = Pending}) -> case iolist_size(Pending) >= ?FLUSH_THRESHOLD of - true -> flush(State); + true -> internal_flush(State); false -> State end. -flush(State = #wstate{pending = []}) -> +internal_flush(State = #wstate{pending = []}) -> State; -flush(State = #wstate{sock = Sock, pending = Pending}) -> +internal_flush(State = #wstate{sock = Sock, pending = Pending}) -> ok = port_cmd(Sock, lists:reverse(Pending)), State#wstate{pending = []}. -- cgit v1.2.1 From ad58ded86094cd49a4333fdd5d79f49feb591d55 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 21 Jan 2013 14:58:13 +0000 Subject: Backed out changeset 0ca8cbef9720 accidentally committed on 'stable' instead of bug25360 branch --- src/rabbit_channel.erl | 3 +-- src/rabbit_writer.erl | 24 +++++++----------------- 2 files changed, 8 insertions(+), 19 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 2b9cffd4..b97af6d8 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -280,8 +280,7 @@ handle_cast(ready_for_close, State = #ch{state = closing, ok = rabbit_writer:send_command_sync(WriterPid, #'channel.close_ok'{}), {stop, normal, State}; -handle_cast(terminate, State = #ch{writer_pid = WriterPid}) -> - ok = rabbit_writer:flush(WriterPid), +handle_cast(terminate, State) -> {stop, normal, State}; handle_cast({command, #'basic.consume_ok'{consumer_tag = ConsumerTag} = Msg}, diff --git a/src/rabbit_writer.erl b/src/rabbit_writer.erl index 059d3839..a7ea3d99 100644 --- a/src/rabbit_writer.erl +++ b/src/rabbit_writer.erl @@ -21,8 +21,7 @@ -export([start/5, start_link/5, start/6, start_link/6]). -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, - flush/1]). + send_command_and_notify/4, send_command_and_notify/5]). -export([internal_send_command/4, internal_send_command/6]). %% internal @@ -70,7 +69,6 @@ (pid(), pid(), 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(), rabbit_framing:amqp_method_record(), rabbit_types:protocol()) @@ -132,7 +130,7 @@ mainloop1(State) -> receive Message -> ?MODULE:mainloop1(handle_message(Message, State)) after 0 -> - ?MODULE:mainloop1(internal_flush(State)) + ?MODULE:mainloop1(flush(State)) end. handle_message({send_command, MethodRecord}, State) -> @@ -140,18 +138,12 @@ handle_message({send_command, MethodRecord}, State) -> handle_message({send_command, MethodRecord, Content}, State) -> 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)), + State1 = flush(internal_send_command_async(MethodRecord, State)), gen_server:reply(From, ok), State1; handle_message({'$gen_call', From, {send_command_sync, MethodRecord, Content}}, State) -> - State1 = internal_flush( - internal_send_command_async(MethodRecord, Content, State)), - gen_server:reply(From, ok), - State1; -handle_message({'$gen_call', From, flush}, State) -> - State1 = internal_flush(State), + State1 = flush(internal_send_command_async(MethodRecord, Content, State)), gen_server:reply(From, ok), State1; handle_message({send_command_and_notify, QPid, ChPid, MethodRecord}, State) -> @@ -200,8 +192,6 @@ send_command_and_notify(W, Q, ChPid, MethodRecord, Content) -> W ! {send_command_and_notify, Q, ChPid, MethodRecord, Content}, ok. -flush(W) -> call(W, flush). - %%--------------------------------------------------------------------------- call(Pid, Msg) -> @@ -261,13 +251,13 @@ internal_send_command_async(MethodRecord, Content, maybe_flush(State = #wstate{pending = Pending}) -> case iolist_size(Pending) >= ?FLUSH_THRESHOLD of - true -> internal_flush(State); + true -> flush(State); false -> State end. -internal_flush(State = #wstate{pending = []}) -> +flush(State = #wstate{pending = []}) -> State; -internal_flush(State = #wstate{sock = Sock, pending = Pending}) -> +flush(State = #wstate{sock = Sock, pending = Pending}) -> ok = port_cmd(Sock, lists:reverse(Pending)), State#wstate{pending = []}. -- cgit v1.2.1 From ac66b65c6359d75ff5adfc7b955e92286990933e Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 21 Jan 2013 14:59:55 +0000 Subject: get the channel to flush the writer when the former is asked to terminate by the reader --- src/rabbit_channel.erl | 3 ++- src/rabbit_writer.erl | 24 +++++++++++++++++------- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index b97af6d8..2b9cffd4 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -280,7 +280,8 @@ handle_cast(ready_for_close, State = #ch{state = closing, ok = rabbit_writer:send_command_sync(WriterPid, #'channel.close_ok'{}), {stop, normal, State}; -handle_cast(terminate, State) -> +handle_cast(terminate, State = #ch{writer_pid = WriterPid}) -> + ok = rabbit_writer:flush(WriterPid), {stop, normal, State}; handle_cast({command, #'basic.consume_ok'{consumer_tag = ConsumerTag} = Msg}, diff --git a/src/rabbit_writer.erl b/src/rabbit_writer.erl index a7ea3d99..059d3839 100644 --- a/src/rabbit_writer.erl +++ b/src/rabbit_writer.erl @@ -21,7 +21,8 @@ -export([start/5, start_link/5, start/6, start_link/6]). -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_and_notify/4, send_command_and_notify/5, + flush/1]). -export([internal_send_command/4, internal_send_command/6]). %% internal @@ -69,6 +70,7 @@ (pid(), pid(), 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(), rabbit_framing:amqp_method_record(), rabbit_types:protocol()) @@ -130,7 +132,7 @@ mainloop1(State) -> receive Message -> ?MODULE:mainloop1(handle_message(Message, State)) after 0 -> - ?MODULE:mainloop1(flush(State)) + ?MODULE:mainloop1(internal_flush(State)) end. handle_message({send_command, MethodRecord}, State) -> @@ -138,12 +140,18 @@ handle_message({send_command, MethodRecord}, State) -> handle_message({send_command, MethodRecord, Content}, State) -> internal_send_command_async(MethodRecord, Content, State); handle_message({'$gen_call', From, {send_command_sync, MethodRecord}}, State) -> - State1 = flush(internal_send_command_async(MethodRecord, State)), + State1 = internal_flush( + internal_send_command_async(MethodRecord, State)), gen_server:reply(From, ok), State1; handle_message({'$gen_call', From, {send_command_sync, MethodRecord, Content}}, State) -> - State1 = flush(internal_send_command_async(MethodRecord, Content, State)), + State1 = internal_flush( + internal_send_command_async(MethodRecord, Content, State)), + gen_server:reply(From, ok), + State1; +handle_message({'$gen_call', From, flush}, State) -> + State1 = internal_flush(State), gen_server:reply(From, ok), State1; handle_message({send_command_and_notify, QPid, ChPid, MethodRecord}, State) -> @@ -192,6 +200,8 @@ send_command_and_notify(W, Q, ChPid, MethodRecord, Content) -> W ! {send_command_and_notify, Q, ChPid, MethodRecord, Content}, ok. +flush(W) -> call(W, flush). + %%--------------------------------------------------------------------------- call(Pid, Msg) -> @@ -251,13 +261,13 @@ internal_send_command_async(MethodRecord, Content, maybe_flush(State = #wstate{pending = Pending}) -> case iolist_size(Pending) >= ?FLUSH_THRESHOLD of - true -> flush(State); + true -> internal_flush(State); false -> State end. -flush(State = #wstate{pending = []}) -> +internal_flush(State = #wstate{pending = []}) -> State; -flush(State = #wstate{sock = Sock, pending = Pending}) -> +internal_flush(State = #wstate{sock = Sock, pending = Pending}) -> ok = port_cmd(Sock, lists:reverse(Pending)), State#wstate{pending = []}. -- 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(-) 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 c408184ffd653dc7b52fe584b9e13f410229e142 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 22 Jan 2013 12:50:15 +0000 Subject: our test writer needs to do a bit more now --- src/rabbit_tests.erl | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index a68caadb..7a0ed1af 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -1084,9 +1084,16 @@ test_policy_validation() -> rabbit_runtime_parameters_test:unregister_policy_validator(), passed. +writer() -> + receive + {'$gen_call', From, flush} -> gen_server:reply(From, ok), + writer(); + shutdown -> ok + end. + test_server_status() -> %% create a few things so there is some useful information to list - Writer = spawn(fun () -> receive shutdown -> ok end end), + Writer = spawn(fun writer/0), {ok, Ch} = rabbit_channel:start_link( 1, self(), Writer, self(), "", rabbit_framing_amqp_0_9_1, user(<<"user">>), <<"/">>, [], self(), -- cgit v1.2.1 From 82be213f82e7877c49f214bde6fcc7c4514e0734 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 22 Jan 2013 12:59:04 +0000 Subject: Remove knowledge of AMQP frames from the limiter. (attempt 2) --- src/rabbit_channel.erl | 24 +++++++++++++++++++++++- src/rabbit_limiter.erl | 10 ++-------- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 5ee030b1..9cb37c4f 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -21,7 +21,8 @@ -behaviour(gen_server2). -export([start_link/11, do/2, do/3, do_flow/3, flush/1, shutdown/1]). --export([send_command/2, deliver/4, flushed/2]). +-export([send_command/2, deliver/4, send_credit_reply/2, send_drained/3, + flushed/2]). -export([list/0, info_keys/0, info/1, info/2, info_all/0, info_all/1]). -export([refresh_config_local/0, ready_for_close/1]). -export([force_event_refresh/0]). @@ -138,6 +139,12 @@ send_command(Pid, Msg) -> deliver(Pid, ConsumerTag, AckRequired, Msg) -> gen_server2:cast(Pid, {deliver, ConsumerTag, AckRequired, Msg}). +send_credit_reply(Pid, Len) -> + gen_server2:cast(Pid, {send_credit_reply, Len}). + +send_drained(Pid, ConsumerTag, Count) -> + gen_server2:cast(Pid, {send_drained, ConsumerTag, Count}). + flushed(Pid, QPid) -> gen_server2:cast(Pid, {flushed, QPid}). @@ -314,6 +321,21 @@ handle_cast({deliver, ConsumerTag, AckRequired, Content), noreply(record_sent(ConsumerTag, AckRequired, Msg, State)); +handle_cast({send_credit_reply, Len}, State = #ch{writer_pid = WriterPid}) -> + ok = rabbit_writer:send_command( + WriterPid, #'basic.credit_ok'{available = Len}), + noreply(State); + +handle_cast({send_drained, ConsumerTag, Count}, + State = #ch{writer_pid = WriterPid}) -> + ok = rabbit_writer:send_command( + WriterPid, #'basic.credit_state'{consumer_tag = ConsumerTag, + credit = 0, + count = Count, + available = 0, + drain = true}), + noreply(State); + handle_cast(force_event_refresh, State) -> rabbit_event:notify(channel_created, infos(?CREATION_EVENT_KEYS, State)), noreply(State); diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index 57fd0c26..401723a4 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -148,8 +148,7 @@ inform(Limiter = #token{q_state = Credits}, {Unblock, Credits2} = update_credit( CTag, Len, ChPid, Credit, Count, Drain, Credits), case Reply of - true -> rabbit_channel:send_command( - ChPid, #'basic.credit_ok'{available = Len}); + true -> rabbit_channel:send_credit_reply(ChPid, Len); false -> ok end, {Unblock, Limiter#token{q_state = Credits2}}. @@ -194,12 +193,7 @@ maybe_drain(_, _, _, _, Credit, Count) -> {Credit, Count}. send_drained(ChPid, CTag, Count) -> - rabbit_channel:send_command(ChPid, - #'basic.credit_state'{consumer_tag = CTag, - credit = 0, - count = Count, - available = 0, - drain = true}). + rabbit_channel:send_drained(ChPid, CTag, Count). update_credit(CTag, Len, ChPid, Credit, Count0, Drain, Credits) -> Count = case dict:find(CTag, Credits) of -- cgit v1.2.1 From e65f518701cbd41ccfeb00691694eac92a902c1f Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 22 Jan 2013 13:06:44 +0000 Subject: Oh yeah, specs. --- src/rabbit_channel.erl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 9cb37c4f..8cd3a580 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -95,6 +95,9 @@ -spec(deliver/4 :: (pid(), rabbit_types:ctag(), boolean(), rabbit_amqqueue:qmsg()) -> 'ok'). +-spec(send_credit_reply/2 :: (pid(), non_neg_integer()) -> 'ok'). +-spec(send_drained/3 :: (pid(), rabbit_types:ctag(), non_neg_integer()) + -> 'ok'). -spec(flushed/2 :: (pid(), pid()) -> 'ok'). -spec(list/0 :: () -> [pid()]). -spec(list_local/0 :: () -> [pid()]). -- cgit v1.2.1 From a09d21e32b4147bab8e33a177a41fc68476e440a Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 22 Jan 2013 13:58:49 +0000 Subject: one test writer is quite enough. and make it work. --- src/rabbit_tests.erl | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 7a0ed1af..1e02ff6b 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -1084,16 +1084,9 @@ test_policy_validation() -> rabbit_runtime_parameters_test:unregister_policy_validator(), passed. -writer() -> - receive - {'$gen_call', From, flush} -> gen_server:reply(From, ok), - writer(); - shutdown -> ok - end. - test_server_status() -> %% create a few things so there is some useful information to list - Writer = spawn(fun writer/0), + Writer = spawn(fun test_writer/0), {ok, Ch} = rabbit_channel:start_link( 1, self(), Writer, self(), "", rabbit_framing_amqp_0_9_1, user(<<"user">>), <<"/">>, [], self(), @@ -1167,10 +1160,15 @@ test_server_status() -> passed. +test_writer() -> test_writer(none). + test_writer(Pid) -> receive - shutdown -> ok; - {send_command, Method} -> Pid ! Method, test_writer(Pid) + {'$gen_call', From, flush} -> gen_server:reply(From, ok), + test_writer(Pid); + {send_command, Method} -> Pid ! Method, + test_writer(Pid); + shutdown -> ok end. test_spawn() -> -- cgit v1.2.1 From 8887710b573db5bb72620a6057a1f461df09c530 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 22 Jan 2013 15:09:33 +0000 Subject: prevent channel from sending anything after close/close_ok The "we have sent a close/close_ok" state is indicated by state=closing. Well, not quite... The channel gets into the 'closing' state via notify_queues. That is called in three places: - 'terminate' - uninteresting since we don't send/do anything after that anyway - handle_exception when encountering a channel level error, i.e. when we send a channel.close - when receiving a 'channel.close'. Note that this isn't quite the same as sending a channel.close_ok. That in fact happens later, after a handshake with the reader. But for our purposes it's good enough. Certainly logically it is perfectly ok for us to stop sending things to the client once we have *seen* a 'close'. So how do we prevent sending in the 'closing' state? Firstly, we introduce a 'send' helper that replaces all rabbit_writer:send_command/2 invocations and is a no-op when state=closing. There is one notable exception - the sending of 'channel.close', which happens when state has already been set to 'closing'. rabbit_writer:send_command/2 isn't the only way to send commands to the writer though. The channel uses a few others: - send_command_sync - this is used to send channel.close_ok. We are in fact in the 'closing' state at that point and nevertheless must send that command (this is the tail end of the aforementioned handshake). So it's fine to leave this invocation as is. - send_command_and_notify - this is used to send messages with basic.deliver. We make that entire code a no-op in the no-op case, i.e. any messages sent by queues to the channel while it is 'closing' are simply dropped on the floor. This is TRTTD since it results in a minimum amount of work and is perfectly safe - no different to the channel terminating while those messages were in flight. - send_command/3 - this is used to send a basic.get_ok. That entire code - part of handle_method - is already guarded by a 'closing' state check that no-ops. It is also used to send basic.returns, which only happens in handling a basic.publish, which in turn is subject to the same handle_method guard. Finally, we add some optimisations to the 'confirm' and 'tx' code, so that all the logic for aggregating confirms and sending acks/nacks (or tx.commit / tx.commit failures) gets skipped when closing. --- src/rabbit_channel.erl | 71 ++++++++++++++++++++++++++------------------------ 1 file changed, 37 insertions(+), 34 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 2b9cffd4..f3a2eb92 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -258,7 +258,7 @@ handle_cast({method, Method, Content, Flow}, end, try handle_method(Method, Content, State) of {reply, Reply, NewState} -> - ok = rabbit_writer:send_command(NewState#ch.writer_pid, Reply), + ok = send(Reply, NewState), noreply(NewState); {noreply, NewState} -> noreply(NewState); @@ -284,15 +284,16 @@ handle_cast(terminate, State = #ch{writer_pid = WriterPid}) -> ok = rabbit_writer:flush(WriterPid), {stop, normal, State}; -handle_cast({command, #'basic.consume_ok'{consumer_tag = ConsumerTag} = Msg}, - State = #ch{writer_pid = WriterPid}) -> - ok = rabbit_writer:send_command(WriterPid, Msg), - noreply(consumer_monitor(ConsumerTag, State)); +handle_cast({command, #'basic.consume_ok'{consumer_tag = CTag} = Msg}, State) -> + ok = send(Msg, State), + noreply(consumer_monitor(CTag, State)); -handle_cast({command, Msg}, State = #ch{writer_pid = WriterPid}) -> - ok = rabbit_writer:send_command(WriterPid, Msg), +handle_cast({command, Msg}, State) -> + ok = send(Msg, State), noreply(State); +handle_cast({deliver, _CTag, _AckReq, _Msg}, State = #ch{state = closing}) -> + noreply(State); handle_cast({deliver, ConsumerTag, AckRequired, Msg = {_QName, QPid, _MsgId, Redelivered, #basic_message{exchange_name = ExchangeName, @@ -401,6 +402,11 @@ return_ok(State, false, Msg) -> {reply, Msg, State}. ok_msg(true, _Msg) -> undefined; ok_msg(false, Msg) -> Msg. +send(_Command, #ch{state = closing}) -> + ok; +send(Command, #ch{writer_pid = WriterPid}) -> + ok = rabbit_writer:send_command(WriterPid, Command). + handle_exception(Reason, State = #ch{protocol = Protocol, channel = Channel, writer_pid = WriterPid, @@ -545,12 +551,10 @@ queue_blocked(QPid, State = #ch{blocking = Blocking}) -> case sets:is_element(QPid, Blocking) of false -> State; true -> Blocking1 = sets:del_element(QPid, Blocking), - ok = case sets:size(Blocking1) of - 0 -> rabbit_writer:send_command( - State#ch.writer_pid, - #'channel.flow_ok'{active = false}); - _ -> ok - end, + case sets:size(Blocking1) of + 0 -> ok = send(#'channel.flow_ok'{active = false}, State); + _ -> ok + end, State#ch{blocking = Blocking1} end. @@ -833,12 +837,9 @@ handle_method(#'basic.recover_async'{requeue = false}, _, _State) -> rabbit_misc:protocol_error(not_implemented, "requeue=false", []); handle_method(#'basic.recover'{requeue = Requeue}, Content, State) -> - {noreply, State2 = #ch{writer_pid = WriterPid}} = - handle_method(#'basic.recover_async'{requeue = Requeue}, - Content, - State), - ok = rabbit_writer:send_command(WriterPid, #'basic.recover_ok'{}), - {noreply, State2}; + {noreply, State1} = handle_method(#'basic.recover_async'{requeue = Requeue}, + Content, State), + {reply, #'basic.recover_ok'{}, State1}; handle_method(#'basic.reject'{delivery_tag = DeliveryTag, requeue = Requeue}, @@ -1145,17 +1146,16 @@ handle_publishing_queue_down(QPid, Reason, State = #ch{unconfirmed = UC}) -> handle_consuming_queue_down(QPid, State = #ch{consumer_mapping = ConsumerMapping, - queue_consumers = QCons, - writer_pid = WriterPid}) -> + queue_consumers = QCons}) -> ConsumerTags = case dict:find(QPid, QCons) of error -> gb_sets:new(); {ok, CTags} -> CTags end, ConsumerMapping1 = gb_sets:fold(fun (CTag, CMap) -> - Cancel = #'basic.cancel'{consumer_tag = CTag, - nowait = true}, - ok = rabbit_writer:send_command(WriterPid, Cancel), + ok = send(#'basic.cancel'{consumer_tag = CTag, + nowait = true}, + State), dict:erase(CTag, CMap) end, ConsumerMapping, ConsumerTags), State#ch{consumer_mapping = ConsumerMapping1, @@ -1368,12 +1368,17 @@ process_routing_result(routed, QPids, XName, MsgSeqNo, _, State) -> send_nacks([], State) -> State; +send_nacks(_MXs, State = #ch{state = closing, + tx_status = none}) -> %% optimisation + State; send_nacks(MXs, State = #ch{tx_status = none}) -> coalesce_and_send([MsgSeqNo || {MsgSeqNo, _} <- MXs], fun(MsgSeqNo, Multiple) -> #'basic.nack'{delivery_tag = MsgSeqNo, multiple = Multiple} end, State); +send_nacks(_MXs, State = #ch{state = closing}) -> %% optimisation + State#ch{tx_status = failed}; send_nacks(_, State) -> maybe_complete_tx(State#ch{tx_status = failed}). @@ -1392,9 +1397,10 @@ send_confirms(State) -> send_confirms([], State) -> State; -send_confirms([MsgSeqNo], State = #ch{writer_pid = WriterPid}) -> - ok = rabbit_writer:send_command(WriterPid, - #'basic.ack'{delivery_tag = MsgSeqNo}), +send_confirms(_Cs, State = #ch{state = closing}) -> %% optimisation + State; +send_confirms([MsgSeqNo], State) -> + ok = send(#'basic.ack'{delivery_tag = MsgSeqNo}, State), State; send_confirms(Cs, State) -> coalesce_and_send(Cs, fun(MsgSeqNo, Multiple) -> @@ -1402,8 +1408,7 @@ send_confirms(Cs, State) -> multiple = Multiple} end, State). -coalesce_and_send(MsgSeqNos, MkMsgFun, - State = #ch{writer_pid = WriterPid, unconfirmed = UC}) -> +coalesce_and_send(MsgSeqNos, MkMsgFun, State = #ch{unconfirmed = UC}) -> SMsgSeqNos = lists:usort(MsgSeqNos), CutOff = case dtree:is_empty(UC) of true -> lists:last(SMsgSeqNos) + 1; @@ -1412,11 +1417,9 @@ coalesce_and_send(MsgSeqNos, MkMsgFun, {Ms, Ss} = lists:splitwith(fun(X) -> X < CutOff end, SMsgSeqNos), case Ms of [] -> ok; - _ -> ok = rabbit_writer:send_command( - WriterPid, MkMsgFun(lists:last(Ms), true)) + _ -> ok = send(MkMsgFun(lists:last(Ms), true), State) end, - [ok = rabbit_writer:send_command( - WriterPid, MkMsgFun(SeqNo, false)) || SeqNo <- Ss], + [ok = send(MkMsgFun(SeqNo, false), State) || SeqNo <- Ss], State. maybe_complete_tx(State = #ch{tx_status = in_progress}) -> @@ -1428,7 +1431,7 @@ maybe_complete_tx(State = #ch{unconfirmed = UC}) -> end. complete_tx(State = #ch{tx_status = committing}) -> - ok = rabbit_writer:send_command(State#ch.writer_pid, #'tx.commit_ok'{}), + ok = send(#'tx.commit_ok'{}, State), State#ch{tx_status = in_progress}; complete_tx(State = #ch{tx_status = failed}) -> {noreply, State1} = handle_exception( -- cgit v1.2.1 From 19e9691700293806f0255efe0f2fda93ced1d312 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 22 Jan 2013 17:53:06 +0000 Subject: Call me sentimental, but reinstate the idea of an ASCII-art rabbit... --- src/rabbit.erl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 1900f794..641f81c0 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -696,7 +696,7 @@ log_broker_started(Plugins) -> fun() -> error_logger:info_msg( "Server startup complete; plugins are: ~p~n", [Plugins]), - io:format("~nBroker running with ~p plugins.~n", + io:format("~n Broker running with ~p plugins.~n", [length(Plugins)]) end). @@ -711,10 +711,10 @@ erts_version_check() -> print_banner() -> {ok, Product} = application:get_key(id), {ok, Version} = application:get_key(vsn), - io:format("~n~s ~s. ~s~n~s~n~n", + io:format("~n## ## ~s ~s. ~s~n## ## ~s~n########## ~n", [Product, Version, ?COPYRIGHT_MESSAGE, ?INFORMATION_MESSAGE]), - io:format("Logs: ~s~n ~s~n", [log_location(kernel), - log_location(sasl)]). + io:format("###### ## Logs: ~s~n########## ~s~n", + [log_location(kernel), log_location(sasl)]). log_banner() -> {ok, Product} = application:get_key(id), -- cgit v1.2.1 From 8e7c8fe9cc77cc63593f450f1a0fe3f99175ee64 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Wed, 23 Jan 2013 12:03:02 +0000 Subject: Update copyright 2013 --- LICENSE-MPL-RabbitMQ | 2 +- check_xref | 2 +- codegen.py | 4 ++-- include/gm_specs.hrl | 2 +- include/rabbit.hrl | 4 ++-- include/rabbit_msg_store.hrl | 2 +- packaging/common/LICENSE.tail | 4 ++-- packaging/common/rabbitmq-script-wrapper | 2 +- packaging/common/rabbitmq-server.ocf | 2 +- packaging/debs/Debian/Makefile | 2 +- packaging/windows-exe/rabbitmq_nsi.in | 2 +- scripts/rabbitmq-defaults | 2 +- scripts/rabbitmq-env | 2 +- scripts/rabbitmq-plugins | 2 +- scripts/rabbitmq-plugins.bat | 2 +- scripts/rabbitmq-server | 2 +- scripts/rabbitmq-server.bat | 2 +- scripts/rabbitmq-service.bat | 2 +- scripts/rabbitmqctl | 2 +- scripts/rabbitmqctl.bat | 2 +- src/app_utils.erl | 2 +- src/background_gc.erl | 2 +- src/credit_flow.erl | 2 +- src/delegate.erl | 2 +- src/delegate_sup.erl | 2 +- src/dtree.erl | 2 +- src/file_handle_cache.erl | 2 +- src/gatherer.erl | 2 +- src/gen_server2.erl | 2 +- src/gm.erl | 2 +- src/gm_soak_test.erl | 2 +- src/gm_speed_test.erl | 2 +- src/gm_tests.erl | 2 +- src/lqueue.erl | 2 +- src/mirrored_supervisor.erl | 2 +- src/mirrored_supervisor_tests.erl | 2 +- src/mnesia_sync.erl | 2 +- src/pg_local.erl | 2 +- src/pmon.erl | 2 +- src/priority_queue.erl | 2 +- src/rabbit.erl | 2 +- src/rabbit_access_control.erl | 2 +- src/rabbit_alarm.erl | 2 +- src/rabbit_amqqueue.erl | 2 +- src/rabbit_amqqueue_process.erl | 2 +- src/rabbit_amqqueue_sup.erl | 2 +- src/rabbit_auth_backend.erl | 2 +- src/rabbit_auth_backend_internal.erl | 2 +- src/rabbit_auth_mechanism.erl | 2 +- src/rabbit_auth_mechanism_amqplain.erl | 2 +- src/rabbit_auth_mechanism_cr_demo.erl | 2 +- src/rabbit_auth_mechanism_plain.erl | 2 +- src/rabbit_backing_queue.erl | 2 +- src/rabbit_backing_queue_qc.erl | 2 +- src/rabbit_basic.erl | 2 +- src/rabbit_binary_generator.erl | 2 +- src/rabbit_binary_parser.erl | 2 +- src/rabbit_binding.erl | 2 +- src/rabbit_channel.erl | 2 +- src/rabbit_channel_sup.erl | 2 +- src/rabbit_channel_sup_sup.erl | 2 +- src/rabbit_client_sup.erl | 2 +- src/rabbit_command_assembler.erl | 2 +- src/rabbit_connection_sup.erl | 2 +- src/rabbit_control_main.erl | 2 +- src/rabbit_direct.erl | 2 +- src/rabbit_disk_monitor.erl | 2 +- src/rabbit_error_logger.erl | 2 +- src/rabbit_error_logger_file_h.erl | 2 +- src/rabbit_event.erl | 2 +- src/rabbit_exchange.erl | 2 +- src/rabbit_exchange_decorator.erl | 2 +- src/rabbit_exchange_type.erl | 2 +- src/rabbit_exchange_type_direct.erl | 2 +- src/rabbit_exchange_type_fanout.erl | 2 +- src/rabbit_exchange_type_headers.erl | 2 +- src/rabbit_exchange_type_invalid.erl | 2 +- src/rabbit_exchange_type_topic.erl | 2 +- src/rabbit_file.erl | 2 +- src/rabbit_framing.erl | 2 +- src/rabbit_guid.erl | 2 +- src/rabbit_heartbeat.erl | 2 +- src/rabbit_limiter.erl | 2 +- src/rabbit_log.erl | 2 +- src/rabbit_memory_monitor.erl | 2 +- src/rabbit_mirror_queue_coordinator.erl | 2 +- src/rabbit_mirror_queue_master.erl | 2 +- src/rabbit_mirror_queue_misc.erl | 2 +- src/rabbit_mirror_queue_slave.erl | 2 +- src/rabbit_mirror_queue_slave_sup.erl | 2 +- src/rabbit_misc.erl | 2 +- src/rabbit_mnesia.erl | 2 +- src/rabbit_msg_file.erl | 2 +- src/rabbit_msg_store.erl | 2 +- src/rabbit_msg_store_ets_index.erl | 2 +- src/rabbit_msg_store_gc.erl | 2 +- src/rabbit_msg_store_index.erl | 2 +- src/rabbit_net.erl | 2 +- src/rabbit_networking.erl | 2 +- src/rabbit_node_monitor.erl | 2 +- src/rabbit_nodes.erl | 2 +- src/rabbit_parameter_validation.erl | 2 +- src/rabbit_plugins.erl | 2 +- src/rabbit_plugins_main.erl | 2 +- src/rabbit_policy.erl | 2 +- src/rabbit_policy_validator.erl | 2 +- src/rabbit_prelaunch.erl | 2 +- src/rabbit_queue_collector.erl | 2 +- src/rabbit_queue_index.erl | 2 +- src/rabbit_reader.erl | 2 +- src/rabbit_registry.erl | 2 +- src/rabbit_restartable_sup.erl | 2 +- src/rabbit_router.erl | 2 +- src/rabbit_runtime_parameter.erl | 2 +- src/rabbit_runtime_parameters.erl | 2 +- src/rabbit_runtime_parameters_test.erl | 2 +- src/rabbit_sasl_report_file_h.erl | 2 +- src/rabbit_ssl.erl | 2 +- src/rabbit_sup.erl | 2 +- src/rabbit_table.erl | 2 +- src/rabbit_tests.erl | 2 +- src/rabbit_tests_event_receiver.erl | 2 +- src/rabbit_trace.erl | 2 +- src/rabbit_types.erl | 2 +- src/rabbit_upgrade.erl | 2 +- src/rabbit_upgrade_functions.erl | 2 +- src/rabbit_variable_queue.erl | 2 +- src/rabbit_version.erl | 2 +- src/rabbit_vhost.erl | 2 +- src/rabbit_vm.erl | 2 +- src/rabbit_writer.erl | 2 +- src/supervisor2.erl | 2 +- src/supervisor2_tests.erl | 2 +- src/tcp_acceptor.erl | 2 +- src/tcp_acceptor_sup.erl | 2 +- src/tcp_listener.erl | 2 +- src/tcp_listener_sup.erl | 2 +- src/test_sup.erl | 2 +- src/vm_memory_monitor.erl | 2 +- src/worker_pool.erl | 2 +- src/worker_pool_sup.erl | 2 +- src/worker_pool_worker.erl | 2 +- 142 files changed, 145 insertions(+), 145 deletions(-) diff --git a/LICENSE-MPL-RabbitMQ b/LICENSE-MPL-RabbitMQ index d50e32ef..4cdf783b 100644 --- a/LICENSE-MPL-RabbitMQ +++ b/LICENSE-MPL-RabbitMQ @@ -447,7 +447,7 @@ EXHIBIT A -Mozilla Public License. The Original Code is RabbitMQ. The Initial Developer of the Original Code is VMware, Inc. - Copyright (c) 2007-2012 VMware, Inc. All rights reserved.'' + Copyright (c) 2007-2013 VMware, Inc. All rights reserved.'' [NOTE: The text of this Exhibit A may differ slightly from the text of the notices in the Source Code files of the Original Code. You should diff --git a/check_xref b/check_xref index 8f65f3b1..e0c049f8 100755 --- a/check_xref +++ b/check_xref @@ -15,7 +15,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2010-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2010-2013 VMware, Inc. All rights reserved. %% main(["-h"]) -> diff --git a/codegen.py b/codegen.py index 5624658b..bf6b70d5 100644 --- a/codegen.py +++ b/codegen.py @@ -11,7 +11,7 @@ ## The Original Code is RabbitMQ. ## ## The Initial Developer of the Original Code is VMware, Inc. -## Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +## Copyright (c) 2007-2013 VMware, Inc. All rights reserved. ## from __future__ import nested_scopes @@ -106,7 +106,7 @@ def printFileHeader(): %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %%""" def genErl(spec): diff --git a/include/gm_specs.hrl b/include/gm_specs.hrl index a317e63b..b3dd6615 100644 --- a/include/gm_specs.hrl +++ b/include/gm_specs.hrl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -ifdef(use_specs). diff --git a/include/rabbit.hrl b/include/rabbit.hrl index b2832b45..19766a00 100644 --- a/include/rabbit.hrl +++ b/include/rabbit.hrl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -record(user, {username, @@ -90,7 +90,7 @@ %%---------------------------------------------------------------------------- --define(COPYRIGHT_MESSAGE, "Copyright (C) 2007-2012 VMware, Inc."). +-define(COPYRIGHT_MESSAGE, "Copyright (C) 2007-2013 VMware, Inc."). -define(INFORMATION_MESSAGE, "Licensed under the MPL. See http://www.rabbitmq.com/"). -define(PROTOCOL_VERSION, "AMQP 0-9-1 / 0-9 / 0-8"). -define(ERTS_MINIMUM, "5.6.3"). diff --git a/include/rabbit_msg_store.hrl b/include/rabbit_msg_store.hrl index f7c10bd8..8665dc55 100644 --- a/include/rabbit_msg_store.hrl +++ b/include/rabbit_msg_store.hrl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -include("rabbit.hrl"). diff --git a/packaging/common/LICENSE.tail b/packaging/common/LICENSE.tail index b9c2629b..431ddeb0 100644 --- a/packaging/common/LICENSE.tail +++ b/packaging/common/LICENSE.tail @@ -56,7 +56,7 @@ The rest of this package is licensed under the Mozilla Public License 1.1 Authors and Copyright are as described below: The Initial Developer of the Original Code is VMware, Inc. - Copyright (c) 2007-2012 VMware, Inc. All rights reserved. + Copyright (c) 2007-2013 VMware, Inc. All rights reserved. MOZILLA PUBLIC LICENSE @@ -508,7 +508,7 @@ EXHIBIT A -Mozilla Public License. The Original Code is RabbitMQ. The Initial Developer of the Original Code is VMware, Inc. - Copyright (c) 2007-2012 VMware, Inc. All rights reserved.'' + Copyright (c) 2007-2013 VMware, Inc. All rights reserved.'' [NOTE: The text of this Exhibit A may differ slightly from the text of the notices in the Source Code files of the Original Code. You should diff --git a/packaging/common/rabbitmq-script-wrapper b/packaging/common/rabbitmq-script-wrapper index e832aed6..b9c6ffbf 100644 --- a/packaging/common/rabbitmq-script-wrapper +++ b/packaging/common/rabbitmq-script-wrapper @@ -12,7 +12,7 @@ ## The Original Code is RabbitMQ. ## ## The Initial Developer of the Original Code is VMware, Inc. -## Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +## Copyright (c) 2007-2013 VMware, Inc. All rights reserved. ## # Escape spaces and quotes, because shell is revolting. diff --git a/packaging/common/rabbitmq-server.ocf b/packaging/common/rabbitmq-server.ocf index 14557286..ba9579b6 100755 --- a/packaging/common/rabbitmq-server.ocf +++ b/packaging/common/rabbitmq-server.ocf @@ -12,7 +12,7 @@ ## The Original Code is RabbitMQ. ## ## The Initial Developer of the Original Code is VMware, Inc. -## Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +## Copyright (c) 2007-2013 VMware, Inc. All rights reserved. ## ## diff --git a/packaging/debs/Debian/Makefile b/packaging/debs/Debian/Makefile index 1e4bf755..c197915d 100644 --- a/packaging/debs/Debian/Makefile +++ b/packaging/debs/Debian/Makefile @@ -28,7 +28,7 @@ package: clean chmod a+x $(UNPACKED_DIR)/debian/rules echo "This package was debianized by Tony Garnock-Jones on\nWed, 3 Jan 2007 15:43:44 +0000.\n\nIt was downloaded from http://www.rabbitmq.com/\n\n" > $(UNPACKED_DIR)/debian/copyright cat $(UNPACKED_DIR)/LICENSE >> $(UNPACKED_DIR)/debian/copyright - echo "\n\nThe Debian packaging is (C) 2007-2012, VMware, Inc. and is licensed\nunder the MPL 1.1, see above.\n" >> $(UNPACKED_DIR)/debian/copyright + echo "\n\nThe Debian packaging is (C) 2007-2013, VMware, Inc. and is licensed\nunder the MPL 1.1, see above.\n" >> $(UNPACKED_DIR)/debian/copyright UNOFFICIAL_RELEASE=$(UNOFFICIAL_RELEASE) VERSION=$(VERSION) ./check-changelog.sh rabbitmq-server $(UNPACKED_DIR) cd $(UNPACKED_DIR); GNUPGHOME=$(GNUPG_PATH)/.gnupg dpkg-buildpackage -rfakeroot $(SIGNING) rm -rf $(UNPACKED_DIR) diff --git a/packaging/windows-exe/rabbitmq_nsi.in b/packaging/windows-exe/rabbitmq_nsi.in index f5257040..b351430e 100644 --- a/packaging/windows-exe/rabbitmq_nsi.in +++ b/packaging/windows-exe/rabbitmq_nsi.in @@ -37,7 +37,7 @@ VIAddVersionKey /LANG=${LANG_ENGLISH} "ProductName" "RabbitMQ Server" ;VIAddVersionKey /LANG=${LANG_ENGLISH} "Comments" "" VIAddVersionKey /LANG=${LANG_ENGLISH} "CompanyName" "VMware, Inc" ;VIAddVersionKey /LANG=${LANG_ENGLISH} "LegalTrademarks" "" ; TODO ? -VIAddVersionKey /LANG=${LANG_ENGLISH} "LegalCopyright" "Copyright (c) 2007-2012 VMware, Inc. All rights reserved." +VIAddVersionKey /LANG=${LANG_ENGLISH} "LegalCopyright" "Copyright (c) 2007-2013 VMware, Inc. All rights reserved." VIAddVersionKey /LANG=${LANG_ENGLISH} "FileDescription" "RabbitMQ Server" VIAddVersionKey /LANG=${LANG_ENGLISH} "FileVersion" "%%VERSION%%" diff --git a/scripts/rabbitmq-defaults b/scripts/rabbitmq-defaults index 4763f086..db1d4f2b 100644 --- a/scripts/rabbitmq-defaults +++ b/scripts/rabbitmq-defaults @@ -12,7 +12,7 @@ ## The Original Code is RabbitMQ. ## ## The Initial Developer of the Original Code is VMware, Inc. -## Copyright (c) 2012 VMware, Inc. All rights reserved. +## Copyright (c) 2012-2013 VMware, Inc. All rights reserved. ## ### next line potentially updated in package install steps diff --git a/scripts/rabbitmq-env b/scripts/rabbitmq-env index 23224943..3721f6c7 100755 --- a/scripts/rabbitmq-env +++ b/scripts/rabbitmq-env @@ -12,7 +12,7 @@ ## The Original Code is RabbitMQ. ## ## The Initial Developer of the Original Code is VMware, Inc. -## Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +## Copyright (c) 2007-2013 VMware, Inc. All rights reserved. ## # Determine where this script is really located (if this script is diff --git a/scripts/rabbitmq-plugins b/scripts/rabbitmq-plugins index 97c74791..43f450c0 100755 --- a/scripts/rabbitmq-plugins +++ b/scripts/rabbitmq-plugins @@ -12,7 +12,7 @@ ## The Original Code is RabbitMQ. ## ## The Initial Developer of the Original Code is VMware, Inc. -## Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +## Copyright (c) 2007-2013 VMware, Inc. All rights reserved. ## # Get default settings with user overrides for (RABBITMQ_) diff --git a/scripts/rabbitmq-plugins.bat b/scripts/rabbitmq-plugins.bat index 341f871a..713d7000 100755 --- a/scripts/rabbitmq-plugins.bat +++ b/scripts/rabbitmq-plugins.bat @@ -12,7 +12,7 @@ REM REM The Original Code is RabbitMQ. REM REM The Initial Developer of the Original Code is VMware, Inc. -REM Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +REM Copyright (c) 2007-2013 VMware, Inc. All rights reserved. REM setlocal diff --git a/scripts/rabbitmq-server b/scripts/rabbitmq-server index e1686627..184ae931 100755 --- a/scripts/rabbitmq-server +++ b/scripts/rabbitmq-server @@ -12,7 +12,7 @@ ## The Original Code is RabbitMQ. ## ## The Initial Developer of the Original Code is VMware, Inc. -## Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +## Copyright (c) 2007-2013 VMware, Inc. All rights reserved. ## # Get default settings with user overrides for (RABBITMQ_) diff --git a/scripts/rabbitmq-server.bat b/scripts/rabbitmq-server.bat index 3aea4c07..9fa304e6 100755 --- a/scripts/rabbitmq-server.bat +++ b/scripts/rabbitmq-server.bat @@ -12,7 +12,7 @@ REM REM The Original Code is RabbitMQ. REM REM The Initial Developer of the Original Code is VMware, Inc. -REM Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +REM Copyright (c) 2007-2013 VMware, Inc. All rights reserved. REM setlocal diff --git a/scripts/rabbitmq-service.bat b/scripts/rabbitmq-service.bat index 4758c861..9c30e74e 100755 --- a/scripts/rabbitmq-service.bat +++ b/scripts/rabbitmq-service.bat @@ -12,7 +12,7 @@ REM REM The Original Code is RabbitMQ. REM REM The Initial Developer of the Original Code is VMware, Inc. -REM Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +REM Copyright (c) 2007-2013 VMware, Inc. All rights reserved. REM setlocal diff --git a/scripts/rabbitmqctl b/scripts/rabbitmqctl index a5fade72..00fffa9f 100755 --- a/scripts/rabbitmqctl +++ b/scripts/rabbitmqctl @@ -12,7 +12,7 @@ ## The Original Code is RabbitMQ. ## ## The Initial Developer of the Original Code is VMware, Inc. -## Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +## Copyright (c) 2007-2013 VMware, Inc. All rights reserved. ## # Get default settings with user overrides for (RABBITMQ_) diff --git a/scripts/rabbitmqctl.bat b/scripts/rabbitmqctl.bat index d8b1eaf1..a6d85552 100755 --- a/scripts/rabbitmqctl.bat +++ b/scripts/rabbitmqctl.bat @@ -12,7 +12,7 @@ REM REM The Original Code is RabbitMQ. REM REM The Initial Developer of the Original Code is VMware, Inc. -REM Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +REM Copyright (c) 2007-2013 VMware, Inc. All rights reserved. REM setlocal diff --git a/src/app_utils.erl b/src/app_utils.erl index fdf6ed41..8da436c0 100644 --- a/src/app_utils.erl +++ b/src/app_utils.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(app_utils). diff --git a/src/background_gc.erl b/src/background_gc.erl index 3dbce330..d684d6ea 100644 --- a/src/background_gc.erl +++ b/src/background_gc.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(background_gc). diff --git a/src/credit_flow.erl b/src/credit_flow.erl index ba99811f..9ffaf247 100644 --- a/src/credit_flow.erl +++ b/src/credit_flow.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(credit_flow). diff --git a/src/delegate.erl b/src/delegate.erl index 9222c34c..b622dc6b 100644 --- a/src/delegate.erl +++ b/src/delegate.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(delegate). diff --git a/src/delegate_sup.erl b/src/delegate_sup.erl index 2a8b915b..30400b3e 100644 --- a/src/delegate_sup.erl +++ b/src/delegate_sup.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(delegate_sup). diff --git a/src/dtree.erl b/src/dtree.erl index ca2d30cf..45eea506 100644 --- a/src/dtree.erl +++ b/src/dtree.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% %% A dual-index tree. diff --git a/src/file_handle_cache.erl b/src/file_handle_cache.erl index 3260d369..d2d4d295 100644 --- a/src/file_handle_cache.erl +++ b/src/file_handle_cache.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(file_handle_cache). diff --git a/src/gatherer.erl b/src/gatherer.erl index 29d2d713..0c257a84 100644 --- a/src/gatherer.erl +++ b/src/gatherer.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(gatherer). diff --git a/src/gen_server2.erl b/src/gen_server2.erl index 78bbbe06..4056e3d9 100644 --- a/src/gen_server2.erl +++ b/src/gen_server2.erl @@ -73,7 +73,7 @@ %% but where the second argument is specifically the priority_queue %% which contains the prioritised message_queue. -%% All modifications are (C) 2009-2012 VMware, Inc. +%% All modifications are (C) 2009-2013 VMware, Inc. %% ``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 diff --git a/src/gm.erl b/src/gm.erl index 2057b1f5..76b535e6 100644 --- a/src/gm.erl +++ b/src/gm.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(gm). diff --git a/src/gm_soak_test.erl b/src/gm_soak_test.erl index 5fbfc223..1034ee2f 100644 --- a/src/gm_soak_test.erl +++ b/src/gm_soak_test.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(gm_soak_test). diff --git a/src/gm_speed_test.erl b/src/gm_speed_test.erl index 84d4ab2f..3fe3b182 100644 --- a/src/gm_speed_test.erl +++ b/src/gm_speed_test.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(gm_speed_test). diff --git a/src/gm_tests.erl b/src/gm_tests.erl index a9c0ba90..efb87a4c 100644 --- a/src/gm_tests.erl +++ b/src/gm_tests.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(gm_tests). diff --git a/src/lqueue.erl b/src/lqueue.erl index c4e046b5..e2ab2380 100644 --- a/src/lqueue.erl +++ b/src/lqueue.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2011-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2011-2013 VMware, Inc. All rights reserved. %% -module(lqueue). diff --git a/src/mirrored_supervisor.erl b/src/mirrored_supervisor.erl index 24c3ebd0..33d09f7f 100644 --- a/src/mirrored_supervisor.erl +++ b/src/mirrored_supervisor.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2011-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2011-2013 VMware, Inc. All rights reserved. %% -module(mirrored_supervisor). diff --git a/src/mirrored_supervisor_tests.erl b/src/mirrored_supervisor_tests.erl index f8cbd853..ea6b82c8 100644 --- a/src/mirrored_supervisor_tests.erl +++ b/src/mirrored_supervisor_tests.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2011-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2011-2013 VMware, Inc. All rights reserved. %% -module(mirrored_supervisor_tests). diff --git a/src/mnesia_sync.erl b/src/mnesia_sync.erl index a3773d90..41a349be 100644 --- a/src/mnesia_sync.erl +++ b/src/mnesia_sync.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(mnesia_sync). diff --git a/src/pg_local.erl b/src/pg_local.erl index e2e82f1f..7377fbf0 100644 --- a/src/pg_local.erl +++ b/src/pg_local.erl @@ -13,7 +13,7 @@ %% versions of Erlang/OTP. The remaining type specs have been %% removed. -%% All modifications are (C) 2010-2012 VMware, Inc. +%% All modifications are (C) 2010-2013 VMware, Inc. %% %CopyrightBegin% %% diff --git a/src/pmon.erl b/src/pmon.erl index 1aeebb72..54c3fc34 100644 --- a/src/pmon.erl +++ b/src/pmon.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2011-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2011-2013 VMware, Inc. All rights reserved. %% -module(pmon). diff --git a/src/priority_queue.erl b/src/priority_queue.erl index 780fa2e9..02a0a1df 100644 --- a/src/priority_queue.erl +++ b/src/priority_queue.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% %% Priority queues have essentially the same interface as ordinary diff --git a/src/rabbit.erl b/src/rabbit.erl index c3a6d283..fa8dc652 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit). diff --git a/src/rabbit_access_control.erl b/src/rabbit_access_control.erl index 75c53511..16387268 100644 --- a/src/rabbit_access_control.erl +++ b/src/rabbit_access_control.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_access_control). diff --git a/src/rabbit_alarm.erl b/src/rabbit_alarm.erl index d7d4d82a..25357b6e 100644 --- a/src/rabbit_alarm.erl +++ b/src/rabbit_alarm.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_alarm). diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 173f7648..07895ae3 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_amqqueue). diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index ab735be6..49fcf070 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_amqqueue_process). diff --git a/src/rabbit_amqqueue_sup.erl b/src/rabbit_amqqueue_sup.erl index a4305e5f..d7257a69 100644 --- a/src/rabbit_amqqueue_sup.erl +++ b/src/rabbit_amqqueue_sup.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_amqqueue_sup). diff --git a/src/rabbit_auth_backend.erl b/src/rabbit_auth_backend.erl index c9475efd..72f81707 100644 --- a/src/rabbit_auth_backend.erl +++ b/src/rabbit_auth_backend.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_auth_backend). diff --git a/src/rabbit_auth_backend_internal.erl b/src/rabbit_auth_backend_internal.erl index 7b9df81e..44231f7b 100644 --- a/src/rabbit_auth_backend_internal.erl +++ b/src/rabbit_auth_backend_internal.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_auth_backend_internal). diff --git a/src/rabbit_auth_mechanism.erl b/src/rabbit_auth_mechanism.erl index c7d74dc3..99e4468e 100644 --- a/src/rabbit_auth_mechanism.erl +++ b/src/rabbit_auth_mechanism.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_auth_mechanism). diff --git a/src/rabbit_auth_mechanism_amqplain.erl b/src/rabbit_auth_mechanism_amqplain.erl index c0d86cd1..1ed54fef 100644 --- a/src/rabbit_auth_mechanism_amqplain.erl +++ b/src/rabbit_auth_mechanism_amqplain.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_auth_mechanism_amqplain). diff --git a/src/rabbit_auth_mechanism_cr_demo.erl b/src/rabbit_auth_mechanism_cr_demo.erl index 5df1d5d7..e4494ab4 100644 --- a/src/rabbit_auth_mechanism_cr_demo.erl +++ b/src/rabbit_auth_mechanism_cr_demo.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_auth_mechanism_cr_demo). diff --git a/src/rabbit_auth_mechanism_plain.erl b/src/rabbit_auth_mechanism_plain.erl index 423170e1..5553a641 100644 --- a/src/rabbit_auth_mechanism_plain.erl +++ b/src/rabbit_auth_mechanism_plain.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_auth_mechanism_plain). diff --git a/src/rabbit_backing_queue.erl b/src/rabbit_backing_queue.erl index af660c60..5e13bc58 100644 --- a/src/rabbit_backing_queue.erl +++ b/src/rabbit_backing_queue.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_backing_queue). diff --git a/src/rabbit_backing_queue_qc.erl b/src/rabbit_backing_queue_qc.erl index b37fbb29..a028602c 100644 --- a/src/rabbit_backing_queue_qc.erl +++ b/src/rabbit_backing_queue_qc.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2011-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2011-2013 VMware, Inc. All rights reserved. %% -module(rabbit_backing_queue_qc). diff --git a/src/rabbit_basic.erl b/src/rabbit_basic.erl index 9bd1fad9..c42289c7 100644 --- a/src/rabbit_basic.erl +++ b/src/rabbit_basic.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_basic). diff --git a/src/rabbit_binary_generator.erl b/src/rabbit_binary_generator.erl index a333c1ce..05040485 100644 --- a/src/rabbit_binary_generator.erl +++ b/src/rabbit_binary_generator.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_binary_generator). diff --git a/src/rabbit_binary_parser.erl b/src/rabbit_binary_parser.erl index 53878d6a..9407dd2e 100644 --- a/src/rabbit_binary_parser.erl +++ b/src/rabbit_binary_parser.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_binary_parser). diff --git a/src/rabbit_binding.erl b/src/rabbit_binding.erl index 2d486651..6096e07b 100644 --- a/src/rabbit_binding.erl +++ b/src/rabbit_binding.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_binding). diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 2b9cffd4..c914a0b9 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_channel). diff --git a/src/rabbit_channel_sup.erl b/src/rabbit_channel_sup.erl index 42459833..8ea44a81 100644 --- a/src/rabbit_channel_sup.erl +++ b/src/rabbit_channel_sup.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_channel_sup). diff --git a/src/rabbit_channel_sup_sup.erl b/src/rabbit_channel_sup_sup.erl index 995c41fb..16fd08be 100644 --- a/src/rabbit_channel_sup_sup.erl +++ b/src/rabbit_channel_sup_sup.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_channel_sup_sup). diff --git a/src/rabbit_client_sup.erl b/src/rabbit_client_sup.erl index c508f1b9..9602c512 100644 --- a/src/rabbit_client_sup.erl +++ b/src/rabbit_client_sup.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_client_sup). diff --git a/src/rabbit_command_assembler.erl b/src/rabbit_command_assembler.erl index adf6e417..a88bec3d 100644 --- a/src/rabbit_command_assembler.erl +++ b/src/rabbit_command_assembler.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_command_assembler). diff --git a/src/rabbit_connection_sup.erl b/src/rabbit_connection_sup.erl index 12a532b6..cc29e41c 100644 --- a/src/rabbit_connection_sup.erl +++ b/src/rabbit_connection_sup.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_connection_sup). diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index 2b7061e8..6a00a0cb 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_control_main). diff --git a/src/rabbit_direct.erl b/src/rabbit_direct.erl index 689e5d83..53144f3f 100644 --- a/src/rabbit_direct.erl +++ b/src/rabbit_direct.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_direct). diff --git a/src/rabbit_disk_monitor.erl b/src/rabbit_disk_monitor.erl index 6330d555..b396b289 100644 --- a/src/rabbit_disk_monitor.erl +++ b/src/rabbit_disk_monitor.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_disk_monitor). diff --git a/src/rabbit_error_logger.erl b/src/rabbit_error_logger.erl index a9af2d8a..1360c82a 100644 --- a/src/rabbit_error_logger.erl +++ b/src/rabbit_error_logger.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_error_logger). diff --git a/src/rabbit_error_logger_file_h.erl b/src/rabbit_error_logger_file_h.erl index 042ab23c..3efc9c0c 100644 --- a/src/rabbit_error_logger_file_h.erl +++ b/src/rabbit_error_logger_file_h.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_error_logger_file_h). diff --git a/src/rabbit_event.erl b/src/rabbit_event.erl index 7d91b6fa..10f8ceb8 100644 --- a/src/rabbit_event.erl +++ b/src/rabbit_event.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_event). diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index a205b23d..2fba941f 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_exchange). diff --git a/src/rabbit_exchange_decorator.erl b/src/rabbit_exchange_decorator.erl index 08819427..befbc462 100644 --- a/src/rabbit_exchange_decorator.erl +++ b/src/rabbit_exchange_decorator.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_exchange_decorator). diff --git a/src/rabbit_exchange_type.erl b/src/rabbit_exchange_type.erl index c5583ffd..1fbcb2d8 100644 --- a/src/rabbit_exchange_type.erl +++ b/src/rabbit_exchange_type.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_exchange_type). diff --git a/src/rabbit_exchange_type_direct.erl b/src/rabbit_exchange_type_direct.erl index 9a5665c0..e54bd66e 100644 --- a/src/rabbit_exchange_type_direct.erl +++ b/src/rabbit_exchange_type_direct.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_exchange_type_direct). diff --git a/src/rabbit_exchange_type_fanout.erl b/src/rabbit_exchange_type_fanout.erl index d9a2f60f..870b327a 100644 --- a/src/rabbit_exchange_type_fanout.erl +++ b/src/rabbit_exchange_type_fanout.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_exchange_type_fanout). diff --git a/src/rabbit_exchange_type_headers.erl b/src/rabbit_exchange_type_headers.erl index 516b78e5..b185cc4a 100644 --- a/src/rabbit_exchange_type_headers.erl +++ b/src/rabbit_exchange_type_headers.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_exchange_type_headers). diff --git a/src/rabbit_exchange_type_invalid.erl b/src/rabbit_exchange_type_invalid.erl index 101fe434..ac6c4b31 100644 --- a/src/rabbit_exchange_type_invalid.erl +++ b/src/rabbit_exchange_type_invalid.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_exchange_type_invalid). diff --git a/src/rabbit_exchange_type_topic.erl b/src/rabbit_exchange_type_topic.erl index 644d9acf..70e32eaa 100644 --- a/src/rabbit_exchange_type_topic.erl +++ b/src/rabbit_exchange_type_topic.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_exchange_type_topic). diff --git a/src/rabbit_file.erl b/src/rabbit_file.erl index 26f74796..3ceb4989 100644 --- a/src/rabbit_file.erl +++ b/src/rabbit_file.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2011-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2011-2013 VMware, Inc. All rights reserved. %% -module(rabbit_file). diff --git a/src/rabbit_framing.erl b/src/rabbit_framing.erl index a79188ab..93305483 100644 --- a/src/rabbit_framing.erl +++ b/src/rabbit_framing.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% %% TODO auto-generate diff --git a/src/rabbit_guid.erl b/src/rabbit_guid.erl index cedbbdb3..d98baf2e 100644 --- a/src/rabbit_guid.erl +++ b/src/rabbit_guid.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_guid). diff --git a/src/rabbit_heartbeat.erl b/src/rabbit_heartbeat.erl index 05aad8c9..e878f3bb 100644 --- a/src/rabbit_heartbeat.erl +++ b/src/rabbit_heartbeat.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_heartbeat). diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index 2b15498e..8a7d14fe 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_limiter). diff --git a/src/rabbit_log.erl b/src/rabbit_log.erl index 8dfa89d3..74cdeb23 100644 --- a/src/rabbit_log.erl +++ b/src/rabbit_log.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_log). diff --git a/src/rabbit_memory_monitor.erl b/src/rabbit_memory_monitor.erl index f22ad874..117ff95a 100644 --- a/src/rabbit_memory_monitor.erl +++ b/src/rabbit_memory_monitor.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% diff --git a/src/rabbit_mirror_queue_coordinator.erl b/src/rabbit_mirror_queue_coordinator.erl index e1a21cf7..625e2f07 100644 --- a/src/rabbit_mirror_queue_coordinator.erl +++ b/src/rabbit_mirror_queue_coordinator.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2010-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2010-2013 VMware, Inc. All rights reserved. %% -module(rabbit_mirror_queue_coordinator). diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index df733546..6db6ce9d 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2010-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2010-2013 VMware, Inc. All rights reserved. %% -module(rabbit_mirror_queue_master). diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 58f20476..05036d35 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2010-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2010-2013 VMware, Inc. All rights reserved. %% -module(rabbit_mirror_queue_misc). diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 5fbda9c5..222457c6 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2010-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2010-2013 VMware, Inc. All rights reserved. %% -module(rabbit_mirror_queue_slave). diff --git a/src/rabbit_mirror_queue_slave_sup.erl b/src/rabbit_mirror_queue_slave_sup.erl index a2034876..be3924f0 100644 --- a/src/rabbit_mirror_queue_slave_sup.erl +++ b/src/rabbit_mirror_queue_slave_sup.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2010-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2010-2013 VMware, Inc. All rights reserved. %% -module(rabbit_mirror_queue_slave_sup). diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 4efde50e..21dbaeb5 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_misc). diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 6576ba52..c87feb22 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_mnesia). diff --git a/src/rabbit_msg_file.erl b/src/rabbit_msg_file.erl index f685b109..81111061 100644 --- a/src/rabbit_msg_file.erl +++ b/src/rabbit_msg_file.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_msg_file). diff --git a/src/rabbit_msg_store.erl b/src/rabbit_msg_store.erl index c2e55022..c2d8e06e 100644 --- a/src/rabbit_msg_store.erl +++ b/src/rabbit_msg_store.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_msg_store). diff --git a/src/rabbit_msg_store_ets_index.erl b/src/rabbit_msg_store_ets_index.erl index 3defeaaf..bbc7db68 100644 --- a/src/rabbit_msg_store_ets_index.erl +++ b/src/rabbit_msg_store_ets_index.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_msg_store_ets_index). diff --git a/src/rabbit_msg_store_gc.erl b/src/rabbit_msg_store_gc.erl index 3b61ed0b..3881de23 100644 --- a/src/rabbit_msg_store_gc.erl +++ b/src/rabbit_msg_store_gc.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_msg_store_gc). diff --git a/src/rabbit_msg_store_index.erl b/src/rabbit_msg_store_index.erl index 6cc0b2a7..f0096446 100644 --- a/src/rabbit_msg_store_index.erl +++ b/src/rabbit_msg_store_index.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_msg_store_index). diff --git a/src/rabbit_net.erl b/src/rabbit_net.erl index 562fc197..b8b03f56 100644 --- a/src/rabbit_net.erl +++ b/src/rabbit_net.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_net). diff --git a/src/rabbit_networking.erl b/src/rabbit_networking.erl index 31eeef73..0a0e51c5 100644 --- a/src/rabbit_networking.erl +++ b/src/rabbit_networking.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_networking). diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index 8d0e4456..c0b11799 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_node_monitor). diff --git a/src/rabbit_nodes.erl b/src/rabbit_nodes.erl index c8d77b0f..c92e5963 100644 --- a/src/rabbit_nodes.erl +++ b/src/rabbit_nodes.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_nodes). diff --git a/src/rabbit_parameter_validation.erl b/src/rabbit_parameter_validation.erl index 24762a73..39d0188c 100644 --- a/src/rabbit_parameter_validation.erl +++ b/src/rabbit_parameter_validation.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_parameter_validation). diff --git a/src/rabbit_plugins.erl b/src/rabbit_plugins.erl index 9f94af7d..bce4b43e 100644 --- a/src/rabbit_plugins.erl +++ b/src/rabbit_plugins.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2011-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2011-2013 VMware, Inc. All rights reserved. %% -module(rabbit_plugins). diff --git a/src/rabbit_plugins_main.erl b/src/rabbit_plugins_main.erl index 2158d1da..308b80cd 100644 --- a/src/rabbit_plugins_main.erl +++ b/src/rabbit_plugins_main.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2011-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2011-2013 VMware, Inc. All rights reserved. %% -module(rabbit_plugins_main). diff --git a/src/rabbit_policy.erl b/src/rabbit_policy.erl index fa13c5dd..e712078b 100644 --- a/src/rabbit_policy.erl +++ b/src/rabbit_policy.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_policy). diff --git a/src/rabbit_policy_validator.erl b/src/rabbit_policy_validator.erl index b59dec2b..75b88c39 100644 --- a/src/rabbit_policy_validator.erl +++ b/src/rabbit_policy_validator.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_policy_validator). diff --git a/src/rabbit_prelaunch.erl b/src/rabbit_prelaunch.erl index 404afe3c..3ce516d0 100644 --- a/src/rabbit_prelaunch.erl +++ b/src/rabbit_prelaunch.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_prelaunch). diff --git a/src/rabbit_queue_collector.erl b/src/rabbit_queue_collector.erl index 6dad01cc..521cd78b 100644 --- a/src/rabbit_queue_collector.erl +++ b/src/rabbit_queue_collector.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_queue_collector). diff --git a/src/rabbit_queue_index.erl b/src/rabbit_queue_index.erl index 21f58154..4559bb8a 100644 --- a/src/rabbit_queue_index.erl +++ b/src/rabbit_queue_index.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_queue_index). diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 928786e9..3a517677 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_reader). diff --git a/src/rabbit_registry.erl b/src/rabbit_registry.erl index 32709d24..60419856 100644 --- a/src/rabbit_registry.erl +++ b/src/rabbit_registry.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_registry). diff --git a/src/rabbit_restartable_sup.erl b/src/rabbit_restartable_sup.erl index 237ab78c..4c4ab2cf 100644 --- a/src/rabbit_restartable_sup.erl +++ b/src/rabbit_restartable_sup.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_restartable_sup). diff --git a/src/rabbit_router.erl b/src/rabbit_router.erl index f4bbda0f..2eaef9a7 100644 --- a/src/rabbit_router.erl +++ b/src/rabbit_router.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_router). diff --git a/src/rabbit_runtime_parameter.erl b/src/rabbit_runtime_parameter.erl index 18668049..8a237105 100644 --- a/src/rabbit_runtime_parameter.erl +++ b/src/rabbit_runtime_parameter.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_runtime_parameter). diff --git a/src/rabbit_runtime_parameters.erl b/src/rabbit_runtime_parameters.erl index 49060409..2615372c 100644 --- a/src/rabbit_runtime_parameters.erl +++ b/src/rabbit_runtime_parameters.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_runtime_parameters). diff --git a/src/rabbit_runtime_parameters_test.erl b/src/rabbit_runtime_parameters_test.erl index d4d7271e..c27f1b4a 100644 --- a/src/rabbit_runtime_parameters_test.erl +++ b/src/rabbit_runtime_parameters_test.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_runtime_parameters_test). diff --git a/src/rabbit_sasl_report_file_h.erl b/src/rabbit_sasl_report_file_h.erl index e8beecfe..566db9a9 100644 --- a/src/rabbit_sasl_report_file_h.erl +++ b/src/rabbit_sasl_report_file_h.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_sasl_report_file_h). diff --git a/src/rabbit_ssl.erl b/src/rabbit_ssl.erl index 22ff555f..b1238623 100644 --- a/src/rabbit_ssl.erl +++ b/src/rabbit_ssl.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_ssl). diff --git a/src/rabbit_sup.erl b/src/rabbit_sup.erl index f142d233..6a6b2feb 100644 --- a/src/rabbit_sup.erl +++ b/src/rabbit_sup.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_sup). diff --git a/src/rabbit_table.erl b/src/rabbit_table.erl index fa1c5bbd..d1c0bb1e 100644 --- a/src/rabbit_table.erl +++ b/src/rabbit_table.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_table). diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 1e02ff6b..87946888 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_tests). diff --git a/src/rabbit_tests_event_receiver.erl b/src/rabbit_tests_event_receiver.erl index 72c07b51..c52394c7 100644 --- a/src/rabbit_tests_event_receiver.erl +++ b/src/rabbit_tests_event_receiver.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_tests_event_receiver). diff --git a/src/rabbit_trace.erl b/src/rabbit_trace.erl index 3a5b96de..59e53be7 100644 --- a/src/rabbit_trace.erl +++ b/src/rabbit_trace.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_trace). diff --git a/src/rabbit_types.erl b/src/rabbit_types.erl index 5bc3d9f5..c6007061 100644 --- a/src/rabbit_types.erl +++ b/src/rabbit_types.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_types). diff --git a/src/rabbit_upgrade.erl b/src/rabbit_upgrade.erl index 455134da..fde0dbe1 100644 --- a/src/rabbit_upgrade.erl +++ b/src/rabbit_upgrade.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_upgrade). diff --git a/src/rabbit_upgrade_functions.erl b/src/rabbit_upgrade_functions.erl index 21fdcd66..457b1567 100644 --- a/src/rabbit_upgrade_functions.erl +++ b/src/rabbit_upgrade_functions.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_upgrade_functions). diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 6dc65bab..eb236f06 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_variable_queue). diff --git a/src/rabbit_version.erl b/src/rabbit_version.erl index 1cc7d6c8..f81a4021 100644 --- a/src/rabbit_version.erl +++ b/src/rabbit_version.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_version). diff --git a/src/rabbit_vhost.erl b/src/rabbit_vhost.erl index 297fa56f..18742ccf 100644 --- a/src/rabbit_vhost.erl +++ b/src/rabbit_vhost.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_vhost). diff --git a/src/rabbit_vm.erl b/src/rabbit_vm.erl index db674f91..b3e9ec66 100644 --- a/src/rabbit_vm.erl +++ b/src/rabbit_vm.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_vm). diff --git a/src/rabbit_writer.erl b/src/rabbit_writer.erl index 059d3839..2d15e6a2 100644 --- a/src/rabbit_writer.erl +++ b/src/rabbit_writer.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(rabbit_writer). diff --git a/src/supervisor2.erl b/src/supervisor2.erl index 5af38573..c98b528d 100644 --- a/src/supervisor2.erl +++ b/src/supervisor2.erl @@ -51,7 +51,7 @@ %% 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. +%% All modifications are (C) 2010-2013 VMware, Inc. %% %% %CopyrightBegin% %% diff --git a/src/supervisor2_tests.erl b/src/supervisor2_tests.erl index e42ded7b..f19a53e6 100644 --- a/src/supervisor2_tests.erl +++ b/src/supervisor2_tests.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2011-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2011-2013 VMware, Inc. All rights reserved. %% -module(supervisor2_tests). diff --git a/src/tcp_acceptor.erl b/src/tcp_acceptor.erl index 344196d7..0248f878 100644 --- a/src/tcp_acceptor.erl +++ b/src/tcp_acceptor.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(tcp_acceptor). diff --git a/src/tcp_acceptor_sup.erl b/src/tcp_acceptor_sup.erl index d8844441..61c747c9 100644 --- a/src/tcp_acceptor_sup.erl +++ b/src/tcp_acceptor_sup.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(tcp_acceptor_sup). diff --git a/src/tcp_listener.erl b/src/tcp_listener.erl index fb01c792..90e84f94 100644 --- a/src/tcp_listener.erl +++ b/src/tcp_listener.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(tcp_listener). diff --git a/src/tcp_listener_sup.erl b/src/tcp_listener_sup.erl index 9ee921b4..7f850dbc 100644 --- a/src/tcp_listener_sup.erl +++ b/src/tcp_listener_sup.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(tcp_listener_sup). diff --git a/src/test_sup.erl b/src/test_sup.erl index 7f4b5049..3342adb5 100644 --- a/src/test_sup.erl +++ b/src/test_sup.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(test_sup). diff --git a/src/vm_memory_monitor.erl b/src/vm_memory_monitor.erl index 5ce894a9..f70156b6 100644 --- a/src/vm_memory_monitor.erl +++ b/src/vm_memory_monitor.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% %% In practice Erlang shouldn't be allowed to grow to more than a half diff --git a/src/worker_pool.erl b/src/worker_pool.erl index c9ecccd6..3bdeb377 100644 --- a/src/worker_pool.erl +++ b/src/worker_pool.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(worker_pool). diff --git a/src/worker_pool_sup.erl b/src/worker_pool_sup.erl index ff356366..b9835f1e 100644 --- a/src/worker_pool_sup.erl +++ b/src/worker_pool_sup.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(worker_pool_sup). diff --git a/src/worker_pool_worker.erl b/src/worker_pool_worker.erl index 1ddcebb2..56e4b7b3 100644 --- a/src/worker_pool_worker.erl +++ b/src/worker_pool_worker.erl @@ -11,7 +11,7 @@ %% The Original Code is RabbitMQ. %% %% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% -module(worker_pool_worker). -- cgit v1.2.1 From 148232580f69c6436aada5644111f8fc2bdf0fe8 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Wed, 23 Jan 2013 16:19:50 +0000 Subject: Reject AMQP 1.0 TLS requests specifically --- src/rabbit_reader.erl | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index ae832749..39affb17 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -699,10 +699,13 @@ handle_input(handshake, <<"AMQP", 1, 1, 9, 1>>, State) -> start_connection({8, 0, 0}, rabbit_framing_amqp_0_8, State); %% ... and finally, the 1.0 spec is crystal clear! Note that the -%% TLS uses a different protocol number, and would go here. handle_input(handshake, <<"AMQP", 0, 1, 0, 0>>, State) -> become_1_0(amqp, {0, 1, 0, 0}, State); +%% 2 stands for TLS +handle_input(handshake, <<"AMQP", 2, 1, 0, 0>>, #v1{sock = Sock}) -> + refuse_1_0_connection(Sock, tls_request_refused); + %% 3 stands for "SASL" handle_input(handshake, <<"AMQP", 3, 1, 0, 0>>, State) -> become_1_0(sasl, {3, 1, 0, 0}, State); @@ -740,6 +743,10 @@ refuse_connection(Sock, Exception) -> ok = inet_op(fun () -> rabbit_net:send(Sock, <<"AMQP",0,0,9,1>>) end), throw(Exception). +refuse_1_0_connection(Sock, Exception) -> + ok = inet_op(fun () -> rabbit_net:send(Sock, <<"AMQP",0,1,0,0>>) end), + throw(Exception). + ensure_stats_timer(State = #v1{connection_state = running}) -> rabbit_event:ensure_stats_timer(State, #v1.stats_timer, emit_stats); ensure_stats_timer(State) -> -- cgit v1.2.1 From 5e5d65352238b22262e71031c75918c8299c7256 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 24 Jan 2013 11:35:17 +0000 Subject: improve/fix connection refusal - respond with a 0-9-1 header when the supplied header is not recognised *unless* it is a 1-0 header *and* the 1-0 plug-in is enabled - respond with a 1-0 header when a 1-0 header with an unsupported protocol id is supplied and the 1-0 plug-in is enabled - log a more informative error when a 1-0 header is supplied and the 1-0 plug-in is not enabled --- src/rabbit_reader.erl | 39 +++++++++++++++++---------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 39affb17..553e7172 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -699,16 +699,8 @@ handle_input(handshake, <<"AMQP", 1, 1, 9, 1>>, State) -> start_connection({8, 0, 0}, rabbit_framing_amqp_0_8, State); %% ... and finally, the 1.0 spec is crystal clear! Note that the -handle_input(handshake, <<"AMQP", 0, 1, 0, 0>>, State) -> - become_1_0(amqp, {0, 1, 0, 0}, State); - -%% 2 stands for TLS -handle_input(handshake, <<"AMQP", 2, 1, 0, 0>>, #v1{sock = Sock}) -> - refuse_1_0_connection(Sock, tls_request_refused); - -%% 3 stands for "SASL" -handle_input(handshake, <<"AMQP", 3, 1, 0, 0>>, State) -> - become_1_0(sasl, {3, 1, 0, 0}, State); +handle_input(handshake, <<"AMQP", Id, 1, 0, 0>>, State) -> + become_1_0(Id, State); handle_input(handshake, <<"AMQP", A, B, C, D>>, #v1{sock = Sock}) -> refuse_connection(Sock, {bad_version, {A, B, C, D}}); @@ -739,13 +731,12 @@ start_connection({ProtocolMajor, ProtocolMinor, _ProtocolRevision}, connection_state = starting}, frame_header, 7). -refuse_connection(Sock, Exception) -> - ok = inet_op(fun () -> rabbit_net:send(Sock, <<"AMQP",0,0,9,1>>) end), +refuse_connection(Sock, Exception, {A, B, C, D}) -> + ok = inet_op(fun () -> rabbit_net:send(Sock, <<"AMQP",A,B,C,D>>) end), throw(Exception). -refuse_1_0_connection(Sock, Exception) -> - ok = inet_op(fun () -> rabbit_net:send(Sock, <<"AMQP",0,1,0,0>>) end), - throw(Exception). +refuse_connection(Sock, Exception) -> + refuse_connection(Sock, Exception, {0, 0, 9, 1}). ensure_stats_timer(State = #v1{connection_state = running}) -> rabbit_event:ensure_stats_timer(State, #v1.stats_timer, emit_stats); @@ -1015,15 +1006,19 @@ emit_stats(State) -> %% 1.0 stub -ifdef(use_specs). --spec(become_1_0/3 :: ('amqp' | 'sasl', - {non_neg_integer(), non_neg_integer(), - non_neg_integer(), non_neg_integer()}, - #v1{}) -> no_return()). +-spec(become_1_0/2 :: (non_neg_integer(), #v1{}) -> no_return()). -endif. -become_1_0(Mode, Version, State = #v1{sock = Sock}) -> +become_1_0(Id, State = #v1{sock = Sock}) -> case code:is_loaded(rabbit_amqp1_0_reader) of - false -> refuse_connection(Sock, {bad_version, Version}); - _ -> throw({become, {rabbit_amqp1_0_reader, become, + false -> refuse_connection(Sock, amqp1_0_plugin_not_enabled); + _ -> Mode = case Id of + 0 -> amqp; + 2 -> sasl; + _ -> refuse_connection( + Sock, {unsupported_amqp1_0_protocol_id, Id}, + {0, 1, 0, 0}) + end, + throw({become, {rabbit_amqp1_0_reader, become, [Mode, pack_for_1_0(State)]}}) end. -- cgit v1.2.1 From 528556fa93c522c12c10e761d4aea0e7fb0bbde2 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 24 Jan 2013 12:37:39 +0000 Subject: add some tests for connection refusal some of this is also tested in the Java client, but I'd rather have it all in one place here. --- src/rabbit_tests.erl | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 87fc6078..95e23d29 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -62,6 +62,7 @@ all_tests() -> passed = test_runtime_parameters(), passed = test_policy_validation(), passed = test_server_status(), + passed = test_amqp_connection_refusal(), passed = test_confirms(), passed = do_if_secondary_node( @@ -1143,10 +1144,7 @@ test_server_status() -> rabbit_misc:r(<<"/">>, queue, <<"foo">>)), %% list connections - [#listener{host = H, port = P} | _] = - [L || L = #listener{node = N} <- rabbit_networking:active_listeners(), - N =:= node()], - + {H, P} = find_listener(), {ok, C} = gen_tcp:connect(H, P, []), gen_tcp:send(C, <<"AMQP", 0, 0, 9, 1>>), timer:sleep(100), @@ -1185,6 +1183,25 @@ test_server_status() -> passed. +test_amqp_connection_refusal() -> + [passed = test_amqp_connection_refusal(V) || + V <- [<<"AMQP",9,9,9,9>>, <<"AMQP",0,1,0,0>>, <<"XXXX",0,0,9,1>>]], + passed. + +test_amqp_connection_refusal(Header) -> + {H, P} = find_listener(), + {ok, C} = gen_tcp:connect(H, P, [binary, {active, false}]), + ok = gen_tcp:send(C, Header), + {ok, <<"AMQP",0,0,9,1>>} = gen_tcp:recv(C, 8, 100), + ok = gen_tcp:close(C), + passed. + +find_listener() -> + [#listener{host = H, port = P} | _] = + [L || L = #listener{node = N} <- rabbit_networking:active_listeners(), + N =:= node()], + {H, P}. + test_writer(Pid) -> receive shutdown -> ok; -- cgit v1.2.1 From 693e2aab0699c7860141e46e1283b012aff44cf1 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Thu, 24 Jan 2013 12:50:00 +0000 Subject: Quick patch to backing queue quickcheck correcting fold fun arity --- src/rabbit_backing_queue_qc.erl | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/rabbit_backing_queue_qc.erl b/src/rabbit_backing_queue_qc.erl index 5b3b8aa8..5feaee46 100644 --- a/src/rabbit_backing_queue_qc.erl +++ b/src/rabbit_backing_queue_qc.erl @@ -334,7 +334,7 @@ postcondition(S, {call, ?BQMOD, fold, [FoldFun, Acc0, _BQ0]}, {Res, _BQ1}) -> {_, Model} = lists:foldl(fun ({_SeqId, {_MsgProps, _Msg}}, {stop, Acc}) -> {stop, Acc}; ({_SeqId, {MsgProps, Msg}}, {cont, Acc}) -> - FoldFun(Msg, MsgProps, Acc) + FoldFun(Msg, MsgProps, false, Acc) end, {cont, Acc0}, gb_trees:to_list(Messages)), true = Model =:= Res; @@ -397,10 +397,11 @@ rand_choice(List, Selection, N) -> N - 1). makefoldfun(Size) -> - fun (Msg, _MsgProps, Acc) -> - case length(Acc) > Size of - false -> {cont, [Msg | Acc]}; - true -> {stop, Acc} + fun (Msg, _MsgProps, Unacked, Acc) -> + case {length(Acc) > Size, Unacked} of + {false, false} -> {cont, [Msg | Acc]}; + {false, true} -> {cont, Acc}; + {true, _} -> {stop, Acc} end end. foldacc() -> []. -- cgit v1.2.1 From 50902853b2f18aa9c53271da41aad0e269533207 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 24 Jan 2013 13:01:30 +0000 Subject: recommend sasl --- src/rabbit_reader.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 553e7172..6d936bbb 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -1016,7 +1016,7 @@ become_1_0(Id, State = #v1{sock = Sock}) -> 2 -> sasl; _ -> refuse_connection( Sock, {unsupported_amqp1_0_protocol_id, Id}, - {0, 1, 0, 0}) + {2, 1, 0, 0}) end, throw({become, {rabbit_amqp1_0_reader, become, [Mode, pack_for_1_0(State)]}}) -- cgit v1.2.1 From 7424bac1a36e1d9512a32edcd1b0bd0b93ab31fc Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 24 Jan 2013 13:04:19 +0000 Subject: s/become/init --- src/rabbit_reader.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 6d936bbb..30ea6a5b 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -1018,7 +1018,7 @@ become_1_0(Id, State = #v1{sock = Sock}) -> Sock, {unsupported_amqp1_0_protocol_id, Id}, {2, 1, 0, 0}) end, - throw({become, {rabbit_amqp1_0_reader, become, + throw({become, {rabbit_amqp1_0_reader, init, [Mode, pack_for_1_0(State)]}}) end. -- cgit v1.2.1 From b7fd5abd547456ae07a8990d477e7821dbaab2bc Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 24 Jan 2013 13:09:35 +0000 Subject: improved connection refusal logic / error message plus some tests to go with that And a tweak to the "become 1.0" API --- src/rabbit_reader.erl | 34 ++++++++++++++++++---------------- src/rabbit_tests.erl | 25 +++++++++++++++++++++---- 2 files changed, 39 insertions(+), 20 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index ae832749..30ea6a5b 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -699,13 +699,8 @@ handle_input(handshake, <<"AMQP", 1, 1, 9, 1>>, State) -> start_connection({8, 0, 0}, rabbit_framing_amqp_0_8, State); %% ... and finally, the 1.0 spec is crystal clear! Note that the -%% TLS uses a different protocol number, and would go here. -handle_input(handshake, <<"AMQP", 0, 1, 0, 0>>, State) -> - become_1_0(amqp, {0, 1, 0, 0}, State); - -%% 3 stands for "SASL" -handle_input(handshake, <<"AMQP", 3, 1, 0, 0>>, State) -> - become_1_0(sasl, {3, 1, 0, 0}, State); +handle_input(handshake, <<"AMQP", Id, 1, 0, 0>>, State) -> + become_1_0(Id, State); handle_input(handshake, <<"AMQP", A, B, C, D>>, #v1{sock = Sock}) -> refuse_connection(Sock, {bad_version, {A, B, C, D}}); @@ -736,10 +731,13 @@ start_connection({ProtocolMajor, ProtocolMinor, _ProtocolRevision}, connection_state = starting}, frame_header, 7). -refuse_connection(Sock, Exception) -> - ok = inet_op(fun () -> rabbit_net:send(Sock, <<"AMQP",0,0,9,1>>) end), +refuse_connection(Sock, Exception, {A, B, C, D}) -> + ok = inet_op(fun () -> rabbit_net:send(Sock, <<"AMQP",A,B,C,D>>) end), throw(Exception). +refuse_connection(Sock, Exception) -> + refuse_connection(Sock, Exception, {0, 0, 9, 1}). + ensure_stats_timer(State = #v1{connection_state = running}) -> rabbit_event:ensure_stats_timer(State, #v1.stats_timer, emit_stats); ensure_stats_timer(State) -> @@ -1008,15 +1006,19 @@ emit_stats(State) -> %% 1.0 stub -ifdef(use_specs). --spec(become_1_0/3 :: ('amqp' | 'sasl', - {non_neg_integer(), non_neg_integer(), - non_neg_integer(), non_neg_integer()}, - #v1{}) -> no_return()). +-spec(become_1_0/2 :: (non_neg_integer(), #v1{}) -> no_return()). -endif. -become_1_0(Mode, Version, State = #v1{sock = Sock}) -> +become_1_0(Id, State = #v1{sock = Sock}) -> case code:is_loaded(rabbit_amqp1_0_reader) of - false -> refuse_connection(Sock, {bad_version, Version}); - _ -> throw({become, {rabbit_amqp1_0_reader, become, + false -> refuse_connection(Sock, amqp1_0_plugin_not_enabled); + _ -> Mode = case Id of + 0 -> amqp; + 2 -> sasl; + _ -> refuse_connection( + Sock, {unsupported_amqp1_0_protocol_id, Id}, + {2, 1, 0, 0}) + end, + throw({become, {rabbit_amqp1_0_reader, init, [Mode, pack_for_1_0(State)]}}) end. diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index b0ff5af9..a2442f17 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -61,6 +61,7 @@ all_tests() -> passed = test_runtime_parameters(), passed = test_policy_validation(), passed = test_server_status(), + passed = test_amqp_connection_refusal(), passed = test_confirms(), passed = do_if_secondary_node( @@ -1119,10 +1120,7 @@ test_server_status() -> rabbit_misc:r(<<"/">>, queue, <<"foo">>)), %% list connections - [#listener{host = H, port = P} | _] = - [L || L = #listener{node = N} <- rabbit_networking:active_listeners(), - N =:= node()], - + {H, P} = find_listener(), {ok, C} = gen_tcp:connect(H, P, []), gen_tcp:send(C, <<"AMQP", 0, 0, 9, 1>>), timer:sleep(100), @@ -1161,6 +1159,25 @@ test_server_status() -> passed. +test_amqp_connection_refusal() -> + [passed = test_amqp_connection_refusal(V) || + V <- [<<"AMQP",9,9,9,9>>, <<"AMQP",0,1,0,0>>, <<"XXXX",0,0,9,1>>]], + passed. + +test_amqp_connection_refusal(Header) -> + {H, P} = find_listener(), + {ok, C} = gen_tcp:connect(H, P, [binary, {active, false}]), + ok = gen_tcp:send(C, Header), + {ok, <<"AMQP",0,0,9,1>>} = gen_tcp:recv(C, 8, 100), + ok = gen_tcp:close(C), + passed. + +find_listener() -> + [#listener{host = H, port = P} | _] = + [L || L = #listener{node = N} <- rabbit_networking:active_listeners(), + N =:= node()], + {H, P}. + test_writer() -> test_writer(none). test_writer(Pid) -> -- cgit v1.2.1 From ec6fe12865e04cb44afca477bfd441268c497378 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 24 Jan 2013 15:15:06 +0000 Subject: Don't just {reply, Cmd, State} since that will end up going into send/2 - which will decide not to send... --- src/rabbit_channel.erl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index f3a2eb92..e0f7b3f9 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -587,8 +587,10 @@ handle_method(_Method, _, #ch{state = starting}) -> handle_method(#'channel.close_ok'{}, _, #ch{state = closing}) -> stop; -handle_method(#'channel.close'{}, _, State = #ch{state = closing}) -> - {reply, #'channel.close_ok'{}, State}; +handle_method(#'channel.close'{}, _, State = #ch{writer_pid = WriterPid, + state = closing}) -> + ok = rabbit_writer:send_command(WriterPid, #'channel.close_ok'{}), + {noreply, State}; handle_method(_Method, _, State = #ch{state = closing}) -> {noreply, State}; -- cgit v1.2.1 From 16ce962c5081a0a101458626bc777513af7b49ae Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 24 Jan 2013 19:10:26 +0000 Subject: nuke active_consumer_count --- src/rabbit_amqqueue_process.erl | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 6ca6399a..fe3a6099 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -94,7 +94,6 @@ messages_unacknowledged, messages, consumers, - active_consumers, memory, slave_pids, synchronised_slave_pids, @@ -665,13 +664,8 @@ check_exclusive_access(none, true, State) -> false -> in_use end. -consumer_count() -> consumer_count(fun (_) -> false end). - -active_consumer_count() -> consumer_count(fun is_ch_blocked/1). - -consumer_count(Exclude) -> - lists:sum([Count || C = #cr{consumer_count = Count} <- all_ch_record(), - not Exclude(C)]). +consumer_count() -> + lists:sum([Count || #cr{consumer_count = Count} <- all_ch_record()]). is_unused(_State) -> consumer_count() == 0. @@ -922,8 +916,6 @@ i(messages, State) -> messages_unacknowledged]]); i(consumers, _) -> consumer_count(); -i(active_consumers, _) -> - active_consumer_count(); i(memory, _) -> {memory, M} = process_info(self(), memory), M; @@ -1141,7 +1133,7 @@ handle_call({basic_cancel, ChPid, ConsumerTag, OkMsg}, From, handle_call(stat, _From, State) -> State1 = #q{backing_queue = BQ, backing_queue_state = BQS} = drop_expired_msgs(ensure_expiry_timer(State)), - reply({ok, BQ:len(BQS), active_consumer_count()}, State1); + reply({ok, BQ:len(BQS), consumer_count()}, State1); handle_call({delete, IfUnused, IfEmpty}, From, State = #q{backing_queue_state = BQS, backing_queue = BQ}) -> -- cgit v1.2.1 From ccd04a74a4780152d42cc0dce4abe9916fd7fdd7 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Fri, 25 Jan 2013 15:41:15 +0000 Subject: Swap SASL and TLS header codes --- src/rabbit_reader.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 8cbbc7c9..af7aac6f 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -1013,10 +1013,10 @@ become_1_0(Id, State = #v1{sock = Sock}) -> false -> refuse_connection(Sock, amqp1_0_plugin_not_enabled); _ -> Mode = case Id of 0 -> amqp; - 2 -> sasl; + 3 -> sasl; _ -> refuse_connection( Sock, {unsupported_amqp1_0_protocol_id, Id}, - {2, 1, 0, 0}) + {3, 1, 0, 0}) end, throw({become, {rabbit_amqp1_0_reader, init, [Mode, pack_for_1_0(State)]}}) -- cgit v1.2.1 From 45f7849be58f78a23e28061fcace8bfdbdaa1e9a Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Fri, 25 Jan 2013 15:47:22 +0000 Subject: Swap SASL and TLS header codes --- src/rabbit_reader.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 8cbbc7c9..af7aac6f 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -1013,10 +1013,10 @@ become_1_0(Id, State = #v1{sock = Sock}) -> false -> refuse_connection(Sock, amqp1_0_plugin_not_enabled); _ -> Mode = case Id of 0 -> amqp; - 2 -> sasl; + 3 -> sasl; _ -> refuse_connection( Sock, {unsupported_amqp1_0_protocol_id, Id}, - {2, 1, 0, 0}) + {3, 1, 0, 0}) end, throw({become, {rabbit_amqp1_0_reader, init, [Mode, pack_for_1_0(State)]}}) -- 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(-) 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(-) 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(-) 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 7474b1ba1fe2cb32d56c8fdbf06427db3979b784 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 28 Jan 2013 13:01:27 +0000 Subject: eliminate superfluous double call --- src/rabbit_mnesia.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index c87feb22..423c325b 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -335,7 +335,7 @@ status() -> IfNonEmpty(ram, cluster_nodes(ram)))}] ++ case mnesia:system_info(is_running) of yes -> RunningNodes = cluster_nodes(running), - [{running_nodes, cluster_nodes(running)}, + [{running_nodes, RunningNodes}, {partitions, mnesia_partitions(RunningNodes)}]; no -> [] end. -- cgit v1.2.1 From 471c6b1c64dfa9dbb4466f23b404b977e5a88f8e Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 28 Jan 2013 13:19:47 +0000 Subject: remove unused function --- src/rabbit_mnesia.erl | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 423c325b..aca32f3c 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -41,10 +41,7 @@ ]). %% Used internally in rpc calls --export([node_info/0, - remove_node_if_mnesia_running/1, - is_running_remote/0 - ]). +-export([node_info/0, remove_node_if_mnesia_running/1]). -include("rabbit.hrl"). @@ -734,8 +731,6 @@ change_extra_db_nodes(ClusterNodes0, CheckOtherNodes) -> Nodes end. -is_running_remote() -> {mnesia:system_info(is_running) =:= yes, node()}. - check_consistency(OTP, Rabbit) -> rabbit_misc:sequence_error( [check_otp_consistency(OTP), check_rabbit_consistency(Rabbit)]). -- cgit v1.2.1 From 7e3540f38cdd75edb7213a9e72ac5596f310f5f2 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 28 Jan 2013 13:35:18 +0000 Subject: handle all returns of mnesia:system_info(is_running) --- src/rabbit_mnesia.erl | 48 +++++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index aca32f3c..c7157753 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -274,16 +274,16 @@ forget_cluster_node(Node, RemoveWhenOffline) -> true -> ok; false -> e(not_a_cluster_node) end, - case {RemoveWhenOffline, mnesia:system_info(is_running)} of - {true, no} -> remove_node_offline_node(Node); - {true, yes} -> e(online_node_offline_flag); - {false, no} -> e(offline_node_no_offline_flag); - {false, yes} -> rabbit_misc:local_info_msg( - "Removing node ~p from cluster~n", [Node]), - case remove_node_if_mnesia_running(Node) of - ok -> ok; - {error, _} = Err -> throw(Err) - end + case {RemoveWhenOffline, is_running()} of + {true, false} -> remove_node_offline_node(Node); + {true, true} -> e(online_node_offline_flag); + {false, false} -> e(offline_node_no_offline_flag); + {false, true} -> rabbit_misc:local_info_msg( + "Removing node ~p from cluster~n", [Node]), + case remove_node_if_mnesia_running(Node) of + ok -> ok; + {error, _} = Err -> throw(Err) + end end. remove_node_offline_node(Node) -> @@ -330,11 +330,11 @@ status() -> end, [{nodes, (IfNonEmpty(disc, cluster_nodes(disc)) ++ IfNonEmpty(ram, cluster_nodes(ram)))}] ++ - case mnesia:system_info(is_running) of - yes -> RunningNodes = cluster_nodes(running), - [{running_nodes, RunningNodes}, - {partitions, mnesia_partitions(RunningNodes)}]; - no -> [] + case is_running() of + true -> RunningNodes = cluster_nodes(running), + [{running_nodes, RunningNodes}, + {partitions, mnesia_partitions(RunningNodes)}]; + false -> [] end. mnesia_partitions(Nodes) -> @@ -342,6 +342,8 @@ mnesia_partitions(Nodes) -> Nodes, rabbit_node_monitor, partitions, []), [Reply || Reply = {_, R} <- Replies, R =/= []]. +is_running() -> mnesia:system_info(is_running) =:= yes. + is_clustered() -> AllNodes = cluster_nodes(all), AllNodes =/= [] andalso AllNodes =/= [node()]. @@ -351,10 +353,10 @@ cluster_nodes(WhichNodes) -> cluster_status(WhichNodes). %% the data from mnesia. Obviously it'll work only when mnesia is %% running. cluster_status_from_mnesia() -> - case mnesia:system_info(is_running) of - no -> + case is_running() of + false -> {error, mnesia_not_running}; - yes -> + true -> %% If the tables are not present, it means that %% `init_db/3' hasn't been run yet. In other words, either %% we are a virgin node or a restarted RAM node. In both @@ -668,8 +670,10 @@ move_db() -> ok. remove_node_if_mnesia_running(Node) -> - case mnesia:system_info(is_running) of - yes -> + case is_running() of + false -> + {error, mnesia_not_running}; + true -> %% Deleting the the schema copy of the node will result in %% the node being removed from the cluster, with that %% change being propagated to all nodes @@ -679,9 +683,7 @@ remove_node_if_mnesia_running(Node) -> ok; {aborted, Reason} -> {error, {failed_to_remove_node, Node, Reason}} - end; - no -> - {error, mnesia_not_running} + end end. leave_cluster() -> -- cgit v1.2.1 From 4d69fda342a85fb5e458128f6e01db1feba1fc00 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 28 Jan 2013 16:17:44 +0000 Subject: ignore 'invoke' on different module rather than exploding. --- src/rabbit_variable_queue.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index eb236f06..1acc9ef0 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -809,7 +809,8 @@ status(#vqstate { {avg_ack_ingress_rate, AvgAckIngressRate}, {avg_ack_egress_rate , AvgAckEgressRate} ]. -invoke(?MODULE, Fun, State) -> Fun(?MODULE, State). +invoke(?MODULE, Fun, State) -> Fun(?MODULE, State); +invoke( _, _, State) -> State. is_duplicate(_Msg, State) -> {false, State}. -- cgit v1.2.1 From 90ddce2d53a84bcddfad4c4ab7242e943ee518d2 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 28 Jan 2013 20:48:06 +0000 Subject: single io:format in order to prevent output interleaving --- src/rabbit.erl | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index c9cf7ea4..6b730fda 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -716,10 +716,13 @@ erts_version_check() -> print_banner() -> {ok, Product} = application:get_key(id), {ok, Version} = application:get_key(vsn), - io:format("~n## ## ~s ~s. ~s~n## ## ~s~n########## ~n", - [Product, Version, ?COPYRIGHT_MESSAGE, ?INFORMATION_MESSAGE]), - io:format("###### ## Logs: ~s~n########## ~s~n", - [log_location(kernel), log_location(sasl)]). + io:format("~n## ## ~s ~s. ~s" + "~n## ## ~s" + "~n##########" + "~n###### ## Logs: ~s" + "~n########## ~s~n", + [Product, Version, ?COPYRIGHT_MESSAGE, ?INFORMATION_MESSAGE, + log_location(kernel), log_location(sasl)]). log_banner() -> {ok, Product} = application:get_key(id), -- cgit v1.2.1 From 55eeddf70f52604f35b4ec98fdba5827d54631f9 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 29 Jan 2013 13:39:03 +0000 Subject: Get rid of credit_map, allow initial credit setting through an argument to basic.consume. --- src/rabbit_channel.erl | 47 ++++++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 56a10676..dae21389 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -39,7 +39,7 @@ queue_names, queue_monitors, consumer_mapping, blocking, queue_consumers, delivering_queues, queue_collector_pid, stats_timer, confirm_enabled, publish_seqno, - unconfirmed, confirmed, capabilities, trace_state, credit_map}). + unconfirmed, confirmed, capabilities, trace_state}). -define(MAX_PERMISSION_CACHE_SIZE, 12). @@ -219,8 +219,7 @@ init([Channel, ReaderPid, WriterPid, ConnPid, ConnName, Protocol, User, VHost, unconfirmed = dtree:empty(), confirmed = [], capabilities = Capabilities, - trace_state = rabbit_trace:init(VHost), - credit_map = dict:new()}, + trace_state = rabbit_trace:init(VHost)}, State1 = rabbit_event:init_stats_timer(State, #ch.stats_timer), rabbit_event:notify(channel_created, infos(?CREATION_EVENT_KEYS, State1)), rabbit_event:if_enabled(State1, #ch.stats_timer, @@ -720,11 +719,11 @@ handle_method(#'basic.consume'{queue = QueueNameBin, no_local = _, % FIXME: implement no_ack = NoAck, exclusive = ExclusiveConsume, - nowait = NoWait}, + nowait = NoWait, + arguments = Args}, _, State = #ch{conn_pid = ConnPid, limiter = Limiter, - consumer_mapping = ConsumerMapping, - credit_map = CreditMap}) -> + consumer_mapping = ConsumerMapping}) -> case dict:find(ConsumerTag, ConsumerMapping) of error -> QueueName = expand_queue_name_shortcut(QueueNameBin, State), @@ -742,15 +741,7 @@ handle_method(#'basic.consume'{queue = QueueNameBin, case rabbit_amqqueue:with_exclusive_access_or_die( QueueName, ConnPid, fun (Q) -> - case dict:find(ActualConsumerTag, CreditMap) of - {ok, {Credit, Count, Drain}} -> - ok = rabbit_amqqueue:inform_limiter( - Q, self(), - {basic_credit, ActualConsumerTag, - Credit, Count, Drain, false}); - error -> - ok - end, + maybe_set_initial_credit(Args, ActualConsumerTag, Q), {rabbit_amqqueue:basic_consume( Q, NoAck, self(), Limiter, ActualConsumerTag, ExclusiveConsume, @@ -760,11 +751,9 @@ handle_method(#'basic.consume'{queue = QueueNameBin, end) of {ok, Q = #amqqueue{pid = QPid, name = QName}} -> CM1 = dict:store(ActualConsumerTag, Q, ConsumerMapping), - CrM1 = dict:erase(ActualConsumerTag, CreditMap), State1 = monitor_delivering_queue( NoAck, QPid, QName, - State#ch{consumer_mapping = CM1, - credit_map = CrM1}), + State#ch{consumer_mapping = CM1}), {noreply, case NoWait of true -> consumer_monitor(ActualConsumerTag, State1); @@ -1131,16 +1120,13 @@ handle_method(#'basic.credit'{consumer_tag = CTag, credit = Credit, count = Count, drain = Drain}, _, - State = #ch{consumer_mapping = Consumers, - credit_map = CMap}) -> + State = #ch{consumer_mapping = Consumers}) -> case dict:find(CTag, Consumers) of {ok, Q} -> ok = rabbit_amqqueue:inform_limiter( Q, self(), {basic_credit, CTag, Credit, Count, Drain, true}), {noreply, State}; - error -> CMap2 = dict:store(CTag, {Credit, Count, Drain}, CMap), - {reply, #'basic.credit_ok'{available = 0}, - State#ch{credit_map = CMap2}} + error -> precondition_failed("unknown consumer tag '~s'", [CTag]) end; handle_method(_MethodRecord, _Content, _State) -> @@ -1209,6 +1195,21 @@ handle_consuming_queue_down(QPid, handle_delivering_queue_down(QPid, State = #ch{delivering_queues = DQ}) -> State#ch{delivering_queues = sets:del_element(QPid, DQ)}. +maybe_set_initial_credit(Arguments, CTag, Q) -> + 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}} -> + ok = rabbit_amqqueue:inform_limiter( + Q, self(), + {basic_credit, CTag, Credit, 0, Drain, + false}); + _ -> + ok + end; + undefined -> ok + end. + binding_action(Fun, ExchangeNameBin, DestinationType, DestinationNameBin, RoutingKey, Arguments, ReturnMethod, NoWait, State = #ch{virtual_host = VHostPath, -- cgit v1.2.1 From 18c857531e971ab691bc24afb076c376d22510f3 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 29 Jan 2013 13:56:24 +0000 Subject: Simplify: convert basic.credit_state to basic.credit_drained, which implicitly asserts that credit is 0, length 0 and drain is true, and tells you how much credit was discarded rather than the new delivery count (so we can soon remove all delivery count code from the broker). --- src/rabbit_channel.erl | 13 +++++-------- src/rabbit_limiter.erl | 6 +++--- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index dae21389..9ffb9112 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -145,8 +145,8 @@ deliver(Pid, ConsumerTag, AckRequired, Msg) -> send_credit_reply(Pid, Len) -> gen_server2:cast(Pid, {send_credit_reply, Len}). -send_drained(Pid, ConsumerTag, Count) -> - gen_server2:cast(Pid, {send_drained, ConsumerTag, Count}). +send_drained(Pid, ConsumerTag, CreditDrained) -> + gen_server2:cast(Pid, {send_drained, ConsumerTag, CreditDrained}). flushed(Pid, QPid) -> gen_server2:cast(Pid, {flushed, QPid}). @@ -330,14 +330,11 @@ handle_cast({send_credit_reply, Len}, State = #ch{writer_pid = WriterPid}) -> WriterPid, #'basic.credit_ok'{available = Len}), noreply(State); -handle_cast({send_drained, ConsumerTag, Count}, +handle_cast({send_drained, ConsumerTag, CreditDrained}, State = #ch{writer_pid = WriterPid}) -> ok = rabbit_writer:send_command( - WriterPid, #'basic.credit_state'{consumer_tag = ConsumerTag, - credit = 0, - count = Count, - available = 0, - drain = true}), + WriterPid, #'basic.credit_drained'{consumer_tag = ConsumerTag, + credit_drained = CreditDrained}), noreply(State); handle_cast(force_event_refresh, State) -> diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index b97d1073..232be83c 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -186,14 +186,14 @@ decr_credit(CTag, Len, ChPid, Cred, Credits) -> maybe_drain(0, true, CTag, ChPid, Credit, Count) -> %% Drain, so advance til credit = 0 NewCount = serial_add(Count, Credit - 2), - send_drained(ChPid, CTag, NewCount), + send_drained(ChPid, CTag, Credit), {0, NewCount}; %% Magic reduction to 0 maybe_drain(_, _, _, _, Credit, Count) -> {Credit, Count}. -send_drained(ChPid, CTag, Count) -> - rabbit_channel:send_drained(ChPid, CTag, Count). +send_drained(ChPid, CTag, CreditDrained) -> + rabbit_channel:send_drained(ChPid, CTag, CreditDrained). update_credit(CTag, Len, ChPid, Credit, Count0, Drain, Credits) -> Count = case dict:find(CTag, Credits) of -- cgit v1.2.1 From 3cd142a2d4027e64883681237de936b27fb0ecf5 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 29 Jan 2013 14:03:20 +0000 Subject: Remove knowledge of delivery-count from the broker. --- src/rabbit_channel.erl | 5 ++--- src/rabbit_limiter.erl | 40 +++++++++++++++------------------------- 2 files changed, 17 insertions(+), 28 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 9ffb9112..7f6dc3c8 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -1115,13 +1115,12 @@ handle_method(#'channel.flow'{active = false}, _, handle_method(#'basic.credit'{consumer_tag = CTag, credit = Credit, - count = Count, drain = Drain}, _, State = #ch{consumer_mapping = Consumers}) -> case dict:find(CTag, Consumers) of {ok, Q} -> ok = rabbit_amqqueue:inform_limiter( Q, self(), - {basic_credit, CTag, Credit, Count, Drain, true}), + {basic_credit, CTag, Credit, Drain, true}), {noreply, State}; error -> precondition_failed("unknown consumer tag '~s'", [CTag]) end; @@ -1199,7 +1198,7 @@ maybe_set_initial_credit(Arguments, CTag, Q) -> {{long, Credit}, {boolean, Drain}} -> ok = rabbit_amqqueue:inform_limiter( Q, self(), - {basic_credit, CTag, Credit, 0, Drain, + {basic_credit, CTag, Credit, Drain, false}); _ -> ok diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index 232be83c..ae9c7918 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -75,7 +75,7 @@ %% notified of a change in the limit or volume that may allow it to %% deliver more messages via the limiter's channel. --record(credit, {count = 0, credit = 0, drain = false}). +-record(credit, {credit = 0, drain = false}). %%---------------------------------------------------------------------------- %% API @@ -144,9 +144,9 @@ is_blocked(Limiter) -> maybe_call(Limiter, is_blocked, false). inform(Limiter = #token{q_state = Credits}, - ChPid, Len, {basic_credit, CTag, Credit, Count, Drain, Reply}) -> + ChPid, Len, {basic_credit, CTag, Credit, Drain, Reply}) -> {Unblock, Credits2} = update_credit( - CTag, Len, ChPid, Credit, Count, Drain, Credits), + CTag, Len, ChPid, Credit, Drain, Credits), case Reply of true -> rabbit_channel:send_credit_reply(ChPid, Len); false -> ok @@ -178,40 +178,30 @@ record_send_q(CTag, Len, ChPid, Credits) -> end. decr_credit(CTag, Len, ChPid, Cred, Credits) -> - #credit{credit = Credit, count = Count, drain = Drain} = Cred, - {NewCredit, NewCount} = maybe_drain(Len - 1, Drain, CTag, ChPid, - Credit - 1, serial_add(Count, 1)), - write_credit(CTag, NewCredit, NewCount, Drain, Credits). - -maybe_drain(0, true, CTag, ChPid, Credit, Count) -> - %% Drain, so advance til credit = 0 - NewCount = serial_add(Count, Credit - 2), + #credit{credit = Credit, drain = Drain} = Cred, + NewCredit = maybe_drain(Len - 1, Drain, CTag, ChPid, Credit - 1), + write_credit(CTag, NewCredit, Drain, Credits). + +maybe_drain(0, true, CTag, ChPid, Credit) -> send_drained(ChPid, CTag, Credit), - {0, NewCount}; %% Magic reduction to 0 + 0; %% Magic reduction to 0 -maybe_drain(_, _, _, _, Credit, Count) -> - {Credit, Count}. +maybe_drain(_, _, _, _, Credit) -> + Credit. send_drained(ChPid, CTag, CreditDrained) -> rabbit_channel:send_drained(ChPid, CTag, CreditDrained). -update_credit(CTag, Len, ChPid, Credit, Count0, Drain, Credits) -> - Count = case dict:find(CTag, Credits) of - %% Use our count if we can, more accurate - {ok, #credit{ count = LocalCount }} -> LocalCount; - %% But if this is new, take it from the adapter - _ -> Count0 - end, - {NewCredit, NewCount} = maybe_drain(Len, Drain, CTag, ChPid, Credit, Count), - NewCredits = write_credit(CTag, NewCredit, NewCount, Drain, Credits), +update_credit(CTag, Len, ChPid, Credit, Drain, Credits) -> + NewCredit = maybe_drain(Len, Drain, CTag, ChPid, Credit), + NewCredits = write_credit(CTag, NewCredit, Drain, Credits), case NewCredit > 0 of true -> {[CTag], NewCredits}; false -> {[], NewCredits} end. -write_credit(CTag, Credit, Count, Drain, Credits) -> +write_credit(CTag, Credit, Drain, Credits) -> dict:store(CTag, #credit{credit = Credit, - count = Count, drain = Drain}, Credits). %%---------------------------------------------------------------------------- -- cgit v1.2.1 From f3025ce7562d1f511baef3dbfb7231651b9e3509 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 29 Jan 2013 14:50:16 +0000 Subject: inform_limiter -> credit. --- src/rabbit_amqqueue.erl | 9 +++++---- src/rabbit_amqqueue_process.erl | 5 +++-- src/rabbit_channel.erl | 11 ++++------- src/rabbit_limiter.erl | 11 ++++++----- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index cb7e961d..b9d41c25 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -28,7 +28,7 @@ -export([consumers/1, consumers_all/1, consumer_info_keys/0]). -export([basic_get/3, basic_consume/7, basic_cancel/4]). -export([notify_sent/2, notify_sent_queue_down/1, unblock/2, flush_all/2]). --export([notify_down_all/2, limit_all/3, inform_limiter/3]). +-export([notify_down_all/2, limit_all/3, credit/6]). -export([on_node_down/1]). -export([update/2, store_queue/1, policy_changed/2]). -export([start_mirroring/1, stop_mirroring/1, sync_mirrors/1, @@ -145,7 +145,8 @@ -spec(notify_down_all/2 :: (qpids(), pid()) -> ok_or_errors()). -spec(limit_all/3 :: (qpids(), pid(), rabbit_limiter:token()) -> ok_or_errors()). --spec(inform_limiter/3 :: (rabbit_types:amqqueue(), pid(), any()) -> 'ok'). +-spec(credit/6 :: (rabbit_types:amqqueue(), pid(), rabbit_types:ctag(), + non_neg_integer(), boolean(), boolean()) -> 'ok'). -spec(basic_get/3 :: (rabbit_types:amqqueue(), pid(), boolean()) -> {'ok', non_neg_integer(), qmsg()} | 'empty'). -spec(basic_consume/7 :: @@ -533,8 +534,8 @@ notify_down_all(QPids, ChPid) -> limit_all(QPids, ChPid, Limiter) -> delegate:cast(QPids, {limit, ChPid, Limiter}). -inform_limiter(#amqqueue{pid = QPid}, ChPid, Msg) -> - delegate:cast(QPid, {inform_limiter, ChPid, Msg}). +credit(#amqqueue{pid = QPid}, ChPid, CTag, Credit, Drain, Reply) -> + delegate:cast(QPid, {credit, ChPid, CTag, Credit, Drain, Reply}). basic_get(#amqqueue{pid = QPid}, ChPid, NoAck) -> delegate:call(QPid, {basic_get, ChPid, NoAck}). diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 4e249365..aaa4b537 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1329,12 +1329,13 @@ handle_cast(stop_mirroring, State = #q{backing_queue = BQ, noreply(State#q{backing_queue = BQ1, backing_queue_state = BQS1}); -handle_cast({inform_limiter, ChPid, Msg}, +handle_cast({credit, ChPid, CTag, Credit, Drain, Reply}, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> #cr{limiter = Lim, blocked_ctags = BCTags} = ch_record(ChPid), - {Unblock, Lim2} = rabbit_limiter:inform(Lim, ChPid, BQ:len(BQS), Msg), + {Unblock, Lim2} = rabbit_limiter:credit( + Lim, ChPid, CTag, Credit, Drain, Reply, BQ:len(BQS)), noreply(possibly_unblock( State, ChPid, fun(C) -> C#cr{blocked_ctags = BCTags -- Unblock, limiter = Lim2} end)); diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 7f6dc3c8..f719d8f3 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -1118,9 +1118,8 @@ handle_method(#'basic.credit'{consumer_tag = CTag, drain = Drain}, _, State = #ch{consumer_mapping = Consumers}) -> case dict:find(CTag, Consumers) of - {ok, Q} -> ok = rabbit_amqqueue:inform_limiter( - Q, self(), - {basic_credit, CTag, Credit, Drain, true}), + {ok, Q} -> ok = rabbit_amqqueue:credit( + Q, self(), CTag, Credit, Drain, true), {noreply, State}; error -> precondition_failed("unknown consumer tag '~s'", [CTag]) end; @@ -1196,10 +1195,8 @@ maybe_set_initial_credit(Arguments, CTag, Q) -> {table, T} -> case {rabbit_misc:table_lookup(T, <<"credit">>), rabbit_misc:table_lookup(T, <<"drain">>)} of {{long, Credit}, {boolean, Drain}} -> - ok = rabbit_amqqueue:inform_limiter( - Q, self(), - {basic_credit, CTag, Credit, Drain, - false}); + ok = rabbit_amqqueue:credit( + Q, self(), CTag, Credit, Drain, false); _ -> ok end; diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index ae9c7918..d9019bfa 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -27,7 +27,7 @@ -export([limit/2, can_ch_send/3, can_cons_send/2, record_cons_send/4, ack/2, register/2, unregister/2]). -export([get_limit/1, block/1, unblock/1, is_blocked/1]). --export([inform/4, forget_consumer/2, copy_queue_state/2]). +-export([credit/7, forget_consumer/2, copy_queue_state/2]). -import(rabbit_misc, [serial_add/2, serial_diff/2]). @@ -57,8 +57,9 @@ -spec(block/1 :: (token()) -> 'ok'). -spec(unblock/1 :: (token()) -> 'ok' | {'disabled', token()}). -spec(is_blocked/1 :: (token()) -> boolean()). --spec(inform/4 :: (token(), pid(), non_neg_integer(), any()) -> - {[rabbit_types:ctag()], token()}). +-spec(credit/7 :: (token(), pid(), rabbit_types:ctag(), + non_neg_integer(), boolean(), boolean(), non_neg_integer()) + -> {[rabbit_types:ctag()], token()}). -spec(forget_consumer/2 :: (token(), rabbit_types:ctag()) -> token()). -spec(copy_queue_state/2 :: (token(), token()) -> token()). @@ -143,8 +144,8 @@ unblock(Limiter) -> is_blocked(Limiter) -> maybe_call(Limiter, is_blocked, false). -inform(Limiter = #token{q_state = Credits}, - ChPid, Len, {basic_credit, CTag, Credit, Drain, Reply}) -> +credit(Limiter = #token{q_state = Credits}, + ChPid, CTag, Credit, Drain, Reply, Len) -> {Unblock, Credits2} = update_credit( CTag, Len, ChPid, Credit, Drain, Credits), case Reply of -- cgit v1.2.1 From c5f449a5e3ab6c7c546b098cb8a4bd555a5f7221 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 29 Jan 2013 15:21:29 +0000 Subject: Unify the various checks before sending, and make sure we don't drop the new limiter on the floor. --- src/rabbit_amqqueue_process.erl | 39 +++++++++++++++++-------------------- src/rabbit_limiter.erl | 43 +++++++++++++++++++++++------------------ 2 files changed, 42 insertions(+), 40 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index aaa4b537..5c1b68f4 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -437,7 +437,9 @@ deliver_msgs_to_consumers(DeliverFun, false, 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}, + State = #q{backing_queue = BQ, + backing_queue_state = BQS}) -> C = ch_record(ChPid), case is_ch_blocked(C) of true -> @@ -446,36 +448,30 @@ deliver_msg_to_consumer(DeliverFun, E = {ChPid, Consumer}, State) -> false -> #cr{limiter = Limiter, ch_pid = ChPid, blocked_ctags = BCTags} = C, #consumer{tag = CTag} = Consumer, - case rabbit_limiter:can_cons_send(Limiter, CTag) of - false -> + case rabbit_limiter:can_send( + Limiter, self(), Consumer#consumer.ack_required, + ChPid, CTag, BQ:len(BQS)) of + consumer_blocked -> block_consumer(C#cr{blocked_ctags = [CTag | BCTags]}, E), {false, State}; - true -> - case rabbit_limiter:can_ch_send( - Limiter, self(), Consumer#consumer.ack_required) of - false -> - block_consumer(C#cr{is_limit_active = true}, E), - {false, State}; - true -> - AC1 = queue:in(E, State#q.active_consumers), - deliver_msg_to_consumer( - DeliverFun, Consumer, C, - State#q{active_consumers = AC1}) - end + channel_blocked -> + block_consumer(C#cr{is_limit_active = true}, E), + {false, State}; + Limiter2 -> + AC1 = queue:in(E, State#q.active_consumers), + deliver_msg_to_consumer( + DeliverFun, Limiter2, Consumer, C, + State#q{active_consumers = AC1}) end end. -deliver_msg_to_consumer(DeliverFun, +deliver_msg_to_consumer(DeliverFun, NewLimiter, #consumer{tag = ConsumerTag, ack_required = AckRequired}, C = #cr{ch_pid = ChPid, acktags = ChAckTags, - limiter = Limiter, unsent_message_count = Count}, - State = #q{q = #amqqueue{name = QName}, - backing_queue = BQ, - backing_queue_state = BQS}) -> - rabbit_limiter:record_cons_send(Limiter, ChPid, ConsumerTag, BQ:len(BQS)), + State = #q{q = #amqqueue{name = QName}}) -> {{Message, IsDelivered, AckTag}, Stop, State1} = DeliverFun(AckRequired, State), rabbit_channel:deliver(ChPid, ConsumerTag, AckRequired, @@ -485,6 +481,7 @@ deliver_msg_to_consumer(DeliverFun, false -> ChAckTags end, update_ch_record(C#cr{acktags = ChAckTags1, + limiter = NewLimiter, unsent_message_count = Count + 1}), {Stop, State1}. diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index d9019bfa..5b4ce802 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -24,8 +24,7 @@ -export([start_link/0, make_token/0, make_token/1, is_enabled/1, enable/2, disable/1]). --export([limit/2, can_ch_send/3, can_cons_send/2, record_cons_send/4, - ack/2, register/2, unregister/2]). +-export([limit/2, can_send/6, ack/2, register/2, unregister/2]). -export([get_limit/1, block/1, unblock/1, is_blocked/1]). -export([credit/7, forget_consumer/2, copy_queue_state/2]). @@ -48,8 +47,9 @@ -spec(enable/2 :: (token(), non_neg_integer()) -> token()). -spec(disable/1 :: (token()) -> token()). -spec(limit/2 :: (token(), non_neg_integer()) -> 'ok' | {'disabled', token()}). --spec(can_ch_send/3 :: (token(), pid(), boolean()) -> boolean()). --spec(can_cons_send/2 :: (token(), rabbit_types:ctag()) -> boolean()). +-spec(can_send/6 :: (token(), pid(), boolean(), pid(), rabbit_types:ctag(), + non_neg_integer()) + -> token() | 'consumer_blocked' | 'channel_blocked'). -spec(ack/2 :: (token(), non_neg_integer()) -> 'ok'). -spec(register/2 :: (token(), pid()) -> 'ok'). -spec(unregister/2 :: (token(), pid()) -> 'ok'). @@ -103,25 +103,30 @@ limit(Limiter, PrefetchCount) -> %% breaching a limit. Note that we don't use maybe_call here in order %% to avoid always going through with_exit_handler/2, even when the %% limiter is disabled. -can_ch_send(#token{pid = Pid, enabled = true}, QPid, AckRequired) -> +can_send(Token, QPid, AckRequired, ChPid, CTag, Len) -> rabbit_misc:with_exit_handler( fun () -> true end, - fun () -> - gen_server2:call(Pid, {can_send, QPid, AckRequired}, infinity) - end); -can_ch_send(_, _, _) -> - true. - -can_cons_send(#token{q_state = Credits}, CTag) -> - case dict:find(CTag, Credits) of - {ok, #credit{credit = C}} when C > 0 -> true; - {ok, #credit{}} -> false; - error -> true + fun () -> can_send0(Token, QPid, AckRequired, ChPid, CTag, Len) end). + +can_send0(Token = #token{pid = Pid, enabled = Enabled, q_state = Credits}, + QPid, AckRequired, ChPid, CTag, Len) -> + ConsAllows = case dict:find(CTag, Credits) of + {ok, #credit{credit = C}} when C > 0 -> true; + {ok, #credit{}} -> false; + error -> true + end, + case ConsAllows of + true -> case Enabled andalso + gen_server2:call( + Pid, {can_send, QPid, AckRequired}, infinity) of + true -> Credits2 = record_send_q( + CTag, Len, ChPid, Credits), + Token#token{q_state = Credits2}; + false -> channel_blocked + end; + false -> consumer_blocked end. -record_cons_send(#token{q_state = QState} = Token, ChPid, CTag, Len) -> - Token#token{q_state = record_send_q(CTag, Len, ChPid, QState)}. - %% Let the limiter know that the channel has received some acks from a %% consumer ack(Limiter, Count) -> maybe_cast(Limiter, {ack, Count}). -- cgit v1.2.1 From a68e32eb4d256be319015a255c64024265f7dce7 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 29 Jan 2013 15:26:16 +0000 Subject: Reduce distance to default. --- src/rabbit_limiter.erl | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index 5b4ce802..cc605b5c 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -15,13 +15,11 @@ %% -module(rabbit_limiter). --include("rabbit_framing.hrl"). -behaviour(gen_server2). -export([init/1, terminate/2, code_change/3, handle_call/3, handle_cast/2, handle_info/2, prioritise_call/3]). - -export([start_link/0, make_token/0, make_token/1, is_enabled/1, enable/2, disable/1]). -export([limit/2, can_send/6, ack/2, register/2, unregister/2]). -- cgit v1.2.1 From 5727b84d107c4116169d3798855c015fe531630e Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 29 Jan 2013 15:39:15 +0000 Subject: Derp. --- src/rabbit_limiter.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index cc605b5c..4796fc0e 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -114,7 +114,7 @@ can_send0(Token = #token{pid = Pid, enabled = Enabled, q_state = Credits}, error -> true end, case ConsAllows of - true -> case Enabled andalso + true -> case not Enabled orelse gen_server2:call( Pid, {can_send, QPid, AckRequired}, infinity) of true -> Credits2 = record_send_q( -- cgit v1.2.1 From 39ac6385e1e3b0d215349ec3dc3476eda9432c7d Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 29 Jan 2013 16:06:46 +0000 Subject: rabbit_limiter:initial_credit/6. --- src/rabbit_amqqueue.erl | 23 ++++++++++++----------- src/rabbit_amqqueue_process.erl | 20 +++++++++++++++----- src/rabbit_channel.erl | 17 +++++++---------- src/rabbit_limiter.erl | 22 ++++++++++++++-------- 4 files changed, 48 insertions(+), 34 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index b9d41c25..3673d06e 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -26,9 +26,9 @@ -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/3, basic_consume/7, basic_cancel/4]). +-export([basic_get/3, basic_consume/8, basic_cancel/4]). -export([notify_sent/2, notify_sent_queue_down/1, unblock/2, flush_all/2]). --export([notify_down_all/2, limit_all/3, credit/6]). +-export([notify_down_all/2, limit_all/3, credit/5]). -export([on_node_down/1]). -export([update/2, store_queue/1, policy_changed/2]). -export([start_mirroring/1, stop_mirroring/1, sync_mirrors/1, @@ -145,13 +145,14 @@ -spec(notify_down_all/2 :: (qpids(), pid()) -> ok_or_errors()). -spec(limit_all/3 :: (qpids(), pid(), rabbit_limiter:token()) -> ok_or_errors()). --spec(credit/6 :: (rabbit_types:amqqueue(), pid(), rabbit_types:ctag(), - non_neg_integer(), boolean(), boolean()) -> 'ok'). +-spec(credit/5 :: (rabbit_types:amqqueue(), pid(), rabbit_types:ctag(), + non_neg_integer(), boolean()) -> 'ok'). -spec(basic_get/3 :: (rabbit_types:amqqueue(), pid(), boolean()) -> {'ok', non_neg_integer(), qmsg()} | 'empty'). --spec(basic_consume/7 :: +-spec(basic_consume/8 :: (rabbit_types:amqqueue(), boolean(), pid(), - rabbit_limiter:token(), rabbit_types:ctag(), boolean(), any()) + rabbit_limiter:token(), rabbit_types:ctag(), boolean(), + {non_neg_integer(), boolean()} | 'none', any()) -> rabbit_types:ok_or_error('exclusive_consume_unavailable')). -spec(basic_cancel/4 :: (rabbit_types:amqqueue(), pid(), rabbit_types:ctag(), any()) -> 'ok'). @@ -534,16 +535,16 @@ notify_down_all(QPids, ChPid) -> limit_all(QPids, ChPid, Limiter) -> delegate:cast(QPids, {limit, ChPid, Limiter}). -credit(#amqqueue{pid = QPid}, ChPid, CTag, Credit, Drain, Reply) -> - delegate:cast(QPid, {credit, ChPid, CTag, Credit, Drain, Reply}). +credit(#amqqueue{pid = QPid}, ChPid, CTag, Credit, Drain) -> + delegate:cast(QPid, {credit, ChPid, CTag, Credit, Drain}). basic_get(#amqqueue{pid = QPid}, ChPid, NoAck) -> delegate:call(QPid, {basic_get, ChPid, NoAck}). basic_consume(#amqqueue{pid = QPid}, NoAck, ChPid, Limiter, - ConsumerTag, ExclusiveConsume, OkMsg) -> - delegate:call(QPid, {basic_consume, NoAck, ChPid, - Limiter, ConsumerTag, ExclusiveConsume, OkMsg}). + ConsumerTag, ExclusiveConsume, CreditArgs, OkMsg) -> + delegate:call(QPid, {basic_consume, NoAck, ChPid, Limiter, + ConsumerTag, ExclusiveConsume, CreditArgs, 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 5c1b68f4..0594e250 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1096,14 +1096,24 @@ handle_call({basic_get, ChPid, NoAck}, _From, end; handle_call({basic_consume, NoAck, ChPid, Limiter, - ConsumerTag, ExclusiveConsume, OkMsg}, - _From, State = #q{exclusive_consumer = Holder}) -> + ConsumerTag, ExclusiveConsume, CreditArgs, OkMsg}, + _From, State = #q{exclusive_consumer = Holder, + backing_queue = BQ, + backing_queue_state = BQS}) -> case check_exclusive_access(Holder, ExclusiveConsume, State) of in_use -> reply({error, exclusive_consume_unavailable}, State); ok -> C = ch_record(ChPid), - C1 = update_consumer_count(C#cr{limiter = Limiter}, +1), + Limiter2 = case CreditArgs of + none -> + Limiter; + {Credit, Drain} -> + rabbit_limiter:initial_credit( + Limiter, ChPid, ConsumerTag, Credit, Drain, + BQ:len(BQS)) + end, + C1 = update_consumer_count(C#cr{limiter = Limiter2}, +1), Consumer = #consumer{tag = ConsumerTag, ack_required = not NoAck}, ExclusiveConsumer = if ExclusiveConsume -> {ChPid, ConsumerTag}; @@ -1326,13 +1336,13 @@ handle_cast(stop_mirroring, State = #q{backing_queue = BQ, noreply(State#q{backing_queue = BQ1, backing_queue_state = BQS1}); -handle_cast({credit, ChPid, CTag, Credit, Drain, Reply}, +handle_cast({credit, ChPid, CTag, Credit, Drain}, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> #cr{limiter = Lim, blocked_ctags = BCTags} = ch_record(ChPid), {Unblock, Lim2} = rabbit_limiter:credit( - Lim, ChPid, CTag, Credit, Drain, Reply, BQ:len(BQS)), + Lim, ChPid, CTag, Credit, Drain, BQ:len(BQS)), noreply(possibly_unblock( State, ChPid, fun(C) -> C#cr{blocked_ctags = BCTags -- Unblock, limiter = Lim2} end)); diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index f719d8f3..20976932 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -717,7 +717,7 @@ handle_method(#'basic.consume'{queue = QueueNameBin, no_ack = NoAck, exclusive = ExclusiveConsume, nowait = NoWait, - arguments = Args}, + arguments = Arguments}, _, State = #ch{conn_pid = ConnPid, limiter = Limiter, consumer_mapping = ConsumerMapping}) -> @@ -738,10 +738,10 @@ handle_method(#'basic.consume'{queue = QueueNameBin, case rabbit_amqqueue:with_exclusive_access_or_die( QueueName, ConnPid, fun (Q) -> - maybe_set_initial_credit(Args, ActualConsumerTag, Q), {rabbit_amqqueue:basic_consume( Q, NoAck, self(), Limiter, ActualConsumerTag, ExclusiveConsume, + parse_credit_args(Arguments), ok_msg(NoWait, #'basic.consume_ok'{ consumer_tag = ActualConsumerTag})), Q} @@ -1119,7 +1119,7 @@ handle_method(#'basic.credit'{consumer_tag = CTag, State = #ch{consumer_mapping = Consumers}) -> case dict:find(CTag, Consumers) of {ok, Q} -> ok = rabbit_amqqueue:credit( - Q, self(), CTag, Credit, Drain, true), + Q, self(), CTag, Credit, Drain), {noreply, State}; error -> precondition_failed("unknown consumer tag '~s'", [CTag]) end; @@ -1190,17 +1190,14 @@ handle_consuming_queue_down(QPid, handle_delivering_queue_down(QPid, State = #ch{delivering_queues = DQ}) -> State#ch{delivering_queues = sets:del_element(QPid, DQ)}. -maybe_set_initial_credit(Arguments, CTag, Q) -> +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}} -> - ok = rabbit_amqqueue:credit( - Q, self(), CTag, Credit, Drain, false); - _ -> - ok + {{long, Credit}, {boolean, Drain}} -> {Credit, Drain}; + _ -> none end; - undefined -> ok + undefined -> none end. binding_action(Fun, ExchangeNameBin, DestinationType, DestinationNameBin, diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index 4796fc0e..865c4677 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -24,7 +24,7 @@ disable/1]). -export([limit/2, can_send/6, ack/2, register/2, unregister/2]). -export([get_limit/1, block/1, unblock/1, is_blocked/1]). --export([credit/7, forget_consumer/2, copy_queue_state/2]). +-export([initial_credit/6, credit/6, forget_consumer/2, copy_queue_state/2]). -import(rabbit_misc, [serial_add/2, serial_diff/2]). @@ -55,8 +55,11 @@ -spec(block/1 :: (token()) -> 'ok'). -spec(unblock/1 :: (token()) -> 'ok' | {'disabled', token()}). -spec(is_blocked/1 :: (token()) -> boolean()). --spec(credit/7 :: (token(), pid(), rabbit_types:ctag(), - non_neg_integer(), boolean(), boolean(), non_neg_integer()) +-spec(initial_credit/6 :: (token(), pid(), rabbit_types:ctag(), + non_neg_integer(), boolean(), non_neg_integer()) + -> token()). +-spec(credit/6 :: (token(), pid(), rabbit_types:ctag(), + non_neg_integer(), boolean(), non_neg_integer()) -> {[rabbit_types:ctag()], token()}). -spec(forget_consumer/2 :: (token(), rabbit_types:ctag()) -> token()). -spec(copy_queue_state/2 :: (token(), token()) -> token()). @@ -147,14 +150,17 @@ unblock(Limiter) -> is_blocked(Limiter) -> maybe_call(Limiter, is_blocked, false). +initial_credit(Limiter = #token{q_state = Credits}, + ChPid, CTag, Credit, Drain, Len) -> + {[], Credits2} = update_credit( + CTag, Len, ChPid, Credit, Drain, Credits), + Limiter#token{q_state = Credits2}. + credit(Limiter = #token{q_state = Credits}, - ChPid, CTag, Credit, Drain, Reply, Len) -> + ChPid, CTag, Credit, Drain, Len) -> {Unblock, Credits2} = update_credit( CTag, Len, ChPid, Credit, Drain, Credits), - case Reply of - true -> rabbit_channel:send_credit_reply(ChPid, Len); - false -> ok - end, + rabbit_channel:send_credit_reply(ChPid, Len), {Unblock, Limiter#token{q_state = Credits2}}. forget_consumer(Limiter = #token{q_state = Credits}, CTag) -> -- cgit v1.2.1 From bebd25e9724ac92941a3903bb44af2c805124821 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 29 Jan 2013 17:50:12 +0000 Subject: remove spurious generality after spending hours trawling through the qi code and its history, Matthew and I are convinced that qi:add_to_journal/3 is unnecessarily general, handling a case that can never arise, namely adding an 'ack' when we do have an entry for the given sequence number but that entry does no contain a 'del'. add_to_journal/3 gets called, indirectly, from four places: 1) load_journal/1. This is always called with no segements in the State. So all the segment journal entries originate from the very add_journal/3 code. And the only way we'd end up with an entry of the form {Pub, no_del, no_ack} and get an 'ack' is if the journal contained a pub and (later) an ack, with no del inbetween. That can only happen through a misuse of the qi API. Which doesn't happen. And there are plenty of other cases (e.g. duplicate dels or acks) where qi insists on callers doing the right thing. 2) publish/5 This ends up adding a {?PUB, no_del, no_ack} entry, so is of no direct concern to our investigation. 3) deliver_or_ack/3 This would hit the aforementioned {Pub, no_del, no_ack} & 'ack' case only if we lost a 'del'. 4) recover_message/5 this only adds an 'ack' to the segment journal if either a) the combination of the segment entries and the segment journal produces an entry {?PUB, del, no_ack}, or b) it's just added a 'del' (thus making a {Pub, no_del, no_ack} entry impossible). Re (a)... for there to be a combined entry of {?PUB, del, no_ack} when the segment journal contains {Pub, no_del, no_ack} (which would trigger the case we are concerned about), the segment would have to contain a 'del' w/o a 'pub', which is impossible. --- src/rabbit_queue_index.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_queue_index.erl b/src/rabbit_queue_index.erl index 4559bb8a..3841b680 100644 --- a/src/rabbit_queue_index.erl +++ b/src/rabbit_queue_index.erl @@ -616,8 +616,8 @@ add_to_journal(RelSeq, Action, JEntries) -> end; ({Pub, no_del, no_ack}) when Action == del -> {Pub, del, no_ack}; - ({Pub, Del, no_ack}) when Action == ack -> - {Pub, Del, ack} + ({Pub, del, no_ack}) when Action == ack -> + {Pub, del, ack} end, array:set(RelSeq, Val, JEntries). -- cgit v1.2.1 From 1c50d72ccdd59c94b1973093749528c899a26bf1 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 30 Jan 2013 11:07:10 +0000 Subject: remove out-of-date comment --- src/rabbit_variable_queue.erl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 18cab48b..faa1b0b1 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -883,8 +883,7 @@ cons_if(true, E, L) -> [E | L]; cons_if(false, _E, L) -> L. gb_sets_maybe_insert(false, _Val, Set) -> Set; -%% when requeueing, we re-add a msg_id to the unconfirmed set -gb_sets_maybe_insert(true, Val, Set) -> gb_sets:add(Val, Set). +gb_sets_maybe_insert(true, Val, Set) -> gb_sets:add(Val, Set). msg_status(IsPersistent, IsDelivered, SeqId, Msg = #basic_message {id = MsgId}, MsgProps) -> -- cgit v1.2.1 From 612f2df8a3c62bbf47069323409e55c9f501d9dd Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 30 Jan 2013 13:41:28 +0000 Subject: only keep track of *unconfirmed* unsync'ed messages ids in qi ...so we don't bloat memory with stuff nobody cares about, and reduce the qi->queue callbacks (to zero when no confirms/tx are used) This does expose qi to a bit more messaging semantics, but imo to an acceptable degree. related changes: - rename the state var to better capture its (revised) meaning - only invoke the OnSyncFun when we have something to say --- src/rabbit_queue_index.erl | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/rabbit_queue_index.erl b/src/rabbit_queue_index.erl index 4559bb8a..7dc69458 100644 --- a/src/rabbit_queue_index.erl +++ b/src/rabbit_queue_index.erl @@ -162,7 +162,7 @@ %%---------------------------------------------------------------------------- -record(qistate, { dir, segments, journal_handle, dirty_count, - max_journal_entries, on_sync, unsynced_msg_ids }). + max_journal_entries, on_sync, unconfirmed }). -record(segment, { num, path, journal_entries, unacked }). @@ -190,7 +190,7 @@ dirty_count :: integer(), max_journal_entries :: non_neg_integer(), on_sync :: on_sync_fun(), - unsynced_msg_ids :: gb_set() + unconfirmed :: gb_set() }). -type(contains_predicate() :: fun ((rabbit_types:msg_id()) -> boolean())). -type(walker(A) :: fun ((A) -> 'finished' | @@ -269,13 +269,16 @@ delete_and_terminate(State) -> State1. publish(MsgId, SeqId, MsgProps, IsPersistent, - State = #qistate { unsynced_msg_ids = UnsyncedMsgIds }) + State = #qistate { unconfirmed = Unconfirmed }) when is_binary(MsgId) -> ?MSG_ID_BYTES = size(MsgId), {JournalHdl, State1} = get_journal_handle( - State #qistate { - unsynced_msg_ids = gb_sets:add_element(MsgId, UnsyncedMsgIds) }), + case MsgProps#message_properties.needs_confirming of + true -> Unconfirmed1 = gb_sets:add_element(MsgId, Unconfirmed), + State #qistate { unconfirmed = Unconfirmed1 }; + false -> State + end), ok = file_handle_cache:append( JournalHdl, [<<(case IsPersistent of true -> ?PUB_PERSIST_JPREFIX; @@ -398,7 +401,7 @@ blank_state_dir(Dir) -> dirty_count = 0, max_journal_entries = MaxJournal, on_sync = fun (_) -> ok end, - unsynced_msg_ids = gb_sets:new() }. + unconfirmed = gb_sets:new() }. clean_filename(Dir) -> filename:join(Dir, ?CLEAN_FILENAME). @@ -732,9 +735,12 @@ deliver_or_ack(Kind, SeqIds, State) -> add_to_journal(SeqId, Kind, StateN) end, State1, SeqIds)). -notify_sync(State = #qistate { unsynced_msg_ids = UG, on_sync = OnSyncFun }) -> - OnSyncFun(UG), - State #qistate { unsynced_msg_ids = gb_sets:new() }. +notify_sync(State = #qistate { unconfirmed = UC, on_sync = OnSyncFun }) -> + case gb_sets:is_empty(UC) of + true -> State; + false -> OnSyncFun(UC), + State #qistate { unconfirmed = gb_sets:new() } + end. %%---------------------------------------------------------------------------- %% segment manipulation -- cgit v1.2.1 From 6dccc04b620268999e96e6eec5cd44efbc8893fd Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 30 Jan 2013 14:05:08 +0000 Subject: don't hold on to 'complete' in-memory qi journal entries When a pub, del and ack for a message are all recorded in the journal then we don't bother writing any of that to the segment files when the journal is flushed, since the message is well and truly in the past and forgotten. So... there is no point keeping the entry in the in-memory journal either, where it just eats up space until a flush for no good reason. --- src/rabbit_queue_index.erl | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/rabbit_queue_index.erl b/src/rabbit_queue_index.erl index 7dc69458..07117f9a 100644 --- a/src/rabbit_queue_index.erl +++ b/src/rabbit_queue_index.erl @@ -610,19 +610,21 @@ add_to_journal(RelSeq, Action, end}; add_to_journal(RelSeq, Action, JEntries) -> - Val = case array:get(RelSeq, JEntries) of - undefined -> - case Action of - ?PUB -> {Action, no_del, no_ack}; - del -> {no_pub, del, no_ack}; - ack -> {no_pub, no_del, ack} - end; - ({Pub, no_del, no_ack}) when Action == del -> - {Pub, del, no_ack}; - ({Pub, Del, no_ack}) when Action == ack -> - {Pub, Del, ack} - end, - array:set(RelSeq, Val, JEntries). + case array:get(RelSeq, JEntries) of + undefined -> + array:set(RelSeq, + case Action of + ?PUB -> {Action, no_del, no_ack}; + del -> {no_pub, del, no_ack}; + ack -> {no_pub, no_del, ack} + end, JEntries); + ({?PUB, del, no_ack}) when Action == ack -> + array:reset(RelSeq, JEntries); + ({Pub, no_del, no_ack}) when Action == del -> + array:set(RelSeq, {Pub, del, no_ack}, JEntries); + ({Pub, Del, no_ack}) when Action == ack -> + array:set(RelSeq, {Pub, Del, ack}, JEntries) + end. maybe_flush_journal(State = #qistate { dirty_count = DCount, max_journal_entries = MaxJournal }) -- cgit v1.2.1 From a39bc21538e580476fca3b5a9252dd360ea82147 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 30 Jan 2013 14:29:16 +0000 Subject: reduce the default qi journal size a large journal size improves performance by - writing larger blocks to disk in one go (since in many scenarios we end up writing the entire journal in one go) - reducing fsync frequency (since in many scenarios we only sync when rolling the journal) - records for messages which get published, delivered and acked in the lifetime of a journal don't need to be written to segment files. To the point where we may be able to skip writing segment files altogether. BUT a large size also has downsides: - increased memory consumption - increased amount of data loss in the event of a crash So how do we pick a figure? Well, ultimately we may want to dynamically adjust the size based on memory pressure, but that is a fairly involved / risky change. Meanwhile, what would be the *lowest* figure that still delivers all of the benefits, just somewhat less than currently? A single qi segment holds entries for 16k messages. We want the journal to be able to hold at least an entire segment's worth of publishes, delivers and acks, which would be 16k x 3 entries. That way the aforementioned segment write reductions can happen when the queue is small. However, with a size of *exactly* 16k x 3, we would generally only avoid whole segment writes when messages get published, delivered and ack'ed straight away, which only happens when consuming in noack mode from empty queues. So we want to add some headroom in order to extend the benefits to a wider range of modes of queue operation. Hence 16k x 4 is a good choice. It should allow most "near empty" fast moving queues to avoid segment writes. --- ebin/rabbit_app.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ebin/rabbit_app.in b/ebin/rabbit_app.in index 16dfd196..f4d4ec3d 100644 --- a/ebin/rabbit_app.in +++ b/ebin/rabbit_app.in @@ -27,7 +27,7 @@ {frame_max, 131072}, {heartbeat, 600}, {msg_store_file_size_limit, 16777216}, - {queue_index_max_journal_entries, 262144}, + {queue_index_max_journal_entries, 65536}, {default_user, <<"guest">>}, {default_pass, <<"guest">>}, {default_user_tags, [administrator]}, -- cgit v1.2.1 From 7f6543ca82df40f3710262aed52a3076898310dc Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 30 Jan 2013 21:16:03 +0000 Subject: ask qi whether it needs sync'ing, and why --- src/rabbit_queue_index.erl | 9 ++++++--- src/rabbit_variable_queue.erl | 40 +++++++++++----------------------------- 2 files changed, 17 insertions(+), 32 deletions(-) diff --git a/src/rabbit_queue_index.erl b/src/rabbit_queue_index.erl index 07117f9a..a055742e 100644 --- a/src/rabbit_queue_index.erl +++ b/src/rabbit_queue_index.erl @@ -210,7 +210,7 @@ -spec(deliver/2 :: ([seq_id()], qistate()) -> qistate()). -spec(ack/2 :: ([seq_id()], qistate()) -> qistate()). -spec(sync/1 :: (qistate()) -> qistate()). --spec(needs_sync/1 :: (qistate()) -> boolean()). +-spec(needs_sync/1 :: (qistate()) -> 'confirms' | boolean()). -spec(flush/1 :: (qistate()) -> qistate()). -spec(read/3 :: (seq_id(), seq_id(), qistate()) -> {[{rabbit_types:msg_id(), seq_id(), @@ -305,8 +305,11 @@ sync(State = #qistate { journal_handle = JournalHdl }) -> needs_sync(#qistate { journal_handle = undefined }) -> false; -needs_sync(#qistate { journal_handle = JournalHdl }) -> - file_handle_cache:needs_sync(JournalHdl). +needs_sync(#qistate { journal_handle = JournalHdl, unconfirmed = UC }) -> + case gb_sets:is_empty(UC) of + true -> file_handle_cache:needs_sync(JournalHdl); + false -> confirms + end. flush(State = #qistate { dirty_count = 0 }) -> State; flush(State) -> flush_journal(State). diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 1acc9ef0..0be81569 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -755,20 +755,17 @@ ram_duration(State = #vqstate { ram_ack_count_prev = RamAckCount }}. needs_timeout(State = #vqstate { index_state = IndexState }) -> - case must_sync_index(State) of - true -> timed; - false -> - case rabbit_queue_index:needs_sync(IndexState) of - true -> idle; - false -> case reduce_memory_use( - fun (_Quota, State1) -> {0, State1} end, - fun (_Quota, State1) -> State1 end, - fun (_Quota, State1) -> {0, State1} end, - State) of - {true, _State} -> idle; - {false, _State} -> false - end - end + case rabbit_queue_index:needs_sync(IndexState) of + confirms -> timed; + true -> idle; + false -> case reduce_memory_use( + fun (_Quota, State1) -> {0, State1} end, + fun (_Quota, State1) -> State1 end, + fun (_Quota, State1) -> {0, State1} end, + State) of + {true, _State} -> idle; + {false, _State} -> false + end end. timeout(State = #vqstate { index_state = IndexState }) -> @@ -1304,21 +1301,6 @@ record_confirms(MsgIdSet, State = #vqstate { msgs_on_disk = MOD, unconfirmed = rabbit_misc:gb_sets_difference(UC, MsgIdSet), confirmed = gb_sets:union(C, MsgIdSet) }. -must_sync_index(#vqstate { msg_indices_on_disk = MIOD, - unconfirmed = UC }) -> - %% If UC is empty then by definition, MIOD and MOD are also empty - %% and there's nothing that can be pending a sync. - - %% If UC is not empty, then we want to find is_empty(UC - MIOD), - %% but the subtraction can be expensive. Thus instead, we test to - %% see if UC is a subset of MIOD. This can only be the case if - %% MIOD == UC, which would indicate that every message in UC is - %% also in MIOD and is thus _all_ pending on a msg_store sync, not - %% on a qi sync. Thus the negation of this is sufficient. Because - %% is_subset is short circuiting, this is more efficient than the - %% subtraction. - not (gb_sets:is_empty(UC) orelse gb_sets:is_subset(UC, MIOD)). - msgs_written_to_disk(Callback, MsgIdSet, ignored) -> Callback(?MODULE, fun (?MODULE, State) -> record_confirms(MsgIdSet, State) end); -- cgit v1.2.1 -- cgit v1.2.1 From 9720758be509d763cb7497fe22e7e4b40fb373e3 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 31 Jan 2013 15:23:50 +0000 Subject: Avoid always going through with_exit_handler/2, since that's what the comment says! --- src/rabbit_limiter.erl | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index 865c4677..35703efa 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -104,22 +104,15 @@ limit(Limiter, PrefetchCount) -> %% breaching a limit. Note that we don't use maybe_call here in order %% to avoid always going through with_exit_handler/2, even when the %% limiter is disabled. -can_send(Token, QPid, AckRequired, ChPid, CTag, Len) -> - rabbit_misc:with_exit_handler( - fun () -> true end, - fun () -> can_send0(Token, QPid, AckRequired, ChPid, CTag, Len) end). - -can_send0(Token = #token{pid = Pid, enabled = Enabled, q_state = Credits}, - QPid, AckRequired, ChPid, CTag, Len) -> +can_send(Token = #token{pid = Pid, enabled = Enabled, q_state = Credits}, + QPid, AckReq, ChPid, CTag, Len) -> ConsAllows = case dict:find(CTag, Credits) of {ok, #credit{credit = C}} when C > 0 -> true; {ok, #credit{}} -> false; error -> true end, case ConsAllows of - true -> case not Enabled orelse - gen_server2:call( - Pid, {can_send, QPid, AckRequired}, infinity) of + true -> case not Enabled orelse call_can_send(Pid, QPid, AckReq) of true -> Credits2 = record_send_q( CTag, Len, ChPid, Credits), Token#token{q_state = Credits2}; @@ -128,6 +121,13 @@ can_send0(Token = #token{pid = Pid, enabled = Enabled, q_state = Credits}, false -> consumer_blocked end. +call_can_send(Pid, QPid, AckRequired) -> + rabbit_misc:with_exit_handler( + fun () -> true end, + fun () -> + gen_server2:call(Pid, {can_send, QPid, AckRequired}, infinity) + end). + %% Let the limiter know that the channel has received some acks from a %% consumer ack(Limiter, Count) -> maybe_cast(Limiter, {ack, Count}). -- 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(-) 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(-) 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 a284ca9d6d39898357359327ccf373cb958d4e21 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 1 Feb 2013 16:34:51 +0000 Subject: rabbit_parameter_validation:enum/1 --- src/rabbit_parameter_validation.erl | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/rabbit_parameter_validation.erl b/src/rabbit_parameter_validation.erl index 39d0188c..a8dfdd3a 100644 --- a/src/rabbit_parameter_validation.erl +++ b/src/rabbit_parameter_validation.erl @@ -16,7 +16,7 @@ -module(rabbit_parameter_validation). --export([number/2, binary/2, boolean/2, list/2, regex/2, proplist/3]). +-export([number/2, binary/2, boolean/2, list/2, regex/2, proplist/3, enum/1]). number(_Name, Term) when is_number(Term) -> ok; @@ -73,3 +73,15 @@ proplist(Name, Constraints, Term) when is_list(Term) -> proplist(Name, _Constraints, Term) -> {error, "~s not a list ~p", [Name, Term]}. + +enum(OptionsA) -> + Options = [list_to_binary(atom_to_list(O)) || O <- OptionsA], + fun (Name, Term) when is_binary(Term) -> + case lists:member(Term, Options) of + true -> ok; + false -> {error, "~s should be one of ~p, actuaklly was ~p", + [Name, Options, Term]} + end; + (Name, Term) -> + {error, "~s should be binary, actually was ~p", [Name, Term]} + end. -- cgit v1.2.1 From abd1657a7d4a18fedc8c84264b37a367fa33fa7f Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 4 Feb 2013 10:56:25 +0000 Subject: We depend on xmerl via mochijson2. --- ebin/rabbit_app.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ebin/rabbit_app.in b/ebin/rabbit_app.in index 16dfd196..ee6e6aeb 100644 --- a/ebin/rabbit_app.in +++ b/ebin/rabbit_app.in @@ -10,7 +10,7 @@ rabbit_sup, rabbit_tcp_client_sup, rabbit_direct_client_sup]}, - {applications, [kernel, stdlib, sasl, mnesia, os_mon]}, + {applications, [kernel, stdlib, sasl, mnesia, os_mon, xmerl]}, %% we also depend on crypto, public_key and ssl but they shouldn't be %% in here as we don't actually want to start it {mod, {rabbit, []}}, -- cgit v1.2.1 From 3cc52f2a7b4322316591a03a7fc564b754f24bef Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 5 Feb 2013 10:42:25 +0000 Subject: Slightly cleaner logging at startup: don't repeat the RabbitMQ or Erlang versions, and split copyright out into its own log message since it is not like the other bits. --- src/rabbit.erl | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 6b730fda..98178a62 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -725,34 +725,29 @@ print_banner() -> log_location(kernel), log_location(sasl)]). log_banner() -> - {ok, Product} = application:get_key(id), - {ok, Version} = application:get_key(vsn), + error_logger:info_msg("~s ~s~n", + [?COPYRIGHT_MESSAGE, ?INFORMATION_MESSAGE]), Settings = [{"node", node()}, {"home dir", home_dir()}, {"config file(s)", config_files()}, {"cookie hash", rabbit_nodes:cookie_hash()}, {"log", log_location(kernel)}, {"sasl log", log_location(sasl)}, - {"database dir", rabbit_mnesia:dir()}, - {"erlang version", erlang:system_info(otp_release)}], + {"database dir", rabbit_mnesia:dir()}], DescrLen = 1 + lists:max([length(K) || {K, _V} <- Settings]), Format = fun (K, V) -> rabbit_misc:format( "~-" ++ integer_to_list(DescrLen) ++ "s: ~s~n", [K, V]) end, Banner = iolist_to_binary( - rabbit_misc:format( - "~s ~s~n~s~n~s~n", - [Product, Version, ?COPYRIGHT_MESSAGE, - ?INFORMATION_MESSAGE]) ++ - [case S of - {"config file(s)" = K, []} -> - Format(K, "(none)"); - {"config file(s)" = K, [V0 | Vs]} -> - Format(K, V0), [Format("", V) || V <- Vs]; - {K, V} -> - Format(K, V) - end || S <- Settings]), + [case S of + {"config file(s)" = K, []} -> + Format(K, "(none)"); + {"config file(s)" = K, [V0 | Vs]} -> + Format(K, V0), [Format("", V) || V <- Vs]; + {K, V} -> + Format(K, V) + end || S <- Settings]), error_logger:info_msg("~s", [Banner]). home_dir() -> -- cgit v1.2.1 From 5296207cb1fe423b141bc7b72716bc5c4c965a36 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 5 Feb 2013 10:48:33 +0000 Subject: Slightly cleaner logging at startup: make the plugin list look nicer. --- src/rabbit.erl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 98178a62..73399c03 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -699,8 +699,11 @@ force_event_refresh() -> log_broker_started(Plugins) -> rabbit_misc:with_local_io( fun() -> + PluginList = iolist_to_binary([rabbit_misc:format(" * ~s~n", [P]) + || P <- Plugins]), error_logger:info_msg( - "Server startup complete; plugins are: ~p~n", [Plugins]), + "Server startup complete; ~b plugins started.~n~s~n", + [length(Plugins), PluginList]), io:format("~n Broker running with ~p plugins.~n", [length(Plugins)]) end). -- cgit v1.2.1 From 8509e6813b697698b1f78300fb0547ae0e83bca6 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Tue, 5 Feb 2013 11:12:48 +0000 Subject: Spelling --- src/rabbit_parameter_validation.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_parameter_validation.erl b/src/rabbit_parameter_validation.erl index a8dfdd3a..a4bd5042 100644 --- a/src/rabbit_parameter_validation.erl +++ b/src/rabbit_parameter_validation.erl @@ -79,7 +79,7 @@ enum(OptionsA) -> fun (Name, Term) when is_binary(Term) -> case lists:member(Term, Options) of true -> ok; - false -> {error, "~s should be one of ~p, actuaklly was ~p", + false -> {error, "~s should be one of ~p, actually was ~p", [Name, Options, Term]} end; (Name, Term) -> -- cgit v1.2.1 From 0962afad0d4490da6dda92d80151389e9c1bb4ed Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 6 Feb 2013 17:11:20 +0000 Subject: Remove durable queues based on a node while forgetting that node. --- src/rabbit_amqqueue.erl | 19 ++++++++++++++++++- src/rabbit_mnesia.erl | 1 + 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 07895ae3..352dea07 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -17,7 +17,7 @@ -module(rabbit_amqqueue). -export([recover/0, stop/0, start/1, declare/5, - delete_immediately/1, delete/3, purge/1]). + delete_immediately/1, delete/3, purge/1, forget_all_durable/1]). -export([pseudo_queue/2]). -export([lookup/1, not_found_or_absent/1, with/2, with/3, with_or_die/2, assert_equivalence/5, @@ -134,6 +134,7 @@ rabbit_types:error('in_use') | rabbit_types:error('not_empty')). -spec(purge/1 :: (rabbit_types:amqqueue()) -> qlen()). +-spec(forget_all_durable/1 :: (node()) -> 'ok'). -spec(deliver/2 :: ([rabbit_types:amqqueue()], rabbit_types:delivery()) -> {routing_result(), qpids()}). -spec(deliver_flow/2 :: ([rabbit_types:amqqueue()], rabbit_types:delivery()) -> @@ -585,6 +586,22 @@ internal_delete(QueueName, QPid) -> end end). +forget_all_durable(Node) -> + %% Note rabbit is not running so we avoid e.g. the worker pool. Also why + %% we don't invoke the return from rabbit_binding:process_deletions/1. + {atomic, ok} = + mnesia:sync_transaction( + fun () -> + Qs = mnesia:match_object(rabbit_durable_queue, + #amqqueue{_ = '_'}, write), + [rabbit_binding:process_deletions( + internal_delete1(Name)) || + #amqqueue{name = Name, pid = Pid} <- Qs, + node(Pid) =:= Node], + ok + end), + ok. + run_backing_queue(QPid, Mod, Fun) -> gen_server2:cast(QPid, {run_backing_queue, Mod, Fun}). diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index c7157753..039a8c60 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -679,6 +679,7 @@ remove_node_if_mnesia_running(Node) -> %% change being propagated to all nodes case mnesia:del_table_copy(schema, Node) of {atomic, ok} -> + rabbit_amqqueue:forget_all_durable(Node), rabbit_node_monitor:notify_left_cluster(Node), ok; {aborted, Reason} -> -- cgit v1.2.1 From e2d9b084c32c3e64c59d88fd34eac386c282d1d7 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 6 Feb 2013 17:31:27 +0000 Subject: Only delete queues for which no HA policy exists. --- src/rabbit_amqqueue.erl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 352dea07..04457ed0 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -596,8 +596,10 @@ forget_all_durable(Node) -> #amqqueue{_ = '_'}, write), [rabbit_binding:process_deletions( internal_delete1(Name)) || - #amqqueue{name = Name, pid = Pid} <- Qs, - node(Pid) =:= Node], + #amqqueue{name = Name, pid = Pid} = Q <- Qs, + node(Pid) =:= Node, + rabbit_policy:get(<<"ha-mode">>, Q) + =:= {error, not_found}], ok end), ok. -- cgit v1.2.1 From e1199f009198360d869c0977545a2ff42a05e5ef Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 8 Feb 2013 10:52:46 +0000 Subject: Change to 'confirms' | 'other' | 'false' --- src/rabbit_queue_index.erl | 7 +++++-- src/rabbit_variable_queue.erl | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/rabbit_queue_index.erl b/src/rabbit_queue_index.erl index 6495115b..ea70208f 100644 --- a/src/rabbit_queue_index.erl +++ b/src/rabbit_queue_index.erl @@ -210,7 +210,7 @@ -spec(deliver/2 :: ([seq_id()], qistate()) -> qistate()). -spec(ack/2 :: ([seq_id()], qistate()) -> qistate()). -spec(sync/1 :: (qistate()) -> qistate()). --spec(needs_sync/1 :: (qistate()) -> 'confirms' | boolean()). +-spec(needs_sync/1 :: (qistate()) -> 'confirms' | 'other' | 'false'). -spec(flush/1 :: (qistate()) -> qistate()). -spec(read/3 :: (seq_id(), seq_id(), qistate()) -> {[{rabbit_types:msg_id(), seq_id(), @@ -307,7 +307,10 @@ needs_sync(#qistate { journal_handle = undefined }) -> false; needs_sync(#qistate { journal_handle = JournalHdl, unconfirmed = UC }) -> case gb_sets:is_empty(UC) of - true -> file_handle_cache:needs_sync(JournalHdl); + true -> case file_handle_cache:needs_sync(JournalHdl) of + true -> other; + false -> false + end; false -> confirms end. diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index a23ebcb6..5d463f57 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -774,7 +774,7 @@ needs_timeout(State = #vqstate { index_state = IndexState, target_ram_count = TargetRamCount }) -> case rabbit_queue_index:needs_sync(IndexState) of confirms -> timed; - true -> idle; + other -> idle; false when TargetRamCount == infinity -> false; false -> case reduce_memory_use( fun (_Quota, State1) -> {0, State1} end, -- cgit v1.2.1 From 5415b94021756730f90af662b83164db1a0c7e62 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 8 Feb 2013 16:09:30 +0000 Subject: Don't repeat yourself. --- src/rabbit_auth_mechanism_amqplain.erl | 3 +-- src/rabbit_auth_mechanism_cr_demo.erl | 3 +-- src/rabbit_auth_mechanism_plain.erl | 3 +-- src/rabbit_exchange_type_direct.erl | 3 +-- src/rabbit_exchange_type_fanout.erl | 3 +-- src/rabbit_exchange_type_headers.erl | 3 +-- src/rabbit_exchange_type_topic.erl | 3 +-- 7 files changed, 7 insertions(+), 14 deletions(-) diff --git a/src/rabbit_auth_mechanism_amqplain.erl b/src/rabbit_auth_mechanism_amqplain.erl index 1ed54fef..847a38f5 100644 --- a/src/rabbit_auth_mechanism_amqplain.erl +++ b/src/rabbit_auth_mechanism_amqplain.erl @@ -33,8 +33,7 @@ %% referring generically to "SASL security mechanism", i.e. the above. description() -> - [{name, <<"AMQPLAIN">>}, - {description, <<"QPid AMQPLAIN mechanism">>}]. + [{description, <<"QPid AMQPLAIN mechanism">>}]. should_offer(_Sock) -> true. diff --git a/src/rabbit_auth_mechanism_cr_demo.erl b/src/rabbit_auth_mechanism_cr_demo.erl index e4494ab4..4b08e4be 100644 --- a/src/rabbit_auth_mechanism_cr_demo.erl +++ b/src/rabbit_auth_mechanism_cr_demo.erl @@ -37,8 +37,7 @@ %% SECURE-OK: "My password is ~s", [Password] description() -> - [{name, <<"RABBIT-CR-DEMO">>}, - {description, <<"RabbitMQ Demo challenge-response authentication " + [{description, <<"RabbitMQ Demo challenge-response authentication " "mechanism">>}]. should_offer(_Sock) -> diff --git a/src/rabbit_auth_mechanism_plain.erl b/src/rabbit_auth_mechanism_plain.erl index 5553a641..a35a133a 100644 --- a/src/rabbit_auth_mechanism_plain.erl +++ b/src/rabbit_auth_mechanism_plain.erl @@ -36,8 +36,7 @@ %% matching and will thus be much faster. description() -> - [{name, <<"PLAIN">>}, - {description, <<"SASL PLAIN authentication mechanism">>}]. + [{description, <<"SASL PLAIN authentication mechanism">>}]. should_offer(_Sock) -> true. diff --git a/src/rabbit_exchange_type_direct.erl b/src/rabbit_exchange_type_direct.erl index e54bd66e..213b24c4 100644 --- a/src/rabbit_exchange_type_direct.erl +++ b/src/rabbit_exchange_type_direct.erl @@ -31,8 +31,7 @@ {enables, kernel_ready}]}). description() -> - [{name, <<"direct">>}, - {description, <<"AMQP direct exchange, as per the AMQP specification">>}]. + [{description, <<"AMQP direct exchange, as per the AMQP specification">>}]. serialise_events() -> false. diff --git a/src/rabbit_exchange_type_fanout.erl b/src/rabbit_exchange_type_fanout.erl index 870b327a..5b17ed56 100644 --- a/src/rabbit_exchange_type_fanout.erl +++ b/src/rabbit_exchange_type_fanout.erl @@ -31,8 +31,7 @@ {enables, kernel_ready}]}). description() -> - [{name, <<"fanout">>}, - {description, <<"AMQP fanout exchange, as per the AMQP specification">>}]. + [{description, <<"AMQP fanout exchange, as per the AMQP specification">>}]. serialise_events() -> false. diff --git a/src/rabbit_exchange_type_headers.erl b/src/rabbit_exchange_type_headers.erl index b185cc4a..75899160 100644 --- a/src/rabbit_exchange_type_headers.erl +++ b/src/rabbit_exchange_type_headers.erl @@ -37,8 +37,7 @@ -endif. description() -> - [{name, <<"headers">>}, - {description, <<"AMQP headers exchange, as per the AMQP specification">>}]. + [{description, <<"AMQP headers exchange, as per the AMQP specification">>}]. serialise_events() -> false. diff --git a/src/rabbit_exchange_type_topic.erl b/src/rabbit_exchange_type_topic.erl index 70e32eaa..bd8ad1ac 100644 --- a/src/rabbit_exchange_type_topic.erl +++ b/src/rabbit_exchange_type_topic.erl @@ -34,8 +34,7 @@ %%---------------------------------------------------------------------------- description() -> - [{name, <<"topic">>}, - {description, <<"AMQP topic exchange, as per the AMQP specification">>}]. + [{description, <<"AMQP topic exchange, as per the AMQP specification">>}]. serialise_events() -> false. -- cgit v1.2.1 From 8ddba1382fb12fd781fb5ad58a6efda609311529 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 11 Feb 2013 11:22:24 +0000 Subject: That doesn't need a name either (in fact even less so, since it never ends up in the registry). --- src/rabbit_exchange_type_invalid.erl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rabbit_exchange_type_invalid.erl b/src/rabbit_exchange_type_invalid.erl index 4a48a458..6b07351a 100644 --- a/src/rabbit_exchange_type_invalid.erl +++ b/src/rabbit_exchange_type_invalid.erl @@ -24,8 +24,7 @@ add_binding/3, remove_bindings/3, assert_args_equivalence/2]). description() -> - [{name, <<"invalid">>}, - {description, + [{description, <<"Dummy exchange type, to be used when the intended one is not found.">> }]. -- cgit v1.2.1 From d4b5bfd64c43191bab8c13c122e57981c6c2eee6 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 11 Feb 2013 12:01:18 +0000 Subject: a little bit of refactoring of channel.flow code --- src/rabbit_channel.erl | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 160512a2..e74211af 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -544,14 +544,17 @@ check_name(_Kind, NameBin) -> queue_blocked(QPid, State = #ch{blocking = Blocking}) -> case sets:is_element(QPid, Blocking) of false -> State; - true -> Blocking1 = sets:del_element(QPid, Blocking), - case sets:size(Blocking1) of - 0 -> ok = send(#'channel.flow_ok'{active = false}, State); - _ -> ok - end, - State#ch{blocking = Blocking1} + true -> maybe_send_flow_ok( + State#ch{blocking = sets:del_element(QPid, Blocking)}) end. +maybe_send_flow_ok(State = #ch{blocking = Blocking}) -> + case sets:size(Blocking) of + 0 -> ok = send(#'channel.flow_ok'{active = false}, State); + _ -> ok + end, + State. + record_confirms([], State) -> State; record_confirms(MXs, State = #ch{confirmed = C}) -> @@ -1082,12 +1085,9 @@ handle_method(#'channel.flow'{active = false}, _, end, State1 = State#ch{limiter = Limiter1}, ok = rabbit_limiter:block(Limiter1), - case consumer_queues(Consumers) of - [] -> {reply, #'channel.flow_ok'{active = false}, State1}; - QPids -> State2 = State1#ch{blocking = sets:from_list(QPids)}, - ok = rabbit_amqqueue:flush_all(QPids, self()), - {noreply, State2} - end; + QPids = consumer_queues(Consumers), + ok = rabbit_amqqueue:flush_all(QPids, self()), + {noreply, maybe_send_flow_ok(State1#ch{blocking = sets:from_list(QPids)})}; handle_method(_MethodRecord, _Content, _State) -> rabbit_misc:protocol_error( -- cgit v1.2.1 From 357701e0ad63c1817e7f7290025ca3aaf5a1385c Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 11 Feb 2013 13:13:50 +0000 Subject: First pass at ha-sync-mode. --- src/rabbit_mirror_queue_misc.erl | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 05036d35..4dd50bce 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -32,6 +32,8 @@ [policy_validator, <<"ha-mode">>, ?MODULE]}}, {mfa, {rabbit_registry, register, [policy_validator, <<"ha-params">>, ?MODULE]}}, + {mfa, {rabbit_registry, register, + [policy_validator, <<"ha-sync-mode">>, ?MODULE]}}, {requires, rabbit_registry}, {enables, recovery}]}). @@ -177,13 +179,19 @@ add_mirror(QName, MirrorNode) -> end end). -start_child(Name, MirrorNode, Q) -> +start_child(Name, MirrorNode, Q = #amqqueue{pid = QPid}) -> case rabbit_misc:with_exit_handler( rabbit_misc:const({ok, down}), fun () -> rabbit_mirror_queue_slave_sup:start_child(MirrorNode, [Q]) end) of {ok, SPid} when is_pid(SPid) -> + case rabbit_policy:get(<<"ha-sync-mode">>, Q) of + {ok,<<"automatic">>} -> + spawn(fun() -> rabbit_amqqueue:sync_mirrors(QPid) end); + _ -> + ok + end, rabbit_log:info("Adding mirror of ~s on node ~p: ~p~n", [rabbit_misc:rs(Name), MirrorNode, SPid]), {ok, started}; @@ -323,9 +331,18 @@ update_mirrors0(OldQ = #amqqueue{name = QName}, %%---------------------------------------------------------------------------- validate_policy(KeyList) -> - validate_policy( - proplists:get_value(<<"ha-mode">>, KeyList), - proplists:get_value(<<"ha-params">>, KeyList, none)). + case validate_policy( + proplists:get_value(<<"ha-mode">>, KeyList), + proplists:get_value(<<"ha-params">>, KeyList, none)) of + ok -> case proplists:get_value( + <<"ha-sync-mode">>, KeyList, <<"manual">>) of + <<"automatic">> -> ok; + <<"manual">> -> ok; + Mode -> {error, "ha-sync-mode must be \"manual\" " + "or \"automatic\", got ~p", [Mode]} + end; + E -> E + end. validate_policy(<<"all">>, none) -> ok; -- cgit v1.2.1 From 9033ea545e7c8ea7ab3193bd28782f62ff700f1e Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 11 Feb 2013 13:57:48 +0000 Subject: a spot of inlining --- src/rabbit_limiter.erl | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index 35703efa..8ed1adc7 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -193,15 +193,12 @@ decr_credit(CTag, Len, ChPid, Cred, Credits) -> write_credit(CTag, NewCredit, Drain, Credits). maybe_drain(0, true, CTag, ChPid, Credit) -> - send_drained(ChPid, CTag, Credit), + rabbit_channel:send_drained(ChPid, CTag, Credit), 0; %% Magic reduction to 0 maybe_drain(_, _, _, _, Credit) -> Credit. -send_drained(ChPid, CTag, CreditDrained) -> - rabbit_channel:send_drained(ChPid, CTag, CreditDrained). - update_credit(CTag, Len, ChPid, Credit, Drain, Credits) -> NewCredit = maybe_drain(Len, Drain, CTag, ChPid, Credit), NewCredits = write_credit(CTag, NewCredit, Drain, Credits), -- cgit v1.2.1 From 3e6eb6cfeeba79356478f151cd8f166d8523ff95 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 11 Feb 2013 14:16:29 +0000 Subject: some inlining and moving around --- src/rabbit_limiter.erl | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index 8ed1adc7..46b465bc 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -181,24 +181,13 @@ copy_queue_state(#token{q_state = Credits}, Token) -> record_send_q(CTag, Len, ChPid, Credits) -> case dict:find(CTag, Credits) of - {ok, Cred} -> - decr_credit(CTag, Len, ChPid, Cred, Credits); + {ok, #credit{credit = Credit, drain = Drain}} -> + NewCredit = maybe_drain(Len - 1, Drain, CTag, ChPid, Credit - 1), + write_credit(CTag, NewCredit, Drain, Credits); error -> Credits end. -decr_credit(CTag, Len, ChPid, Cred, Credits) -> - #credit{credit = Credit, drain = Drain} = Cred, - NewCredit = maybe_drain(Len - 1, Drain, CTag, ChPid, Credit - 1), - write_credit(CTag, NewCredit, Drain, Credits). - -maybe_drain(0, true, CTag, ChPid, Credit) -> - rabbit_channel:send_drained(ChPid, CTag, Credit), - 0; %% Magic reduction to 0 - -maybe_drain(_, _, _, _, Credit) -> - Credit. - update_credit(CTag, Len, ChPid, Credit, Drain, Credits) -> NewCredit = maybe_drain(Len, Drain, CTag, ChPid, Credit), NewCredits = write_credit(CTag, NewCredit, Drain, Credits), @@ -208,8 +197,14 @@ update_credit(CTag, Len, ChPid, Credit, Drain, Credits) -> end. write_credit(CTag, Credit, Drain, Credits) -> - dict:store(CTag, #credit{credit = Credit, - drain = Drain}, Credits). + dict:store(CTag, #credit{credit = Credit, drain = Drain}, Credits). + +maybe_drain(0, true, CTag, ChPid, Credit) -> + rabbit_channel:send_drained(ChPid, CTag, Credit), + 0; %% Magic reduction to 0 + +maybe_drain(_, _, _, _, Credit) -> + Credit. %%---------------------------------------------------------------------------- %% gen_server callbacks -- cgit v1.2.1 From 7183f57012eeb61d6de136548db5ef50abdb1d37 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 11 Feb 2013 17:44:40 +0000 Subject: Tweak the message to make it slightly more obvious if it has hung. --- src/rabbit.erl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 73399c03..a32acbc2 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -704,7 +704,7 @@ log_broker_started(Plugins) -> error_logger:info_msg( "Server startup complete; ~b plugins started.~n~s~n", [length(Plugins), PluginList]), - io:format("~n Broker running with ~p plugins.~n", + io:format(" startup complete with ~p plugins.~n", [length(Plugins)]) end). @@ -723,7 +723,9 @@ print_banner() -> "~n## ## ~s" "~n##########" "~n###### ## Logs: ~s" - "~n########## ~s~n", + "~n########## ~s" + "~n" + "~n Starting...", [Product, Version, ?COPYRIGHT_MESSAGE, ?INFORMATION_MESSAGE, log_location(kernel), log_location(sasl)]). -- cgit v1.2.1 From a5433ec58fd8fe908a73d0aa45e6a99f56049736 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 11 Feb 2013 18:38:49 +0000 Subject: minor optimisation --- src/rabbit_amqqueue_process.erl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 0594e250..88d13290 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -621,11 +621,12 @@ possibly_unblock(State, ChPid, Update) -> not_found -> State; C -> - C1 = #cr{blocked_ctags = BCTags1} = Update(C), + C1 = #cr{blocked_ctags = BCTags} = Update(C), + IsBlocked = is_ch_blocked(C1), {Blocked, Unblocked} = lists:partition( fun({_ChPid, #consumer{tag = CTag}}) -> - is_ch_blocked(C1) orelse lists:member(CTag, BCTags1) + IsBlocked orelse lists:member(CTag, BCTags) end, queue:to_list(C1#cr.blocked_consumers)), case Unblocked of [] -> update_ch_record(C1), -- cgit v1.2.1 From 5d1494b77057c472e10b77957b31c14f926af719 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 12 Feb 2013 09:35:45 +0000 Subject: startup logging tweaks --- src/rabbit.erl | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index a32acbc2..55736b28 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -435,8 +435,9 @@ start(normal, []) -> case erts_version_check() of ok -> {ok, Vsn} = application:get_key(rabbit, vsn), - error_logger:info_msg("Starting RabbitMQ ~s on Erlang ~s~n", - [Vsn, erlang:system_info(otp_release)]), + error_logger:info_msg("Starting RabbitMQ ~s on Erlang ~s~n~s~n~s~n", + [Vsn, erlang:system_info(otp_release), + ?COPYRIGHT_MESSAGE, ?INFORMATION_MESSAGE]), {ok, SupPid} = rabbit_sup:start_link(), true = register(rabbit, self()), print_banner(), @@ -704,8 +705,7 @@ log_broker_started(Plugins) -> error_logger:info_msg( "Server startup complete; ~b plugins started.~n~s~n", [length(Plugins), PluginList]), - io:format(" startup complete with ~p plugins.~n", - [length(Plugins)]) + io:format(" completed with ~p plugins.~n", [length(Plugins)]) end). erts_version_check() -> @@ -725,13 +725,11 @@ print_banner() -> "~n###### ## Logs: ~s" "~n########## ~s" "~n" - "~n Starting...", + "~n Starting broker...", [Product, Version, ?COPYRIGHT_MESSAGE, ?INFORMATION_MESSAGE, log_location(kernel), log_location(sasl)]). log_banner() -> - error_logger:info_msg("~s ~s~n", - [?COPYRIGHT_MESSAGE, ?INFORMATION_MESSAGE]), Settings = [{"node", node()}, {"home dir", home_dir()}, {"config file(s)", config_files()}, -- cgit v1.2.1 From 6f4290c5279bb1ac8c954bda6a1a97af9d437ec0 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 12 Feb 2013 13:30:30 +0000 Subject: Move rabbit_channel:send_drained/2 invocations into the queue module, and make sure we send drained for all consumers in drain mode. --- src/rabbit_amqqueue_process.erl | 43 +++++++++++++++++---------- src/rabbit_channel.erl | 17 ++++++----- src/rabbit_limiter.erl | 65 +++++++++++++++++++---------------------- 3 files changed, 67 insertions(+), 58 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 88d13290..f5648eca 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -405,6 +405,21 @@ erase_ch_record(#cr{ch_pid = ChPid, erase({ch, ChPid}), ok. +maybe_send_drained(#q{backing_queue = BQ, + backing_queue_state = BQS}) -> + case BQ:len(BQS) of + 0 -> [maybe_send_drained(C) || C <- all_ch_record()]; + _ -> ok + end; +maybe_send_drained(C = #cr{ch_pid = ChPid, limiter = Limiter}) -> + case rabbit_limiter:drained(Limiter) of + {[], Limiter} -> + ok; + {CTagCredit, Limiter2} -> + rabbit_channel:send_drained(ChPid, CTagCredit), + update_ch_record(C#cr{limiter = Limiter2}) + end. + update_consumer_count(C = #cr{consumer_count = 0, limiter = Limiter}, +1) -> ok = rabbit_limiter:register(Limiter, self()), update_ch_record(C#cr{consumer_count = 1}); @@ -437,9 +452,7 @@ deliver_msgs_to_consumers(DeliverFun, false, deliver_msgs_to_consumers(DeliverFun, Stop, State1) end. -deliver_msg_to_consumer(DeliverFun, E = {ChPid, Consumer}, - State = #q{backing_queue = BQ, - backing_queue_state = BQS}) -> +deliver_msg_to_consumer(DeliverFun, E = {ChPid, Consumer}, State) -> C = ch_record(ChPid), case is_ch_blocked(C) of true -> @@ -449,8 +462,7 @@ deliver_msg_to_consumer(DeliverFun, E = {ChPid, Consumer}, #cr{limiter = Limiter, ch_pid = ChPid, blocked_ctags = BCTags} = C, #consumer{tag = CTag} = Consumer, case rabbit_limiter:can_send( - Limiter, self(), Consumer#consumer.ack_required, - ChPid, CTag, BQ:len(BQS)) of + Limiter, self(), Consumer#consumer.ack_required, CTag) of consumer_blocked -> block_consumer(C#cr{blocked_ctags = [CTag | BCTags]}, E), {false, State}; @@ -483,6 +495,7 @@ deliver_msg_to_consumer(DeliverFun, NewLimiter, update_ch_record(C#cr{acktags = ChAckTags1, limiter = NewLimiter, unsent_message_count = Count + 1}), + maybe_send_drained(State1), {Stop, State1}. deliver_from_queue_deliver(AckRequired, State) -> @@ -1098,9 +1111,7 @@ handle_call({basic_get, ChPid, NoAck}, _From, handle_call({basic_consume, NoAck, ChPid, Limiter, ConsumerTag, ExclusiveConsume, CreditArgs, OkMsg}, - _From, State = #q{exclusive_consumer = Holder, - backing_queue = BQ, - backing_queue_state = BQS}) -> + _From, State = #q{exclusive_consumer = Holder}) -> case check_exclusive_access(Holder, ExclusiveConsume, State) of in_use -> reply({error, exclusive_consume_unavailable}, State); @@ -1111,8 +1122,7 @@ handle_call({basic_consume, NoAck, ChPid, Limiter, Limiter; {Credit, Drain} -> rabbit_limiter:initial_credit( - Limiter, ChPid, ConsumerTag, Credit, Drain, - BQ:len(BQS)) + Limiter, ConsumerTag, Credit, Drain) end, C1 = update_consumer_count(C#cr{limiter = Limiter2}, +1), Consumer = #consumer{tag = ConsumerTag, @@ -1132,6 +1142,7 @@ handle_call({basic_consume, NoAck, ChPid, Limiter, AC1 = queue:in(E, State1#q.active_consumers), run_message_queue(State1#q{active_consumers = AC1}) end, + maybe_send_drained(State2), emit_consumer_created(ChPid, ConsumerTag, ExclusiveConsume, not NoAck, qname(State2)), reply(ok, State2) @@ -1342,11 +1353,13 @@ handle_cast({credit, ChPid, CTag, Credit, Drain}, backing_queue_state = BQS}) -> #cr{limiter = Lim, blocked_ctags = BCTags} = ch_record(ChPid), - {Unblock, Lim2} = rabbit_limiter:credit( - Lim, ChPid, CTag, Credit, Drain, BQ:len(BQS)), - noreply(possibly_unblock( - State, ChPid, fun(C) -> C#cr{blocked_ctags = BCTags -- Unblock, - limiter = Lim2} end)); + {Unblock, Lim2} = rabbit_limiter:credit(Lim, CTag, Credit, Drain), + rabbit_channel:send_credit_reply(ChPid, BQ:len(BQS)), + State1 = possibly_unblock( + State, ChPid, fun(C) -> C#cr{blocked_ctags = BCTags -- Unblock, + limiter = Lim2} end), + maybe_send_drained(State1), + noreply(State1); handle_cast(wake_up, State) -> noreply(State). diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index c1eb126c..aed25344 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -21,7 +21,7 @@ -behaviour(gen_server2). -export([start_link/11, do/2, do/3, do_flow/3, flush/1, shutdown/1]). --export([send_command/2, deliver/4, send_credit_reply/2, send_drained/3, +-export([send_command/2, deliver/4, send_credit_reply/2, send_drained/2, flushed/2]). -export([list/0, info_keys/0, info/1, info/2, info_all/0, info_all/1]). -export([refresh_config_local/0, ready_for_close/1]). @@ -96,7 +96,7 @@ (pid(), rabbit_types:ctag(), boolean(), rabbit_amqqueue:qmsg()) -> 'ok'). -spec(send_credit_reply/2 :: (pid(), non_neg_integer()) -> 'ok'). --spec(send_drained/3 :: (pid(), rabbit_types:ctag(), non_neg_integer()) +-spec(send_drained/2 :: (pid(), [{rabbit_types:ctag(), non_neg_integer()}]) -> 'ok'). -spec(flushed/2 :: (pid(), pid()) -> 'ok'). -spec(list/0 :: () -> [pid()]). @@ -145,8 +145,8 @@ deliver(Pid, ConsumerTag, AckRequired, Msg) -> send_credit_reply(Pid, Len) -> gen_server2:cast(Pid, {send_credit_reply, Len}). -send_drained(Pid, ConsumerTag, CreditDrained) -> - gen_server2:cast(Pid, {send_drained, ConsumerTag, CreditDrained}). +send_drained(Pid, CTagCredit) -> + gen_server2:cast(Pid, {send_drained, CTagCredit}). flushed(Pid, QPid) -> gen_server2:cast(Pid, {flushed, QPid}). @@ -330,11 +330,12 @@ handle_cast({send_credit_reply, Len}, State = #ch{writer_pid = WriterPid}) -> WriterPid, #'basic.credit_ok'{available = Len}), noreply(State); -handle_cast({send_drained, ConsumerTag, CreditDrained}, +handle_cast({send_drained, CTagCredit}, State = #ch{writer_pid = WriterPid}) -> - ok = rabbit_writer:send_command( - WriterPid, #'basic.credit_drained'{consumer_tag = ConsumerTag, - credit_drained = CreditDrained}), + [ok = rabbit_writer:send_command( + WriterPid, #'basic.credit_drained'{consumer_tag = ConsumerTag, + credit_drained = CreditDrained}) + || {ConsumerTag, CreditDrained} <- CTagCredit], noreply(State); handle_cast(force_event_refresh, State) -> diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index 46b465bc..1ee5448e 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -22,9 +22,10 @@ handle_info/2, prioritise_call/3]). -export([start_link/0, make_token/0, make_token/1, is_enabled/1, enable/2, disable/1]). --export([limit/2, can_send/6, ack/2, register/2, unregister/2]). +-export([limit/2, can_send/4, ack/2, register/2, unregister/2]). -export([get_limit/1, block/1, unblock/1, is_blocked/1]). --export([initial_credit/6, credit/6, forget_consumer/2, copy_queue_state/2]). +-export([initial_credit/4, credit/4, drained/1, forget_consumer/2, + copy_queue_state/2]). -import(rabbit_misc, [serial_add/2, serial_diff/2]). @@ -45,8 +46,7 @@ -spec(enable/2 :: (token(), non_neg_integer()) -> token()). -spec(disable/1 :: (token()) -> token()). -spec(limit/2 :: (token(), non_neg_integer()) -> 'ok' | {'disabled', token()}). --spec(can_send/6 :: (token(), pid(), boolean(), pid(), rabbit_types:ctag(), - non_neg_integer()) +-spec(can_send/4 :: (token(), pid(), boolean(), rabbit_types:ctag()) -> token() | 'consumer_blocked' | 'channel_blocked'). -spec(ack/2 :: (token(), non_neg_integer()) -> 'ok'). -spec(register/2 :: (token(), pid()) -> 'ok'). @@ -55,12 +55,12 @@ -spec(block/1 :: (token()) -> 'ok'). -spec(unblock/1 :: (token()) -> 'ok' | {'disabled', token()}). -spec(is_blocked/1 :: (token()) -> boolean()). --spec(initial_credit/6 :: (token(), pid(), rabbit_types:ctag(), - non_neg_integer(), boolean(), non_neg_integer()) - -> token()). --spec(credit/6 :: (token(), pid(), rabbit_types:ctag(), - non_neg_integer(), boolean(), non_neg_integer()) +-spec(initial_credit/4 :: (token(), rabbit_types:ctag(), + non_neg_integer(), boolean()) -> token()). +-spec(credit/4 :: (token(), rabbit_types:ctag(), non_neg_integer(), boolean()) -> {[rabbit_types:ctag()], token()}). +-spec(drained/1 :: (token()) + -> {[{rabbit_types:ctag(), non_neg_integer()}], token()}). -spec(forget_consumer/2 :: (token(), rabbit_types:ctag()) -> token()). -spec(copy_queue_state/2 :: (token(), token()) -> token()). @@ -105,7 +105,7 @@ limit(Limiter, PrefetchCount) -> %% to avoid always going through with_exit_handler/2, even when the %% limiter is disabled. can_send(Token = #token{pid = Pid, enabled = Enabled, q_state = Credits}, - QPid, AckReq, ChPid, CTag, Len) -> + QPid, AckReq, CTag) -> ConsAllows = case dict:find(CTag, Credits) of {ok, #credit{credit = C}} when C > 0 -> true; {ok, #credit{}} -> false; @@ -113,8 +113,7 @@ can_send(Token = #token{pid = Pid, enabled = Enabled, q_state = Credits}, end, case ConsAllows of true -> case not Enabled orelse call_can_send(Pid, QPid, AckReq) of - true -> Credits2 = record_send_q( - CTag, Len, ChPid, Credits), + true -> Credits2 = record_send_q(CTag, Credits), Token#token{q_state = Credits2}; false -> channel_blocked end; @@ -150,19 +149,24 @@ unblock(Limiter) -> is_blocked(Limiter) -> maybe_call(Limiter, is_blocked, false). -initial_credit(Limiter = #token{q_state = Credits}, - ChPid, CTag, Credit, Drain, Len) -> - {[], Credits2} = update_credit( - CTag, Len, ChPid, Credit, Drain, Credits), +initial_credit(Limiter = #token{q_state = Credits}, CTag, Credit, Drain) -> + {[], Credits2} = update_credit(CTag, Credit, Drain, Credits), Limiter#token{q_state = Credits2}. -credit(Limiter = #token{q_state = Credits}, - ChPid, CTag, Credit, Drain, Len) -> - {Unblock, Credits2} = update_credit( - CTag, Len, ChPid, Credit, Drain, Credits), - rabbit_channel:send_credit_reply(ChPid, Len), +credit(Limiter = #token{q_state = Credits}, CTag, Credit, Drain) -> + {Unblock, Credits2} = update_credit(CTag, Credit, Drain, Credits), {Unblock, Limiter#token{q_state = Credits2}}. +drained(Limiter = #token{q_state = Credits}) -> + {CTagCredits, Credits2} = + dict:fold( + fun (CTag, #credit{credit = C, drain = true}, {Acc, Creds0}) -> + {[{CTag, C} | Acc], write_credit(CTag, 0, false, Creds0)}; + (_CTag, #credit{credit = _C, drain = false}, {Acc, Creds0}) -> + {Acc, Creds0} + end, {[], Credits}, Credits), + {CTagCredits, Limiter#token{q_state = Credits2}}. + forget_consumer(Limiter = #token{q_state = Credits}, CTag) -> Limiter#token{q_state = dict:erase(CTag, Credits)}. @@ -179,19 +183,17 @@ copy_queue_state(#token{q_state = Credits}, Token) -> %% we get the queue to hold a bit of state for us (#token.q_state), and %% maintain a fiction that the limiter is making the decisions... -record_send_q(CTag, Len, ChPid, Credits) -> +record_send_q(CTag, Credits) -> case dict:find(CTag, Credits) of {ok, #credit{credit = Credit, drain = Drain}} -> - NewCredit = maybe_drain(Len - 1, Drain, CTag, ChPid, Credit - 1), - write_credit(CTag, NewCredit, Drain, Credits); + write_credit(CTag, Credit, Drain, Credits); error -> Credits end. -update_credit(CTag, Len, ChPid, Credit, Drain, Credits) -> - NewCredit = maybe_drain(Len, Drain, CTag, ChPid, Credit), - NewCredits = write_credit(CTag, NewCredit, Drain, Credits), - case NewCredit > 0 of +update_credit(CTag, Credit, Drain, Credits) -> + NewCredits = write_credit(CTag, Credit, Drain, Credits), + case Credit > 0 of true -> {[CTag], NewCredits}; false -> {[], NewCredits} end. @@ -199,13 +201,6 @@ update_credit(CTag, Len, ChPid, Credit, Drain, Credits) -> write_credit(CTag, Credit, Drain, Credits) -> dict:store(CTag, #credit{credit = Credit, drain = Drain}, Credits). -maybe_drain(0, true, CTag, ChPid, Credit) -> - rabbit_channel:send_drained(ChPid, CTag, Credit), - 0; %% Magic reduction to 0 - -maybe_drain(_, _, _, _, Credit) -> - Credit. - %%---------------------------------------------------------------------------- %% gen_server callbacks %%---------------------------------------------------------------------------- -- cgit v1.2.1 From 966b9a29d54321e965efd3a9e5a0112da4ed2e52 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 12 Feb 2013 14:39:23 +0000 Subject: s/q_state/credits/g --- src/rabbit_limiter.erl | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index 1ee5448e..0d836ca6 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -31,7 +31,7 @@ %%---------------------------------------------------------------------------- --record(token, {pid, enabled, q_state}). +-record(token, {pid, enabled, credits}). -ifdef(use_specs). @@ -87,7 +87,7 @@ start_link() -> gen_server2:start_link(?MODULE, [], []). make_token() -> make_token(undefined). make_token(Pid) -> #token{pid = Pid, enabled = false, - q_state = dict:new()}. + credits = dict:new()}. is_enabled(#token{enabled = Enabled}) -> Enabled. @@ -104,7 +104,7 @@ limit(Limiter, PrefetchCount) -> %% breaching a limit. Note that we don't use maybe_call here in order %% to avoid always going through with_exit_handler/2, even when the %% limiter is disabled. -can_send(Token = #token{pid = Pid, enabled = Enabled, q_state = Credits}, +can_send(Token = #token{pid = Pid, enabled = Enabled, credits = Credits}, QPid, AckReq, CTag) -> ConsAllows = case dict:find(CTag, Credits) of {ok, #credit{credit = C}} when C > 0 -> true; @@ -114,7 +114,7 @@ can_send(Token = #token{pid = Pid, enabled = Enabled, q_state = Credits}, case ConsAllows of true -> case not Enabled orelse call_can_send(Pid, QPid, AckReq) of true -> Credits2 = record_send_q(CTag, Credits), - Token#token{q_state = Credits2}; + Token#token{credits = Credits2}; false -> channel_blocked end; false -> consumer_blocked @@ -149,15 +149,15 @@ unblock(Limiter) -> is_blocked(Limiter) -> maybe_call(Limiter, is_blocked, false). -initial_credit(Limiter = #token{q_state = Credits}, CTag, Credit, Drain) -> +initial_credit(Limiter = #token{credits = Credits}, CTag, Credit, Drain) -> {[], Credits2} = update_credit(CTag, Credit, Drain, Credits), - Limiter#token{q_state = Credits2}. + Limiter#token{credits = Credits2}. -credit(Limiter = #token{q_state = Credits}, CTag, Credit, Drain) -> +credit(Limiter = #token{credits = Credits}, CTag, Credit, Drain) -> {Unblock, Credits2} = update_credit(CTag, Credit, Drain, Credits), - {Unblock, Limiter#token{q_state = Credits2}}. + {Unblock, Limiter#token{credits = Credits2}}. -drained(Limiter = #token{q_state = Credits}) -> +drained(Limiter = #token{credits = Credits}) -> {CTagCredits, Credits2} = dict:fold( fun (CTag, #credit{credit = C, drain = true}, {Acc, Creds0}) -> @@ -165,13 +165,13 @@ drained(Limiter = #token{q_state = Credits}) -> (_CTag, #credit{credit = _C, drain = false}, {Acc, Creds0}) -> {Acc, Creds0} end, {[], Credits}, Credits), - {CTagCredits, Limiter#token{q_state = Credits2}}. + {CTagCredits, Limiter#token{credits = Credits2}}. -forget_consumer(Limiter = #token{q_state = Credits}, CTag) -> - Limiter#token{q_state = dict:erase(CTag, Credits)}. +forget_consumer(Limiter = #token{credits = Credits}, CTag) -> + Limiter#token{credits = dict:erase(CTag, Credits)}. -copy_queue_state(#token{q_state = Credits}, Token) -> - Token#token{q_state = Credits}. +copy_queue_state(#token{credits = Credits}, Token) -> + Token#token{credits = Credits}. %%---------------------------------------------------------------------------- %% Queue-local code @@ -180,7 +180,7 @@ copy_queue_state(#token{q_state = Credits}, Token) -> %% We want to do all the AMQP 1.0-ish link level credit calculations in the %% queue (to do them elsewhere introduces a ton of races). However, it's a big %% chunk of code that is conceptually very linked to the limiter concept. So -%% we get the queue to hold a bit of state for us (#token.q_state), and +%% we get the queue to hold a bit of state for us (#token.credits), and %% maintain a fiction that the limiter is making the decisions... record_send_q(CTag, Credits) -> -- cgit v1.2.1 From 4fe1e2e497cff37bc578775f89b6ca7361ff4c3a Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 12 Feb 2013 14:55:13 +0000 Subject: Move blocked_ctags into the limiter. --- src/rabbit_amqqueue_process.erl | 24 +++++++++--------------- src/rabbit_limiter.erl | 31 ++++++++++++++++++++----------- 2 files changed, 29 insertions(+), 26 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index f5648eca..d932d1b6 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -68,9 +68,6 @@ %% Queue of {ChPid, #consumer{}} for consumers which have %% been blocked for any reason blocked_consumers, - %% List of consumer tags which have individually been - %% blocked by the limiter. - blocked_ctags, %% The limiter itself limiter, %% Has the limiter imposed a channel-wide block, either @@ -375,7 +372,6 @@ ch_record(ChPid) -> acktags = queue:new(), consumer_count = 0, blocked_consumers = queue:new(), - blocked_ctags = [], is_limit_active = false, limiter = rabbit_limiter:make_token(), unsent_message_count = 0}, @@ -459,12 +455,12 @@ deliver_msg_to_consumer(DeliverFun, E = {ChPid, Consumer}, State) -> block_consumer(C, E), {false, State}; false -> - #cr{limiter = Limiter, ch_pid = ChPid, blocked_ctags = BCTags} = C, + #cr{limiter = Limiter, ch_pid = ChPid} = C, #consumer{tag = CTag} = Consumer, case rabbit_limiter:can_send( Limiter, self(), Consumer#consumer.ack_required, CTag) of - consumer_blocked -> - block_consumer(C#cr{blocked_ctags = [CTag | BCTags]}, E), + {consumer_blocked, Limiter2} -> + block_consumer(C#cr{limiter = Limiter2}, E), {false, State}; channel_blocked -> block_consumer(C#cr{is_limit_active = true}, E), @@ -634,12 +630,12 @@ possibly_unblock(State, ChPid, Update) -> not_found -> State; C -> - C1 = #cr{blocked_ctags = BCTags} = Update(C), - IsBlocked = is_ch_blocked(C1), + C1 = #cr{limiter = Limiter} = Update(C), {Blocked, Unblocked} = lists:partition( fun({_ChPid, #consumer{tag = CTag}}) -> - IsBlocked orelse lists:member(CTag, BCTags) + is_ch_blocked(C1) orelse + rabbit_limiter:is_consumer_blocked(Limiter, CTag) end, queue:to_list(C1#cr.blocked_consumers)), case Unblocked of [] -> update_ch_record(C1), @@ -1351,13 +1347,11 @@ handle_cast(stop_mirroring, State = #q{backing_queue = BQ, handle_cast({credit, ChPid, CTag, Credit, Drain}, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> - #cr{limiter = Lim, - blocked_ctags = BCTags} = ch_record(ChPid), - {Unblock, Lim2} = rabbit_limiter:credit(Lim, CTag, Credit, Drain), + #cr{limiter = Lim} = ch_record(ChPid), + Lim2 = rabbit_limiter:credit(Lim, CTag, Credit, Drain), rabbit_channel:send_credit_reply(ChPid, BQ:len(BQS)), State1 = possibly_unblock( - State, ChPid, fun(C) -> C#cr{blocked_ctags = BCTags -- Unblock, - limiter = Lim2} end), + State, ChPid, fun(C) -> C#cr{limiter = Lim2} end), maybe_send_drained(State1), noreply(State1); diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index 0d836ca6..5774aee0 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -23,7 +23,7 @@ -export([start_link/0, make_token/0, make_token/1, is_enabled/1, enable/2, disable/1]). -export([limit/2, can_send/4, ack/2, register/2, unregister/2]). --export([get_limit/1, block/1, unblock/1, is_blocked/1]). +-export([get_limit/1, block/1, unblock/1, is_consumer_blocked/2, is_blocked/1]). -export([initial_credit/4, credit/4, drained/1, forget_consumer/2, copy_queue_state/2]). @@ -31,7 +31,7 @@ %%---------------------------------------------------------------------------- --record(token, {pid, enabled, credits}). +-record(token, {pid, enabled, credits, blocked_ctags}). -ifdef(use_specs). @@ -55,10 +55,11 @@ -spec(block/1 :: (token()) -> 'ok'). -spec(unblock/1 :: (token()) -> 'ok' | {'disabled', token()}). -spec(is_blocked/1 :: (token()) -> boolean()). +-spec(is_consumer_blocked/2 :: (token(), rabbit_types:ctag()) -> boolean()). -spec(initial_credit/4 :: (token(), rabbit_types:ctag(), non_neg_integer(), boolean()) -> token()). -spec(credit/4 :: (token(), rabbit_types:ctag(), non_neg_integer(), boolean()) - -> {[rabbit_types:ctag()], token()}). + -> token()). -spec(drained/1 :: (token()) -> {[{rabbit_types:ctag(), non_neg_integer()}], token()}). -spec(forget_consumer/2 :: (token(), rabbit_types:ctag()) -> token()). @@ -86,8 +87,10 @@ start_link() -> gen_server2:start_link(?MODULE, [], []). make_token() -> make_token(undefined). -make_token(Pid) -> #token{pid = Pid, enabled = false, - credits = dict:new()}. +make_token(Pid) -> #token{pid = Pid, + enabled = false, + credits = dict:new(), + blocked_ctags = []}. is_enabled(#token{enabled = Enabled}) -> Enabled. @@ -104,7 +107,8 @@ limit(Limiter, PrefetchCount) -> %% breaching a limit. Note that we don't use maybe_call here in order %% to avoid always going through with_exit_handler/2, even when the %% limiter is disabled. -can_send(Token = #token{pid = Pid, enabled = Enabled, credits = Credits}, +can_send(Token = #token{pid = Pid, enabled = Enabled, credits = Credits, + blocked_ctags = BCTags}, QPid, AckReq, CTag) -> ConsAllows = case dict:find(CTag, Credits) of {ok, #credit{credit = C}} when C > 0 -> true; @@ -117,7 +121,7 @@ can_send(Token = #token{pid = Pid, enabled = Enabled, credits = Credits}, Token#token{credits = Credits2}; false -> channel_blocked end; - false -> consumer_blocked + false -> {consumer_blocked, Token#token{blocked_ctags = [CTag|BCTags]}} end. call_can_send(Pid, QPid, AckRequired) -> @@ -146,6 +150,9 @@ block(Limiter) -> unblock(Limiter) -> maybe_call(Limiter, {unblock, Limiter}, ok). +is_consumer_blocked(#token{blocked_ctags = BCTags}, CTag) -> + lists:member(CTag, BCTags). + is_blocked(Limiter) -> maybe_call(Limiter, is_blocked, false). @@ -153,9 +160,11 @@ initial_credit(Limiter = #token{credits = Credits}, CTag, Credit, Drain) -> {[], Credits2} = update_credit(CTag, Credit, Drain, Credits), Limiter#token{credits = Credits2}. -credit(Limiter = #token{credits = Credits}, CTag, Credit, Drain) -> +credit(Limiter = #token{credits = Credits, blocked_ctags = BCTags}, + CTag, Credit, Drain) -> {Unblock, Credits2} = update_credit(CTag, Credit, Drain, Credits), - {Unblock, Limiter#token{credits = Credits2}}. + Limiter#token{credits = Credits2, + blocked_ctags = BCTags -- Unblock}. drained(Limiter = #token{credits = Credits}) -> {CTagCredits, Credits2} = @@ -170,8 +179,8 @@ drained(Limiter = #token{credits = Credits}) -> forget_consumer(Limiter = #token{credits = Credits}, CTag) -> Limiter#token{credits = dict:erase(CTag, Credits)}. -copy_queue_state(#token{credits = Credits}, Token) -> - Token#token{credits = Credits}. +copy_queue_state(#token{credits = Credits, blocked_ctags = BCTags}, Token) -> + Token#token{credits = Credits, blocked_ctags = BCTags}. %%---------------------------------------------------------------------------- %% Queue-local code -- cgit v1.2.1 From 7703c46ae0313f203cc2dfbc0f11348001967629 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 12 Feb 2013 14:57:59 +0000 Subject: Remove tags from blocked_ctags when a consumer goes away. --- src/rabbit_limiter.erl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index 5774aee0..efc499cd 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -176,8 +176,10 @@ drained(Limiter = #token{credits = Credits}) -> end, {[], Credits}, Credits), {CTagCredits, Limiter#token{credits = Credits2}}. -forget_consumer(Limiter = #token{credits = Credits}, CTag) -> - Limiter#token{credits = dict:erase(CTag, Credits)}. +forget_consumer(Limiter = #token{credits = Credits, + blocked_ctags = BCTags}, CTag) -> + Limiter#token{credits = dict:erase(CTag, Credits), + blocked_ctags = BCTags -- [CTag]}. copy_queue_state(#token{credits = Credits, blocked_ctags = BCTags}, Token) -> Token#token{credits = Credits, blocked_ctags = BCTags}. -- cgit v1.2.1 From 30272f48513eb72dde747709338e7e50000ab2a4 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 12 Feb 2013 16:26:23 +0000 Subject: Clear drain flag when we run out of credit. --- src/rabbit_limiter.erl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index efc499cd..e76fc217 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -209,8 +209,11 @@ update_credit(CTag, Credit, Drain, Credits) -> false -> {[], NewCredits} end. -write_credit(CTag, Credit, Drain, Credits) -> - dict:store(CTag, #credit{credit = Credit, drain = Drain}, Credits). +write_credit(CTag, Credit, Drain, Credits) when Credit > 0 -> + dict:store(CTag, #credit{credit = Credit, drain = Drain}, Credits); +%% Using up all credit means we do not need to send a drained event +write_credit(CTag, Credit, _Drain, Credits) -> + dict:store(CTag, #credit{credit = Credit, drain = false}, Credits). %%---------------------------------------------------------------------------- %% gen_server callbacks -- cgit v1.2.1 From 023d62f9efb9f54a973454b7e86f6ea150c96eb1 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 12 Feb 2013 16:50:25 +0000 Subject: simplify & optimise maybe_send_drained - use BQ:is_empty instead of BQ:len - make use of Stop flag --- src/rabbit_amqqueue_process.erl | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index d932d1b6..35f19493 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -401,13 +401,15 @@ erase_ch_record(#cr{ch_pid = ChPid, erase({ch, ChPid}), ok. -maybe_send_drained(#q{backing_queue = BQ, - backing_queue_state = BQS}) -> - case BQ:len(BQS) of - 0 -> [maybe_send_drained(C) || C <- all_ch_record()]; - _ -> ok - end; -maybe_send_drained(C = #cr{ch_pid = ChPid, limiter = Limiter}) -> +maybe_send_drained(#q{backing_queue = BQ, backing_queue_state = BQS}) -> + case BQ:is_empty(BQS) of + true -> send_drained(); + false -> ok + end. + +send_drained() -> [send_drained(C) || C <- all_ch_record()]. + +send_drained(C = #cr{ch_pid = ChPid, limiter = Limiter}) -> case rabbit_limiter:drained(Limiter) of {[], Limiter} -> ok; @@ -435,6 +437,7 @@ is_ch_blocked(#cr{unsent_message_count = Count, is_limit_active = Limited}) -> Limited orelse Count >= ?UNSENT_MESSAGE_LIMIT. deliver_msgs_to_consumers(_DeliverFun, true, State) -> + send_drained(), {true, State}; deliver_msgs_to_consumers(DeliverFun, false, State = #q{active_consumers = ActiveConsumers}) -> @@ -491,7 +494,6 @@ deliver_msg_to_consumer(DeliverFun, NewLimiter, update_ch_record(C#cr{acktags = ChAckTags1, limiter = NewLimiter, unsent_message_count = Count + 1}), - maybe_send_drained(State1), {Stop, State1}. deliver_from_queue_deliver(AckRequired, State) -> -- cgit v1.2.1 From eae2569cdced54ac44f2119cf4e7ffb876347cb6 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 12 Feb 2013 17:46:50 +0000 Subject: optimise --- src/rabbit_amqqueue_process.erl | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index fe3a6099..3bae4cfb 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -714,7 +714,13 @@ calculate_msg_expiry(#basic_message{content = Content}, TTL) -> drop_expired_msgs(State = #q{backing_queue_state = BQS, backing_queue = BQ }) -> - Now = now_micros(), + case BQ:is_empty(BQS) of + true -> State; + false -> drop_expired_msgs(now_micros(), State) + end. + +drop_expired_msgs(Now, State = #q{backing_queue_state = BQS, + backing_queue = BQ }) -> ExpirePred = fun (#message_properties{expiry = Exp}) -> Now >= Exp end, {Props, State1} = with_dlx( -- cgit v1.2.1 From 37dd46271a805a90b49d993d49ade8b70c47f1ef Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 12 Feb 2013 18:04:31 +0000 Subject: Tweak nodes policy to allow master removal and thus queue migration --- src/rabbit_mirror_queue_misc.erl | 49 ++++++++++++++++++++++++--------------- src/rabbit_mirror_queue_slave.erl | 27 ++++++++++++--------- src/rabbit_tests.erl | 46 +++++++++++++++++++++--------------- 3 files changed, 73 insertions(+), 49 deletions(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 05036d35..cc2d7c77 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -235,13 +235,13 @@ suggested_queue_nodes(Q) -> %% rabbit_mnesia:cluster_nodes(running) out of a loop or %% transaction or both. suggested_queue_nodes(Q, PossibleNodes) -> - {MNode0, SNodes} = actual_queue_nodes(Q), + {MNode0, SNodes, SSNodes} = actual_queue_nodes(Q), MNode = case MNode0 of none -> node(); _ -> MNode0 end, suggested_queue_nodes(policy(<<"ha-mode">>, Q), policy(<<"ha-params">>, Q), - {MNode, SNodes}, PossibleNodes). + {MNode, SNodes, SSNodes}, PossibleNodes). policy(Policy, Q) -> case rabbit_policy:get(Policy, Q) of @@ -249,15 +249,20 @@ policy(Policy, Q) -> _ -> none end. -suggested_queue_nodes(<<"all">>, _Params, {MNode, _SNodes}, Possible) -> - {MNode, Possible -- [MNode]}; -suggested_queue_nodes(<<"nodes">>, Nodes0, {MNode, _SNodes}, Possible) -> +suggested_queue_nodes(<<"all">>, _Params, {MNode, _SNodes, _SSNodes}, Poss) -> + {MNode, Poss -- [MNode]}; +suggested_queue_nodes(<<"nodes">>, Nodes0, {MNode, _SNodes, SSNodes}, Poss) -> Nodes1 = [list_to_atom(binary_to_list(Node)) || Node <- Nodes0], - %% If the current master is currently not in the nodes specified, - %% act like it is for the purposes below - otherwise we will not - %% return it in the results... - Nodes = lists:usort([MNode | Nodes1]), - Unavailable = Nodes -- Possible, + %% If the current master is not in the nodes specified, then what we want + %% to do depends on whether there are any synchronised slaves. If there + %% are then we can just kill the current master - the admin has asked for + %% a migration and we should give it to them. If there are not however + %% then we must keep the master around so as not to lose messages. + Nodes = case SSNodes of + [] -> lists:usort([MNode | Nodes1]); + _ -> Nodes1 + end, + Unavailable = Nodes -- Poss, Available = Nodes -- Unavailable, case Available of [] -> %% We have never heard of anything? Not much we can do but @@ -265,21 +270,24 @@ suggested_queue_nodes(<<"nodes">>, Nodes0, {MNode, _SNodes}, Possible) -> {MNode, []}; _ -> case lists:member(MNode, Available) of true -> {MNode, Available -- [MNode]}; - false -> promote_slave(Available) + false -> %% Make the sure new master is synced! In order to + %% get here SSNodes must not be empty. + [NewMNode | _] = SSNodes, + {NewMNode, Available -- [NewMNode]} end end; %% When we need to add nodes, we randomise our candidate list as a %% crude form of load-balancing. TODO it would also be nice to -%% randomise the list of ones to remove when we have too many - but -%% that would fail to take account of synchronisation... -suggested_queue_nodes(<<"exactly">>, Count, {MNode, SNodes}, Possible) -> +%% randomise the list of ones to remove when we have too many - we +%% would have to take account of synchronisation though. +suggested_queue_nodes(<<"exactly">>, Count, {MNode, SNodes, _SSNodes}, Poss) -> SCount = Count - 1, {MNode, case SCount > length(SNodes) of - true -> Cand = shuffle((Possible -- [MNode]) -- SNodes), + true -> Cand = shuffle((Poss -- [MNode]) -- SNodes), SNodes ++ lists:sublist(Cand, SCount - length(SNodes)); false -> lists:sublist(SNodes, SCount) end}; -suggested_queue_nodes(_, _, {MNode, _}, _) -> +suggested_queue_nodes(_, _, {MNode, _, _}, _) -> {MNode, []}. shuffle(L) -> @@ -288,11 +296,14 @@ shuffle(L) -> {_, L1} = lists:unzip(lists:keysort(1, [{random:uniform(), N} || N <- L])), L1. -actual_queue_nodes(#amqqueue{pid = MPid, slave_pids = SPids}) -> +actual_queue_nodes(#amqqueue{pid = MPid, + slave_pids = SPids, + sync_slave_pids = SSPids}) -> + Nodes = fun (L) -> [node(Pid) || Pid <- L] end, {case MPid of none -> none; _ -> node(MPid) - end, [node(Pid) || Pid <- SPids]}. + end, Nodes(SPids), Nodes(SSPids)}. is_mirrored(Q) -> case policy(<<"ha-mode">>, Q) of @@ -313,7 +324,7 @@ update_mirrors(OldQ = #amqqueue{pid = QPid}, update_mirrors0(OldQ = #amqqueue{name = QName}, NewQ = #amqqueue{name = QName}) -> - All = fun ({A,B}) -> [A|B] end, + All = fun (Tuple) -> [element(1, Tuple) | element(2, Tuple)] end, OldNodes = All(actual_queue_nodes(OldQ)), NewNodes = All(suggested_queue_nodes(NewQ)), add_mirrors(QName, NewNodes -- OldNodes), diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 69a3be2b..b435e0f3 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -830,16 +830,21 @@ update_ram_duration(BQ, BQS) -> rabbit_memory_monitor:report_ram_duration(self(), RamDuration), BQ:set_ram_duration_target(DesiredDuration, BQS1). +%% [1] - the arrival of this newly synced slave may cause the master to die if +%% the admin has requested a migration-type change to policy. record_synchronised(#amqqueue { name = QName }) -> Self = self(), - rabbit_misc:execute_mnesia_transaction( - fun () -> - case mnesia:read({rabbit_queue, QName}) of - [] -> - ok; - [Q = #amqqueue { sync_slave_pids = SSPids }] -> - rabbit_mirror_queue_misc:store_updated_slaves( - Q #amqqueue { sync_slave_pids = [Self | SSPids] }), - ok - end - end). + case rabbit_misc:execute_mnesia_transaction( + fun () -> + case mnesia:read({rabbit_queue, QName}) of + [] -> + ok; + [Q1 = #amqqueue { sync_slave_pids = SSPids }] -> + Q2 = Q1#amqqueue{sync_slave_pids = [Self | SSPids]}, + rabbit_mirror_queue_misc:store_updated_slaves(Q2), + {ok, Q1, Q2} + end + end) of + ok -> ok; + {ok, Q1, Q2} -> rabbit_mirror_queue_misc:update_mirrors(Q1, Q2) %% [1] + end. diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index f5ea4fba..9bc4288d 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -912,10 +912,10 @@ test_arguments_parser() -> test_dynamic_mirroring() -> %% Just unit tests of the node selection logic, see multi node %% tests for the rest... - Test = fun ({NewM, NewSs, ExtraSs}, Policy, Params, {OldM, OldSs}, All) -> + Test = fun ({NewM, NewSs, ExtraSs}, Policy, Params, CurrentState, All) -> {NewM, NewSs0} = rabbit_mirror_queue_misc:suggested_queue_nodes( - Policy, Params, {OldM, OldSs}, All), + Policy, Params, CurrentState, All), NewSs1 = lists:sort(NewSs0), case dm_list_match(NewSs, NewSs1, ExtraSs) of ok -> ok; @@ -923,28 +923,36 @@ test_dynamic_mirroring() -> end end, - Test({a,[b,c],0},<<"all">>,'_',{a,[]}, [a,b,c]), - Test({a,[b,c],0},<<"all">>,'_',{a,[b,c]},[a,b,c]), - Test({a,[b,c],0},<<"all">>,'_',{a,[d]}, [a,b,c]), + Test({a,[b,c],0},<<"all">>,'_',{a,[], []}, [a,b,c]), + Test({a,[b,c],0},<<"all">>,'_',{a,[b,c],[b,c]},[a,b,c]), + Test({a,[b,c],0},<<"all">>,'_',{a,[d], [d]}, [a,b,c]), + + N = fun (Atoms) -> [list_to_binary(atom_to_list(A)) || A <- Atoms] end, %% Add a node - Test({a,[b,c],0},<<"nodes">>,[<<"a">>,<<"b">>,<<"c">>],{a,[b]},[a,b,c,d]), - Test({b,[a,c],0},<<"nodes">>,[<<"a">>,<<"b">>,<<"c">>],{b,[a]},[a,b,c,d]), + Test({a,[b,c],0},<<"nodes">>,N([a,b,c]),{a,[b],[b]},[a,b,c,d]), + Test({b,[a,c],0},<<"nodes">>,N([a,b,c]),{b,[a],[a]},[a,b,c,d]), %% Add two nodes and drop one - Test({a,[b,c],0},<<"nodes">>,[<<"a">>,<<"b">>,<<"c">>],{a,[d]},[a,b,c,d]), + Test({a,[b,c],0},<<"nodes">>,N([a,b,c]),{a,[d],[d]},[a,b,c,d]), %% Don't try to include nodes that are not running - Test({a,[b], 0},<<"nodes">>,[<<"a">>,<<"b">>,<<"f">>],{a,[b]},[a,b,c,d]), + Test({a,[b], 0},<<"nodes">>,N([a,b,f]),{a,[b],[b]},[a,b,c,d]), %% If we can't find any of the nodes listed then just keep the master - Test({a,[], 0},<<"nodes">>,[<<"f">>,<<"g">>,<<"h">>],{a,[b]},[a,b,c,d]), - %% And once that's happened, still keep the master even when not listed - Test({a,[b,c],0},<<"nodes">>,[<<"b">>,<<"c">>], {a,[]}, [a,b,c,d]), - - Test({a,[], 1},<<"exactly">>,2,{a,[]}, [a,b,c,d]), - Test({a,[], 2},<<"exactly">>,3,{a,[]}, [a,b,c,d]), - Test({a,[c], 0},<<"exactly">>,2,{a,[c]}, [a,b,c,d]), - Test({a,[c], 1},<<"exactly">>,3,{a,[c]}, [a,b,c,d]), - Test({a,[c], 0},<<"exactly">>,2,{a,[c,d]},[a,b,c,d]), - Test({a,[c,d],0},<<"exactly">>,3,{a,[c,d]},[a,b,c,d]), + Test({a,[], 0},<<"nodes">>,N([f,g,h]),{a,[b],[b]},[a,b,c,d]), + %% And once that's happened, still keep the master even when not listed, + %% if nothing is synced + Test({a,[b,c],0},<<"nodes">>,N([b,c]), {a,[], []}, [a,b,c,d]), + Test({a,[b,c],0},<<"nodes">>,N([b,c]), {a,[b],[]}, [a,b,c,d]), + %% But if something is synced we can lose the master - but make + %% sure we pick the new master from the nodes which are synced! + Test({b,[c], 0},<<"nodes">>,N([b,c]), {a,[b],[b]},[a,b,c,d]), + Test({b,[c], 0},<<"nodes">>,N([c,b]), {a,[b],[b]},[a,b,c,d]), + + Test({a,[], 1},<<"exactly">>,2,{a,[], []}, [a,b,c,d]), + Test({a,[], 2},<<"exactly">>,3,{a,[], []}, [a,b,c,d]), + Test({a,[c], 0},<<"exactly">>,2,{a,[c], [c]}, [a,b,c,d]), + Test({a,[c], 1},<<"exactly">>,3,{a,[c], [c]}, [a,b,c,d]), + Test({a,[c], 0},<<"exactly">>,2,{a,[c,d],[c,d]},[a,b,c,d]), + Test({a,[c,d],0},<<"exactly">>,3,{a,[c,d],[c,d]},[a,b,c,d]), passed. -- cgit v1.2.1 From 77ad4b912377de31ace4afbc4bc3db96188d74c7 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 12 Feb 2013 21:57:24 +0000 Subject: remove icky use of element/2 --- src/rabbit_mirror_queue_misc.erl | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index cc2d7c77..5cba2d43 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -324,10 +324,11 @@ update_mirrors(OldQ = #amqqueue{pid = QPid}, update_mirrors0(OldQ = #amqqueue{name = QName}, NewQ = #amqqueue{name = QName}) -> - All = fun (Tuple) -> [element(1, Tuple) | element(2, Tuple)] end, - OldNodes = All(actual_queue_nodes(OldQ)), - NewNodes = All(suggested_queue_nodes(NewQ)), - add_mirrors(QName, NewNodes -- OldNodes), + {OldMNode, OldSNodes, _} = actual_queue_nodes(OldQ), + {NewMNode, NewSNodes} = suggested_queue_nodes(NewQ), + OldNodes = [OldMNode | OldSNodes], + NewNodes = [NewMNode | NewSNodes], + add_mirrors (QName, NewNodes -- OldNodes), drop_mirrors(QName, OldNodes -- NewNodes), ok. -- cgit v1.2.1 From dd65549abcab4554f46ff1f41057b81c3fa00812 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 12 Feb 2013 22:34:33 +0000 Subject: drop expired messages post basic_get ...so messages with an expiry that are at the head of the queue after a basic.get do not get stuck there in the absence of other queue activity. Rather than simply adding a call to drop_expired_messages/1 after the call to fetch/1 in the basic_get code, we insert the call into fetch/1, which allows us to remove it from the other call site. Thus fetch/1 preserves the invariant we are after, namely that whenever a queue has a message at the head with an expiry, there is a timer set to drop said message. Note that the message count returned by basic.get does not reflect the dropping of expired messages after the fetched message. That's ok since we make no guarantee that messages are expired straight away. And note that on 'default' (rather than 'stable') the behaviour is actually different; due to various other changes there we will in fact return the reduced count. --- src/rabbit_amqqueue_process.erl | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 49fcf070..e3885644 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -478,11 +478,10 @@ deliver_msg_to_consumer(DeliverFun, {Stop, State1}. deliver_from_queue_deliver(AckRequired, State) -> - {{Message, IsDelivered, AckTag, _Remaining}, State1} = + {{Message, IsDelivered, AckTag, _Remaining}, + State1 = #q{backing_queue = BQ, backing_queue_state = BQS}} = fetch(AckRequired, State), - State2 = #q{backing_queue = BQ, backing_queue_state = BQS} = - drop_expired_messages(State1), - {{Message, IsDelivered, AckTag}, BQ:is_empty(BQS), State2}. + {{Message, IsDelivered, AckTag}, BQ:is_empty(BQS), State1}. confirm_messages([], State) -> State; @@ -579,7 +578,7 @@ requeue_and_run(AckTags, State = #q{backing_queue = BQ, fetch(AckRequired, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> {Result, BQS1} = BQ:fetch(AckRequired, BQS), - {Result, State#q{backing_queue_state = BQS1}}. + {Result, drop_expired_messages(State#q{backing_queue_state = BQS1})}. ack(AckTags, ChPid, State) -> subtract_acks(ChPid, AckTags, State, -- cgit v1.2.1 From c38d0b886c801a4745d45730b4f565c41bd1f9b4 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 13 Feb 2013 10:57:31 +0000 Subject: Auto sync when policy changes to require it, in case we are already mirrored but have unsynced slaves at that point. --- src/rabbit_mirror_queue_misc.erl | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 4dd50bce..8192b092 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -179,19 +179,14 @@ add_mirror(QName, MirrorNode) -> end end). -start_child(Name, MirrorNode, Q = #amqqueue{pid = QPid}) -> +start_child(Name, MirrorNode, Q) -> case rabbit_misc:with_exit_handler( rabbit_misc:const({ok, down}), fun () -> rabbit_mirror_queue_slave_sup:start_child(MirrorNode, [Q]) end) of {ok, SPid} when is_pid(SPid) -> - case rabbit_policy:get(<<"ha-sync-mode">>, Q) of - {ok,<<"automatic">>} -> - spawn(fun() -> rabbit_amqqueue:sync_mirrors(QPid) end); - _ -> - ok - end, + maybe_auto_sync(Q), rabbit_log:info("Adding mirror of ~s on node ~p: ~p~n", [rabbit_misc:rs(Name), MirrorNode, SPid]), {ok, started}; @@ -310,6 +305,14 @@ is_mirrored(Q) -> _ -> false end. +maybe_auto_sync(Q = #amqqueue{pid = QPid}) -> + case policy(<<"ha-sync-mode">>, Q) of + <<"automatic">> -> + spawn(fun() -> rabbit_amqqueue:sync_mirrors(QPid) end); + _ -> + ok + end. + update_mirrors(OldQ = #amqqueue{pid = QPid}, NewQ = #amqqueue{pid = QPid}) -> case {is_mirrored(OldQ), is_mirrored(NewQ)} of @@ -326,6 +329,7 @@ update_mirrors0(OldQ = #amqqueue{name = QName}, NewNodes = All(suggested_queue_nodes(NewQ)), add_mirrors(QName, NewNodes -- OldNodes), drop_mirrors(QName, OldNodes -- NewNodes), + maybe_auto_sync(NewQ), ok. %%---------------------------------------------------------------------------- -- cgit v1.2.1 From b57e18458b542429967d4a5c1e83a7cde0c2f516 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 13 Feb 2013 15:20:34 +0000 Subject: Today's arbitrary startup banner change: centre the rabbit. --- src/rabbit.erl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 55736b28..c004c489 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -719,13 +719,13 @@ erts_version_check() -> print_banner() -> {ok, Product} = application:get_key(id), {ok, Version} = application:get_key(vsn), - io:format("~n## ## ~s ~s. ~s" - "~n## ## ~s" - "~n##########" - "~n###### ## Logs: ~s" - "~n########## ~s" - "~n" - "~n Starting broker...", + io:format("~n ~s ~s. ~s" + "~n ## ## ~s" + "~n ## ##" + "~n ########## Logs: ~s" + "~n ###### ## ~s" + "~n ##########" + "~n Starting broker...", [Product, Version, ?COPYRIGHT_MESSAGE, ?INFORMATION_MESSAGE, log_location(kernel), log_location(sasl)]). -- cgit v1.2.1 From f994b25859adf71955c64e20a74e85c076977c26 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 13 Feb 2013 21:22:54 +0000 Subject: don't invoke run_message_queue in handle_cast/run_backing_queue Since that would only be necessary of the BQ:invoke modified the consumers, which it can't, or added messages to the queue, which it shouldn't. --- src/rabbit_amqqueue_process.erl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 00a3a85a..4cd21c0a 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1216,8 +1216,7 @@ handle_cast(_, State = #q{delayed_stop = DS}) when DS =/= undefined -> handle_cast({run_backing_queue, Mod, Fun}, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> - noreply(run_message_queue( - State#q{backing_queue_state = BQ:invoke(Mod, Fun, BQS)})); + noreply(State#q{backing_queue_state = BQ:invoke(Mod, Fun, BQS)}); handle_cast({deliver, Delivery = #delivery{sender = Sender}, Delivered, Flow}, State = #q{senders = Senders}) -> -- cgit v1.2.1 From 65491f7748c0c79029fe4f601638d101d75b8d56 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 13 Feb 2013 21:25:44 +0000 Subject: drop_expired_msgs only when the queue head changes And on recover. And when the timer goes off. That's all we need. new call sites: - in deliver_or_enqueue/3, when enqueuing a message (that we couldn't deliver to a consumer straight away) with an expiry to the head. the queue. NB: Previously we were always (re)setting a timer when enqueuing a message with an expiry, which is wasteful when the new message isn't at the head (i.e. the queue was non-empty) or when it needs expiring immediately. - requeue_and_run/2, since a message may get requeued to the head. This call site arises due to removal of the run_message_queue/1 call site (see below). unchanged call sites: - init_ttl/2 - this is the recovery case - fetch/2, after fetching - this is the basic "queue head changes" case - handle_info/drop_expired - this is the message expiry timer removed call sites: - run_message_queue/1 - this internally calls fetch/2 (see above) but also invoking drop_expired_msgs at the beginning. This now happens at the call sites, where it is necessary. Which actually only is in requeue_and_run, and not the others, none of which change the queue content prior to calling run_message_queue/1 - possibly_unblock/3 - unblocking of consumers - handle_call/basic_consumer - adding a consumer - handle_call/basic_get, prior to the call to fetch/2. - handle_call/stat --- src/rabbit_amqqueue_process.erl | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 4cd21c0a..4a0ccf81 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -519,13 +519,11 @@ discard(#delivery{sender = SenderPid, BQS1 = BQ:discard(MsgId, SenderPid, BQS), State1#q{backing_queue_state = BQS1}. -run_message_queue(State) -> - State1 = #q{backing_queue = BQ, backing_queue_state = BQS} = - drop_expired_msgs(State), - {_IsEmpty1, State2} = deliver_msgs_to_consumers( +run_message_queue(State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> + {_IsEmpty1, State1} = deliver_msgs_to_consumers( fun deliver_from_queue_deliver/2, - BQ:is_empty(BQS), State1), - State2. + BQ:is_empty(BQS), State), + State1. attempt_delivery(Delivery = #delivery{sender = SenderPid, message = Message}, Props, Delivered, State = #q{backing_queue = BQ, @@ -559,15 +557,27 @@ deliver_or_enqueue(Delivery = #delivery{message = Message, sender = SenderPid}, {false, State2 = #q{ttl = 0, dlx = undefined}} -> discard(Delivery, State2); {false, State2 = #q{backing_queue = BQ, backing_queue_state = BQS}} -> + IsEmpty = BQ:is_empty(BQS), BQS1 = BQ:publish(Message, Props, Delivered, SenderPid, BQS), - ensure_ttl_timer(Props#message_properties.expiry, - State2#q{backing_queue_state = BQS1}) + State3 = State2#q{backing_queue_state = BQS1}, + %% optimisation: it would be perfectly safe to always + %% invoke drop_expired_msgs here, but that is expensive so + %% we only do that IFF the new message ends up at the head + %% of the queue (because the queue was empty) and has an + %% expiry. Only then may it need expiring straight away, + %% or, if expiry is not due yet, the expiry timer may need + %% (re)scheduling. + case {IsEmpty, Props#message_properties.expiry} of + {false, _} -> State3; + {true, undefined} -> State3; + {true, _} -> drop_expired_msgs(State3) + end end. requeue_and_run(AckTags, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> {_MsgIds, BQS1} = BQ:requeue(AckTags, BQS), - run_message_queue(State#q{backing_queue_state = BQS1}). + run_message_queue(drop_expired_msgs(State#q{backing_queue_state = BQS1})). fetch(AckRequired, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> @@ -1064,7 +1074,7 @@ handle_call({basic_get, ChPid, NoAck}, _From, State = #q{q = #amqqueue{name = QName}}) -> AckRequired = not NoAck, State1 = ensure_expiry_timer(State), - case fetch(AckRequired, drop_expired_msgs(State1)) of + case fetch(AckRequired, State1) of {empty, State2} -> reply(empty, State2); {{Message, IsDelivered, AckTag}, State2} -> @@ -1137,7 +1147,7 @@ handle_call({basic_cancel, ChPid, ConsumerTag, OkMsg}, From, handle_call(stat, _From, State) -> State1 = #q{backing_queue = BQ, backing_queue_state = BQS} = - drop_expired_msgs(ensure_expiry_timer(State)), + ensure_expiry_timer(State), reply({ok, BQ:len(BQS), consumer_count()}, State1); handle_call({delete, IfUnused, IfEmpty}, From, -- cgit v1.2.1 From 76780acfaa2c1396db2b1c5e30bfe3a03fe91d12 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 14 Feb 2013 11:44:47 +0000 Subject: remove superfluous vqstate field --- src/rabbit_backing_queue.erl | 2 -- src/rabbit_variable_queue.erl | 12 +++--------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/rabbit_backing_queue.erl b/src/rabbit_backing_queue.erl index c2b52a7c..2f247448 100644 --- a/src/rabbit_backing_queue.erl +++ b/src/rabbit_backing_queue.erl @@ -18,8 +18,6 @@ -ifdef(use_specs). --export_type([async_callback/0]). - %% We can't specify a per-queue ack/state with callback signatures -type(ack() :: any()). -type(state() :: any()). diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 5d463f57..f7c6c729 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -262,8 +262,6 @@ durable, transient_threshold, - async_callback, - len, persistent_count, @@ -356,8 +354,6 @@ durable :: boolean(), transient_threshold :: non_neg_integer(), - async_callback :: rabbit_backing_queue:async_callback(), - len :: non_neg_integer(), persistent_count :: non_neg_integer(), @@ -426,7 +422,7 @@ init(Queue, Recover, AsyncCallback) -> init(#amqqueue { name = QueueName, durable = IsDurable }, false, AsyncCallback, MsgOnDiskFun, MsgIdxOnDiskFun) -> IndexState = rabbit_queue_index:init(QueueName, MsgIdxOnDiskFun), - init(IsDurable, IndexState, 0, [], AsyncCallback, + init(IsDurable, IndexState, 0, [], case IsDurable of true -> msg_store_client_init(?PERSISTENT_MSG_STORE, MsgOnDiskFun, AsyncCallback); @@ -454,7 +450,7 @@ init(#amqqueue { name = QueueName, durable = true }, true, rabbit_msg_store:contains(MsgId, PersistentClient) end, MsgIdxOnDiskFun), - init(true, IndexState, DeltaCount, Terms1, AsyncCallback, + init(true, IndexState, DeltaCount, Terms1, PersistentClient, TransientClient). terminate(_Reason, State) -> @@ -1004,7 +1000,7 @@ update_rate(Now, Then, Count, {OThen, OCount}) -> %% Internal major helpers for Public API %%---------------------------------------------------------------------------- -init(IsDurable, IndexState, DeltaCount, Terms, AsyncCallback, +init(IsDurable, IndexState, DeltaCount, Terms, PersistentClient, TransientClient) -> {LowSeqId, NextSeqId, IndexState1} = rabbit_queue_index:bounds(IndexState), @@ -1030,8 +1026,6 @@ init(IsDurable, IndexState, DeltaCount, Terms, AsyncCallback, durable = IsDurable, transient_threshold = NextSeqId, - async_callback = AsyncCallback, - len = DeltaCount1, persistent_count = DeltaCount1, -- cgit v1.2.1 From ae3922b6d9c69c508d984c14cdaec2b1730c5f4e Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 14 Feb 2013 12:24:53 +0000 Subject: If we try to stop while starting, wait until we have stopped starting before stopping. --- src/rabbit.erl | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/rabbit.erl b/src/rabbit.erl index fa8dc652..5f36f17d 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -342,6 +342,7 @@ handle_app_error(App, Reason) -> throw({could_not_start, App, Reason}). start_it(StartFun) -> + register(rabbit_boot, self()), try StartFun() catch @@ -350,11 +351,18 @@ start_it(StartFun) -> _:Reason -> boot_error(Reason, erlang:get_stacktrace()) after + %% In the boot/0 case the process exits - but in the start/0 + %% case it is some random RPC server and does not. + unregister(rabbit_boot), %% give the error loggers some time to catch up timer:sleep(100) end. stop() -> + case whereis(rabbit_boot) of + undefined -> ok; + _ -> await_startup() + end, rabbit_log:info("Stopping RabbitMQ~n"), ok = app_utils:stop_applications(app_shutdown_order()). -- cgit v1.2.1 From 29d0633258b641ba97b373b2395ce7543c0f5bba Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 14 Feb 2013 12:51:11 +0000 Subject: Don't stomp on the (possible) registered name of the thing that invokes boot/0 or start/0, create a new process to hold the name instead. --- src/rabbit.erl | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 5f36f17d..ed0152b4 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -342,7 +342,8 @@ handle_app_error(App, Reason) -> throw({could_not_start, App, Reason}). start_it(StartFun) -> - register(rabbit_boot, self()), + Marker = spawn_link(fun() -> receive finished -> ok end end), + register(rabbit_boot, Marker), try StartFun() catch @@ -351,9 +352,7 @@ start_it(StartFun) -> _:Reason -> boot_error(Reason, erlang:get_stacktrace()) after - %% In the boot/0 case the process exits - but in the start/0 - %% case it is some random RPC server and does not. - unregister(rabbit_boot), + Marker ! finished, %% give the error loggers some time to catch up timer:sleep(100) end. -- cgit v1.2.1 From b96fb4eec639909ecf3b31deff9e79c0f96e9ae3 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 14 Feb 2013 13:09:38 +0000 Subject: In case the containing process is trapping exits. --- src/rabbit.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rabbit.erl b/src/rabbit.erl index ed0152b4..2a9e9ec9 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -352,6 +352,7 @@ start_it(StartFun) -> _:Reason -> boot_error(Reason, erlang:get_stacktrace()) after + unlink(Marker), Marker ! finished, %% give the error loggers some time to catch up timer:sleep(100) -- cgit v1.2.1 From 5633573518b153c36fc02adcf9e3f6a697e1f37c Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 14 Feb 2013 15:31:58 +0000 Subject: cosmetic --- src/rabbit.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 2a9e9ec9..77537994 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -342,7 +342,7 @@ handle_app_error(App, Reason) -> throw({could_not_start, App, Reason}). start_it(StartFun) -> - Marker = spawn_link(fun() -> receive finished -> ok end end), + Marker = spawn_link(fun() -> receive stop -> ok end end), register(rabbit_boot, Marker), try StartFun() @@ -353,7 +353,7 @@ start_it(StartFun) -> boot_error(Reason, erlang:get_stacktrace()) after unlink(Marker), - Marker ! finished, + Marker ! stop, %% give the error loggers some time to catch up timer:sleep(100) end. -- cgit v1.2.1 From 2532b6c41a1179e745e2f42235040b54de3472fa Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 15 Feb 2013 00:02:15 +0000 Subject: explain --- src/rabbit_channel.erl | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index e74211af..eb03bf54 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -589,6 +589,16 @@ handle_method(_Method, _, State = #ch{state = closing}) -> handle_method(#'channel.close'{}, _, State = #ch{reader_pid = ReaderPid}) -> {ok, State1} = notify_queues(State), + %% We issue the channel.close_ok response after a handshake with + %% the reader, the other half of which is the + %% ready_for_close. That way the reader forgets about the channel + %% before we send the response (and this channel process + %% terminates). If we didn't do that, a channel.open for the same + %% channel number, which a client is entitled to send as soon as + %% it has received the close_ok, might be received by the reader + %% before it has seen the termination and hence be sent to the + %% old, now dead/dying channel process, instead of a new process, + %% and thus lost. ReaderPid ! {channel_closing, self()}, {noreply, State1}; -- cgit v1.2.1 From 0f24c233d0e4b131906dcda8b2fbfc6f34183244 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 15 Feb 2013 00:02:52 +0000 Subject: tweak --- src/rabbit_channel.erl | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index eb03bf54..0510afa9 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -590,15 +590,14 @@ handle_method(_Method, _, State = #ch{state = closing}) -> handle_method(#'channel.close'{}, _, State = #ch{reader_pid = ReaderPid}) -> {ok, State1} = notify_queues(State), %% We issue the channel.close_ok response after a handshake with - %% the reader, the other half of which is the - %% ready_for_close. That way the reader forgets about the channel - %% before we send the response (and this channel process - %% terminates). If we didn't do that, a channel.open for the same - %% channel number, which a client is entitled to send as soon as - %% it has received the close_ok, might be received by the reader - %% before it has seen the termination and hence be sent to the - %% old, now dead/dying channel process, instead of a new process, - %% and thus lost. + %% the reader, the other half of which is ready_for_close. That + %% way the reader forgets about the channel before we send the + %% response (and this channel process terminates). If we didn't do + %% that, a channel.open for the same channel number, which a + %% client is entitled to send as soon as it has received the + %% close_ok, might be received by the reader before it has seen + %% the termination and hence be sent to the old, now dead/dying + %% channel process, instead of a new process, and thus lost. ReaderPid ! {channel_closing, self()}, {noreply, State1}; -- cgit v1.2.1 From d8f3b60cd86d708abf4736f2e9acc04ab9a2f94a Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 15 Feb 2013 11:44:05 +0000 Subject: Typo --- src/rabbit_mirror_queue_misc.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 5cba2d43..d0291814 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -270,7 +270,7 @@ suggested_queue_nodes(<<"nodes">>, Nodes0, {MNode, _SNodes, SSNodes}, Poss) -> {MNode, []}; _ -> case lists:member(MNode, Available) of true -> {MNode, Available -- [MNode]}; - false -> %% Make the sure new master is synced! In order to + false -> %% Make sure the new master is synced! In order to %% get here SSNodes must not be empty. [NewMNode | _] = SSNodes, {NewMNode, Available -- [NewMNode]} -- cgit v1.2.1 From f8331aa6edeabc82931aa016d926d57427d71d1c Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 15 Feb 2013 14:21:30 +0000 Subject: It's 'name', not 'key'. --- src/rabbit_vhost.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_vhost.erl b/src/rabbit_vhost.erl index 18742ccf..839ebb96 100644 --- a/src/rabbit_vhost.erl +++ b/src/rabbit_vhost.erl @@ -95,9 +95,9 @@ internal_delete(VHostPath) -> || Info <- rabbit_auth_backend_internal:list_vhost_permissions(VHostPath)], [ok = rabbit_runtime_parameters:clear(VHostPath, proplists:get_value(component, Info), - proplists:get_value(key, Info)) + proplists:get_value(name, Info)) || Info <- rabbit_runtime_parameters:list(VHostPath)], - [ok = rabbit_policy:delete(VHostPath, proplists:get_value(key, Info)) + [ok = rabbit_policy:delete(VHostPath, proplists:get_value(name, Info)) || Info <- rabbit_policy:list(VHostPath)], ok = mnesia:delete({rabbit_vhost, VHostPath}), ok. -- cgit v1.2.1 From badde5b893506ee1137dabbad19e5d4bec3a6c46 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 15 Feb 2013 14:30:38 +0000 Subject: Take note of RABBITMQ_SERVICENAME, same as rabbitmq-service.bat does. --- scripts/rabbitmq-plugins.bat | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/rabbitmq-plugins.bat b/scripts/rabbitmq-plugins.bat index 713d7000..4b4dbe47 100755 --- a/scripts/rabbitmq-plugins.bat +++ b/scripts/rabbitmq-plugins.bat @@ -23,8 +23,12 @@ set TDP0=%~dp0 set STAR=%* setlocal enabledelayedexpansion +if "!RABBITMQ_SERVICENAME!"=="" ( + set RABBITMQ_SERVICENAME=RabbitMQ +) + if "!RABBITMQ_BASE!"=="" ( - set RABBITMQ_BASE=!APPDATA!\RabbitMQ + set RABBITMQ_BASE=!APPDATA!\!RABBITMQ_SERVICENAME! ) if not exist "!ERLANG_HOME!\bin\erl.exe" ( -- cgit v1.2.1 From 81097e2b80cb2a31fb285f87b57c6440d1565e05 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 15 Feb 2013 16:14:14 +0000 Subject: make reader's handle_dependent_exit clearer - handle the no-op case (controlled exit of a channel we've forgotten about already) explicitly - better clause order and formatting. --- src/rabbit_reader.erl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index af7aac6f..f249bc9b 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -437,13 +437,13 @@ close_connection(State = #v1{queue_collector = Collector, handle_dependent_exit(ChPid, Reason, State) -> case {channel_cleanup(ChPid), termination_kind(Reason)} of - {undefined, uncontrolled} -> - exit({abnormal_dependent_exit, ChPid, Reason}); - {_Channel, controlled} -> - maybe_close(control_throttle(State)); - {Channel, uncontrolled} -> - maybe_close(handle_exception(control_throttle(State), - Channel, Reason)) + {undefined, controlled} -> State; + {undefined, uncontrolled} -> exit({abnormal_dependent_exit, + ChPid, Reason}); + {_Channel, controlled} -> maybe_close(control_throttle(State)); + {Channel, uncontrolled} -> maybe_close( + handle_exception(control_throttle(State), + Channel, Reason)) end. terminate_channels() -> -- cgit v1.2.1 From bee34f70c1174aaa99c189da3d017c474198f9fa Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 15 Feb 2013 17:27:47 +0000 Subject: make reader's handling of channel.close_ok more obviously correct by re-introducing a call to control_throttle. This was present in 3.0.x, and is needed there, but is actually not needed here, due to other changes made in the area. But the reason is quite subtle... For control_throttle to do something here, the channel_cleanup would have to be called for a channel for which we have run out of credit. Now, credit is only consumed when sending content-bearing methods to the channel with rabbit_channel:do_flow. There is a subsequent invocation of control_throttle in the code which will set the connection_state to 'blocking'. But this is immediately followed by a call to post_process_frame. The frame we are looking at must be the last frame of a content-bearing method, since otherwise the method would not be complete and we wouldn't have passed it to the channel. Hence that frame can only be a content_header or, more likely, a content_body. For both of these, post_process_frame invokes maybe_block, which will turn a 'blocking' into 'blocked'. And from that point onwards we will no longer read anything from the socket or process anything already in buf. So we certainly can't be processing a channel.close_ok. In other words, post_process_frame can only be invoked with a channel.close_ok frame when the connection_state is 'running', or blocking/blocked for a reason other than having run out of credit for a channel, i.e. an alarm. Therefore forgetting about the channel as part of the channel_cleanup call does not have an effect on credit_flow:blocked(). And hence an invocation of control_throttle will not alter the connection_state and is therefore unnecessary. --- src/rabbit_reader.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index f249bc9b..d2655a90 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -636,7 +636,8 @@ process_frame(Frame, Channel, State) -> post_process_frame({method, 'channel.close_ok', _}, ChPid, State) -> channel_cleanup(ChPid), - State; + %% this is not strictly necessary, but more obviously correct + control_throttle(State); post_process_frame({content_header, _, _, _, _}, _ChPid, State) -> maybe_block(State); post_process_frame({content_body, _}, _ChPid, State) -> -- cgit v1.2.1 From 8e613f029f032a24948c8f63a8d4b9d33cfa5af8 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 15 Feb 2013 18:55:22 +0000 Subject: refactor: more symmetry in rabbit_reader:handle_dependent_exit/3 It makes no difference whether we call handle_exception before or after control_throttle, so lets use an order that more clearly calls out the similarity to the controlled exit case. --- src/rabbit_reader.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index d2655a90..203ec3f0 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -441,9 +441,9 @@ handle_dependent_exit(ChPid, Reason, State) -> {undefined, uncontrolled} -> exit({abnormal_dependent_exit, ChPid, Reason}); {_Channel, controlled} -> maybe_close(control_throttle(State)); - {Channel, uncontrolled} -> maybe_close( - handle_exception(control_throttle(State), - Channel, Reason)) + {Channel, uncontrolled} -> State1 = handle_exception( + State, Channel, Reason), + maybe_close(control_throttle(State1)) end. terminate_channels() -> -- cgit v1.2.1 From a77f31a8bbbdf93637207c3d85cbb3fa41691a93 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 15 Feb 2013 18:55:47 +0000 Subject: explain a reader sublety --- src/rabbit_reader.erl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 203ec3f0..af331a62 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -636,7 +636,9 @@ process_frame(Frame, Channel, State) -> post_process_frame({method, 'channel.close_ok', _}, ChPid, State) -> channel_cleanup(ChPid), - %% this is not strictly necessary, but more obviously correct + %% This is not strictly necessary, but more obviously + %% correct. Also note that we do not need to call maybe_close/1 + %% since we cannot possibly be in the 'closing' state. control_throttle(State); post_process_frame({content_header, _, _, _, _}, _ChPid, State) -> maybe_block(State); -- cgit v1.2.1 From 0ecd80bdd7bdc211f775ac409ebe424ea3fa90a6 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 18 Feb 2013 14:37:00 +0000 Subject: Remove rabbit_runtime_parameter:validate_clear/3, and instead just validate that the parameter exists. --- src/rabbit_policy.erl | 5 +---- src/rabbit_runtime_parameter.erl | 3 --- src/rabbit_runtime_parameters.erl | 22 +++++++--------------- src/rabbit_runtime_parameters_test.erl | 6 +----- src/rabbit_tests.erl | 4 ++++ 5 files changed, 13 insertions(+), 27 deletions(-) diff --git a/src/rabbit_policy.erl b/src/rabbit_policy.erl index e712078b..7398cd2d 100644 --- a/src/rabbit_policy.erl +++ b/src/rabbit_policy.erl @@ -26,7 +26,7 @@ -export([register/0]). -export([name/1, get/2, set/1]). --export([validate/4, validate_clear/3, notify/4, notify_clear/3]). +-export([validate/4, notify/4, notify_clear/3]). -export([parse_set/5, set/5, delete/2, lookup/2, list/0, list/1, list_formatted/1, info_keys/0]). @@ -146,9 +146,6 @@ validate(_VHost, <<"policy">>, Name, Term) -> rabbit_parameter_validation:proplist( Name, policy_validation(), Term). -validate_clear(_VHost, <<"policy">>, _Name) -> - ok. - notify(VHost, <<"policy">>, _Name, _Term) -> update_policies(VHost). diff --git a/src/rabbit_runtime_parameter.erl b/src/rabbit_runtime_parameter.erl index 8a237105..6b62c974 100644 --- a/src/rabbit_runtime_parameter.erl +++ b/src/rabbit_runtime_parameter.erl @@ -23,8 +23,6 @@ -callback validate(rabbit_types:vhost(), binary(), binary(), term()) -> validate_results(). --callback validate_clear(rabbit_types:vhost(), binary(), - binary()) -> validate_results(). -callback notify(rabbit_types:vhost(), binary(), binary(), term()) -> 'ok'. -callback notify_clear(rabbit_types:vhost(), binary(), binary()) -> 'ok'. @@ -35,7 +33,6 @@ behaviour_info(callbacks) -> [ {validate, 4}, - {validate_clear, 3}, {notify, 4}, {notify_clear, 3} ]; diff --git a/src/rabbit_runtime_parameters.erl b/src/rabbit_runtime_parameters.erl index 2615372c..b1100b65 100644 --- a/src/rabbit_runtime_parameters.erl +++ b/src/rabbit_runtime_parameters.erl @@ -120,21 +120,13 @@ clear(VHost, Component, Name) -> clear_any(VHost, Component, Name). clear_any(VHost, Component, Name) -> - case clear_any0(VHost, Component, Name) of - ok -> ok; - {errors, L} -> format_error(L) - end. - -clear_any0(VHost, Component, Name) -> - case lookup_component(Component) of - {ok, Mod} -> case flatten_errors( - Mod:validate_clear(VHost, Component, Name)) of - ok -> mnesia_clear(VHost, Component, Name), - Mod:notify_clear(VHost, Component, Name), - ok; - E -> E - end; - E -> E + case lookup(VHost, Component, Name) of + not_found -> {error_string, "Parameter does not exist"}; + _ -> mnesia_clear(VHost, Component, Name), + case lookup_component(Component) of + {ok, Mod} -> Mod:notify_clear(VHost, Component, Name); + _ -> ok + end end. mnesia_clear(VHost, Component, Name) -> diff --git a/src/rabbit_runtime_parameters_test.erl b/src/rabbit_runtime_parameters_test.erl index c27f1b4a..05c1dbc1 100644 --- a/src/rabbit_runtime_parameters_test.erl +++ b/src/rabbit_runtime_parameters_test.erl @@ -18,7 +18,7 @@ -behaviour(rabbit_runtime_parameter). -behaviour(rabbit_policy_validator). --export([validate/4, validate_clear/3, notify/4, notify_clear/3]). +-export([validate/4, notify/4, notify_clear/3]). -export([register/0, unregister/0]). -export([validate_policy/1]). -export([register_policy_validator/0, unregister_policy_validator/0]). @@ -35,10 +35,6 @@ validate(_, <<"test">>, <<"good">>, _Term) -> ok; validate(_, <<"test">>, <<"maybe">>, <<"good">>) -> ok; validate(_, <<"test">>, _, _) -> {error, "meh", []}. -validate_clear(_, <<"test">>, <<"good">>) -> ok; -validate_clear(_, <<"test">>, <<"maybe">>) -> ok; -validate_clear(_, <<"test">>, _) -> {error, "meh", []}. - notify(_, _, _, _) -> ok. notify_clear(_, _, _) -> ok. diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 87946888..60ee26e5 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -1061,7 +1061,11 @@ test_runtime_parameters() -> ok = control_action(clear_parameter, ["test", "maybe"]), {error_string, _} = control_action(clear_parameter, ["test", "neverexisted"]), + + %% We can delete for a component that no longer exists + Good(["test", "good", "\"ignore\""]), rabbit_runtime_parameters_test:unregister(), + ok = control_action(clear_parameter, ["test", "good"]), passed. test_policy_validation() -> -- cgit v1.2.1 From 05544ad98014efecd237a42af07958f82703867b Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 18 Feb 2013 16:47:40 +0000 Subject: cluster_cp_mode --- ebin/rabbit_app.in | 1 + src/rabbit_mnesia.erl | 6 ++++++ src/rabbit_node_monitor.erl | 26 ++++++++++++++++++++++++++ 3 files changed, 33 insertions(+) diff --git a/ebin/rabbit_app.in b/ebin/rabbit_app.in index ad961a44..d08d502b 100644 --- a/ebin/rabbit_app.in +++ b/ebin/rabbit_app.in @@ -44,6 +44,7 @@ {log_levels, [{connection, info}]}, {ssl_cert_login_from, distinguished_name}, {reverse_dns_lookups, false}, + {cluster_cp_mode, false}, {tcp_listen_options, [binary, {packet, raw}, {reuseaddr, true}, diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index c39e898c..ecb03f54 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -26,6 +26,7 @@ status/0, is_clustered/0, + majority/0, cluster_nodes/1, node_type/0, dir/0, @@ -67,6 +68,7 @@ -spec(status/0 :: () -> [{'nodes', [{node_type(), [node()]}]} | {'running_nodes', [node()]} | {'partitions', [{node(), [node()]}]}]). +-spec(majority/0 :: () -> boolean()). -spec(is_clustered/0 :: () -> boolean()). -spec(cluster_nodes/1 :: ('all' | 'disc' | 'ram' | 'running') -> [node()]). -spec(node_type/0 :: () -> node_type()). @@ -338,6 +340,10 @@ status() -> false -> [] end. +majority() -> + ensure_mnesia_running(), + (length(cluster_nodes(running)) / length(cluster_nodes(all))) > 0.5. + mnesia_partitions(Nodes) -> {Replies, _BadNodes} = rpc:multicall( Nodes, rabbit_node_monitor, partitions, []), diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index 71c2c80a..7b7fed5c 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -250,6 +250,10 @@ handle_info({mnesia_system_event, ordsets:add_element(Node, ordsets:from_list(Partitions))), {noreply, State#state{partitions = Partitions1}}; +handle_info({mnesia_system_event, {mnesia_down, _Node}}, State) -> + handle_dead_according_to_mnesia_rabbit(), + {noreply, State}; + handle_info(_Info, State) -> {noreply, State}. @@ -272,6 +276,28 @@ handle_dead_rabbit(Node) -> ok = rabbit_alarm:on_node_down(Node), ok = rabbit_mnesia:on_node_down(Node). +%% Since we will be introspecting the cluster in response to this, we +%% must only do so based on Mnesia having noticed the other node being +%% down - otherwise we have a race. +handle_dead_according_to_mnesia_rabbit() -> + case application:get_env(rabbit, cluster_cp_mode) of + {ok, true} -> case rabbit_mnesia:majority() of + true -> ok; + false -> stop_and_halt() + end; + {ok, false} -> ok + end, + ok. + +stop_and_halt() -> + rabbit_log:warning("Cluster minority status detected - stopping~n", []), + spawn(fun () -> + %% If our group leader is inside an application we are about + %% to stop, application:stop/1 does not return. + group_leader(whereis(init), self()), + rabbit:stop_and_halt() + end). + handle_live_rabbit(Node) -> ok = rabbit_alarm:on_node_up(Node), ok = rabbit_mnesia:on_node_up(Node). -- cgit v1.2.1 From b1ddc98ab0ddcdfa065abcc8088722243b946d4e Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 18 Feb 2013 18:34:20 +0000 Subject: Ensure that we resume socket reading in all edge cases Specifically in all the cases where handle_other might have changed the connection_state. This is most straightforward and obvious to guarantee by always invoking recvloop after handle_other, unless we are stopping. This does expose an inconsistency in the various non-throw/exit termination cases: two of them were returning State, the other ok. Let's go with the latter; it's easiest. We also take the opportunity to eliminate 'Deb' from the handle_other signature. This is only needed in the {system, ...} message case, which we now handle specially. --- src/rabbit_reader.erl | 102 ++++++++++++++++++++++++++------------------------ 1 file changed, 54 insertions(+), 48 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 3a517677..0b5155da 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -277,24 +277,33 @@ recvloop(Deb, State = #v1{recv_len = RecvLen, buf = Buf, buf_len = BufLen}) -> mainloop(Deb, State = #v1{sock = Sock, buf = Buf, buf_len = BufLen}) -> case rabbit_net:recv(Sock) of - {data, Data} -> recvloop(Deb, State#v1{buf = [Data | Buf], - buf_len = BufLen + size(Data), - pending_recv = false}); - closed -> case State#v1.connection_state of - closed -> State; - _ -> throw(connection_closed_abruptly) - end; - {error, Reason} -> throw({inet_error, Reason}); - {other, Other} -> handle_other(Other, Deb, State) + {data, Data} -> + recvloop(Deb, State#v1{buf = [Data | Buf], + buf_len = BufLen + size(Data), + pending_recv = false}); + closed when State#v1.connection_state =:= closed -> + ok; + closed -> + throw(connection_closed_abruptly); + {error, Reason} -> + throw({inet_error, Reason}); + {other, {system, From, Request}} -> + sys:handle_system_msg(Request, From, State#v1.parent, + ?MODULE, Deb, State); + {other, Other} -> + case handle_other(Other, State) of + stop -> ok; + NewState -> recvloop(Deb, NewState) + end end. -handle_other({conserve_resources, Conserve}, Deb, State) -> - recvloop(Deb, control_throttle(State#v1{conserve_resources = Conserve})); -handle_other({channel_closing, ChPid}, Deb, State) -> +handle_other({conserve_resources, Conserve}, State) -> + control_throttle(State#v1{conserve_resources = Conserve}); +handle_other({channel_closing, ChPid}, State) -> ok = rabbit_channel:ready_for_close(ChPid), channel_cleanup(ChPid), - mainloop(Deb, maybe_close(control_throttle(State))); -handle_other({'EXIT', Parent, Reason}, _Deb, State = #v1{parent = Parent}) -> + maybe_close(control_throttle(State)); +handle_other({'EXIT', Parent, Reason}, State = #v1{parent = Parent}) -> terminate(io_lib:format("broker forced connection closure " "with reason '~w'", [Reason]), State), %% this is what we are expected to do according to @@ -306,59 +315,56 @@ handle_other({'EXIT', Parent, Reason}, _Deb, State = #v1{parent = Parent}) -> %% initiated by our parent it is probably more important to exit %% quickly. exit(Reason); -handle_other({channel_exit, _Channel, E = {writer, send_failed, _Error}}, - _Deb, _State) -> +handle_other({channel_exit, _Channel, E = {writer, send_failed, _E}}, _State) -> throw(E); -handle_other({channel_exit, Channel, Reason}, Deb, State) -> - mainloop(Deb, handle_exception(State, Channel, Reason)); -handle_other({'DOWN', _MRef, process, ChPid, Reason}, Deb, State) -> - mainloop(Deb, handle_dependent_exit(ChPid, Reason, State)); -handle_other(terminate_connection, _Deb, State) -> - State; -handle_other(handshake_timeout, Deb, State) +handle_other({channel_exit, Channel, Reason}, State) -> + handle_exception(State, Channel, Reason); +handle_other({'DOWN', _MRef, process, ChPid, Reason}, State) -> + handle_dependent_exit(ChPid, Reason, State); +handle_other(terminate_connection, _State) -> + stop; +handle_other(handshake_timeout, State) when ?IS_RUNNING(State) orelse State#v1.connection_state =:= closing orelse State#v1.connection_state =:= closed -> - mainloop(Deb, State); -handle_other(handshake_timeout, _Deb, State) -> + State; +handle_other(handshake_timeout, State) -> throw({handshake_timeout, State#v1.callback}); -handle_other(heartbeat_timeout, Deb, State = #v1{connection_state = closed}) -> - mainloop(Deb, State); -handle_other(heartbeat_timeout, _Deb, #v1{connection_state = S}) -> +handle_other(heartbeat_timeout, State = #v1{connection_state = closed}) -> + State; +handle_other(heartbeat_timeout, #v1{connection_state = S}) -> throw({heartbeat_timeout, S}); -handle_other({'$gen_call', From, {shutdown, Explanation}}, Deb, State) -> +handle_other({'$gen_call', From, {shutdown, Explanation}}, State) -> {ForceTermination, NewState} = terminate(Explanation, State), gen_server:reply(From, ok), case ForceTermination of - force -> ok; - normal -> mainloop(Deb, NewState) + force -> stop; + normal -> NewState end; -handle_other({'$gen_call', From, info}, Deb, State) -> +handle_other({'$gen_call', From, info}, State) -> gen_server:reply(From, infos(?INFO_KEYS, State)), - mainloop(Deb, State); -handle_other({'$gen_call', From, {info, Items}}, Deb, State) -> + State; +handle_other({'$gen_call', From, {info, Items}}, State) -> gen_server:reply(From, try {ok, infos(Items, State)} catch Error -> {error, Error} end), - mainloop(Deb, State); -handle_other({'$gen_cast', force_event_refresh}, Deb, State) + State; +handle_other({'$gen_cast', force_event_refresh}, State) when ?IS_RUNNING(State) -> rabbit_event:notify(connection_created, [{type, network} | infos(?CREATION_EVENT_KEYS, State)]), - mainloop(Deb, State); -handle_other({'$gen_cast', force_event_refresh}, Deb, State) -> + State; +handle_other({'$gen_cast', force_event_refresh}, State) -> %% Ignore, we will emit a created event once we start running. - mainloop(Deb, State); -handle_other(ensure_stats, Deb, State) -> - mainloop(Deb, ensure_stats_timer(State)); -handle_other(emit_stats, Deb, State) -> - mainloop(Deb, emit_stats(State)); -handle_other({system, From, Request}, Deb, State = #v1{parent = Parent}) -> - sys:handle_system_msg(Request, From, Parent, ?MODULE, Deb, State); -handle_other({bump_credit, Msg}, Deb, State) -> + State; +handle_other(ensure_stats, State) -> + ensure_stats_timer(State); +handle_other(emit_stats, State) -> + emit_stats(State); +handle_other({bump_credit, Msg}, State) -> credit_flow:handle_bump_msg(Msg), - recvloop(Deb, control_throttle(State)); -handle_other(Other, _Deb, _State) -> + control_throttle(State); +handle_other(Other, _State) -> %% internal error -> something worth dying for exit({unexpected_message, Other}). -- cgit v1.2.1 From cbf31df3f7e86ecd91746df92a504d3c64b85b38 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 20 Feb 2013 12:05:22 +0000 Subject: Remove spurious blank line in the logs. --- src/rabbit.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 4487f07c..f3ba022a 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -711,7 +711,7 @@ log_broker_started(Plugins) -> PluginList = iolist_to_binary([rabbit_misc:format(" * ~s~n", [P]) || P <- Plugins]), error_logger:info_msg( - "Server startup complete; ~b plugins started.~n~s~n", + "Server startup complete; ~b plugins started.~n~s", [length(Plugins), PluginList]), io:format(" completed with ~p plugins.~n", [length(Plugins)]) end). -- 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(-) 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 4bc0fdc948a9221d3136d306fc3beb4c04aa6575 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 20 Feb 2013 14:57:53 +0000 Subject: Neatness --- src/rabbit_amqqueue_process.erl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 35a28b6b..8ba9b4d2 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -471,12 +471,12 @@ deliver_msg_to_consumer(DeliverFun, E = {ChPid, Consumer}, State) -> Limiter2 -> AC1 = queue:in(E, State#q.active_consumers), deliver_msg_to_consumer( - DeliverFun, Limiter2, Consumer, C, + DeliverFun, Consumer, C#cr{limiter = Limiter2}, State#q{active_consumers = AC1}) end end. -deliver_msg_to_consumer(DeliverFun, NewLimiter, +deliver_msg_to_consumer(DeliverFun, #consumer{tag = ConsumerTag, ack_required = AckRequired}, C = #cr{ch_pid = ChPid, @@ -492,7 +492,6 @@ deliver_msg_to_consumer(DeliverFun, NewLimiter, false -> ChAckTags end, update_ch_record(C#cr{acktags = ChAckTags1, - limiter = NewLimiter, unsent_message_count = Count + 1}), {Stop, State1}. -- cgit v1.2.1 From 21967553cfe335810ee6bf922e1b71ee8acfaa16 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 20 Feb 2013 17:20:02 +0000 Subject: Be more careful about where we send_drained from. --- src/rabbit_amqqueue_process.erl | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 8ba9b4d2..5c376681 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -407,6 +407,12 @@ maybe_send_drained(#q{backing_queue = BQ, backing_queue_state = BQS}) -> false -> ok end. +maybe_send_drained(C, #q{backing_queue = BQ, backing_queue_state = BQS}) -> + case BQ:is_empty(BQS) of + true -> send_drained(C); + false -> ok + end. + send_drained() -> [send_drained(C) || C <- all_ch_record()]. send_drained(C = #cr{ch_pid = ChPid, limiter = Limiter}) -> @@ -437,7 +443,6 @@ is_ch_blocked(#cr{unsent_message_count = Count, is_limit_active = Limited}) -> Limited orelse Count >= ?UNSENT_MESSAGE_LIMIT. deliver_msgs_to_consumers(_DeliverFun, true, State) -> - send_drained(), {true, State}; deliver_msgs_to_consumers(DeliverFun, false, State = #q{active_consumers = ActiveConsumers}) -> @@ -603,12 +608,16 @@ deliver_or_enqueue(Delivery = #delivery{message = Message, sender = SenderPid}, requeue_and_run(AckTags, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> {_MsgIds, BQS1} = BQ:requeue(AckTags, BQS), - run_message_queue(drop_expired_msgs(State#q{backing_queue_state = BQS1})). + State1 = drop_expired_msgs(State#q{backing_queue_state = BQS1}), + maybe_send_drained(State1), + run_message_queue(State1). fetch(AckRequired, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> {Result, BQS1} = BQ:fetch(AckRequired, BQS), - {Result, drop_expired_msgs(State#q{backing_queue_state = BQS1})}. + State1 = drop_expired_msgs(State#q{backing_queue_state = BQS1}), + maybe_send_drained(State1), + {Result, State1}. ack(AckTags, ChPid, State) -> subtract_acks(ChPid, AckTags, State, @@ -752,6 +761,11 @@ calculate_msg_expiry(#basic_message{content = Content}, TTL) -> T -> now_micros() + T * 1000 end. +%% Logically this function should invoke maybe_send_drained/1. However, that's +%% expensive, and some frequent callers of drop_expired_msgs/1 (in particular +%% deliver_or_enqueue/3) cannot possibly cause the queue to become empty, so +%% instead we push the responsibility to the call sites. So be cautious when +%% adding new ones. drop_expired_msgs(State = #q{backing_queue_state = BQS, backing_queue = BQ }) -> case BQ:is_empty(BQS) of @@ -1154,7 +1168,7 @@ handle_call({basic_consume, NoAck, ChPid, Limiter, AC1 = queue:in(E, State1#q.active_consumers), run_message_queue(State1#q{active_consumers = AC1}) end, - maybe_send_drained(State2), + maybe_send_drained(C1, State), emit_consumer_created(ChPid, ConsumerTag, ExclusiveConsume, not NoAck, qname(State2)), reply(ok, State2) @@ -1204,7 +1218,9 @@ handle_call({delete, IfUnused, IfEmpty}, From, handle_call(purge, _From, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> {Count, BQS1} = BQ:purge(BQS), - reply({ok, Count}, State#q{backing_queue_state = BQS1}); + State1 = State#q{backing_queue_state = BQS1}, + maybe_send_drained(State1), + reply({ok, Count}, State1); handle_call({requeue, AckTags, ChPid}, From, State) -> gen_server2:reply(From, ok), @@ -1367,7 +1383,7 @@ handle_cast({credit, ChPid, CTag, Credit, Drain}, rabbit_channel:send_credit_reply(ChPid, BQ:len(BQS)), State1 = possibly_unblock( State, ChPid, fun(C) -> C#cr{limiter = Lim2} end), - maybe_send_drained(State1), + maybe_send_drained(lookup_ch(ChPid), State), noreply(State1); handle_cast(wake_up, State) -> @@ -1389,7 +1405,9 @@ handle_info(maybe_expire, State) -> end; handle_info(drop_expired, State) -> - noreply(drop_expired_msgs(State#q{ttl_timer_ref = undefined})); + State1 = drop_expired_msgs(State#q{ttl_timer_ref = undefined}), + maybe_send_drained(State1), + noreply(State1); handle_info(emit_stats, State) -> emit_stats(State), -- cgit v1.2.1 From 44a07f8a313dd5745355b4e148a74b572984f15d Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 20 Feb 2013 17:23:39 +0000 Subject: Ahem --- src/rabbit_amqqueue_process.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 5c376681..78b0d23d 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1168,7 +1168,7 @@ handle_call({basic_consume, NoAck, ChPid, Limiter, AC1 = queue:in(E, State1#q.active_consumers), run_message_queue(State1#q{active_consumers = AC1}) end, - maybe_send_drained(C1, State), + maybe_send_drained(C1, State2), emit_consumer_created(ChPid, ConsumerTag, ExclusiveConsume, not NoAck, qname(State2)), reply(ok, State2) @@ -1383,7 +1383,7 @@ handle_cast({credit, ChPid, CTag, Credit, Drain}, rabbit_channel:send_credit_reply(ChPid, BQ:len(BQS)), State1 = possibly_unblock( State, ChPid, fun(C) -> C#cr{limiter = Lim2} end), - maybe_send_drained(lookup_ch(ChPid), State), + maybe_send_drained(lookup_ch(ChPid), State1), noreply(State1); handle_cast(wake_up, State) -> -- cgit v1.2.1 From 773cdb96c27f1f36257ca020e33808c8402510df Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 20 Feb 2013 17:33:35 +0000 Subject: Only send_drained if we have become empty... --- src/rabbit_amqqueue_process.erl | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 78b0d23d..829798c7 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -401,13 +401,16 @@ erase_ch_record(#cr{ch_pid = ChPid, erase({ch, ChPid}), ok. -maybe_send_drained(#q{backing_queue = BQ, backing_queue_state = BQS}) -> +maybe_send_drained(true, _State) -> + ok; + +maybe_send_drained(false, #q{backing_queue = BQ, backing_queue_state = BQS}) -> case BQ:is_empty(BQS) of true -> send_drained(); false -> ok end. -maybe_send_drained(C, #q{backing_queue = BQ, backing_queue_state = BQS}) -> +maybe_send_drained_cons(C, #q{backing_queue = BQ, backing_queue_state = BQS}) -> case BQ:is_empty(BQS) of true -> send_drained(C); false -> ok @@ -609,14 +612,14 @@ requeue_and_run(AckTags, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> {_MsgIds, BQS1} = BQ:requeue(AckTags, BQS), State1 = drop_expired_msgs(State#q{backing_queue_state = BQS1}), - maybe_send_drained(State1), + maybe_send_drained(BQ:is_empty(BQS), State1), run_message_queue(State1). fetch(AckRequired, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> {Result, BQS1} = BQ:fetch(AckRequired, BQS), State1 = drop_expired_msgs(State#q{backing_queue_state = BQS1}), - maybe_send_drained(State1), + maybe_send_drained(Result =:= empty, State1), {Result, State1}. ack(AckTags, ChPid, State) -> @@ -1168,7 +1171,7 @@ handle_call({basic_consume, NoAck, ChPid, Limiter, AC1 = queue:in(E, State1#q.active_consumers), run_message_queue(State1#q{active_consumers = AC1}) end, - maybe_send_drained(C1, State2), + maybe_send_drained_cons(C1, State2), emit_consumer_created(ChPid, ConsumerTag, ExclusiveConsume, not NoAck, qname(State2)), reply(ok, State2) @@ -1219,7 +1222,7 @@ handle_call(purge, _From, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> {Count, BQS1} = BQ:purge(BQS), State1 = State#q{backing_queue_state = BQS1}, - maybe_send_drained(State1), + maybe_send_drained(Count =:= 0, State1), reply({ok, Count}, State1); handle_call({requeue, AckTags, ChPid}, From, State) -> @@ -1383,7 +1386,7 @@ handle_cast({credit, ChPid, CTag, Credit, Drain}, rabbit_channel:send_credit_reply(ChPid, BQ:len(BQS)), State1 = possibly_unblock( State, ChPid, fun(C) -> C#cr{limiter = Lim2} end), - maybe_send_drained(lookup_ch(ChPid), State1), + maybe_send_drained_cons(lookup_ch(ChPid), State1), noreply(State1); handle_cast(wake_up, State) -> @@ -1404,9 +1407,10 @@ handle_info(maybe_expire, State) -> false -> noreply(ensure_expiry_timer(State)) end; -handle_info(drop_expired, State) -> +handle_info(drop_expired, State = #q{backing_queue = BQ, + backing_queue_state = BQS}) -> State1 = drop_expired_msgs(State#q{ttl_timer_ref = undefined}), - maybe_send_drained(State1), + maybe_send_drained(BQ:is_empty(BQS), State1), noreply(State1); handle_info(emit_stats, State) -> -- cgit v1.2.1 From 3378756291aca8f634e136fb96a118eb464ddad3 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 20 Feb 2013 17:48:37 +0000 Subject: cosmetic --- src/rabbit_amqqueue_process.erl | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 829798c7..5fd3377a 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -403,10 +403,9 @@ erase_ch_record(#cr{ch_pid = ChPid, maybe_send_drained(true, _State) -> ok; - maybe_send_drained(false, #q{backing_queue = BQ, backing_queue_state = BQS}) -> case BQ:is_empty(BQS) of - true -> send_drained(); + true -> [send_drained(C) || C <- all_ch_record()]; false -> ok end. @@ -416,15 +415,12 @@ maybe_send_drained_cons(C, #q{backing_queue = BQ, backing_queue_state = BQS}) -> false -> ok end. -send_drained() -> [send_drained(C) || C <- all_ch_record()]. - send_drained(C = #cr{ch_pid = ChPid, limiter = Limiter}) -> case rabbit_limiter:drained(Limiter) of - {[], Limiter} -> - ok; - {CTagCredit, Limiter2} -> - rabbit_channel:send_drained(ChPid, CTagCredit), - update_ch_record(C#cr{limiter = Limiter2}) + {[], Limiter} -> ok; + {CTagCredit, Limiter2} -> rabbit_channel:send_drained( + ChPid, CTagCredit), + update_ch_record(C#cr{limiter = Limiter2}) end. update_consumer_count(C = #cr{consumer_count = 0, limiter = Limiter}, +1) -> -- cgit v1.2.1 From 1a3bde0490e78fe41ff61f903a4c688b2ccf284d Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 20 Feb 2013 17:49:03 +0000 Subject: eliminate non-linear BQS usage --- src/rabbit_amqqueue_process.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 5fd3377a..de3e73ee 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -606,9 +606,10 @@ deliver_or_enqueue(Delivery = #delivery{message = Message, sender = SenderPid}, requeue_and_run(AckTags, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> + WasEmpty = BQ:is_empty(BQS), {_MsgIds, BQS1} = BQ:requeue(AckTags, BQS), State1 = drop_expired_msgs(State#q{backing_queue_state = BQS1}), - maybe_send_drained(BQ:is_empty(BQS), State1), + maybe_send_drained(WasEmpty, State1), run_message_queue(State1). fetch(AckRequired, State = #q{backing_queue = BQ, -- cgit v1.2.1 From cf56799e48ed7dedbf0060f217b96d50c2868ddc Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 20 Feb 2013 17:54:00 +0000 Subject: cosmetic --- src/rabbit_amqqueue_process.erl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index de3e73ee..9fd85bd6 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -761,11 +761,11 @@ calculate_msg_expiry(#basic_message{content = Content}, TTL) -> T -> now_micros() + T * 1000 end. -%% Logically this function should invoke maybe_send_drained/1. However, that's -%% expensive, and some frequent callers of drop_expired_msgs/1 (in particular -%% deliver_or_enqueue/3) cannot possibly cause the queue to become empty, so -%% instead we push the responsibility to the call sites. So be cautious when -%% adding new ones. +%% Logically this function should invoke maybe_send_drained/2. +%% However, that is expensive. Since some frequent callers of +%% drop_expired_msgs/1, in particular deliver_or_enqueue/3, cannot +%% possibly cause the queue to become empty, we push the +%% responsibility to the callers. So be cautious when adding new ones. drop_expired_msgs(State = #q{backing_queue_state = BQS, backing_queue = BQ }) -> case BQ:is_empty(BQS) of -- cgit v1.2.1 From e7a061a08b7c91393beaf7d377b5efb8a9c633db Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 20 Feb 2013 17:55:23 +0000 Subject: cosmetic --- src/rabbit_amqqueue_process.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 4a0ccf81..be7ee097 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -738,8 +738,8 @@ drop_expired_msgs(Now, State = #q{backing_queue_state = BQS, fun () -> {Next, BQS1} = BQ:dropwhile(ExpirePred, BQS), {Next, State#q{backing_queue_state = BQS1}} end), ensure_ttl_timer(case Props of - undefined -> undefined; - #message_properties{expiry = Exp} -> Exp + undefined -> undefined; + #message_properties{expiry = Exp} -> Exp end, State1). with_dlx(undefined, _With, Without) -> Without(); -- cgit v1.2.1 From adf8c37c62b490fb42db906a318fe957ac39d299 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 20 Feb 2013 19:41:11 +0000 Subject: introduce is_empty(State) helper and in the resulting refactor also remove a non-linear BQS access in handle_info/drop_expired. --- src/rabbit_amqqueue_process.erl | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 9fd85bd6..fb60a043 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -353,9 +353,10 @@ stop_ttl_timer(State) -> rabbit_misc:stop_timer(State, #q.ttl_timer_ref). ensure_stats_timer(State) -> rabbit_event:ensure_stats_timer(State, #q.stats_timer, emit_stats). -assert_invariant(#q{active_consumers = AC, - backing_queue = BQ, backing_queue_state = BQS}) -> - true = (queue:is_empty(AC) orelse BQ:is_empty(BQS)). +assert_invariant(State = #q{active_consumers = AC}) -> + true = (queue:is_empty(AC) orelse is_empty(State)). + +is_empty({backing_queue = BQ, backing_queue_state = BQS}) -> BQ:is_empty(BQS). lookup_ch(ChPid) -> case get({ch, ChPid}) of @@ -401,16 +402,14 @@ erase_ch_record(#cr{ch_pid = ChPid, erase({ch, ChPid}), ok. -maybe_send_drained(true, _State) -> - ok; -maybe_send_drained(false, #q{backing_queue = BQ, backing_queue_state = BQS}) -> - case BQ:is_empty(BQS) of +maybe_send_drained(WasEmpty, State) -> + case WasEmpty andalso is_empty(State) of true -> [send_drained(C) || C <- all_ch_record()]; false -> ok end. -maybe_send_drained_cons(C, #q{backing_queue = BQ, backing_queue_state = BQS}) -> - case BQ:is_empty(BQS) of +maybe_send_drained_cons(C, State) -> + case is_empty(State) of true -> send_drained(C); false -> ok end. @@ -500,9 +499,8 @@ deliver_msg_to_consumer(DeliverFun, {Stop, State1}. deliver_from_queue_deliver(AckRequired, State) -> - {Result, State1 = #q{backing_queue = BQ, backing_queue_state = BQS}} = - fetch(AckRequired, State), - {Result, BQ:is_empty(BQS), State1}. + {Result, State1} = fetch(AckRequired, State), + {Result, is_empty(State1), State1}. confirm_messages([], State) -> State; @@ -549,10 +547,10 @@ discard(#delivery{sender = SenderPid, BQS1 = BQ:discard(MsgId, SenderPid, BQS), State1#q{backing_queue_state = BQS1}. -run_message_queue(State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> +run_message_queue(State) -> {_IsEmpty1, State1} = deliver_msgs_to_consumers( fun deliver_from_queue_deliver/2, - BQ:is_empty(BQS), State), + is_empty(State), State), State1. attempt_delivery(Delivery = #delivery{sender = SenderPid, message = Message}, @@ -766,9 +764,8 @@ calculate_msg_expiry(#basic_message{content = Content}, TTL) -> %% drop_expired_msgs/1, in particular deliver_or_enqueue/3, cannot %% possibly cause the queue to become empty, we push the %% responsibility to the callers. So be cautious when adding new ones. -drop_expired_msgs(State = #q{backing_queue_state = BQS, - backing_queue = BQ }) -> - case BQ:is_empty(BQS) of +drop_expired_msgs(State) -> + case is_empty(State) of true -> State; false -> drop_expired_msgs(now_micros(), State) end. @@ -1404,10 +1401,10 @@ handle_info(maybe_expire, State) -> false -> noreply(ensure_expiry_timer(State)) end; -handle_info(drop_expired, State = #q{backing_queue = BQ, - backing_queue_state = BQS}) -> +handle_info(drop_expired, State) -> + WasEmpty = is_empty(State), State1 = drop_expired_msgs(State#q{ttl_timer_ref = undefined}), - maybe_send_drained(BQ:is_empty(BQS), State1), + maybe_send_drained(WasEmpty, State1), noreply(State1); handle_info(emit_stats, State) -> -- cgit v1.2.1 From d011068eabb9861e49e3c1323a9bf1a8465a2b30 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 20 Feb 2013 19:42:27 +0000 Subject: refactor possibly_unblock --- src/rabbit_amqqueue_process.erl | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index fb60a043..0530d650 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -644,25 +644,26 @@ remove_consumers(ChPid, Queue, QName) -> possibly_unblock(State, ChPid, Update) -> case lookup_ch(ChPid) of - not_found -> + not_found -> State; + C -> possibly_unblock(State, Update(C)) + end. + +possibly_unblock(State, C = #cr{limiter = Limiter}) -> + IsChBlocked = is_ch_blocked(C), + case lists:partition( + fun({_ChPid, #consumer{tag = CTag}}) -> + IsChBlocked orelse + rabbit_limiter:is_consumer_blocked(Limiter, CTag) + end, queue:to_list(C#cr.blocked_consumers)) of + {_, []} -> + update_ch_record(C), State; - C -> - C1 = #cr{limiter = Limiter} = Update(C), - {Blocked, Unblocked} = - lists:partition( - fun({_ChPid, #consumer{tag = CTag}}) -> - is_ch_blocked(C1) orelse - rabbit_limiter:is_consumer_blocked(Limiter, CTag) - end, queue:to_list(C1#cr.blocked_consumers)), - case Unblocked of - [] -> update_ch_record(C1), - State; - _ -> update_ch_record( - C1#cr{blocked_consumers = queue:from_list(Blocked)}), - AC1 = queue:join(State#q.active_consumers, - queue:from_list(Unblocked)), - run_message_queue(State#q{active_consumers = AC1}) - end + {Blocked, Unblocked} -> + BlockedQ = queue:from_list(Blocked), + UnblockedQ = 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}) end. should_auto_delete(#q{q = #amqqueue{auto_delete = false}}) -> false; -- cgit v1.2.1 From cba68ce1a820cd6f57dc86482049714734d309cd Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 20 Feb 2013 19:45:01 +0000 Subject: get rid of maybe_send_drained_cons and optimise handle_cast/credit along the way --- src/rabbit_amqqueue_process.erl | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 0530d650..c0e74129 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -408,12 +408,6 @@ maybe_send_drained(WasEmpty, State) -> false -> ok end. -maybe_send_drained_cons(C, State) -> - case is_empty(State) of - true -> send_drained(C); - false -> ok - end. - send_drained(C = #cr{ch_pid = ChPid, limiter = Limiter}) -> case rabbit_limiter:drained(Limiter) of {[], Limiter} -> ok; @@ -1166,7 +1160,10 @@ handle_call({basic_consume, NoAck, ChPid, Limiter, AC1 = queue:in(E, State1#q.active_consumers), run_message_queue(State1#q{active_consumers = AC1}) end, - maybe_send_drained_cons(C1, State2), + case is_empty(State2) of + true -> send_drained(lookup_ch(ChPid)); + false -> ok + end, emit_consumer_created(ChPid, ConsumerTag, ExclusiveConsume, not NoAck, qname(State2)), reply(ok, State2) @@ -1376,13 +1373,16 @@ handle_cast(stop_mirroring, State = #q{backing_queue = BQ, handle_cast({credit, ChPid, CTag, Credit, Drain}, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> - #cr{limiter = Lim} = ch_record(ChPid), - Lim2 = rabbit_limiter:credit(Lim, CTag, Credit, Drain), - rabbit_channel:send_credit_reply(ChPid, BQ:len(BQS)), - State1 = possibly_unblock( - State, ChPid, fun(C) -> C#cr{limiter = Lim2} end), - maybe_send_drained_cons(lookup_ch(ChPid), State1), - noreply(State1); + Len = BQ:len(BQS), + rabbit_channel:send_credit_reply(ChPid, Len), + C = #cr{limiter = Lim} = lookup_ch(ChPid), + C1 = C#cr{limiter = rabbit_limiter:credit(Lim, CTag, Credit, Drain)}, + noreply(case Drain andalso Len == 0 of + true -> update_ch_record(C1), + send_drained(C1), + State; + false -> possibly_unblock(State, C1) + end); handle_cast(wake_up, State) -> noreply(State). -- cgit v1.2.1 From 6bde32b8f6af20212b815d62e5aac051af14d62f Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 20 Feb 2013 19:54:22 +0000 Subject: introduce is_empty(State) helper in rabbit_amqqueue_process ported from bug 23749 --- src/rabbit_amqqueue_process.erl | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index be7ee097..4f0da702 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -347,9 +347,10 @@ stop_ttl_timer(State) -> rabbit_misc:stop_timer(State, #q.ttl_timer_ref). ensure_stats_timer(State) -> rabbit_event:ensure_stats_timer(State, #q.stats_timer, emit_stats). -assert_invariant(#q{active_consumers = AC, - backing_queue = BQ, backing_queue_state = BQS}) -> - true = (queue:is_empty(AC) orelse BQ:is_empty(BQS)). +assert_invariant(State = #q{active_consumers = AC}) -> + true = (queue:is_empty(AC) orelse is_empty(State)). + +is_empty({backing_queue = BQ, backing_queue_state = BQS}) -> BQ:is_empty(BQS). lookup_ch(ChPid) -> case get({ch, ChPid}) of @@ -470,9 +471,8 @@ deliver_msg_to_consumer(DeliverFun, {Stop, State1}. deliver_from_queue_deliver(AckRequired, State) -> - {Result, State1 = #q{backing_queue = BQ, backing_queue_state = BQS}} = - fetch(AckRequired, State), - {Result, BQ:is_empty(BQS), State1}. + {Result, State1} = fetch(AckRequired, State), + {Result, is_empty(State1), State1}. confirm_messages([], State) -> State; @@ -519,10 +519,10 @@ discard(#delivery{sender = SenderPid, BQS1 = BQ:discard(MsgId, SenderPid, BQS), State1#q{backing_queue_state = BQS1}. -run_message_queue(State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> +run_message_queue(State) -> {_IsEmpty1, State1} = deliver_msgs_to_consumers( fun deliver_from_queue_deliver/2, - BQ:is_empty(BQS), State), + is_empty(State), State), State1. attempt_delivery(Delivery = #delivery{sender = SenderPid, message = Message}, @@ -721,9 +721,8 @@ calculate_msg_expiry(#basic_message{content = Content}, TTL) -> T -> now_micros() + T * 1000 end. -drop_expired_msgs(State = #q{backing_queue_state = BQS, - backing_queue = BQ }) -> - case BQ:is_empty(BQS) of +drop_expired_msgs(State) -> + case is_empty(State) of true -> State; false -> drop_expired_msgs(now_micros(), State) end. -- cgit v1.2.1 From a18a941f0a8d06e310fd03787d3c35ce82cdeec5 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 20 Feb 2013 20:11:20 +0000 Subject: cosmetic move functions to a better place --- src/rabbit_amqqueue_process.erl | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 830ad35b..c822c2e7 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -402,20 +402,6 @@ erase_ch_record(#cr{ch_pid = ChPid, erase({ch, ChPid}), ok. -maybe_send_drained(WasEmpty, State) -> - case WasEmpty andalso is_empty(State) of - true -> [send_drained(C) || C <- all_ch_record()]; - false -> ok - end. - -send_drained(C = #cr{ch_pid = ChPid, limiter = Limiter}) -> - case rabbit_limiter:drained(Limiter) of - {[], Limiter} -> ok; - {CTagCredit, Limiter2} -> rabbit_channel:send_drained( - ChPid, CTagCredit), - update_ch_record(C#cr{limiter = Limiter2}) - end. - update_consumer_count(C = #cr{consumer_count = 0, limiter = Limiter}, +1) -> ok = rabbit_limiter:register(Limiter, self()), update_ch_record(C#cr{consumer_count = 1}); @@ -434,6 +420,20 @@ block_consumer(C = #cr{blocked_consumers = Blocked}, QEntry) -> is_ch_blocked(#cr{unsent_message_count = Count, is_limit_active = Limited}) -> Limited orelse Count >= ?UNSENT_MESSAGE_LIMIT. +maybe_send_drained(WasEmpty, State) -> + case WasEmpty andalso is_empty(State) of + true -> [send_drained(C) || C <- all_ch_record()]; + false -> ok + end. + +send_drained(C = #cr{ch_pid = ChPid, limiter = Limiter}) -> + case rabbit_limiter:drained(Limiter) of + {[], Limiter} -> ok; + {CTagCredit, Limiter2} -> rabbit_channel:send_drained( + ChPid, CTagCredit), + update_ch_record(C#cr{limiter = Limiter2}) + end. + deliver_msgs_to_consumers(_DeliverFun, true, State) -> {true, State}; deliver_msgs_to_consumers(DeliverFun, false, -- cgit v1.2.1 From 6fdbdcfe1fd05d45ef06c9634726798e2e1d9539 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 20 Feb 2013 20:24:32 +0000 Subject: oops --- src/rabbit_amqqueue_process.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index c822c2e7..7a16865b 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -421,7 +421,7 @@ is_ch_blocked(#cr{unsent_message_count = Count, is_limit_active = Limited}) -> Limited orelse Count >= ?UNSENT_MESSAGE_LIMIT. maybe_send_drained(WasEmpty, State) -> - case WasEmpty andalso is_empty(State) of + case (not WasEmpty) andalso is_empty(State) of true -> [send_drained(C) || C <- all_ch_record()]; false -> ok end. -- cgit v1.2.1 From e7cc227a8f508bd8e2845be6e178de46617bef6f Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 20 Feb 2013 20:42:27 +0000 Subject: cosmetic - reduce distance to 'default' --- src/rabbit_amqqueue_process.erl | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 7a16865b..e30a9839 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -451,26 +451,23 @@ deliver_msgs_to_consumers(DeliverFun, false, deliver_msg_to_consumer(DeliverFun, E = {ChPid, Consumer}, State) -> C = ch_record(ChPid), case is_ch_blocked(C) of - true -> - block_consumer(C, E), - {false, State}; - false -> - #cr{limiter = Limiter, ch_pid = ChPid} = C, - #consumer{tag = CTag} = Consumer, - case rabbit_limiter:can_send( - Limiter, self(), Consumer#consumer.ack_required, CTag) of - {consumer_blocked, Limiter2} -> - block_consumer(C#cr{limiter = Limiter2}, E), - {false, State}; - channel_blocked -> - block_consumer(C#cr{is_limit_active = true}, E), - {false, State}; - Limiter2 -> - AC1 = queue:in(E, State#q.active_consumers), - deliver_msg_to_consumer( - DeliverFun, Consumer, C#cr{limiter = Limiter2}, - State#q{active_consumers = AC1}) - end + true -> block_consumer(C, E), + {false, State}; + false -> case rabbit_limiter:can_send(C#cr.limiter, self(), + Consumer#consumer.ack_required, + Consumer#consumer.tag) of + {consumer_blocked, Limiter2} -> + block_consumer(C#cr{limiter = Limiter2}, E), + {false, State}; + channel_blocked -> + block_consumer(C#cr{is_limit_active = true}, E), + {false, State}; + Limiter2 -> + AC1 = queue:in(E, State#q.active_consumers), + deliver_msg_to_consumer( + DeliverFun, Consumer, C#cr{limiter = Limiter2}, + State#q{active_consumers = AC1}) + end end. deliver_msg_to_consumer(DeliverFun, -- cgit v1.2.1 From 9d115a8217adba13989efb9fb20660e6ea39241e Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 20 Feb 2013 20:47:45 +0000 Subject: refactor it's convenient for callers to have maybe_send_drained thread through the state --- src/rabbit_amqqueue_process.erl | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index e30a9839..6de1d0a4 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -424,7 +424,8 @@ maybe_send_drained(WasEmpty, State) -> case (not WasEmpty) andalso is_empty(State) of true -> [send_drained(C) || C <- all_ch_record()]; false -> ok - end. + end, + State. send_drained(C = #cr{ch_pid = ChPid, limiter = Limiter}) -> case rabbit_limiter:drained(Limiter) of @@ -598,15 +599,13 @@ requeue_and_run(AckTags, State = #q{backing_queue = BQ, WasEmpty = BQ:is_empty(BQS), {_MsgIds, BQS1} = BQ:requeue(AckTags, BQS), State1 = drop_expired_msgs(State#q{backing_queue_state = BQS1}), - maybe_send_drained(WasEmpty, State1), - run_message_queue(State1). + run_message_queue(maybe_send_drained(WasEmpty, State1)). fetch(AckRequired, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> {Result, BQS1} = BQ:fetch(AckRequired, BQS), State1 = drop_expired_msgs(State#q{backing_queue_state = BQS1}), - maybe_send_drained(Result =:= empty, State1), - {Result, State1}. + {Result, maybe_send_drained(Result =:= empty, State1)}. ack(AckTags, ChPid, State) -> subtract_acks(ChPid, AckTags, State, @@ -1211,8 +1210,7 @@ handle_call(purge, _From, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> {Count, BQS1} = BQ:purge(BQS), State1 = State#q{backing_queue_state = BQS1}, - maybe_send_drained(Count =:= 0, State1), - reply({ok, Count}, State1); + reply({ok, Count}, maybe_send_drained(Count =:= 0, State1)); handle_call({requeue, AckTags, ChPid}, From, State) -> gen_server2:reply(From, ok), @@ -1402,8 +1400,7 @@ handle_info(maybe_expire, State) -> handle_info(drop_expired, State) -> WasEmpty = is_empty(State), State1 = drop_expired_msgs(State#q{ttl_timer_ref = undefined}), - maybe_send_drained(WasEmpty, State1), - noreply(State1); + noreply(maybe_send_drained(WasEmpty, State1)); handle_info(emit_stats, State) -> emit_stats(State), -- cgit v1.2.1 From 94dd69341f9da0970111072c44ecff9e140c7926 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 20 Feb 2013 21:29:04 +0000 Subject: optimise possibly_unblock when the channel is blocked there is no point going through the expensive consumer re-partitioning --- src/rabbit_amqqueue_process.erl | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 6de1d0a4..2b11c90d 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -639,21 +639,24 @@ possibly_unblock(State, ChPid, Update) -> end. possibly_unblock(State, C = #cr{limiter = Limiter}) -> - IsChBlocked = is_ch_blocked(C), - case lists:partition( - fun({_ChPid, #consumer{tag = CTag}}) -> - IsChBlocked orelse - rabbit_limiter:is_consumer_blocked(Limiter, CTag) - end, 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), - update_ch_record(C#cr{blocked_consumers = BlockedQ}), - AC1 = queue:join(State#q.active_consumers, UnblockedQ), - run_message_queue(State#q{active_consumers = AC1}) + case is_ch_blocked(C) of + true -> update_ch_record(C), + State; + false -> case lists:partition( + fun({_ChPid, #consumer{tag = CTag}}) -> + rabbit_limiter:is_consumer_blocked( + Limiter, CTag) + end, 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), + update_ch_record(C#cr{blocked_consumers = BlockedQ}), + AC1 = queue:join(State#q.active_consumers, UnblockedQ), + run_message_queue(State#q{active_consumers = AC1}) + end end. should_auto_delete(#q{q = #amqqueue{auto_delete = false}}) -> false; -- cgit v1.2.1 From 43ec388e97d0effb26b6ef4aef8d1a676acd9c94 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 20 Feb 2013 21:54:45 +0000 Subject: oops --- src/rabbit_amqqueue_process.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 4f0da702..ef48bb5d 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -350,7 +350,7 @@ ensure_stats_timer(State) -> assert_invariant(State = #q{active_consumers = AC}) -> true = (queue:is_empty(AC) orelse is_empty(State)). -is_empty({backing_queue = BQ, backing_queue_state = BQS}) -> BQ:is_empty(BQS). +is_empty(#q{backing_queue = BQ, backing_queue_state = BQS}) -> BQ:is_empty(BQS). lookup_ch(ChPid) -> case get({ch, ChPid}) of -- cgit v1.2.1 From 82cb8cff4c2a73b62ca014d21769729b6e14e2a5 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 21 Feb 2013 11:38:53 +0000 Subject: simplify queue's basic_consume handler - the call to update_ch_record in the is_ch_blocked(C1) == false branch was superfluos since the preceding update_consumer_count calls update_ch_record - all the checking whether the channel is blocked, and associated branching was just an optimisation. And not a particularly important one, since a) the "a new consumer comes along while its channel is blocked" case is hardly on the critical path, and b) exactly the same check is performed as part of run_message_queue (in deliver_msg_to_consumer/3). So get rid of it. - the is_empty & send_drained logic can be invoked earlier, which allows us to use the #cr we have rather than looking it up again. We can do this since the only case we need to catch here is that of a consumer coming along while the queue is empty already. If it becomes empty as part of run_message_queue then send_drained will be invoked in 'fetch'. --- src/rabbit_amqqueue_process.erl | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index a43dbdcc..c02fd6b5 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1142,6 +1142,10 @@ handle_call({basic_consume, NoAck, ChPid, Limiter, Limiter, ConsumerTag, Credit, Drain) end, C1 = update_consumer_count(C#cr{limiter = Limiter2}, +1), + case is_empty(State) of + true -> send_drained(C1); + false -> ok + end, Consumer = #consumer{tag = ConsumerTag, ack_required = not NoAck}, ExclusiveConsumer = if ExclusiveConsume -> {ChPid, ConsumerTag}; @@ -1150,22 +1154,10 @@ handle_call({basic_consume, NoAck, ChPid, Limiter, State1 = State#q{has_had_consumers = true, exclusive_consumer = ExclusiveConsumer}, ok = maybe_send_reply(ChPid, OkMsg), - E = {ChPid, Consumer}, - State2 = - case is_ch_blocked(C1) of - true -> block_consumer(C1, E), - State1; - false -> update_ch_record(C1), - AC1 = queue:in(E, State1#q.active_consumers), - run_message_queue(State1#q{active_consumers = AC1}) - end, - case is_empty(State2) of - true -> send_drained(lookup_ch(ChPid)); - false -> ok - end, emit_consumer_created(ChPid, ConsumerTag, ExclusiveConsume, - not NoAck, qname(State2)), - reply(ok, State2) + not NoAck, qname(State1)), + AC1 = queue:in({ChPid, Consumer}, State1#q.active_consumers), + reply(ok, run_message_queue(State1#q{active_consumers = AC1})) end; handle_call({basic_cancel, ChPid, ConsumerTag, OkMsg}, From, -- cgit v1.2.1 From 97757bbfc78bac5f02b5d7d9b1a4630cb41852f7 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 21 Feb 2013 11:50:19 +0000 Subject: Remove blocked_ctags, and a few knock-on simplifications. --- src/rabbit_amqqueue_process.erl | 6 ++--- src/rabbit_limiter.erl | 57 +++++++++++++++-------------------------- 2 files changed, 24 insertions(+), 39 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index c02fd6b5..79a98208 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -457,8 +457,8 @@ deliver_msg_to_consumer(DeliverFun, E = {ChPid, Consumer}, State) -> false -> case rabbit_limiter:can_send(C#cr.limiter, self(), Consumer#consumer.ack_required, Consumer#consumer.tag) of - {consumer_blocked, Limiter2} -> - block_consumer(C#cr{limiter = Limiter2}, E), + consumer_blocked -> + block_consumer(C, E), {false, State}; channel_blocked -> block_consumer(C#cr{is_limit_active = true}, E), @@ -1138,7 +1138,7 @@ handle_call({basic_consume, NoAck, ChPid, Limiter, none -> Limiter; {Credit, Drain} -> - rabbit_limiter:initial_credit( + rabbit_limiter:credit( Limiter, ConsumerTag, Credit, Drain) end, C1 = update_consumer_count(C#cr{limiter = Limiter2}, +1), diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index e76fc217..ece3d1a4 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -24,14 +24,13 @@ disable/1]). -export([limit/2, can_send/4, ack/2, register/2, unregister/2]). -export([get_limit/1, block/1, unblock/1, is_consumer_blocked/2, is_blocked/1]). --export([initial_credit/4, credit/4, drained/1, forget_consumer/2, - copy_queue_state/2]). +-export([credit/4, drained/1, forget_consumer/2, copy_queue_state/2]). -import(rabbit_misc, [serial_add/2, serial_diff/2]). %%---------------------------------------------------------------------------- --record(token, {pid, enabled, credits, blocked_ctags}). +-record(token, {pid, enabled, credits}). -ifdef(use_specs). @@ -56,8 +55,6 @@ -spec(unblock/1 :: (token()) -> 'ok' | {'disabled', token()}). -spec(is_blocked/1 :: (token()) -> boolean()). -spec(is_consumer_blocked/2 :: (token(), rabbit_types:ctag()) -> boolean()). --spec(initial_credit/4 :: (token(), rabbit_types:ctag(), - non_neg_integer(), boolean()) -> token()). -spec(credit/4 :: (token(), rabbit_types:ctag(), non_neg_integer(), boolean()) -> token()). -spec(drained/1 :: (token()) @@ -87,10 +84,9 @@ start_link() -> gen_server2:start_link(?MODULE, [], []). make_token() -> make_token(undefined). -make_token(Pid) -> #token{pid = Pid, - enabled = false, - credits = dict:new(), - blocked_ctags = []}. +make_token(Pid) -> #token{pid = Pid, + enabled = false, + credits = dict:new()}. is_enabled(#token{enabled = Enabled}) -> Enabled. @@ -107,21 +103,15 @@ limit(Limiter, PrefetchCount) -> %% breaching a limit. Note that we don't use maybe_call here in order %% to avoid always going through with_exit_handler/2, even when the %% limiter is disabled. -can_send(Token = #token{pid = Pid, enabled = Enabled, credits = Credits, - blocked_ctags = BCTags}, +can_send(Token = #token{pid = Pid, enabled = Enabled, credits = Credits}, QPid, AckReq, CTag) -> - ConsAllows = case dict:find(CTag, Credits) of - {ok, #credit{credit = C}} when C > 0 -> true; - {ok, #credit{}} -> false; - error -> true - end, - case ConsAllows of - true -> case not Enabled orelse call_can_send(Pid, QPid, AckReq) of + case is_consumer_blocked(Token, CTag) of + false -> case not Enabled orelse call_can_send(Pid, QPid, AckReq) of true -> Credits2 = record_send_q(CTag, Credits), Token#token{credits = Credits2}; false -> channel_blocked end; - false -> {consumer_blocked, Token#token{blocked_ctags = [CTag|BCTags]}} + true -> consumer_blocked end. call_can_send(Pid, QPid, AckRequired) -> @@ -150,21 +140,18 @@ block(Limiter) -> unblock(Limiter) -> maybe_call(Limiter, {unblock, Limiter}, ok). -is_consumer_blocked(#token{blocked_ctags = BCTags}, CTag) -> - lists:member(CTag, BCTags). +is_consumer_blocked(#token{credits = Credits}, CTag) -> + case dict:find(CTag, Credits) of + {ok, #credit{credit = C}} when C > 0 -> false; + {ok, #credit{}} -> true; + error -> false + end. is_blocked(Limiter) -> maybe_call(Limiter, is_blocked, false). -initial_credit(Limiter = #token{credits = Credits}, CTag, Credit, Drain) -> - {[], Credits2} = update_credit(CTag, Credit, Drain, Credits), - Limiter#token{credits = Credits2}. - -credit(Limiter = #token{credits = Credits, blocked_ctags = BCTags}, - CTag, Credit, Drain) -> - {Unblock, Credits2} = update_credit(CTag, Credit, Drain, Credits), - Limiter#token{credits = Credits2, - blocked_ctags = BCTags -- Unblock}. +credit(Limiter = #token{credits = Credits}, CTag, Credit, Drain) -> + Limiter#token{credits = update_credit(CTag, Credit, Drain, Credits)}. drained(Limiter = #token{credits = Credits}) -> {CTagCredits, Credits2} = @@ -176,13 +163,11 @@ drained(Limiter = #token{credits = Credits}) -> end, {[], Credits}, Credits), {CTagCredits, Limiter#token{credits = Credits2}}. -forget_consumer(Limiter = #token{credits = Credits, - blocked_ctags = BCTags}, CTag) -> - Limiter#token{credits = dict:erase(CTag, Credits), - blocked_ctags = BCTags -- [CTag]}. +forget_consumer(Limiter = #token{credits = Credits}, CTag) -> + Limiter#token{credits = dict:erase(CTag, Credits)}. -copy_queue_state(#token{credits = Credits, blocked_ctags = BCTags}, Token) -> - Token#token{credits = Credits, blocked_ctags = BCTags}. +copy_queue_state(#token{credits = Credits}, Token) -> + Token#token{credits = Credits}. %%---------------------------------------------------------------------------- %% Queue-local code -- cgit v1.2.1 From ef744142e180f95adbeac63c7ef6628ec196793f Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 21 Feb 2013 12:00:16 +0000 Subject: Use gb_trees rather than dict for performance. --- src/rabbit_limiter.erl | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index ece3d1a4..801b748e 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -86,7 +86,7 @@ start_link() -> gen_server2:start_link(?MODULE, [], []). make_token() -> make_token(undefined). make_token(Pid) -> #token{pid = Pid, enabled = false, - credits = dict:new()}. + credits = gb_trees:empty()}. is_enabled(#token{enabled = Enabled}) -> Enabled. @@ -141,10 +141,10 @@ unblock(Limiter) -> maybe_call(Limiter, {unblock, Limiter}, ok). is_consumer_blocked(#token{credits = Credits}, CTag) -> - case dict:find(CTag, Credits) of - {ok, #credit{credit = C}} when C > 0 -> false; - {ok, #credit{}} -> true; - error -> false + case gb_trees:lookup(CTag, Credits) of + {value, #credit{credit = C}} when C > 0 -> false; + {value, #credit{}} -> true; + none -> false end. is_blocked(Limiter) -> @@ -155,16 +155,16 @@ credit(Limiter = #token{credits = Credits}, CTag, Credit, Drain) -> drained(Limiter = #token{credits = Credits}) -> {CTagCredits, Credits2} = - dict:fold( - fun (CTag, #credit{credit = C, drain = true}, {Acc, Creds0}) -> + lists:foldl( + fun ({CTag, #credit{credit = C, drain = true}}, {Acc, Creds0}) -> {[{CTag, C} | Acc], write_credit(CTag, 0, false, Creds0)}; - (_CTag, #credit{credit = _C, drain = false}, {Acc, Creds0}) -> + ({_CTag, #credit{credit = _C, drain = false}}, {Acc, Creds0}) -> {Acc, Creds0} - end, {[], Credits}, Credits), + end, {[], Credits}, gb_trees:to_list(Credits)), {CTagCredits, Limiter#token{credits = Credits2}}. forget_consumer(Limiter = #token{credits = Credits}, CTag) -> - Limiter#token{credits = dict:erase(CTag, Credits)}. + Limiter#token{credits = gb_trees:delete_any(CTag, Credits)}. copy_queue_state(#token{credits = Credits}, Token) -> Token#token{credits = Credits}. @@ -180,10 +180,10 @@ copy_queue_state(#token{credits = Credits}, Token) -> %% maintain a fiction that the limiter is making the decisions... record_send_q(CTag, Credits) -> - case dict:find(CTag, Credits) of - {ok, #credit{credit = Credit, drain = Drain}} -> + case gb_trees:lookup(CTag, Credits) of + {value, #credit{credit = Credit, drain = Drain}} -> write_credit(CTag, Credit, Drain, Credits); - error -> + none -> Credits end. @@ -195,10 +195,16 @@ update_credit(CTag, Credit, Drain, Credits) -> end. write_credit(CTag, Credit, Drain, Credits) when Credit > 0 -> - dict:store(CTag, #credit{credit = Credit, drain = Drain}, Credits); + write_credit0(CTag, #credit{credit = Credit, drain = Drain}, Credits); %% Using up all credit means we do not need to send a drained event write_credit(CTag, Credit, _Drain, Credits) -> - dict:store(CTag, #credit{credit = Credit, drain = false}, Credits). + write_credit0(CTag, #credit{credit = Credit, drain = false}, Credits). + +write_credit0(CTag, Credit, Credits) -> + case gb_trees:is_defined(CTag, Credits) of + true -> gb_trees:update(CTag, Credit, Credits); + false -> gb_trees:insert(CTag, Credit, Credits) + end. %%---------------------------------------------------------------------------- %% gen_server callbacks -- cgit v1.2.1 From 905806e093153f0245d051b9a66927fcd9810715 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 21 Feb 2013 12:10:31 +0000 Subject: Oops --- src/rabbit_limiter.erl | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index 801b748e..dbad59f2 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -157,7 +157,7 @@ drained(Limiter = #token{credits = Credits}) -> {CTagCredits, Credits2} = lists:foldl( fun ({CTag, #credit{credit = C, drain = true}}, {Acc, Creds0}) -> - {[{CTag, C} | Acc], write_credit(CTag, 0, false, Creds0)}; + {[{CTag, C} | Acc], update_credit(CTag, 0, false, Creds0)}; ({_CTag, #credit{credit = _C, drain = false}}, {Acc, Creds0}) -> {Acc, Creds0} end, {[], Credits}, gb_trees:to_list(Credits)), @@ -182,25 +182,18 @@ copy_queue_state(#token{credits = Credits}, Token) -> record_send_q(CTag, Credits) -> case gb_trees:lookup(CTag, Credits) of {value, #credit{credit = Credit, drain = Drain}} -> - write_credit(CTag, Credit, Drain, Credits); + update_credit(CTag, Credit, Drain, Credits); none -> Credits end. -update_credit(CTag, Credit, Drain, Credits) -> - NewCredits = write_credit(CTag, Credit, Drain, Credits), - case Credit > 0 of - true -> {[CTag], NewCredits}; - false -> {[], NewCredits} - end. - -write_credit(CTag, Credit, Drain, Credits) when Credit > 0 -> - write_credit0(CTag, #credit{credit = Credit, drain = Drain}, Credits); +update_credit(CTag, Credit, Drain, Credits) when Credit > 0 -> + update_credit0(CTag, #credit{credit = Credit, drain = Drain}, Credits); %% Using up all credit means we do not need to send a drained event -write_credit(CTag, Credit, _Drain, Credits) -> - write_credit0(CTag, #credit{credit = Credit, drain = false}, Credits). +update_credit(CTag, Credit, _Drain, Credits) -> + update_credit0(CTag, #credit{credit = Credit, drain = false}, Credits). -write_credit0(CTag, Credit, Credits) -> +update_credit0(CTag, Credit, Credits) -> case gb_trees:is_defined(CTag, Credits) of true -> gb_trees:update(CTag, Credit, Credits); false -> gb_trees:insert(CTag, Credit, Credits) -- cgit v1.2.1 From f1fd5e8e2e0b210968b90f44c2935e2ffe3c7b89 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 21 Feb 2013 12:12:51 +0000 Subject: Correct use of gb_trees APIs... --- src/rabbit_limiter.erl | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index dbad59f2..e16e5dc7 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -155,12 +155,12 @@ credit(Limiter = #token{credits = Credits}, CTag, Credit, Drain) -> drained(Limiter = #token{credits = Credits}) -> {CTagCredits, Credits2} = - lists:foldl( - fun ({CTag, #credit{credit = C, drain = true}}, {Acc, Creds0}) -> + rabbit_misc:gb_trees_fold( + fun (CTag, #credit{credit = C, drain = true}, {Acc, Creds0}) -> {[{CTag, C} | Acc], update_credit(CTag, 0, false, Creds0)}; - ({_CTag, #credit{credit = _C, drain = false}}, {Acc, Creds0}) -> + (_CTag, #credit{credit = _C, drain = false}, {Acc, Creds0}) -> {Acc, Creds0} - end, {[], Credits}, gb_trees:to_list(Credits)), + end, {[], Credits}, Credits), {CTagCredits, Limiter#token{credits = Credits2}}. forget_consumer(Limiter = #token{credits = Credits}, CTag) -> @@ -188,16 +188,10 @@ record_send_q(CTag, Credits) -> end. update_credit(CTag, Credit, Drain, Credits) when Credit > 0 -> - update_credit0(CTag, #credit{credit = Credit, drain = Drain}, Credits); + gb_trees:enter(CTag, #credit{credit = Credit, drain = Drain}, Credits); %% Using up all credit means we do not need to send a drained event update_credit(CTag, Credit, _Drain, Credits) -> - update_credit0(CTag, #credit{credit = Credit, drain = false}, Credits). - -update_credit0(CTag, Credit, Credits) -> - case gb_trees:is_defined(CTag, Credits) of - true -> gb_trees:update(CTag, Credit, Credits); - false -> gb_trees:insert(CTag, Credit, Credits) - end. + gb_trees:enter(CTag, #credit{credit = Credit, drain = false}, Credits). %%---------------------------------------------------------------------------- %% gen_server callbacks -- cgit v1.2.1 From 0ddcdb98560e45b65df0e9d5a5d0b4d3f5c1de29 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 21 Feb 2013 12:21:26 +0000 Subject: simplifying refactor --- src/rabbit_limiter.erl | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index e16e5dc7..fe46b876 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -107,8 +107,8 @@ can_send(Token = #token{pid = Pid, enabled = Enabled, credits = Credits}, QPid, AckReq, CTag) -> case is_consumer_blocked(Token, CTag) of false -> case not Enabled orelse call_can_send(Pid, QPid, AckReq) of - true -> Credits2 = record_send_q(CTag, Credits), - Token#token{credits = Credits2}; + true -> Token#token{ + credits = record_send_q(CTag, Credits)}; false -> channel_blocked end; true -> consumer_blocked @@ -173,11 +173,12 @@ copy_queue_state(#token{credits = Credits}, Token) -> %% Queue-local code %%---------------------------------------------------------------------------- -%% We want to do all the AMQP 1.0-ish link level credit calculations in the -%% queue (to do them elsewhere introduces a ton of races). However, it's a big -%% chunk of code that is conceptually very linked to the limiter concept. So -%% we get the queue to hold a bit of state for us (#token.credits), and -%% maintain a fiction that the limiter is making the decisions... +%% We want to do all the AMQP 1.0-ish link level credit calculations +%% in the queue (to do them elsewhere introduces a ton of +%% races). However, it's a big chunk of code that is conceptually very +%% linked to the limiter concept. So we get the queue to hold a bit of +%% state for us (#token.credits), and maintain a fiction that the +%% limiter is making the decisions... record_send_q(CTag, Credits) -> case gb_trees:lookup(CTag, Credits) of @@ -187,11 +188,10 @@ record_send_q(CTag, Credits) -> Credits end. -update_credit(CTag, Credit, Drain, Credits) when Credit > 0 -> - gb_trees:enter(CTag, #credit{credit = Credit, drain = Drain}, Credits); -%% Using up all credit means we do not need to send a drained event -update_credit(CTag, Credit, _Drain, Credits) -> - gb_trees:enter(CTag, #credit{credit = Credit, drain = false}, Credits). +update_credit(CTag, Credit, Drain, Credits) -> + %% Using up all credit implies no need to send a 'drained' event + Drain1 = Drain andalso Credit > 0, + gb_trees:enter(CTag, #credit{credit = Credit, drain = Drain1}, Credits). %%---------------------------------------------------------------------------- %% gen_server callbacks -- cgit v1.2.1 From 55a99c8b531251b90341667bc7934bf24fa6c39e Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 21 Feb 2013 12:27:31 +0000 Subject: Well, that was embarassing. --- src/rabbit_limiter.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index fe46b876..1e806cd3 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -183,7 +183,7 @@ copy_queue_state(#token{credits = Credits}, Token) -> record_send_q(CTag, Credits) -> case gb_trees:lookup(CTag, Credits) of {value, #credit{credit = Credit, drain = Drain}} -> - update_credit(CTag, Credit, Drain, Credits); + update_credit(CTag, Credit - 1, Drain, Credits); none -> Credits end. -- cgit v1.2.1 From d1562e9de47255303213793205f648c64aa542d1 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 21 Feb 2013 12:43:50 +0000 Subject: simplify queue's basic_consume handler backported from bug23749 branch - the call to update_ch_record in the is_ch_blocked(C1) == false branch was superfluos since the preceding update_consumer_count calls update_ch_record - all the checking whether the channel is blocked, and associated branching was just an optimisation. And not a particularly important one, since a) the "a new consumer comes along while its channel is blocked" case is hardly on the critical path, and b) exactly the same check is performed as part of run_message_queue (in deliver_msg_to_consumer/3). So get rid of it. --- src/rabbit_amqqueue_process.erl | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index ef48bb5d..fba58d38 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1097,7 +1097,7 @@ handle_call({basic_consume, NoAck, ChPid, Limiter, reply({error, exclusive_consume_unavailable}, State); ok -> C = ch_record(ChPid), - C1 = update_consumer_count(C#cr{limiter = Limiter}, +1), + update_consumer_count(C#cr{limiter = Limiter}, +1), Consumer = #consumer{tag = ConsumerTag, ack_required = not NoAck}, ExclusiveConsumer = if ExclusiveConsume -> {ChPid, ConsumerTag}; @@ -1106,18 +1106,10 @@ handle_call({basic_consume, NoAck, ChPid, Limiter, State1 = State#q{has_had_consumers = true, exclusive_consumer = ExclusiveConsumer}, ok = maybe_send_reply(ChPid, OkMsg), - E = {ChPid, Consumer}, - State2 = - case is_ch_blocked(C1) of - true -> block_consumer(C1, E), - State1; - false -> update_ch_record(C1), - AC1 = queue:in(E, State1#q.active_consumers), - run_message_queue(State1#q{active_consumers = AC1}) - end, emit_consumer_created(ChPid, ConsumerTag, ExclusiveConsume, - not NoAck, qname(State2)), - reply(ok, State2) + not NoAck, qname(State1)), + AC1 = queue:in({ChPid, Consumer}, State1#q.active_consumers), + reply(ok, run_message_queue(State1#q{active_consumers = AC1})) end; handle_call({basic_cancel, ChPid, ConsumerTag, OkMsg}, From, -- cgit v1.2.1 From 9a1acf8cb9c863c5f5e46309abec07009224252d Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Thu, 21 Feb 2013 18:05:35 +0000 Subject: Enforce queue limit with requeue --- src/rabbit_amqqueue_process.erl | 66 +++++++++++++++++++++++------------------ 1 file changed, 37 insertions(+), 29 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 72d6ab43..08d68e4c 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -560,46 +560,51 @@ deliver_or_enqueue(Delivery = #delivery{message = Message, sender = SenderPid}, %% The next one is an optimisation {false, State2 = #q{ttl = 0, dlx = undefined}} -> discard(Delivery, State2); - {false, State2} -> - State3 = #q{backing_queue = BQ, backing_queue_state = BQS} = - maybe_drop_head(State2), - IsEmpty = BQ:is_empty(BQS), + {false, State2 = #q{backing_queue = BQ, backing_queue_state = BQS}} -> BQS1 = BQ:publish(Message, Props, Delivered, SenderPid, BQS), - State4 = State3#q{backing_queue_state = BQS1}, + {Dropped, State3} = + maybe_drop_head(State2#q{backing_queue_state = BQS1}), + QLen = BQ:len(BQS1), %% optimisation: it would be perfectly safe to always %% invoke drop_expired_msgs here, but that is expensive so - %% we only do that IFF the new message ends up at the head - %% of the queue (because the queue was empty) and has an - %% expiry. Only then may it need expiring straight away, - %% or, if expiry is not due yet, the expiry timer may need - %% (re)scheduling. - case {IsEmpty, Props#message_properties.expiry} of - {false, _} -> State4; - {true, undefined} -> State4; - {true, _} -> drop_expired_msgs(State4) + %% we only do that if a new message that might have an + %% expiry ends up at the head of the queue. If the head + %% remains unchanged, or if the newly published message + %% has no expiry and becomes the head of the queue then + %% the call is unnecessary. + case {Dropped > 0, QLen =:= 1, Props#message_properties.expiry} of + {false, false, _} -> State3; + {true, true, undefined} -> State3; + {_, _, _} -> drop_expired_msgs(State3) end end. maybe_drop_head(State = #q{max_length = undefined}) -> - State; + {0, State}; maybe_drop_head(State = #q{max_length = MaxLen, backing_queue = BQ, backing_queue_state = BQS}) -> - case BQ:len(BQS) >= MaxLen of - true -> with_dlx( - State#q.dlx, - fun (X) -> dead_letter_maxlen_msgs(X, State) end, - fun () -> - {_, BQS1} = BQ:drop(false, BQS), - State#q{backing_queue_state = BQS1} - end); - false -> State + case BQ:len(BQS) - MaxLen of + Excess when Excess > 0 -> + {Excess, + with_dlx( + State#q.dlx, + fun (X) -> dead_letter_maxlen_msgs(X, Excess, State) end, + fun () -> + {_, BQS1} = lists:foldl(fun (_, {_, BQS0}) -> + BQ:drop(false, BQS0) + end, {ok, BQS}, + lists:seq(1, Excess)), + State#q{backing_queue_state = BQS1} + end)}; + _ -> {0, State} end. requeue_and_run(AckTags, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> {_MsgIds, BQS1} = BQ:requeue(AckTags, BQS), - run_message_queue(drop_expired_msgs(State#q{backing_queue_state = BQS1})). + {_Dropped, State1} = maybe_drop_head(State#q{backing_queue_state = BQS1}), + run_message_queue(drop_expired_msgs(State1)). fetch(AckRequired, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> @@ -783,12 +788,15 @@ dead_letter_rejected_msgs(AckTags, X, State = #q{backing_queue = BQ}) -> end, rejected, X, State), State1. -dead_letter_maxlen_msgs(X, State = #q{backing_queue = BQ}) -> +dead_letter_maxlen_msgs(X, Excess, State = #q{backing_queue = BQ}) -> {ok, State1} = dead_letter_msgs( - fun (DLFun, Acc, BQS1) -> - {{Msg, _, AckTag}, BQS2} = BQ:fetch(true, BQS1), - {ok, DLFun(Msg, AckTag, Acc), BQS2} + fun (DLFun, Acc, BQS) -> + lists:foldl(fun (_, {ok, Acc0, BQS0}) -> + {{Msg, _, AckTag}, BQS1} = + BQ:fetch(true, BQS0), + {ok, DLFun(Msg, AckTag, Acc0), BQS1} + end, {ok, Acc, BQS}, lists:seq(1, Excess)) end, maxlen, X, State), State1. -- cgit v1.2.1 From 54529e84ae3d00fa24a2040fe9a1e342d3be5bba Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 25 Feb 2013 10:32:29 +0000 Subject: continue accepting when client sends RST before we accepted --- src/tcp_acceptor.erl | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/tcp_acceptor.erl b/src/tcp_acceptor.erl index 0248f878..c76681c2 100644 --- a/src/tcp_acceptor.erl +++ b/src/tcp_acceptor.erl @@ -61,15 +61,13 @@ handle_info({inet_async, LSock, Ref, {ok, Sock}}, %% accept more accept(State); -handle_info({inet_async, LSock, Ref, {error, closed}}, - State=#state{sock=LSock, ref=Ref}) -> - %% It would be wrong to attempt to restart the acceptor when we - %% know this will fail. - {stop, normal, State}; - handle_info({inet_async, LSock, Ref, {error, Reason}}, State=#state{sock=LSock, ref=Ref}) -> - {stop, {accept_failed, Reason}, State}; + case Reason of + closed -> {stop, normal, State}; %% listening socket closed + econnaborted -> accept(State); %% client sent RST before we accepted + _ -> {stop, {accept_failed, Reason}, State} + end; handle_info(_Info, State) -> {noreply, State}. -- cgit v1.2.1 From 4bb2c550bb6220c16738973586f669b11bf1dd2f Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 25 Feb 2013 10:48:04 +0000 Subject: ignore early enotconn, and report early socket errors more nicely --- src/rabbit_reader.erl | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 0b5155da..d0d8e8c1 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -177,32 +177,37 @@ server_capabilities(_) -> log(Level, Fmt, Args) -> rabbit_log:log(connection, Level, Fmt, Args). +socket_error(Reason) -> + log(error, "error on AMQP connection ~p: ~p (~s)~n", + [self(), Reason, rabbit_misc:format_inet_error(Reason)]). + inet_op(F) -> rabbit_misc:throw_on_error(inet_error, F). socket_op(Sock, Fun) -> case Fun(Sock) of {ok, Res} -> Res; - {error, Reason} -> log(error, "error on AMQP connection ~p: ~p~n", - [self(), Reason]), + {error, Reason} -> socket_error(Reason), %% NB: this is tcp socket, even in case of ssl rabbit_net:fast_close(Sock), exit(normal) end. -name(Sock) -> - socket_op(Sock, fun (S) -> rabbit_net:connection_string(S, inbound) end). - -socket_ends(Sock) -> - socket_op(Sock, fun (S) -> rabbit_net:socket_ends(S, inbound) end). - start_connection(Parent, ChannelSupSupPid, Collector, StartHeartbeatFun, Deb, Sock, SockTransform) -> process_flag(trap_exit, true), - Name = name(Sock), + Name = case rabbit_net:connection_string(Sock, inbound) of + {ok, Str} -> Str; + {error, enotconn} -> rabbit_net:fast_close(Sock), + exit(normal); + {error, Reason} -> socket_error(Reason), + rabbit_net:fast_close(Sock), + exit(normal) + end, log(info, "accepting AMQP connection ~p (~s)~n", [self(), Name]), ClientSock = socket_op(Sock, SockTransform), erlang:send_after(?HANDSHAKE_TIMEOUT * 1000, self(), handshake_timeout), - {PeerHost, PeerPort, Host, Port} = socket_ends(Sock), + {PeerHost, PeerPort, Host, Port} = + socket_op(Sock, fun (S) -> rabbit_net:socket_ends(S, inbound) end), State = #v1{parent = Parent, sock = ClientSock, name = list_to_binary(Name), -- cgit v1.2.1 From 2f6dee1b58d41bef547ae7292bada83d2feab6ec Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 25 Feb 2013 17:25:45 +0000 Subject: Keep name around for logging / info item. --- src/rabbit_reader.erl | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index b8ff9c9f..54fd3c51 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -783,7 +783,7 @@ handle_method0(#'connection.start_ok'{mechanism = Mechanism, Connection#connection{ client_properties = ClientProperties, capabilities = Capabilities, - auth_mechanism = AuthMechanism, + auth_mechanism = {Mechanism, AuthMechanism}, auth_state = AuthMechanism:init(Sock)}}, auth_phase(Response, State); @@ -911,15 +911,14 @@ auth_mechanisms_binary(Sock) -> auth_phase(Response, State = #v1{connection = Connection = #connection{protocol = Protocol, - auth_mechanism = AuthMechanism, + 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", - [proplists:get_value(name, AuthMechanism:description()), - io_lib:format(Msg, Args)]); + [Name, io_lib:format(Msg, Args)]); {protocol_error, Msg, Args} -> rabbit_misc:protocol_error(syntax_error, Msg, Args); {challenge, Challenge, AuthState1} -> @@ -979,10 +978,8 @@ ic(vhost, #connection{vhost = VHost}) -> VHost; ic(timeout, #connection{timeout_sec = Timeout}) -> Timeout; ic(frame_max, #connection{frame_max = FrameMax}) -> FrameMax; ic(client_properties, #connection{client_properties = CP}) -> CP; -ic(auth_mechanism, #connection{auth_mechanism = none}) -> - none; -ic(auth_mechanism, #connection{auth_mechanism = Mechanism}) -> - proplists:get_value(name, Mechanism:description()); +ic(auth_mechanism, #connection{auth_mechanism = none}) -> none; +ic(auth_mechanism, #connection{auth_mechanism = {Name, _Mod}}) -> Name; ic(Item, #connection{}) -> throw({bad_argument, Item}). socket_info(Get, Select, #v1{sock = Sock}) -> -- cgit v1.2.1 From 7c99d0f8b3d5ca07e19ab5736b26b5b8892e151e Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 26 Feb 2013 15:31:01 +0000 Subject: move socket buffer tuning into tcp_acceptor --- src/rabbit_net.erl | 10 +--------- src/rabbit_reader.erl | 1 - src/tcp_acceptor.erl | 22 ++++++++++++++++++++-- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/rabbit_net.erl b/src/rabbit_net.erl index b8b03f56..b53c16bf 100644 --- a/src/rabbit_net.erl +++ b/src/rabbit_net.erl @@ -20,7 +20,7 @@ -export([is_ssl/1, ssl_info/1, controlling_process/2, getstat/2, recv/1, async_recv/3, port_command/2, getopts/2, setopts/2, send/2, close/1, fast_close/1, sockname/1, peername/1, peercert/1, - tune_buffer_size/1, connection_string/2, socket_ends/2]). + connection_string/2, socket_ends/2]). %%--------------------------------------------------------------------------- @@ -69,7 +69,6 @@ -spec(peercert/1 :: (socket()) -> 'nossl' | ok_val_or_error(rabbit_ssl:certificate())). --spec(tune_buffer_size/1 :: (socket()) -> ok_or_any_error()). -spec(connection_string/2 :: (socket(), 'inbound' | 'outbound') -> ok_val_or_error(string())). -spec(socket_ends/2 :: @@ -189,13 +188,6 @@ peername(Sock) when is_port(Sock) -> inet:peername(Sock). peercert(Sock) when ?IS_SSL(Sock) -> ssl:peercert(Sock#ssl_socket.ssl); peercert(Sock) when is_port(Sock) -> nossl. -tune_buffer_size(Sock) -> - case getopts(Sock, [sndbuf, recbuf, buffer]) of - {ok, BufSizes} -> BufSz = lists:max([Sz || {_Opt, Sz} <- BufSizes]), - setopts(Sock, [{buffer, BufSz}]); - Err -> Err - end. - connection_string(Sock, Direction) -> case socket_ends(Sock, Direction) of {ok, {FromAddress, FromPort, ToAddress, ToPort}} -> diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 2bdef92d..6294451b 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -250,7 +250,6 @@ start_connection(Parent, ConnSupPid, Collector, StartHeartbeatFun, Deb, last_blocked_by = none, last_blocked_at = never}}, try - ok = inet_op(fun () -> rabbit_net:tune_buffer_size(ClientSock) end), run({?MODULE, recvloop, [Deb, switch_callback(rabbit_event:init_stats_timer( State, #v1.stats_timer), diff --git a/src/tcp_acceptor.erl b/src/tcp_acceptor.erl index c76681c2..2725be31 100644 --- a/src/tcp_acceptor.erl +++ b/src/tcp_acceptor.erl @@ -55,8 +55,19 @@ handle_info({inet_async, LSock, Ref, {ok, Sock}}, inet_db:register_socket(Sock, Mod), %% handle - file_handle_cache:transfer(apply(M, F, A ++ [Sock])), - ok = file_handle_cache:obtain(), + case tune_buffer_size(Sock) of + ok -> file_handle_cache:transfer( + apply(M, F, A ++ [Sock])), + ok = file_handle_cache:obtain(); + {error, enotconn} -> catch port_close(Sock); + {error, Err} -> {ok, {IPAddress, Port}} = inet:sockname(LSock), + error_logger:error_msg( + "failed to tune buffer size of " + "connection accepted on ~s:~p - ~p (~s)~n", + [rabbit_misc:ntoab(IPAddress), Port, + Err, rabbit_misc:format_inet_error(Err)]), + catch port_close(Sock) + end, %% accept more accept(State); @@ -85,3 +96,10 @@ accept(State = #state{sock=LSock}) -> {ok, Ref} -> {noreply, State#state{ref=Ref}}; Error -> {stop, {cannot_accept, Error}, State} end. + +tune_buffer_size(Sock) -> + case inet:getopts(Sock, [sndbuf, recbuf, buffer]) of + {ok, BufSizes} -> BufSz = lists:max([Sz || {_Opt, Sz} <- BufSizes]), + inet:setopts(Sock, [{buffer, BufSz}]); + Error -> Error + end. -- cgit v1.2.1 From 4ebf0a93d4d8188b26da5d148b33d72e446aba96 Mon Sep 17 00:00:00 2001 From: Alvaro Videla Date: Tue, 26 Feb 2013 17:58:01 +0000 Subject: specifies boot file for rabbit_prelaunch call --- scripts/rabbitmq-server | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/rabbitmq-server b/scripts/rabbitmq-server index 3253bd7b..4af38eeb 100755 --- a/scripts/rabbitmq-server +++ b/scripts/rabbitmq-server @@ -83,6 +83,7 @@ esac RABBITMQ_EBIN_ROOT="${RABBITMQ_HOME}/ebin" if ! ${ERL_DIR}erl -pa "$RABBITMQ_EBIN_ROOT" \ + -boot "${CLEAN_BOOT_FILE}" \ -noinput \ -hidden \ -s rabbit_prelaunch \ -- cgit v1.2.1 From fca69ebecd5f17e5857225a1bab6434e8b391161 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 26 Feb 2013 18:04:45 +0000 Subject: Remove concept of strictness, thus renaming list_strict/1 to list_component/1 and removing list_strict/2 altogether, plus a few bits of therefore dead code. --- src/rabbit_runtime_parameters.erl | 36 ++++++++++-------------------------- 1 file changed, 10 insertions(+), 26 deletions(-) diff --git a/src/rabbit_runtime_parameters.erl b/src/rabbit_runtime_parameters.erl index b1100b65..05520170 100644 --- a/src/rabbit_runtime_parameters.erl +++ b/src/rabbit_runtime_parameters.erl @@ -19,7 +19,7 @@ -include("rabbit.hrl"). -export([parse_set/4, set/4, set_any/4, clear/3, clear_any/3, list/0, list/1, - list_strict/1, list/2, list_strict/2, list_formatted/1, lookup/3, + list_component/1, list/2, list_formatted/1, lookup/3, value/3, value/4, info_keys/0]). %%---------------------------------------------------------------------------- @@ -40,12 +40,9 @@ -> ok_or_error_string()). -spec(list/0 :: () -> [rabbit_types:infos()]). -spec(list/1 :: (rabbit_types:vhost() | '_') -> [rabbit_types:infos()]). --spec(list_strict/1 :: (binary() | '_') - -> [rabbit_types:infos()] | 'not_found'). +-spec(list_component/1 :: (binary()) -> [rabbit_types:infos()]). -spec(list/2 :: (rabbit_types:vhost() | '_', binary() | '_') -> [rabbit_types:infos()]). --spec(list_strict/2 :: (rabbit_types:vhost() | '_', binary() | '_') - -> [rabbit_types:infos()] | 'not_found'). -spec(list_formatted/1 :: (rabbit_types:vhost()) -> [rabbit_types:infos()]). -spec(lookup/3 :: (rabbit_types:vhost(), binary(), binary()) -> rabbit_types:infos() | 'not_found'). @@ -139,21 +136,14 @@ list() -> [p(P) || #runtime_parameters{ key = {_VHost, Comp, _Name}} = P <- rabbit_misc:dirty_read_all(?TABLE), Comp /= <<"policy">>]. -list(VHost) -> list(VHost, '_', []). -list_strict(Component) -> list('_', Component, not_found). -list(VHost, Component) -> list(VHost, Component, []). -list_strict(VHost, Component) -> list(VHost, Component, not_found). - -list(VHost, Component, Default) -> - case component_good(Component) of - true -> Match = #runtime_parameters{key = {VHost, Component, '_'}, - _ = '_'}, - [p(P) || #runtime_parameters{ key = {_VHost, Comp, _Name}} = P <- - mnesia:dirty_match_object(?TABLE, Match), - Comp =/= <<"policy">> orelse - Component =:= <<"policy">>]; - _ -> Default - end. +list(VHost) -> list(VHost, '_'). +list_component(Component) -> list('_', Component). + +list(VHost, Component) -> + Match = #runtime_parameters{key = {VHost, Component, '_'}, _ = '_'}, + [p(P) || #runtime_parameters{key = {_VHost, Comp, _Name}} = P <- + mnesia:dirty_match_object(?TABLE, Match), + Comp =/= <<"policy">> orelse Component =:= <<"policy">>]. list_formatted(VHost) -> [pset(value, format(pget(value, P)), P) || P <- list(VHost)]. @@ -208,12 +198,6 @@ info_keys() -> [component, name, value]. %%--------------------------------------------------------------------------- -component_good('_') -> true; -component_good(Component) -> case lookup_component(Component) of - {ok, _} -> true; - _ -> false - end. - lookup_component(Component) -> case rabbit_registry:lookup_module( runtime_parameter, list_to_atom(binary_to_list(Component))) of -- cgit v1.2.1 From 6a8a3b788a351bcc8104acbbd7a2a2215c676126 Mon Sep 17 00:00:00 2001 From: Alvaro Videla Date: Wed, 27 Feb 2013 11:37:07 +0000 Subject: fixes Erlang FINAL_ROOTDIR --- packaging/standalone/Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packaging/standalone/Makefile b/packaging/standalone/Makefile index 1e548789..39ffadab 100644 --- a/packaging/standalone/Makefile +++ b/packaging/standalone/Makefile @@ -53,6 +53,10 @@ dist: # remove empty lib/rabbit-$(VERSION) folder rm -rf $(RLS_DIR)/lib/rabbit-$(VERSION) +# fix Erlang ROOTDIR + sed -e 's:%FINAL_ROOTDIR%:\$$(dirname \$$0)/../..:' $(RLS_DIR)/erts-5.9.3/bin/erl.src \ + > $(RLS_DIR)/erts-5.9.3/bin/erl + tar -zcf $(TARGET_TARBALL).tar.gz -C $(TARGET_DIR)/release $(TARGET_DIR) rm -rf $(SOURCE_DIR) $(TARGET_DIR) -- cgit v1.2.1 From d15d2415fbbf4cc5dd518fb9d972b1e2e0d47562 Mon Sep 17 00:00:00 2001 From: Alvaro Videla Date: Wed, 27 Feb 2013 14:18:25 +0000 Subject: adds realpath to erl mac script --- packaging/standalone/Makefile | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packaging/standalone/Makefile b/packaging/standalone/Makefile index 39ffadab..77d2bd9b 100644 --- a/packaging/standalone/Makefile +++ b/packaging/standalone/Makefile @@ -54,8 +54,12 @@ dist: rm -rf $(RLS_DIR)/lib/rabbit-$(VERSION) # fix Erlang ROOTDIR - sed -e 's:%FINAL_ROOTDIR%:\$$(dirname \$$0)/../..:' $(RLS_DIR)/erts-5.9.3/bin/erl.src \ - > $(RLS_DIR)/erts-5.9.3/bin/erl + sed -e 's:%FINAL_ROOTDIR%:\$$(dirname `realpath \$$0`)/../..:' $(RLS_DIR)/erts-$(ERTS_VSN)/bin/erl.src \ + > $(RLS_DIR)/erts-$(ERTS_VSN)/bin/erl.tmp + sed -e '/^ROOTDIR.*/i \ + realpath() { [[ \$$1 = /* ]] && echo "\$$1" || echo "\$$PWD/\$${1#./}" ; }' \ + $(RLS_DIR)/erts-$(ERTS_VSN)/bin/erl.tmp > $(RLS_DIR)/erts-$(ERTS_VSN)/bin/erl + rm $(RLS_DIR)/erts-$(ERTS_VSN)/bin/erl.tmp tar -zcf $(TARGET_TARBALL).tar.gz -C $(TARGET_DIR)/release $(TARGET_DIR) rm -rf $(SOURCE_DIR) $(TARGET_DIR) -- cgit v1.2.1 From 2e9879c6cde8d540cd8cd3e56223f0a3bbe22556 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 27 Feb 2013 14:20:07 +0000 Subject: When we lose majority, stop the applications and wait for the cluster to come back. --- src/rabbit_node_monitor.erl | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index 7b7fed5c..42df6e5d 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -283,21 +283,34 @@ handle_dead_according_to_mnesia_rabbit() -> case application:get_env(rabbit, cluster_cp_mode) of {ok, true} -> case rabbit_mnesia:majority() of true -> ok; - false -> stop_and_halt() + false -> await_cluster_recovery() end; {ok, false} -> ok end, ok. -stop_and_halt() -> - rabbit_log:warning("Cluster minority status detected - stopping~n", []), +await_cluster_recovery() -> + rabbit_log:warning("Cluster minority status detected - awaiting recovery~n", + []), + Nodes = rabbit_mnesia:cluster_nodes(all), spawn(fun () -> %% If our group leader is inside an application we are about %% to stop, application:stop/1 does not return. group_leader(whereis(init), self()), - rabbit:stop_and_halt() + rabbit:stop(), + wait_for_cluster_recovery(Nodes) end). +wait_for_cluster_recovery(Nodes) -> + [erlang:disconnect_node(Node) || Node <- Nodes], + mnesia:start(), + case rabbit_mnesia:majority() of + true -> rabbit:start(); + false -> mnesia:stop(), + timer:sleep(1000), + wait_for_cluster_recovery(Nodes) + end. + handle_live_rabbit(Node) -> ok = rabbit_alarm:on_node_up(Node), ok = rabbit_mnesia:on_node_up(Node). -- cgit v1.2.1 From fad1b961ea4d53670381caa4b701add21ee406b4 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 27 Feb 2013 14:25:12 +0000 Subject: Filter out all those events that look like: =INFO REPORT==== 27-Feb-2013::14:17:46 === application: mnesia exited: stopped type: temporary since they are not very interesting and this bug makes them appear to a highly verbose extent. --- src/rabbit_error_logger_file_h.erl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/rabbit_error_logger_file_h.erl b/src/rabbit_error_logger_file_h.erl index 3efc9c0c..c00c1df9 100644 --- a/src/rabbit_error_logger_file_h.erl +++ b/src/rabbit_error_logger_file_h.erl @@ -76,6 +76,8 @@ init_file(File, PrevHandler) -> Error -> Error end. +handle_event({info_report, _, {_, std_info, _}}, State) -> + ok; %% filter out "application: foo; exited: stopped; type: temporary" handle_event(Event, State) -> error_logger_file_h:handle_event(Event, State). -- cgit v1.2.1 From 2ecc9ff3c42959e4ac267b07a4e7dee962e710ad Mon Sep 17 00:00:00 2001 From: Alvaro Videla Date: Wed, 27 Feb 2013 14:27:24 +0000 Subject: reorders sed calls --- packaging/standalone/Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packaging/standalone/Makefile b/packaging/standalone/Makefile index 77d2bd9b..91100628 100644 --- a/packaging/standalone/Makefile +++ b/packaging/standalone/Makefile @@ -54,11 +54,11 @@ dist: rm -rf $(RLS_DIR)/lib/rabbit-$(VERSION) # fix Erlang ROOTDIR - sed -e 's:%FINAL_ROOTDIR%:\$$(dirname `realpath \$$0`)/../..:' $(RLS_DIR)/erts-$(ERTS_VSN)/bin/erl.src \ - > $(RLS_DIR)/erts-$(ERTS_VSN)/bin/erl.tmp sed -e '/^ROOTDIR.*/i \ realpath() { [[ \$$1 = /* ]] && echo "\$$1" || echo "\$$PWD/\$${1#./}" ; }' \ - $(RLS_DIR)/erts-$(ERTS_VSN)/bin/erl.tmp > $(RLS_DIR)/erts-$(ERTS_VSN)/bin/erl + $(RLS_DIR)/erts-$(ERTS_VSN)/bin/erl.src > $(RLS_DIR)/erts-$(ERTS_VSN)/bin/erl.tmp + sed -e 's:%FINAL_ROOTDIR%:\$$(dirname `realpath \$$0`)/../..:' $(RLS_DIR)/erts-$(ERTS_VSN)/bin/erl.tmp \ + > $(RLS_DIR)/erts-$(ERTS_VSN)/bin/erl rm $(RLS_DIR)/erts-$(ERTS_VSN)/bin/erl.tmp tar -zcf $(TARGET_TARBALL).tar.gz -C $(TARGET_DIR)/release $(TARGET_DIR) -- cgit v1.2.1 From 9142ce00fec31cb22cb0351a4b4a257cdffbdfc9 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 27 Feb 2013 14:43:44 +0000 Subject: Base the whole thing off net_adm:ping/1 - because we might see other nodes come back but also be waiting (in the no-majority case, and RAM nodes). Better to detect they exist and come back than to stay stuck because they don't happen to be running Mnesia. --- src/rabbit_mnesia.erl | 6 ------ src/rabbit_node_monitor.erl | 15 ++++++++++----- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index ecb03f54..c39e898c 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -26,7 +26,6 @@ status/0, is_clustered/0, - majority/0, cluster_nodes/1, node_type/0, dir/0, @@ -68,7 +67,6 @@ -spec(status/0 :: () -> [{'nodes', [{node_type(), [node()]}]} | {'running_nodes', [node()]} | {'partitions', [{node(), [node()]}]}]). --spec(majority/0 :: () -> boolean()). -spec(is_clustered/0 :: () -> boolean()). -spec(cluster_nodes/1 :: ('all' | 'disc' | 'ram' | 'running') -> [node()]). -spec(node_type/0 :: () -> node_type()). @@ -340,10 +338,6 @@ status() -> false -> [] end. -majority() -> - ensure_mnesia_running(), - (length(cluster_nodes(running)) / length(cluster_nodes(all))) > 0.5. - mnesia_partitions(Nodes) -> {Replies, _BadNodes} = rpc:multicall( Nodes, rabbit_node_monitor, partitions, []), diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index 42df6e5d..249c17a4 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -281,7 +281,7 @@ handle_dead_rabbit(Node) -> %% down - otherwise we have a race. handle_dead_according_to_mnesia_rabbit() -> case application:get_env(rabbit, cluster_cp_mode) of - {ok, true} -> case rabbit_mnesia:majority() of + {ok, true} -> case majority() of true -> ok; false -> await_cluster_recovery() end; @@ -289,6 +289,13 @@ handle_dead_according_to_mnesia_rabbit() -> end, ok. +majority() -> + Nodes = rabbit_mnesia:cluster_nodes(all), + Alive = [Status || N <- Nodes, + Status <- [net_adm:ping(N)], + Status =:= pong], + length(Alive) / length(Nodes) > 0.5. + await_cluster_recovery() -> rabbit_log:warning("Cluster minority status detected - awaiting recovery~n", []), @@ -303,11 +310,9 @@ await_cluster_recovery() -> wait_for_cluster_recovery(Nodes) -> [erlang:disconnect_node(Node) || Node <- Nodes], - mnesia:start(), - case rabbit_mnesia:majority() of + case majority() of true -> rabbit:start(); - false -> mnesia:stop(), - timer:sleep(1000), + false -> timer:sleep(1000), wait_for_cluster_recovery(Nodes) end. -- cgit v1.2.1 From cb97ff80e3a876bfce7e22d213be66fa780032e8 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 27 Feb 2013 14:47:03 +0000 Subject: Simplify --- src/rabbit_node_monitor.erl | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index 249c17a4..fd8080bc 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -291,9 +291,7 @@ handle_dead_according_to_mnesia_rabbit() -> majority() -> Nodes = rabbit_mnesia:cluster_nodes(all), - Alive = [Status || N <- Nodes, - Status <- [net_adm:ping(N)], - Status =:= pong], + Alive = [N || N <- Nodes, pong =:= net_adm:ping(N)], length(Alive) / length(Nodes) > 0.5. await_cluster_recovery() -> -- cgit v1.2.1 From 2279502946e436f40368d66477edafe5c6616f60 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 27 Feb 2013 15:30:43 +0000 Subject: We no longer need two different death detectors since we no longer look at Mnesia for majorityness. --- src/rabbit_node_monitor.erl | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index fd8080bc..ad2003a5 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -250,10 +250,6 @@ handle_info({mnesia_system_event, ordsets:add_element(Node, ordsets:from_list(Partitions))), {noreply, State#state{partitions = Partitions1}}; -handle_info({mnesia_system_event, {mnesia_down, _Node}}, State) -> - handle_dead_according_to_mnesia_rabbit(), - {noreply, State}; - handle_info(_Info, State) -> {noreply, State}. @@ -274,12 +270,7 @@ handle_dead_rabbit(Node) -> ok = rabbit_networking:on_node_down(Node), ok = rabbit_amqqueue:on_node_down(Node), ok = rabbit_alarm:on_node_down(Node), - ok = rabbit_mnesia:on_node_down(Node). - -%% Since we will be introspecting the cluster in response to this, we -%% must only do so based on Mnesia having noticed the other node being -%% down - otherwise we have a race. -handle_dead_according_to_mnesia_rabbit() -> + ok = rabbit_mnesia:on_node_down(Node), case application:get_env(rabbit, cluster_cp_mode) of {ok, true} -> case majority() of true -> ok; -- cgit v1.2.1 From 4c4b2942703c5c254c8af173656522665d9dd934 Mon Sep 17 00:00:00 2001 From: Alvaro Videla Date: Wed, 27 Feb 2013 15:46:12 +0000 Subject: removes sed invocations in favor of patch --- packaging/standalone/Makefile | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/packaging/standalone/Makefile b/packaging/standalone/Makefile index 91100628..89ccde93 100644 --- a/packaging/standalone/Makefile +++ b/packaging/standalone/Makefile @@ -54,12 +54,7 @@ dist: rm -rf $(RLS_DIR)/lib/rabbit-$(VERSION) # fix Erlang ROOTDIR - sed -e '/^ROOTDIR.*/i \ - realpath() { [[ \$$1 = /* ]] && echo "\$$1" || echo "\$$PWD/\$${1#./}" ; }' \ - $(RLS_DIR)/erts-$(ERTS_VSN)/bin/erl.src > $(RLS_DIR)/erts-$(ERTS_VSN)/bin/erl.tmp - sed -e 's:%FINAL_ROOTDIR%:\$$(dirname `realpath \$$0`)/../..:' $(RLS_DIR)/erts-$(ERTS_VSN)/bin/erl.tmp \ - > $(RLS_DIR)/erts-$(ERTS_VSN)/bin/erl - rm $(RLS_DIR)/erts-$(ERTS_VSN)/bin/erl.tmp + patch -o $(RLS_DIR)/erts-$(ERTS_VSN)/bin/erl $(RLS_DIR)/erts-$(ERTS_VSN)/bin/erl.src < erl.diff tar -zcf $(TARGET_TARBALL).tar.gz -C $(TARGET_DIR)/release $(TARGET_DIR) rm -rf $(SOURCE_DIR) $(TARGET_DIR) -- cgit v1.2.1 From 6551ce1f20f5d6dfbac6e3c994f8cc2273fc1917 Mon Sep 17 00:00:00 2001 From: Alvaro Videla Date: Wed, 27 Feb 2013 15:46:53 +0000 Subject: adds missing patch --- packaging/standalone/erl.diff | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 packaging/standalone/erl.diff diff --git a/packaging/standalone/erl.diff b/packaging/standalone/erl.diff new file mode 100644 index 00000000..c51bfe22 --- /dev/null +++ b/packaging/standalone/erl.diff @@ -0,0 +1,5 @@ +20c20,21 +< ROOTDIR="%FINAL_ROOTDIR%" +--- +> realpath() { [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}" ; } +> ROOTDIR="$(dirname `realpath $0`)/../.." -- cgit v1.2.1 From 9e125a71712752f84550490e357e867206e000a3 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Fri, 1 Mar 2013 14:34:35 +0000 Subject: Check queue length using correct version of backing queue state --- src/rabbit_amqqueue_process.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 08d68e4c..18b641d4 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -562,9 +562,9 @@ deliver_or_enqueue(Delivery = #delivery{message = Message, sender = SenderPid}, discard(Delivery, State2); {false, State2 = #q{backing_queue = BQ, backing_queue_state = BQS}} -> BQS1 = BQ:publish(Message, Props, Delivered, SenderPid, BQS), - {Dropped, State3} = + {Dropped, State3 = #q{backing_queue_state = BQS2}} = maybe_drop_head(State2#q{backing_queue_state = BQS1}), - QLen = BQ:len(BQS1), + QLen = BQ:len(BQS2), %% optimisation: it would be perfectly safe to always %% invoke drop_expired_msgs here, but that is expensive so %% we only do that if a new message that might have an -- cgit v1.2.1 From 5ae47d98f0fe2f67503646316d7a0df186be80b6 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 1 Mar 2013 14:47:47 +0000 Subject: Rename this thing, to make space for bug 25471 --- ebin/rabbit_app.in | 2 +- src/rabbit_node_monitor.erl | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/ebin/rabbit_app.in b/ebin/rabbit_app.in index d08d502b..339fa69e 100644 --- a/ebin/rabbit_app.in +++ b/ebin/rabbit_app.in @@ -44,7 +44,7 @@ {log_levels, [{connection, info}]}, {ssl_cert_login_from, distinguished_name}, {reverse_dns_lookups, false}, - {cluster_cp_mode, false}, + {cluster_partition_handling, ignore}, {tcp_listen_options, [binary, {packet, raw}, {reuseaddr, true}, diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index ad2003a5..5d587977 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -271,12 +271,18 @@ handle_dead_rabbit(Node) -> ok = rabbit_amqqueue:on_node_down(Node), ok = rabbit_alarm:on_node_down(Node), ok = rabbit_mnesia:on_node_down(Node), - case application:get_env(rabbit, cluster_cp_mode) of - {ok, true} -> case majority() of - true -> ok; - false -> await_cluster_recovery() - end; - {ok, false} -> ok + case application:get_env(rabbit, cluster_partition_handling) of + {ok, pause_minority} -> + case majority() of + true -> ok; + false -> await_cluster_recovery() + end; + {ok, ignore} -> + ok; + {ok, Term} -> + rabbit_log:warning("cluster_partition_handling ~p unrecognised, " + "assuming 'ignore'~n", [Term]), + ok end, ok. -- cgit v1.2.1 From 09fafa37397b909563c7416045234ef762e26ac0 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 1 Mar 2013 17:38:10 +0000 Subject: Allow subscribing to node down events from the node monitor. --- src/rabbit_node_monitor.erl | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index c0b11799..c4f06921 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -24,7 +24,7 @@ write_cluster_status/1, read_cluster_status/0, update_cluster_status/0, reset_cluster_status/0]). -export([notify_node_up/0, notify_joined_cluster/0, notify_left_cluster/1]). --export([partitions/0]). +-export([partitions/0, subscribe/1]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, @@ -33,7 +33,7 @@ -define(SERVER, ?MODULE). -define(RABBIT_UP_RPC_TIMEOUT, 2000). --record(state, {monitors, partitions}). +-record(state, {monitors, partitions, subscribers}). %%---------------------------------------------------------------------------- @@ -54,6 +54,7 @@ -spec(notify_left_cluster/1 :: (node()) -> 'ok'). -spec(partitions/0 :: () -> {node(), [{atom(), node()}]}). +-spec(subscribe/1 :: (pid()) -> 'ok'). -endif. @@ -179,6 +180,9 @@ notify_left_cluster(Node) -> partitions() -> gen_server:call(?SERVER, partitions, infinity). +subscribe(Pid) -> + gen_server:cast(?SERVER, {subscribe, Pid}). + %%---------------------------------------------------------------------------- %% gen_server callbacks %%---------------------------------------------------------------------------- @@ -190,8 +194,9 @@ init([]) -> %% happen. process_flag(trap_exit, true), {ok, _} = mnesia:subscribe(system), - {ok, #state{monitors = pmon:new(), - partitions = []}}. + {ok, #state{monitors = pmon:new(), + subscribers = pmon:new(), + partitions = []}}. handle_call(partitions, _From, State = #state{partitions = Partitions}) -> {reply, {node(), Partitions}, State}; @@ -232,17 +237,24 @@ handle_cast({left_cluster, Node}, State) -> write_cluster_status({del_node(Node, AllNodes), del_node(Node, DiscNodes), del_node(Node, RunningNodes)}), {noreply, State}; +handle_cast({subscribe, Pid}, State = #state{subscribers = Subscribers}) -> + {noreply, State#state{subscribers = pmon:monitor(Pid, Subscribers)}}; handle_cast(_Msg, State) -> {noreply, State}. handle_info({'DOWN', _MRef, process, {rabbit, Node}, _Reason}, - State = #state{monitors = Monitors}) -> + State = #state{monitors = Monitors, subscribers = Subscribers}) -> rabbit_log:info("rabbit on node ~p down~n", [Node]), {AllNodes, DiscNodes, RunningNodes} = read_cluster_status(), write_cluster_status({AllNodes, DiscNodes, del_node(Node, RunningNodes)}), ok = handle_dead_rabbit(Node), + [P ! {node_down, Node} || P <- pmon:monitored(Subscribers)], {noreply, State#state{monitors = pmon:erase({rabbit, Node}, Monitors)}}; +handle_info({'DOWN', _MRef, process, Pid, _Reason}, + State = #state{subscribers = Subscribers}) -> + {noreply, State#state{subscribers = pmon:erase(Pid, Subscribers)}}; + handle_info({mnesia_system_event, {inconsistent_database, running_partitioned_network, Node}}, State = #state{partitions = Partitions}) -> -- 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(-) 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 d8be7a482b7ed4c339639f3c7404f6ef51f1c7d0 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 5 Mar 2013 16:01:56 +0000 Subject: We don't need this - net_adm:ping/1 will not return pong for nodes that have gone down before net_ticktime expires (it will hang for until then instead). --- src/rabbit_node_monitor.erl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index 08c5a25f..55b078c3 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -208,9 +208,10 @@ handle_call(_Request, _From, State) -> %% mnesia information since the message can (and will) overtake the %% mnesia propagation. handle_cast({node_up, Node, NodeType}, - State = #state{monitors = Monitors}) -> + State = #state{monitors = Monitors, partitions = Partitions}) -> + State1 = State#state{partitions = Partitions -- [Node]}, case pmon:is_monitored({rabbit, Node}, Monitors) of - true -> {noreply, State}; + true -> {noreply, State1}; false -> rabbit_log:info("rabbit on node ~p up~n", [Node]), {AllNodes, DiscNodes, RunningNodes} = read_cluster_status(), write_cluster_status({add_node(Node, AllNodes), @@ -220,7 +221,7 @@ handle_cast({node_up, Node, NodeType}, end, add_node(Node, RunningNodes)}), ok = handle_live_rabbit(Node), - {noreply, State#state{ + {noreply, State1#state{ monitors = pmon:monitor({rabbit, Node}, Monitors)}} end; handle_cast({joined_cluster, Node, NodeType}, State) -> @@ -316,7 +317,6 @@ await_cluster_recovery() -> end). wait_for_cluster_recovery(Nodes) -> - [erlang:disconnect_node(Node) || Node <- Nodes], case majority() of true -> rabbit:start(); false -> timer:sleep(1000), -- 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(-) 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 -- cgit v1.2.1 From febba0f6d331e91226eba91586a7889796e4ea1d Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 6 Mar 2013 15:32:29 +0000 Subject: Unused variable --- src/rabbit_error_logger_file_h.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_error_logger_file_h.erl b/src/rabbit_error_logger_file_h.erl index c00c1df9..b6e2c15e 100644 --- a/src/rabbit_error_logger_file_h.erl +++ b/src/rabbit_error_logger_file_h.erl @@ -76,7 +76,7 @@ init_file(File, PrevHandler) -> Error -> Error end. -handle_event({info_report, _, {_, std_info, _}}, State) -> +handle_event({info_report, _, {_, std_info, _}}, _State) -> ok; %% filter out "application: foo; exited: stopped; type: temporary" handle_event(Event, State) -> error_logger_file_h:handle_event(Event, State). -- cgit v1.2.1 From 2e55dc4d5db9f4c782c96e16a0769bd9e86d9f1b Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 6 Mar 2013 15:45:47 +0000 Subject: Register the process name to make sure we only have one running at a time. --- src/rabbit_node_monitor.erl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index 55b078c3..47c753e3 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -312,6 +312,9 @@ await_cluster_recovery() -> %% If our group leader is inside an application we are about %% to stop, application:stop/1 does not return. group_leader(whereis(init), self()), + %% Ensure only one restarting process at a time, will + %% exit(badarg) (harmlessly) if one is already running + register(rabbit_restarting_process, self()), rabbit:stop(), wait_for_cluster_recovery(Nodes) end). -- 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(-) 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 01e9aa59c72a9937795619208e3d5c220d4a815b Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Wed, 6 Mar 2013 21:07:31 +0000 Subject: Permit exchange decorators to modify routing decisions --- src/rabbit_exchange.erl | 39 ++++++++++++++++++++++++++++----------- src/rabbit_exchange_decorator.erl | 6 +++++- src/rabbit_registry.erl | 11 ++++++----- 3 files changed, 39 insertions(+), 17 deletions(-) diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index 88033f77..0a3849ef 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -117,14 +117,15 @@ callback(X = #exchange{type = XType}, Fun, Serial0, Args) -> is_atom(Serial0) -> fun (_Bool) -> Serial0 end end, [ok = apply(M, Fun, [Serial(M:serialise_events(X)) | Args]) || - M <- decorators()], + M <- registry_lookup(exchange_decorator)], Module = type_to_module(XType), apply(Module, Fun, [Serial(Module:serialise_events()) | Args]). policy_changed(X1, X2) -> callback(X1, policy_changed, none, [X1, X2]). serialise_events(X = #exchange{type = Type}) -> - lists:any(fun (M) -> M:serialise_events(X) end, decorators()) + lists:any(fun (M) -> M:serialise_events(X) end, + registry_lookup(exchange_decorator)) orelse (type_to_module(Type)):serialise_events(). serial(#exchange{name = XName} = X) -> @@ -136,8 +137,15 @@ serial(#exchange{name = XName} = X) -> (false) -> none end. -decorators() -> - [M || {_, M} <- rabbit_registry:lookup_all(exchange_decorator)]. +registry_lookup(exchange_decorator_route = Class) -> + case get(exchange_decorator_route_modules) of + undefined -> Mods = [M || {_, M} <- rabbit_registry:lookup_all(Class)], + put(exchange_decorator_route_modules, Mods), + Mods; + Mods -> Mods + end; +registry_lookup(Class) -> + [M || {_, M} <- rabbit_registry:lookup_all(Class)]. declare(XName, Type, Durable, AutoDelete, Internal, Args) -> X = rabbit_policy:set(#exchange{name = XName, @@ -304,16 +312,25 @@ info_all(VHostPath) -> map(VHostPath, fun (X) -> info(X) end). info_all(VHostPath, Items) -> map(VHostPath, fun (X) -> info(X, Items) end). -%% Optimisation -route(#exchange{name = #resource{name = <<"">>, virtual_host = VHost}}, - #delivery{message = #basic_message{routing_keys = RKs}}) -> - [rabbit_misc:r(VHost, queue, RK) || RK <- lists:usort(RKs)]; +route(#exchange{name = #resource{name = RName, virtual_host = VHost} = XName} = X, + #delivery{message = #basic_message{routing_keys = RKs}} = Delivery) -> + case registry_lookup(exchange_decorator_route) == [] andalso + RName == <<"">> of + true -> [rabbit_misc:r(VHost, queue, RK) || RK <- lists:usort(RKs)]; + false -> QNames = route1(Delivery, {[X], XName, []}), + lists:usort(decorate_route(X, Delivery, QNames)) + end. -route(X = #exchange{name = XName}, Delivery) -> - route1(Delivery, {[X], XName, []}). +decorate_route(X, Delivery, QNames) -> + {Add, Remove} = + lists:foldl(fun (Decorator, {Add, Remove}) -> + {A1, R1} = Decorator:route(X, Delivery, QNames), + {A1 ++ Add, R1 ++ Remove} + end, {[], []}, registry_lookup(exchange_decorator_route)), + QNames ++ Add -- Remove. route1(_, {[], _, QNames}) -> - lists:usort(QNames); + QNames; route1(Delivery, {[X = #exchange{type = Type} | WorkList], SeenXs, QNames}) -> DstNames = process_alternate( X, ((type_to_module(Type)):route(X, Delivery))), diff --git a/src/rabbit_exchange_decorator.erl b/src/rabbit_exchange_decorator.erl index befbc462..4e395cbe 100644 --- a/src/rabbit_exchange_decorator.erl +++ b/src/rabbit_exchange_decorator.erl @@ -58,13 +58,17 @@ -callback policy_changed ( serial(), rabbit_types:exchange(), rabbit_types:exchange()) -> 'ok'. +-callback route ( rabbit_types:exchange(), rabbit_types:delivery(), + [rabbit_amqqueue:name()]) -> + {[rabbit_amqqueue:name()], [rabbit_amqqueue:name()]}. + -else. -export([behaviour_info/1]). behaviour_info(callbacks) -> [{description, 0}, {serialise_events, 1}, {create, 2}, {delete, 3}, - {add_binding, 3}, {remove_bindings, 3}, {policy_changed, 3}]; + {add_binding, 3}, {remove_bindings, 3}, {policy_changed, 3}, {route, 3}]; behaviour_info(_Other) -> undefined. diff --git a/src/rabbit_registry.erl b/src/rabbit_registry.erl index 60419856..3514e780 100644 --- a/src/rabbit_registry.erl +++ b/src/rabbit_registry.erl @@ -104,11 +104,12 @@ sanity_check_module(ClassModule, Module) -> true -> ok end. -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(policy_validator) -> rabbit_policy_validator. +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(exchange_decorator_route) -> rabbit_exchange_decorator; +class_module(policy_validator) -> rabbit_policy_validator. %%--------------------------------------------------------------------------- -- cgit v1.2.1 From 0f471c546032d497ef2090fab8c4e4c7ad6c7a27 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Thu, 7 Mar 2013 10:01:39 +0000 Subject: Optimisation --- src/rabbit_exchange.erl | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index 0a3849ef..0e7872f6 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -314,11 +314,16 @@ info_all(VHostPath, Items) -> map(VHostPath, fun (X) -> info(X, Items) end). route(#exchange{name = #resource{name = RName, virtual_host = VHost} = XName} = X, #delivery{message = #basic_message{routing_keys = RKs}} = Delivery) -> - case registry_lookup(exchange_decorator_route) == [] andalso - RName == <<"">> of - true -> [rabbit_misc:r(VHost, queue, RK) || RK <- lists:usort(RKs)]; - false -> QNames = route1(Delivery, {[X], XName, []}), - lists:usort(decorate_route(X, Delivery, QNames)) + case {registry_lookup(exchange_decorator_route) == [], RName == <<"">>} of + {true, true} -> + [rabbit_misc:r(VHost, queue, RK) || RK <- lists:usort(RKs)]; + {NoDecor, _} -> + QNames = route1(Delivery, {[X], XName, []}), + lists:usort( + case NoDecor of + true -> QNames; + false -> decorate_route(X, Delivery, QNames) + end) end. decorate_route(X, Delivery, QNames) -> -- cgit v1.2.1 From 42cff68581fe5772de741012b3d75f0c336a4fd6 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 7 Mar 2013 12:26:09 +0000 Subject: Oops --- src/rabbit_error_logger_file_h.erl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/rabbit_error_logger_file_h.erl b/src/rabbit_error_logger_file_h.erl index b6e2c15e..eb6247e0 100644 --- a/src/rabbit_error_logger_file_h.erl +++ b/src/rabbit_error_logger_file_h.erl @@ -76,8 +76,9 @@ init_file(File, PrevHandler) -> Error -> Error end. -handle_event({info_report, _, {_, std_info, _}}, _State) -> - ok; %% filter out "application: foo; exited: stopped; type: temporary" +%% filter out "application: foo; exited: stopped; type: temporary" +handle_event({info_report, _, {_, std_info, _}}, State) -> + {ok, State}; handle_event(Event, State) -> error_logger_file_h:handle_event(Event, State). -- cgit v1.2.1 From bd2905c7b21c4f75fea8bd1d61e49043c7bf6dc2 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Fri, 8 Mar 2013 12:18:26 +0000 Subject: Mostly callback description --- src/rabbit_exchange.erl | 11 +++++------ src/rabbit_exchange_decorator.erl | 8 ++++++-- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index 0e7872f6..7179454d 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -317,13 +317,12 @@ route(#exchange{name = #resource{name = RName, virtual_host = VHost} = XName} = case {registry_lookup(exchange_decorator_route) == [], RName == <<"">>} of {true, true} -> [rabbit_misc:r(VHost, queue, RK) || RK <- lists:usort(RKs)]; - {NoDecor, _} -> + {NoDecorator, _} -> QNames = route1(Delivery, {[X], XName, []}), - lists:usort( - case NoDecor of - true -> QNames; - false -> decorate_route(X, Delivery, QNames) - end) + lists:usort(case NoDecorator of + true -> QNames; + false -> decorate_route(X, Delivery, QNames) + end) end. decorate_route(X, Delivery, QNames) -> diff --git a/src/rabbit_exchange_decorator.erl b/src/rabbit_exchange_decorator.erl index 4e395cbe..70ba4d22 100644 --- a/src/rabbit_exchange_decorator.erl +++ b/src/rabbit_exchange_decorator.erl @@ -21,9 +21,8 @@ %% 1) It applies to all exchanges as soon as it is installed, therefore %% 2) It is not allowed to affect validation, so no validate/1 or %% assert_args_equivalence/2 -%% 3) It also can't affect routing %% -%% It's possible in the future we might relax 3), or even make these +%% It's possible in the future we might make decorators %% able to manipulate messages as they are published. -ifdef(use_specs). @@ -58,6 +57,11 @@ -callback policy_changed ( serial(), rabbit_types:exchange(), rabbit_types:exchange()) -> 'ok'. +%% called after exchange routing +%% return value is a tuple of two lists: queues to be added +%% and queues to be removed from the list of destination queues. +%% decorators must register separately for this callback using +%% exchange_decorator_route. -callback route ( rabbit_types:exchange(), rabbit_types:delivery(), [rabbit_amqqueue:name()]) -> {[rabbit_amqqueue:name()], [rabbit_amqqueue:name()]}. -- 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(-) 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(-) 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 880c8b9e42628d18eac69baec31e6cbf1fdf562e Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Mon, 11 Mar 2013 14:16:32 +0000 Subject: Minimise routing exchange decorator API --- src/rabbit_exchange.erl | 28 +++++++++++++--------------- src/rabbit_exchange_decorator.erl | 14 ++++++-------- 2 files changed, 19 insertions(+), 23 deletions(-) diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index 7179454d..0d1e9831 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -312,26 +312,24 @@ info_all(VHostPath) -> map(VHostPath, fun (X) -> info(X) end). info_all(VHostPath, Items) -> map(VHostPath, fun (X) -> info(X, Items) end). -route(#exchange{name = #resource{name = RName, virtual_host = VHost} = XName} = X, +route(#exchange{name = #resource{virtual_host = VHost, + name = RName} = XName} = X, #delivery{message = #basic_message{routing_keys = RKs}} = Delivery) -> - case {registry_lookup(exchange_decorator_route) == [], RName == <<"">>} of - {true, true} -> + case {registry_lookup(exchange_decorator_route), RName == <<"">>} of + {[], true} -> + %% Optimisation [rabbit_misc:r(VHost, queue, RK) || RK <- lists:usort(RKs)]; - {NoDecorator, _} -> + {Decorators, _} -> QNames = route1(Delivery, {[X], XName, []}), - lists:usort(case NoDecorator of - true -> QNames; - false -> decorate_route(X, Delivery, QNames) - end) + lists:usort(decorate_route(Decorators, X, Delivery, QNames)) end. -decorate_route(X, Delivery, QNames) -> - {Add, Remove} = - lists:foldl(fun (Decorator, {Add, Remove}) -> - {A1, R1} = Decorator:route(X, Delivery, QNames), - {A1 ++ Add, R1 ++ Remove} - end, {[], []}, registry_lookup(exchange_decorator_route)), - QNames ++ Add -- Remove. +decorate_route([], _X, _Delivery, QNames) -> + QNames; +decorate_route(Decorators, X, Delivery, QNames) -> + lists:foldl(fun (Decorator, QNamesAcc) -> + Decorator:route(X, Delivery) ++ QNamesAcc + end, QNames, Decorators). route1(_, {[], _, QNames}) -> QNames; diff --git a/src/rabbit_exchange_decorator.erl b/src/rabbit_exchange_decorator.erl index 70ba4d22..05077f03 100644 --- a/src/rabbit_exchange_decorator.erl +++ b/src/rabbit_exchange_decorator.erl @@ -58,13 +58,11 @@ serial(), rabbit_types:exchange(), rabbit_types:exchange()) -> 'ok'. %% called after exchange routing -%% return value is a tuple of two lists: queues to be added -%% and queues to be removed from the list of destination queues. -%% decorators must register separately for this callback using -%% exchange_decorator_route. --callback route ( rabbit_types:exchange(), rabbit_types:delivery(), - [rabbit_amqqueue:name()]) -> - {[rabbit_amqqueue:name()], [rabbit_amqqueue:name()]}. +%% return value is a list of queues to be added to the list of +%% destination queues. decorators must register separately for +%% this callback using exchange_decorator_route. +-callback route ( rabbit_types:exchange(), rabbit_types:delivery()) -> + [rabbit_amqqueue:name()]. -else. @@ -72,7 +70,7 @@ behaviour_info(callbacks) -> [{description, 0}, {serialise_events, 1}, {create, 2}, {delete, 3}, - {add_binding, 3}, {remove_bindings, 3}, {policy_changed, 3}, {route, 3}]; + {add_binding, 3}, {remove_bindings, 3}, {policy_changed, 3}, {route, 2}]; behaviour_info(_Other) -> undefined. -- 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(-) 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 1beda96451823e8f072a585b6eb9926260a6c41e Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 11 Mar 2013 16:59:29 +0000 Subject: Cosmetic --- src/rabbit_exchange_decorator.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_exchange_decorator.erl b/src/rabbit_exchange_decorator.erl index 05077f03..414f9c60 100644 --- a/src/rabbit_exchange_decorator.erl +++ b/src/rabbit_exchange_decorator.erl @@ -54,14 +54,14 @@ [rabbit_types:binding()]) -> 'ok'. %% called when the policy attached to this exchange changes. --callback policy_changed ( +-callback policy_changed( serial(), rabbit_types:exchange(), rabbit_types:exchange()) -> 'ok'. %% called after exchange routing %% return value is a list of queues to be added to the list of %% destination queues. decorators must register separately for %% this callback using exchange_decorator_route. --callback route ( rabbit_types:exchange(), rabbit_types:delivery()) -> +-callback route(rabbit_types:exchange(), rabbit_types:delivery()) -> [rabbit_amqqueue:name()]. -else. -- cgit v1.2.1 From fb4f1957d4560922abc5cabd5531f3cc9f43eb48 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 11 Mar 2013 17:22:52 +0000 Subject: Eliminate a foldl --- src/rabbit_exchange.erl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index 0d1e9831..c5a6309a 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -327,9 +327,8 @@ route(#exchange{name = #resource{virtual_host = VHost, decorate_route([], _X, _Delivery, QNames) -> QNames; decorate_route(Decorators, X, Delivery, QNames) -> - lists:foldl(fun (Decorator, QNamesAcc) -> - Decorator:route(X, Delivery) ++ QNamesAcc - end, QNames, Decorators). + QNames ++ + lists:append([Decorator:route(X, Delivery) || Decorator <- Decorators]). route1(_, {[], _, QNames}) -> QNames; -- cgit v1.2.1 -- cgit v1.2.1 From 62d12b03e9abe95c60bb00e7faafb6849dc21f9f Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 12 Mar 2013 16:57:29 +0000 Subject: Oops. This was part of an (early, wrong) attempt at bug 25474 which got committed as part of f1317bb80df9 (bug 25358) by mistake. Remove. --- src/rabbit_node_monitor.erl | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index 47c753e3..98e26a6a 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -208,10 +208,9 @@ handle_call(_Request, _From, State) -> %% mnesia information since the message can (and will) overtake the %% mnesia propagation. handle_cast({node_up, Node, NodeType}, - State = #state{monitors = Monitors, partitions = Partitions}) -> - State1 = State#state{partitions = Partitions -- [Node]}, + State = #state{monitors = Monitors}) -> case pmon:is_monitored({rabbit, Node}, Monitors) of - true -> {noreply, State1}; + true -> {noreply, State}; false -> rabbit_log:info("rabbit on node ~p up~n", [Node]), {AllNodes, DiscNodes, RunningNodes} = read_cluster_status(), write_cluster_status({add_node(Node, AllNodes), @@ -221,7 +220,7 @@ handle_cast({node_up, Node, NodeType}, end, add_node(Node, RunningNodes)}), ok = handle_live_rabbit(Node), - {noreply, State1#state{ + {noreply, State#state{ monitors = pmon:monitor({rabbit, Node}, Monitors)}} end; handle_cast({joined_cluster, Node, NodeType}, State) -> -- cgit v1.2.1 From 5dea4c63b56e8b9a7acdf0cedbe5fa051ea5e266 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 12 Mar 2013 17:55:05 +0000 Subject: Treat {inconsistent_database, running_partitioned_network, Node} as being sort of like {node_up, Node, NodeType}. It's not perfect, but it's the best we're going to get. --- src/rabbit_node_monitor.erl | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index 98e26a6a..3d900d26 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -257,10 +257,19 @@ handle_info({'DOWN', _MRef, process, Pid, _Reason}, handle_info({mnesia_system_event, {inconsistent_database, running_partitioned_network, Node}}, - State = #state{partitions = Partitions}) -> + State = #state{partitions = Partitions, + monitors = Monitors}) -> + %% We will not get a node_up from this node - yet we should treat it as + %% up (mostly). + State1 = case pmon:is_monitored({rabbit, Node}, Monitors) of + true -> State; + false -> State#state{ + monitors = pmon:monitor({rabbit, Node}, Monitors)} + end, + ok = handle_live_rabbit(Node), Partitions1 = ordsets:to_list( ordsets:add_element(Node, ordsets:from_list(Partitions))), - {noreply, State#state{partitions = Partitions1}}; + {noreply, State1#state{partitions = Partitions1}}; handle_info(_Info, State) -> {noreply, State}. -- cgit v1.2.1 From 38f82dfc2e3743f101b2921750ea8d4bd679c5d2 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 12 Mar 2013 17:56:18 +0000 Subject: If we have been partitioned, and we are now in the only remaining partition, we no longer care about partitions - forget them. Note that we do not attempt to deal with individual (other) partitions going away, it's only safe to forget *any* of them when we have seen the back of *all* of them. --- src/rabbit_node_monitor.erl | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index 3d900d26..558596ef 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -249,7 +249,8 @@ handle_info({'DOWN', _MRef, process, {rabbit, Node}, _Reason}, write_cluster_status({AllNodes, DiscNodes, del_node(Node, RunningNodes)}), ok = handle_dead_rabbit(Node), [P ! {node_down, Node} || P <- pmon:monitored(Subscribers)], - {noreply, State#state{monitors = pmon:erase({rabbit, Node}, Monitors)}}; + {noreply, handle_dead_rabbit_state( + State#state{monitors = pmon:erase({rabbit, Node}, Monitors)})}; handle_info({'DOWN', _MRef, process, Pid, _Reason}, State = #state{subscribers = Subscribers}) -> @@ -308,9 +309,14 @@ handle_dead_rabbit(Node) -> ok. majority() -> + length(alive_nodes()) / length(rabbit_mnesia:cluster_nodes(all)) > 0.5. + +%% mnesia:system_info(db_nodes) (and hence +%% rabbit_mnesia:cluster_nodes(running)) does not give reliable results +%% when partitioned. +alive_nodes() -> Nodes = rabbit_mnesia:cluster_nodes(all), - Alive = [N || N <- Nodes, pong =:= net_adm:ping(N)], - length(Alive) / length(Nodes) > 0.5. + [N || N <- Nodes, pong =:= net_adm:ping(N)]. await_cluster_recovery() -> rabbit_log:warning("Cluster minority status detected - awaiting recovery~n", @@ -334,6 +340,18 @@ wait_for_cluster_recovery(Nodes) -> wait_for_cluster_recovery(Nodes) end. +handle_dead_rabbit_state(State = #state{partitions = Partitions}) -> + %% If we have been partitioned, and we are now in the only remaining + %% partition, we no longer care about partitions - forget them. Note + %% that we do not attempt to deal with individual (other) partitions + %% going away, it's only safe to forget *any* of them when we have seen + %% the back of *all* of them. + Partitions1 = case Partitions -- (Partitions -- alive_nodes()) of + [] -> []; + _ -> Partitions + end, + State#state{partitions = Partitions1}. + handle_live_rabbit(Node) -> ok = rabbit_alarm:on_node_up(Node), ok = rabbit_mnesia:on_node_up(Node). -- cgit v1.2.1 From 3bd41da26d35ac05d6408496bcdd5a3da54997ce Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 12 Mar 2013 18:43:24 +0000 Subject: don't leave garbage behind in policy validation test --- src/rabbit_tests.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 27807b62..1188c554 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -1094,6 +1094,7 @@ test_policy_validation() -> {error_string, _} = SetPol("testpos", [-1, 0, 1]), {error_string, _} = SetPol("testeven", [ 1, 2, 3]), + ok = control_action(clear_policy, ["name"]), rabbit_runtime_parameters_test:unregister_policy_validator(), passed. -- cgit v1.2.1 From 3eb7ee986cf0900652532b76c6719da376980328 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 13 Mar 2013 17:16:41 +0000 Subject: rabbit_exchange_type:validate_binding/2. --- src/rabbit_binding.erl | 5 ++++- src/rabbit_exchange.erl | 3 ++- src/rabbit_exchange_type.erl | 7 ++++++- src/rabbit_exchange_type_direct.erl | 6 ++++-- src/rabbit_exchange_type_fanout.erl | 4 +++- src/rabbit_exchange_type_headers.erl | 4 +++- src/rabbit_exchange_type_invalid.erl | 6 ++++-- src/rabbit_exchange_type_topic.erl | 4 +++- 8 files changed, 29 insertions(+), 10 deletions(-) diff --git a/src/rabbit_binding.erl b/src/rabbit_binding.erl index 6096e07b..54136404 100644 --- a/src/rabbit_binding.erl +++ b/src/rabbit_binding.erl @@ -153,7 +153,10 @@ exists(Binding) -> add(Binding) -> add(Binding, fun (_Src, _Dst) -> ok end). -add(Binding, InnerFun) -> +add(Binding = #binding{source = XName}, InnerFun) -> + {ok, X = #exchange{type = XType}} = rabbit_exchange:lookup(XName), + Module = rabbit_exchange:type_to_module(XType), + Module:validate_binding(X, Binding), binding_action( Binding, fun (Src, Dst, B) -> diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index c5a6309a..94a37148 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -22,7 +22,7 @@ assert_equivalence/6, assert_args_equivalence/2, check_type/1, lookup/1, lookup_or_die/1, list/1, lookup_scratch/2, update_scratch/3, info_keys/0, info/1, info/2, info_all/1, info_all/2, - route/2, delete/2]). + route/2, delete/2, type_to_module/1]). %% these must be run inside a mnesia tx -export([maybe_auto_delete/1, serial/1, peek_serial/1, update/2]). @@ -83,6 +83,7 @@ (name(), boolean())-> 'ok' | rabbit_types:error('not_found') | rabbit_types:error('in_use')). +-spec(type_to_module/1 :: (type()) -> atom()). -spec(maybe_auto_delete/1:: (rabbit_types:exchange()) -> 'not_deleted' | {'deleted', rabbit_binding:deletions()}). diff --git a/src/rabbit_exchange_type.erl b/src/rabbit_exchange_type.erl index 1fbcb2d8..01001fa2 100644 --- a/src/rabbit_exchange_type.erl +++ b/src/rabbit_exchange_type.erl @@ -37,6 +37,10 @@ %% called BEFORE declaration, to check args etc; may exit with #amqp_error{} -callback validate(rabbit_types:exchange()) -> 'ok'. +%% called BEFORE declaration, to check args etc; may exit with #amqp_error{} +-callback validate_binding( + rabbit_types:exchange(), rabbit_types:binding()) -> 'ok'. + %% called after declaration and recovery -callback create(tx(), rabbit_types:exchange()) -> 'ok'. @@ -67,7 +71,8 @@ -export([behaviour_info/1]). behaviour_info(callbacks) -> - [{description, 0}, {serialise_events, 0}, {route, 2}, {validate, 1}, + [{description, 0}, {serialise_events, 0}, {route, 2}, + {validate, 1}, {validate_binding, 2}, {create, 2}, {delete, 3}, {add_binding, 3}, {remove_bindings, 3}, {assert_args_equivalence, 2}, {policy_changed, 3}]; behaviour_info(_Other) -> diff --git a/src/rabbit_exchange_type_direct.erl b/src/rabbit_exchange_type_direct.erl index 213b24c4..2f216678 100644 --- a/src/rabbit_exchange_type_direct.erl +++ b/src/rabbit_exchange_type_direct.erl @@ -20,8 +20,9 @@ -behaviour(rabbit_exchange_type). -export([description/0, serialise_events/0, route/2]). --export([validate/1, create/2, delete/3, policy_changed/3, - add_binding/3, remove_bindings/3, assert_args_equivalence/2]). +-export([validate/1, validate_binding/2, + create/2, delete/3, policy_changed/3, add_binding/3, + remove_bindings/3, assert_args_equivalence/2]). -rabbit_boot_step({?MODULE, [{description, "exchange type direct"}, @@ -40,6 +41,7 @@ route(#exchange{name = Name}, rabbit_router:match_routing_key(Name, Routes). validate(_X) -> ok. +validate_binding(_X, _B) -> ok. create(_Tx, _X) -> ok. delete(_Tx, _X, _Bs) -> ok. policy_changed(_Tx, _X1, _X2) -> ok. diff --git a/src/rabbit_exchange_type_fanout.erl b/src/rabbit_exchange_type_fanout.erl index 5b17ed56..612bf4d4 100644 --- a/src/rabbit_exchange_type_fanout.erl +++ b/src/rabbit_exchange_type_fanout.erl @@ -20,7 +20,8 @@ -behaviour(rabbit_exchange_type). -export([description/0, serialise_events/0, route/2]). --export([validate/1, create/2, delete/3, policy_changed/3, add_binding/3, +-export([validate/1, validate_binding/2, + create/2, delete/3, policy_changed/3, add_binding/3, remove_bindings/3, assert_args_equivalence/2]). -rabbit_boot_step({?MODULE, @@ -39,6 +40,7 @@ route(#exchange{name = Name}, _Delivery) -> rabbit_router:match_routing_key(Name, ['_']). validate(_X) -> ok. +validate_binding(_X, _B) -> ok. create(_Tx, _X) -> ok. delete(_Tx, _X, _Bs) -> ok. policy_changed(_Tx, _X1, _X2) -> ok. diff --git a/src/rabbit_exchange_type_headers.erl b/src/rabbit_exchange_type_headers.erl index 75899160..dbc587ae 100644 --- a/src/rabbit_exchange_type_headers.erl +++ b/src/rabbit_exchange_type_headers.erl @@ -21,7 +21,8 @@ -behaviour(rabbit_exchange_type). -export([description/0, serialise_events/0, route/2]). --export([validate/1, create/2, delete/3, policy_changed/3, add_binding/3, +-export([validate/1, validate_binding/2, + create/2, delete/3, policy_changed/3, add_binding/3, remove_bindings/3, assert_args_equivalence/2]). -rabbit_boot_step({?MODULE, @@ -113,6 +114,7 @@ headers_match([{PK, PT, PV} | PRest], [{DK, DT, DV} | DRest], headers_match(PRest, DRest, AllMatch1, AnyMatch1, MatchKind). validate(_X) -> ok. +validate_binding(_X, _B) -> ok. create(_Tx, _X) -> ok. delete(_Tx, _X, _Bs) -> ok. policy_changed(_Tx, _X1, _X2) -> ok. diff --git a/src/rabbit_exchange_type_invalid.erl b/src/rabbit_exchange_type_invalid.erl index 6b07351a..72607809 100644 --- a/src/rabbit_exchange_type_invalid.erl +++ b/src/rabbit_exchange_type_invalid.erl @@ -20,8 +20,9 @@ -behaviour(rabbit_exchange_type). -export([description/0, serialise_events/0, route/2]). --export([validate/1, create/2, delete/3, policy_changed/3, - add_binding/3, remove_bindings/3, assert_args_equivalence/2]). +-export([validate/1, validate_binding/2, + create/2, delete/3, policy_changed/3, add_binding/3, + remove_bindings/3, assert_args_equivalence/2]). description() -> [{description, @@ -41,6 +42,7 @@ route(#exchange{name = Name, type = Type}, _) -> [rabbit_misc:rs(Name), Type]). validate(_X) -> ok. +validate_binding(_X, _B) -> ok. create(_Tx, _X) -> ok. delete(_Tx, _X, _Bs) -> ok. policy_changed(_Tx, _X1, _X2) -> ok. diff --git a/src/rabbit_exchange_type_topic.erl b/src/rabbit_exchange_type_topic.erl index bd8ad1ac..22b65ec2 100644 --- a/src/rabbit_exchange_type_topic.erl +++ b/src/rabbit_exchange_type_topic.erl @@ -21,7 +21,8 @@ -behaviour(rabbit_exchange_type). -export([description/0, serialise_events/0, route/2]). --export([validate/1, create/2, delete/3, policy_changed/3, add_binding/3, +-export([validate/1, validate_binding/2, + create/2, delete/3, policy_changed/3, add_binding/3, remove_bindings/3, assert_args_equivalence/2]). -rabbit_boot_step({?MODULE, @@ -47,6 +48,7 @@ route(#exchange{name = X}, end || RKey <- Routes]). validate(_X) -> ok. +validate_binding(_X, _B) -> ok. create(_Tx, _X) -> ok. delete(transaction, #exchange{name = X}, _Bs) -> -- cgit v1.2.1 From 797354c2bf2baa43eb9e44eb4d636e876f403e5c Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 13 Mar 2013 17:41:27 +0000 Subject: Take advantage of the new mechanism to validate x-match. --- src/rabbit_exchange_type_headers.erl | 38 +++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/src/rabbit_exchange_type_headers.erl b/src/rabbit_exchange_type_headers.erl index dbc587ae..a78dce73 100644 --- a/src/rabbit_exchange_type_headers.erl +++ b/src/rabbit_exchange_type_headers.erl @@ -51,14 +51,25 @@ route(#exchange{name = Name}, rabbit_router:match_bindings( Name, fun (#binding{args = Spec}) -> headers_match(Spec, Headers) end). -default_headers_match_kind() -> all. +validate_binding(_X, #binding{args = Args}) -> + case rabbit_misc:table_lookup(Args, <<"x-match">>) of + {longstr, <<"all">>} -> ok; + {longstr, <<"any">>} -> ok; + {longstr, Other} -> rabbit_misc:protocol_error( + precondition_failed, + "Invalid x-match field value ~p; " + "expected all or any", [Other]); + {Type, Other} -> rabbit_misc:protocol_error( + precondition_failed, + "Invalid x-match field type ~p (value ~p); " + "expected longstr", [Type, Other]); + undefined -> rabbit_misc:protocol_error( + precondition_failed, + "x-match field missing", []) + end. parse_x_match(<<"all">>) -> all; -parse_x_match(<<"any">>) -> any; -parse_x_match(Other) -> - rabbit_log:warning("Invalid x-match field value ~p; expected all or any", - [Other]), - default_headers_match_kind(). +parse_x_match(<<"any">>) -> any. %% Horrendous matching algorithm. Depends for its merge-like %% (linear-time) behaviour on the lists:keysort @@ -69,17 +80,9 @@ parse_x_match(Other) -> %% In other words: REQUIRES BOTH PATTERN AND DATA TO BE SORTED ASCENDING BY KEY. %% !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! %% -headers_match(Pattern, Data) -> - MatchKind = case lists:keysearch(<<"x-match">>, 1, Pattern) of - {value, {_, longstr, MK}} -> parse_x_match(MK); - {value, {_, Type, MK}} -> - rabbit_log:warning("Invalid x-match field type ~p " - "(value ~p); expected longstr", - [Type, MK]), - default_headers_match_kind(); - _ -> default_headers_match_kind() - end, - headers_match(Pattern, Data, true, false, MatchKind). +headers_match(Args, Data) -> + {longstr, MK} = rabbit_misc:table_lookup(Args, <<"x-match">>), + headers_match(Args, Data, true, false, parse_x_match(MK)). headers_match([], _Data, AllMatch, _AnyMatch, all) -> AllMatch; @@ -114,7 +117,6 @@ headers_match([{PK, PT, PV} | PRest], [{DK, DT, DV} | DRest], headers_match(PRest, DRest, AllMatch1, AnyMatch1, MatchKind). validate(_X) -> ok. -validate_binding(_X, _B) -> ok. create(_Tx, _X) -> ok. delete(_Tx, _X, _Bs) -> ok. policy_changed(_Tx, _X1, _X2) -> ok. -- cgit v1.2.1 From 3356b11127663b75359de18873496793e83829ee Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 13 Mar 2013 18:34:05 +0000 Subject: cosmetic --- src/rabbit_exchange.erl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index c5a6309a..dcad68cb 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -440,8 +440,7 @@ peek_serial(XName, LockType) -> end. invalid_module(T) -> - rabbit_log:warning( - "Could not find exchange type ~s.~n", [T]), + rabbit_log:warning("Could not find exchange type ~s.~n", [T]), put({xtype_to_module, T}, rabbit_exchange_type_invalid), rabbit_exchange_type_invalid. -- cgit v1.2.1 From faf5177eebe306d79d91a8e1d6b59cd72daa81a8 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 14 Mar 2013 11:35:07 +0000 Subject: Move the check inside the tx - this means we only lookup the exchange once and there's no possible race. It means we have to convert the exception to an {error, #amqp_error{}} which then gets converted back to an exception, but never mind... --- src/rabbit_binding.erl | 30 +++++++++++++++++------------- src/rabbit_exchange.erl | 15 +++++++++++++-- 2 files changed, 30 insertions(+), 15 deletions(-) diff --git a/src/rabbit_binding.erl b/src/rabbit_binding.erl index 54136404..6bc17482 100644 --- a/src/rabbit_binding.erl +++ b/src/rabbit_binding.erl @@ -153,22 +153,26 @@ exists(Binding) -> add(Binding) -> add(Binding, fun (_Src, _Dst) -> ok end). -add(Binding = #binding{source = XName}, InnerFun) -> - {ok, X = #exchange{type = XType}} = rabbit_exchange:lookup(XName), - Module = rabbit_exchange:type_to_module(XType), - Module:validate_binding(X, Binding), +add(Binding, InnerFun) -> binding_action( Binding, fun (Src, Dst, B) -> - %% this argument is used to check queue exclusivity; - %% in general, we want to fail on that in preference to - %% anything else - case InnerFun(Src, Dst) of - ok -> case mnesia:read({rabbit_route, B}) of - [] -> add(Src, Dst, B); - [_] -> fun rabbit_misc:const_ok/0 - end; - {error, _} = Err -> rabbit_misc:const(Err) + case rabbit_exchange:validate_binding(Src, B) of + ok -> + %% this argument is used to check queue exclusivity; + %% in general, we want to fail on that in preference to + %% anything else + case InnerFun(Src, Dst) of + ok -> + case mnesia:read({rabbit_route, B}) of + [] -> add(Src, Dst, B); + [_] -> fun rabbit_misc:const_ok/0 + end; + {error, _} = Err -> + rabbit_misc:const(Err) + end; + {error, _} = Err -> + rabbit_misc:const(Err) end end). diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index 94a37148..18ec83c1 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -22,7 +22,7 @@ assert_equivalence/6, assert_args_equivalence/2, check_type/1, lookup/1, lookup_or_die/1, list/1, lookup_scratch/2, update_scratch/3, info_keys/0, info/1, info/2, info_all/1, info_all/2, - route/2, delete/2, type_to_module/1]). + route/2, delete/2, validate_binding/2]). %% these must be run inside a mnesia tx -export([maybe_auto_delete/1, serial/1, peek_serial/1, update/2]). @@ -83,7 +83,9 @@ (name(), boolean())-> 'ok' | rabbit_types:error('not_found') | rabbit_types:error('in_use')). --spec(type_to_module/1 :: (type()) -> atom()). +-spec(validate_binding/2 :: + (rabbit_types:exchange(), rabbit_types:binding()) + -> rabbit_types:ok_or_error(rabbit_types:amqp_error())). -spec(maybe_auto_delete/1:: (rabbit_types:exchange()) -> 'not_deleted' | {'deleted', rabbit_binding:deletions()}). @@ -400,6 +402,15 @@ delete(XName, IfUnused) -> end end). +validate_binding(X = #exchange{type = XType}, Binding) -> + Module = type_to_module(XType), + try + Module:validate_binding(X, Binding) + catch + exit:Error -> + {error, Error} + end. + maybe_auto_delete(#exchange{auto_delete = false}) -> not_deleted; maybe_auto_delete(#exchange{auto_delete = true} = X) -> -- cgit v1.2.1 From 7547f5d2eb519b2c35bf5ad25eaec6d881b54743 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 14 Mar 2013 12:46:15 +0000 Subject: Cache the result of the external command --- src/rabbit_disk_monitor.erl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/rabbit_disk_monitor.erl b/src/rabbit_disk_monitor.erl index b396b289..3bb163a1 100644 --- a/src/rabbit_disk_monitor.erl +++ b/src/rabbit_disk_monitor.erl @@ -31,6 +31,7 @@ -record(state, {dir, limit, + actual, timeout, timer, alarmed @@ -106,8 +107,8 @@ handle_call({set_check_interval, Timeout}, _From, State) -> {ok, cancel} = timer:cancel(State#state.timer), {reply, ok, State#state{timeout = Timeout, timer = start_timer(Timeout)}}; -handle_call(get_disk_free, _From, State = #state { dir = Dir }) -> - {reply, get_disk_free(Dir), State}; +handle_call(get_disk_free, _From, State = #state { actual = Actual }) -> + {reply, Actual, State}; handle_call(_Request, _From, State) -> {noreply, State}. @@ -156,7 +157,7 @@ internal_update(State = #state { limit = Limit, _ -> ok end, - State #state {alarmed = NewAlarmed}. + State #state {alarmed = NewAlarmed, actual = CurrentFreeBytes}. get_disk_free(Dir) -> get_disk_free(Dir, os:type()). -- cgit v1.2.1 From 50396d3a7c29d7e39d6c28383f7dbbf59ab46fc2 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Thu, 14 Mar 2013 17:27:24 +0000 Subject: rabbit_client_sup accepts supervision options --- src/rabbit_client_sup.erl | 16 ++++++++++------ src/rabbit_direct.erl | 2 +- src/rabbit_networking.erl | 2 +- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/rabbit_client_sup.erl b/src/rabbit_client_sup.erl index 9602c512..54bb8671 100644 --- a/src/rabbit_client_sup.erl +++ b/src/rabbit_client_sup.erl @@ -37,12 +37,16 @@ %%---------------------------------------------------------------------------- -start_link(Callback) -> - supervisor2:start_link(?MODULE, Callback). +start_link(CallbackOpts) -> + supervisor2:start_link(?MODULE, CallbackOpts). -start_link(SupName, Callback) -> - supervisor2:start_link(SupName, ?MODULE, Callback). +start_link(SupName, CallbackOpts) -> + supervisor2:start_link(SupName, ?MODULE, CallbackOpts). -init({M,F,A}) -> +init({{M,F,A},Opts}) -> + {Shutdown, Type} = case rabbit_misc:pget(worker_type, Opts, supervisor) of + supervisor -> {infinity, supervisor}; + worker -> {?MAX_WAIT, worker} + end, {ok, {{simple_one_for_one_terminate, 0, 1}, - [{client, {M,F,A}, temporary, infinity, supervisor, [M]}]}}. + [{client, {M,F,A}, temporary, Shutdown, Type, [M]}]}}. diff --git a/src/rabbit_direct.erl b/src/rabbit_direct.erl index 53144f3f..036f354b 100644 --- a/src/rabbit_direct.erl +++ b/src/rabbit_direct.erl @@ -50,7 +50,7 @@ boot() -> rabbit_sup:start_supervisor_child( rabbit_direct_client_sup, rabbit_client_sup, [{local, rabbit_direct_client_sup}, - {rabbit_channel_sup, start_link, []}]). + {{rabbit_channel_sup, start_link, []}, []}]). force_event_refresh() -> [Pid ! force_event_refresh || Pid<- list()], diff --git a/src/rabbit_networking.erl b/src/rabbit_networking.erl index 0a0e51c5..517fa360 100644 --- a/src/rabbit_networking.erl +++ b/src/rabbit_networking.erl @@ -139,7 +139,7 @@ boot_ssl() -> start() -> rabbit_sup:start_supervisor_child( rabbit_tcp_client_sup, rabbit_client_sup, [{local, rabbit_tcp_client_sup}, - {rabbit_connection_sup,start_link,[]}]). + {{rabbit_connection_sup,start_link,[]}, []}]). ensure_ssl() -> ok = app_utils:start_applications([crypto, public_key, ssl]), -- cgit v1.2.1 From 74e8e188e249319d66fe816f448ca2c19a50330d Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 15 Mar 2013 14:00:15 +0000 Subject: Push protocol errors closer to the edge. --- src/rabbit_binding.erl | 4 +++- src/rabbit_channel.erl | 2 ++ src/rabbit_exchange.erl | 9 ++------- src/rabbit_exchange_type.erl | 6 +++--- src/rabbit_exchange_type_headers.erl | 17 ++++++++--------- 5 files changed, 18 insertions(+), 20 deletions(-) diff --git a/src/rabbit_binding.erl b/src/rabbit_binding.erl index 6bc17482..f1197a84 100644 --- a/src/rabbit_binding.erl +++ b/src/rabbit_binding.erl @@ -39,7 +39,9 @@ {'resources_missing', [{'not_found', (rabbit_types:binding_source() | rabbit_types:binding_destination())} | - {'absent', rabbit_types:amqqueue()}]})). + {'absent', rabbit_types:amqqueue()}]} | + {'binding_invalid', string(), [any()]})). + -type(bind_ok_or_error() :: 'ok' | bind_errors() | rabbit_types:error('binding_not_found')). -type(bind_res() :: bind_ok_or_error() | rabbit_misc:thunk(bind_ok_or_error())). diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 0510afa9..792a06c9 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -1194,6 +1194,8 @@ binding_action(Fun, ExchangeNameBin, DestinationType, DestinationNameBin, not_found, "no binding ~s between ~s and ~s", [RoutingKey, rabbit_misc:rs(ExchangeName), rabbit_misc:rs(DestinationName)]); + {error, {binding_invalid, Fmt, Args}} -> + rabbit_misc:protocol_error(precondition_failed, Fmt, Args); {error, #amqp_error{} = Error} -> rabbit_misc:protocol_error(Error); ok -> return_ok(State, NoWait, ReturnMethod) diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index 18ec83c1..2de7f8a4 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -85,7 +85,7 @@ rabbit_types:error('in_use')). -spec(validate_binding/2 :: (rabbit_types:exchange(), rabbit_types:binding()) - -> rabbit_types:ok_or_error(rabbit_types:amqp_error())). + -> rabbit_types:ok_or_error({'binding_invalid', string(), [any()]})). -spec(maybe_auto_delete/1:: (rabbit_types:exchange()) -> 'not_deleted' | {'deleted', rabbit_binding:deletions()}). @@ -404,12 +404,7 @@ delete(XName, IfUnused) -> validate_binding(X = #exchange{type = XType}, Binding) -> Module = type_to_module(XType), - try - Module:validate_binding(X, Binding) - catch - exit:Error -> - {error, Error} - end. + Module:validate_binding(X, Binding). maybe_auto_delete(#exchange{auto_delete = false}) -> not_deleted; diff --git a/src/rabbit_exchange_type.erl b/src/rabbit_exchange_type.erl index 01001fa2..acb7d772 100644 --- a/src/rabbit_exchange_type.erl +++ b/src/rabbit_exchange_type.erl @@ -37,9 +37,9 @@ %% called BEFORE declaration, to check args etc; may exit with #amqp_error{} -callback validate(rabbit_types:exchange()) -> 'ok'. -%% called BEFORE declaration, to check args etc; may exit with #amqp_error{} --callback validate_binding( - rabbit_types:exchange(), rabbit_types:binding()) -> 'ok'. +%% called BEFORE declaration, to check args etc +-callback validate_binding(rabbit_types:exchange(), rabbit_types:binding()) -> + rabbit_types:ok_or_error({'binding_invalid', string(), [any()]}). %% called after declaration and recovery -callback create(tx(), rabbit_types:exchange()) -> 'ok'. diff --git a/src/rabbit_exchange_type_headers.erl b/src/rabbit_exchange_type_headers.erl index a78dce73..c03dc5d2 100644 --- a/src/rabbit_exchange_type_headers.erl +++ b/src/rabbit_exchange_type_headers.erl @@ -55,17 +55,16 @@ validate_binding(_X, #binding{args = Args}) -> case rabbit_misc:table_lookup(Args, <<"x-match">>) of {longstr, <<"all">>} -> ok; {longstr, <<"any">>} -> ok; - {longstr, Other} -> rabbit_misc:protocol_error( - precondition_failed, + {longstr, Other} -> {error, + {binding_invalid, "Invalid x-match field value ~p; " - "expected all or any", [Other]); - {Type, Other} -> rabbit_misc:protocol_error( - precondition_failed, + "expected all or any", [Other]}}; + {Type, Other} -> {error, + {binding_invalid, "Invalid x-match field type ~p (value ~p); " - "expected longstr", [Type, Other]); - undefined -> rabbit_misc:protocol_error( - precondition_failed, - "x-match field missing", []) + "expected longstr", [Type, Other]}}; + undefined -> {error, + {binding_invalid, "x-match field missing", []}} end. parse_x_match(<<"all">>) -> all; -- cgit v1.2.1 From ee334419d66b60de7c85bdf233ffc9042ed2df40 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 15 Mar 2013 14:24:46 +0000 Subject: move spec expansion to right place --- src/rabbit_binding.erl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/rabbit_binding.erl b/src/rabbit_binding.erl index f1197a84..cb86e5ae 100644 --- a/src/rabbit_binding.erl +++ b/src/rabbit_binding.erl @@ -39,11 +39,12 @@ {'resources_missing', [{'not_found', (rabbit_types:binding_source() | rabbit_types:binding_destination())} | - {'absent', rabbit_types:amqqueue()}]} | - {'binding_invalid', string(), [any()]})). + {'absent', rabbit_types:amqqueue()}]})). -type(bind_ok_or_error() :: 'ok' | bind_errors() | - rabbit_types:error('binding_not_found')). + rabbit_types:error( + 'binding_not_found' | + {'binding_invalid', string(), [any()]})). -type(bind_res() :: bind_ok_or_error() | rabbit_misc:thunk(bind_ok_or_error())). -type(inner_fun() :: fun((rabbit_types:exchange(), -- cgit v1.2.1 From a4956c5164e652c38a7ad1af3e89b1f7af931672 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 15 Mar 2013 14:38:19 +0000 Subject: correct a spec error discovered by R16B dialyzer --- src/rabbit.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index f3ba022a..3cfa21ba 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -236,7 +236,7 @@ {memory, any()}]). -spec(is_running/0 :: () -> boolean()). -spec(is_running/1 :: (node()) -> boolean()). --spec(environment/0 :: () -> [{param() | term()}]). +-spec(environment/0 :: () -> [{param(), term()}]). -spec(rotate_logs/1 :: (file_suffix()) -> rabbit_types:ok_or_error(any())). -spec(force_event_refresh/0 :: () -> 'ok'). -- cgit v1.2.1 From ba8dd4cb725663a646ee69faba6bc7c1c185d62e Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sun, 17 Mar 2013 20:10:07 +0000 Subject: get rid of superfluous serial() arg to {XT,XD}:policy_changed --- src/rabbit_exchange.erl | 5 ++++- src/rabbit_exchange_decorator.erl | 6 +++--- src/rabbit_exchange_type.erl | 6 +++--- src/rabbit_exchange_type_direct.erl | 4 ++-- src/rabbit_exchange_type_fanout.erl | 4 ++-- src/rabbit_exchange_type_headers.erl | 4 ++-- src/rabbit_exchange_type_invalid.erl | 4 ++-- src/rabbit_exchange_type_topic.erl | 4 ++-- 8 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index 27d60872..5f4fb9ec 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -124,7 +124,10 @@ callback(X = #exchange{type = XType}, Fun, Serial0, Args) -> Module = type_to_module(XType), apply(Module, Fun, [Serial(Module:serialise_events()) | Args]). -policy_changed(X1, X2) -> callback(X1, policy_changed, none, [X1, X2]). +policy_changed(X = #exchange{type = XType}, X1) -> + [ok = M:policy_changed(X, X1) || + M <- [type_to_module(XType) | registry_lookup(exchange_decorator)]], + ok. serialise_events(X = #exchange{type = Type}) -> lists:any(fun (M) -> M:serialise_events(X) end, diff --git a/src/rabbit_exchange_decorator.erl b/src/rabbit_exchange_decorator.erl index 414f9c60..b7ef4c45 100644 --- a/src/rabbit_exchange_decorator.erl +++ b/src/rabbit_exchange_decorator.erl @@ -54,8 +54,8 @@ [rabbit_types:binding()]) -> 'ok'. %% called when the policy attached to this exchange changes. --callback policy_changed( - serial(), rabbit_types:exchange(), rabbit_types:exchange()) -> 'ok'. +-callback policy_changed(rabbit_types:exchange(), rabbit_types:exchange()) -> + 'ok'. %% called after exchange routing %% return value is a list of queues to be added to the list of @@ -70,7 +70,7 @@ behaviour_info(callbacks) -> [{description, 0}, {serialise_events, 1}, {create, 2}, {delete, 3}, - {add_binding, 3}, {remove_bindings, 3}, {policy_changed, 3}, {route, 2}]; + {add_binding, 3}, {remove_bindings, 3}, {policy_changed, 2}, {route, 2}]; behaviour_info(_Other) -> undefined. diff --git a/src/rabbit_exchange_type.erl b/src/rabbit_exchange_type.erl index acb7d772..fd37631b 100644 --- a/src/rabbit_exchange_type.erl +++ b/src/rabbit_exchange_type.erl @@ -63,8 +63,8 @@ 'ok' | rabbit_types:connection_exit(). %% called when the policy attached to this exchange changes. --callback policy_changed(serial(), rabbit_types:exchange(), - rabbit_types:exchange()) -> 'ok'. +-callback policy_changed(rabbit_types:exchange(), rabbit_types:exchange()) -> + 'ok'. -else. @@ -74,7 +74,7 @@ behaviour_info(callbacks) -> [{description, 0}, {serialise_events, 0}, {route, 2}, {validate, 1}, {validate_binding, 2}, {create, 2}, {delete, 3}, {add_binding, 3}, {remove_bindings, 3}, - {assert_args_equivalence, 2}, {policy_changed, 3}]; + {assert_args_equivalence, 2}, {policy_changed, 2}]; behaviour_info(_Other) -> undefined. diff --git a/src/rabbit_exchange_type_direct.erl b/src/rabbit_exchange_type_direct.erl index 2f216678..10a79c55 100644 --- a/src/rabbit_exchange_type_direct.erl +++ b/src/rabbit_exchange_type_direct.erl @@ -21,7 +21,7 @@ -export([description/0, serialise_events/0, route/2]). -export([validate/1, validate_binding/2, - create/2, delete/3, policy_changed/3, add_binding/3, + create/2, delete/3, policy_changed/2, add_binding/3, remove_bindings/3, assert_args_equivalence/2]). -rabbit_boot_step({?MODULE, @@ -44,7 +44,7 @@ validate(_X) -> ok. validate_binding(_X, _B) -> ok. create(_Tx, _X) -> ok. delete(_Tx, _X, _Bs) -> ok. -policy_changed(_Tx, _X1, _X2) -> ok. +policy_changed(_X1, _X2) -> ok. add_binding(_Tx, _X, _B) -> ok. remove_bindings(_Tx, _X, _Bs) -> ok. assert_args_equivalence(X, Args) -> diff --git a/src/rabbit_exchange_type_fanout.erl b/src/rabbit_exchange_type_fanout.erl index 612bf4d4..3ebd8548 100644 --- a/src/rabbit_exchange_type_fanout.erl +++ b/src/rabbit_exchange_type_fanout.erl @@ -21,7 +21,7 @@ -export([description/0, serialise_events/0, route/2]). -export([validate/1, validate_binding/2, - create/2, delete/3, policy_changed/3, add_binding/3, + create/2, delete/3, policy_changed/2, add_binding/3, remove_bindings/3, assert_args_equivalence/2]). -rabbit_boot_step({?MODULE, @@ -43,7 +43,7 @@ validate(_X) -> ok. validate_binding(_X, _B) -> ok. create(_Tx, _X) -> ok. delete(_Tx, _X, _Bs) -> ok. -policy_changed(_Tx, _X1, _X2) -> ok. +policy_changed(_X1, _X2) -> ok. add_binding(_Tx, _X, _B) -> ok. remove_bindings(_Tx, _X, _Bs) -> ok. assert_args_equivalence(X, Args) -> diff --git a/src/rabbit_exchange_type_headers.erl b/src/rabbit_exchange_type_headers.erl index c03dc5d2..cf2d3140 100644 --- a/src/rabbit_exchange_type_headers.erl +++ b/src/rabbit_exchange_type_headers.erl @@ -22,7 +22,7 @@ -export([description/0, serialise_events/0, route/2]). -export([validate/1, validate_binding/2, - create/2, delete/3, policy_changed/3, add_binding/3, + create/2, delete/3, policy_changed/2, add_binding/3, remove_bindings/3, assert_args_equivalence/2]). -rabbit_boot_step({?MODULE, @@ -118,7 +118,7 @@ headers_match([{PK, PT, PV} | PRest], [{DK, DT, DV} | DRest], validate(_X) -> ok. create(_Tx, _X) -> ok. delete(_Tx, _X, _Bs) -> ok. -policy_changed(_Tx, _X1, _X2) -> ok. +policy_changed(_X1, _X2) -> ok. add_binding(_Tx, _X, _B) -> ok. remove_bindings(_Tx, _X, _Bs) -> ok. assert_args_equivalence(X, Args) -> diff --git a/src/rabbit_exchange_type_invalid.erl b/src/rabbit_exchange_type_invalid.erl index 72607809..07a8004a 100644 --- a/src/rabbit_exchange_type_invalid.erl +++ b/src/rabbit_exchange_type_invalid.erl @@ -21,7 +21,7 @@ -export([description/0, serialise_events/0, route/2]). -export([validate/1, validate_binding/2, - create/2, delete/3, policy_changed/3, add_binding/3, + create/2, delete/3, policy_changed/2, add_binding/3, remove_bindings/3, assert_args_equivalence/2]). description() -> @@ -45,7 +45,7 @@ validate(_X) -> ok. validate_binding(_X, _B) -> ok. create(_Tx, _X) -> ok. delete(_Tx, _X, _Bs) -> ok. -policy_changed(_Tx, _X1, _X2) -> ok. +policy_changed(_X1, _X2) -> ok. add_binding(_Tx, _X, _B) -> ok. remove_bindings(_Tx, _X, _Bs) -> ok. assert_args_equivalence(X, Args) -> diff --git a/src/rabbit_exchange_type_topic.erl b/src/rabbit_exchange_type_topic.erl index 22b65ec2..ce76ccb0 100644 --- a/src/rabbit_exchange_type_topic.erl +++ b/src/rabbit_exchange_type_topic.erl @@ -22,7 +22,7 @@ -export([description/0, serialise_events/0, route/2]). -export([validate/1, validate_binding/2, - create/2, delete/3, policy_changed/3, add_binding/3, + create/2, delete/3, policy_changed/2, add_binding/3, remove_bindings/3, assert_args_equivalence/2]). -rabbit_boot_step({?MODULE, @@ -59,7 +59,7 @@ delete(transaction, #exchange{name = X}, _Bs) -> delete(none, _Exchange, _Bs) -> ok. -policy_changed(_Tx, _X1, _X2) -> ok. +policy_changed(_X1, _X2) -> ok. add_binding(transaction, _Exchange, Binding) -> internal_add_binding(Binding); -- cgit v1.2.1 From 1233f304858989a08c12579248d5e17c858c5dd9 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sun, 17 Mar 2013 20:14:47 +0000 Subject: cosmetic: make callback function def order match usage --- src/rabbit_exchange_decorator.erl | 10 +++++----- src/rabbit_exchange_type.erl | 12 ++++++------ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/rabbit_exchange_decorator.erl b/src/rabbit_exchange_decorator.erl index b7ef4c45..8f17adfc 100644 --- a/src/rabbit_exchange_decorator.erl +++ b/src/rabbit_exchange_decorator.erl @@ -45,6 +45,10 @@ -callback delete(tx(), rabbit_types:exchange(), [rabbit_types:binding()]) -> 'ok'. +%% called when the policy attached to this exchange changes. +-callback policy_changed(rabbit_types:exchange(), rabbit_types:exchange()) -> + 'ok'. + %% called after a binding has been added or recovered -callback add_binding(serial(), rabbit_types:exchange(), rabbit_types:binding()) -> 'ok'. @@ -53,10 +57,6 @@ -callback remove_bindings(serial(), rabbit_types:exchange(), [rabbit_types:binding()]) -> 'ok'. -%% called when the policy attached to this exchange changes. --callback policy_changed(rabbit_types:exchange(), rabbit_types:exchange()) -> - 'ok'. - %% called after exchange routing %% return value is a list of queues to be added to the list of %% destination queues. decorators must register separately for @@ -70,7 +70,7 @@ behaviour_info(callbacks) -> [{description, 0}, {serialise_events, 1}, {create, 2}, {delete, 3}, - {add_binding, 3}, {remove_bindings, 3}, {policy_changed, 2}, {route, 2}]; + {policy_changed, 2}, {add_binding, 3}, {remove_bindings, 3}, {route, 2}]; behaviour_info(_Other) -> undefined. diff --git a/src/rabbit_exchange_type.erl b/src/rabbit_exchange_type.erl index fd37631b..ebc59501 100644 --- a/src/rabbit_exchange_type.erl +++ b/src/rabbit_exchange_type.erl @@ -48,6 +48,10 @@ -callback delete(tx(), rabbit_types:exchange(), [rabbit_types:binding()]) -> 'ok'. +%% called when the policy attached to this exchange changes. +-callback policy_changed(rabbit_types:exchange(), rabbit_types:exchange()) -> + 'ok'. + %% called after a binding has been added or recovered -callback add_binding(serial(), rabbit_types:exchange(), rabbit_types:binding()) -> 'ok'. @@ -62,19 +66,15 @@ rabbit_framing:amqp_table()) -> 'ok' | rabbit_types:connection_exit(). -%% called when the policy attached to this exchange changes. --callback policy_changed(rabbit_types:exchange(), rabbit_types:exchange()) -> - 'ok'. - -else. -export([behaviour_info/1]). behaviour_info(callbacks) -> [{description, 0}, {serialise_events, 0}, {route, 2}, - {validate, 1}, {validate_binding, 2}, + {validate, 1}, {validate_binding, 2}, {policy_changed, 2}, {create, 2}, {delete, 3}, {add_binding, 3}, {remove_bindings, 3}, - {assert_args_equivalence, 2}, {policy_changed, 2}]; + {assert_args_equivalence, 2}]; behaviour_info(_Other) -> undefined. -- cgit v1.2.1 From 8c1d2292c3b68f7251f8b8e593a77a388db90a7c Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 18 Mar 2013 11:17:02 +0000 Subject: More archs --- packaging/debs/apt-repository/distributions | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/debs/apt-repository/distributions b/packaging/debs/apt-repository/distributions index 183eb034..61fd778a 100644 --- a/packaging/debs/apt-repository/distributions +++ b/packaging/debs/apt-repository/distributions @@ -2,6 +2,6 @@ Origin: RabbitMQ Label: RabbitMQ Repository for Debian / Ubuntu etc Suite: testing Codename: kitten -Architectures: arm hppa ia64 mips mipsel s390 sparc i386 amd64 powerpc source +Architectures: AVR32 alpha amd64 arm armel armhf hppa hurd-i386 i386 ia64 kfreebsd-amd64 kfreebsd-i386 m32 m68k mips mipsel netbsd-alpha netbsd-i386 powerpc s390 s390x sh sparc Components: main Description: RabbitMQ Repository for Debian / Ubuntu etc -- cgit v1.2.1 From 01012b70dadc9f8e1bca13330caaacff04505f71 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Mon, 18 Mar 2013 12:01:48 +0000 Subject: Code comment about clearing partitions --- src/rabbit_node_monitor.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index 558596ef..de53b7f0 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -344,8 +344,8 @@ handle_dead_rabbit_state(State = #state{partitions = Partitions}) -> %% If we have been partitioned, and we are now in the only remaining %% partition, we no longer care about partitions - forget them. Note %% that we do not attempt to deal with individual (other) partitions - %% going away, it's only safe to forget *any* of them when we have seen - %% the back of *all* of them. + %% going away. It's only safe to forget anything about partitions when + %% there are no partitions. Partitions1 = case Partitions -- (Partitions -- alive_nodes()) of [] -> []; _ -> Partitions -- cgit v1.2.1 From 8f15e63d08a2c30bd5db9252a49250f9269f0411 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Mon, 18 Mar 2013 13:59:51 +0000 Subject: Different registration of exchange decorators that modify routing --- src/rabbit_exchange_decorator.erl | 9 +------ src/rabbit_registry.erl | 49 ++++++++++++++++++++++++++++++++------- 2 files changed, 41 insertions(+), 17 deletions(-) diff --git a/src/rabbit_exchange_decorator.erl b/src/rabbit_exchange_decorator.erl index 414f9c60..491c9d27 100644 --- a/src/rabbit_exchange_decorator.erl +++ b/src/rabbit_exchange_decorator.erl @@ -57,20 +57,13 @@ -callback policy_changed( serial(), rabbit_types:exchange(), rabbit_types:exchange()) -> 'ok'. -%% called after exchange routing -%% return value is a list of queues to be added to the list of -%% destination queues. decorators must register separately for -%% this callback using exchange_decorator_route. --callback route(rabbit_types:exchange(), rabbit_types:delivery()) -> - [rabbit_amqqueue:name()]. - -else. -export([behaviour_info/1]). behaviour_info(callbacks) -> [{description, 0}, {serialise_events, 1}, {create, 2}, {delete, 3}, - {add_binding, 3}, {remove_bindings, 3}, {policy_changed, 3}, {route, 2}]; + {add_binding, 3}, {remove_bindings, 3}, {policy_changed, 3}]; behaviour_info(_Other) -> undefined. diff --git a/src/rabbit_registry.erl b/src/rabbit_registry.erl index 3514e780..db07dcdb 100644 --- a/src/rabbit_registry.erl +++ b/src/rabbit_registry.erl @@ -84,12 +84,44 @@ internal_binary_to_type(TypeBin) when is_binary(TypeBin) -> internal_register(Class, TypeName, ModuleName) when is_atom(Class), is_binary(TypeName), is_atom(ModuleName) -> ok = sanity_check_module(class_module(Class), ModuleName), - true = ets:insert(?ETS_NAME, - {{Class, internal_binary_to_type(TypeName)}, ModuleName}), + RegArg = {{Class, internal_binary_to_type(TypeName)}, ModuleName}, + true = ets:insert(?ETS_NAME, RegArg), + conditional_register(RegArg), ok. internal_unregister(Class, TypeName) -> - true = ets:delete(?ETS_NAME, {Class, internal_binary_to_type(TypeName)}), + UnregArg = {Class, internal_binary_to_type(TypeName)}, + conditional_unregister(UnregArg), + true = ets:delete(?ETS_NAME, UnregArg), + ok. + +conditional_register({{exchange_decorator, Type}, ModuleName}) -> + case erlang:function_exported(ModuleName, route, 2) of + true -> + true = ets:insert(?ETS_NAME, {{exchange_decorator_route, Type}, + ModuleName}), + ok; + false -> + ok + end; +conditional_register(_) -> + ok. + +conditional_unregister({exchange_decorator, Type}) -> + case lookup_module(exchange_decorator, Type) of + {ok, ModuleName} -> + case erlang:function_exported(ModuleName, route, 2) of + true -> + true = ets:delete(?ETS_NAME, {exchange_decorator_route, + Type}), + ok; + false -> + ok + end; + {error, not_found} -> + ok + end; +conditional_unregister(_) -> ok. sanity_check_module(ClassModule, Module) -> @@ -104,12 +136,11 @@ sanity_check_module(ClassModule, Module) -> true -> ok end. -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(exchange_decorator_route) -> rabbit_exchange_decorator; -class_module(policy_validator) -> rabbit_policy_validator. +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(policy_validator) -> rabbit_policy_validator. %%--------------------------------------------------------------------------- -- cgit v1.2.1 From f50d14c60299ad1e121cf24e710c8efe8c208a8b Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 18 Mar 2013 14:59:29 +0000 Subject: Bug 25497 really should have been branched from stable, so backport it. --- packaging/debs/apt-repository/distributions | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/debs/apt-repository/distributions b/packaging/debs/apt-repository/distributions index 183eb034..61fd778a 100644 --- a/packaging/debs/apt-repository/distributions +++ b/packaging/debs/apt-repository/distributions @@ -2,6 +2,6 @@ Origin: RabbitMQ Label: RabbitMQ Repository for Debian / Ubuntu etc Suite: testing Codename: kitten -Architectures: arm hppa ia64 mips mipsel s390 sparc i386 amd64 powerpc source +Architectures: AVR32 alpha amd64 arm armel armhf hppa hurd-i386 i386 ia64 kfreebsd-amd64 kfreebsd-i386 m32 m68k mips mipsel netbsd-alpha netbsd-i386 powerpc s390 s390x sh sparc Components: main Description: RabbitMQ Repository for Debian / Ubuntu etc -- cgit v1.2.1 From fd238eda9de479403daf9debbbea760988291c19 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 19 Mar 2013 11:10:40 +0000 Subject: limiter API revision, part 1/2 - channel-side API --- src/rabbit_amqqueue.erl | 2 +- src/rabbit_amqqueue_process.erl | 19 ++-- src/rabbit_channel.erl | 68 ++++++------- src/rabbit_channel_sup.erl | 4 +- src/rabbit_limiter.erl | 207 +++++++++++++++++++--------------------- 5 files changed, 139 insertions(+), 161 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 82ac74fa..bd5de239 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -144,7 +144,7 @@ -spec(ack/3 :: (pid(), [msg_id()], pid()) -> 'ok'). -spec(reject/4 :: (pid(), [msg_id()], boolean(), pid()) -> 'ok'). -spec(notify_down_all/2 :: (qpids(), pid()) -> ok_or_errors()). --spec(limit_all/3 :: (qpids(), pid(), rabbit_limiter:token()) -> +-spec(limit_all/3 :: (qpids(), pid(), rabbit_limiter:lstate()) -> ok_or_errors()). -spec(basic_get/3 :: (rabbit_types:amqqueue(), pid(), boolean()) -> {'ok', non_neg_integer(), qmsg()} | 'empty'). diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 18b641d4..0ddc9eba 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -372,7 +372,7 @@ ch_record(ChPid) -> consumer_count = 0, blocked_consumers = queue:new(), is_limit_active = false, - limiter = rabbit_limiter:make_token(), + limiter = undefined, unsent_message_count = 0}, put(Key, C), C; @@ -395,18 +395,17 @@ store_ch_record(C = #cr{ch_pid = ChPid}) -> erase_ch_record(#cr{ch_pid = ChPid, limiter = Limiter, monitor_ref = MonitorRef}) -> - ok = rabbit_limiter:unregister(Limiter, self()), + ok = rabbit_limiter:unregister(Limiter), erlang:demonitor(MonitorRef), erase({ch, ChPid}), ok. update_consumer_count(C = #cr{consumer_count = 0, limiter = Limiter}, +1) -> - ok = rabbit_limiter:register(Limiter, self()), + ok = rabbit_limiter:register(Limiter), update_ch_record(C#cr{consumer_count = 1}); update_consumer_count(C = #cr{consumer_count = 1, limiter = Limiter}, -1) -> - ok = rabbit_limiter:unregister(Limiter, self()), - update_ch_record(C#cr{consumer_count = 0, - limiter = rabbit_limiter:make_token()}); + ok = rabbit_limiter:unregister(Limiter), + update_ch_record(C#cr{consumer_count = 0, limiter = undefined}); update_consumer_count(C = #cr{consumer_count = Count}, Delta) -> update_ch_record(C#cr{consumer_count = Count + Delta}). @@ -444,7 +443,7 @@ deliver_msg_to_consumer(DeliverFun, E = {ChPid, Consumer}, State) -> case is_ch_blocked(C) of true -> block_consumer(C, E), {false, State}; - false -> case rabbit_limiter:can_send(C#cr.limiter, self(), + false -> case rabbit_limiter:can_send(C#cr.limiter, Consumer#consumer.ack_required) of false -> block_consumer(C#cr{is_limit_active = true}, E), {false, State}; @@ -1308,11 +1307,11 @@ handle_cast({limit, ChPid, Limiter}, State) -> limiter = OldLimiter, is_limit_active = OldLimited}) -> case (ConsumerCount =/= 0 andalso - not rabbit_limiter:is_enabled(OldLimiter)) of - true -> ok = rabbit_limiter:register(Limiter, self()); + not rabbit_limiter:is_active(OldLimiter)) of + true -> ok = rabbit_limiter:register(Limiter); false -> ok end, - Limited = OldLimited andalso rabbit_limiter:is_enabled(Limiter), + Limited = OldLimited andalso rabbit_limiter:is_active(Limiter), C#cr{limiter = Limiter, is_limit_active = Limited} end)); diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 792a06c9..cda5747a 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -82,7 +82,7 @@ (channel_number(), pid(), pid(), pid(), string(), rabbit_types:protocol(), rabbit_types:user(), rabbit_types:vhost(), rabbit_framing:amqp_table(), - pid(), rabbit_limiter:token()) -> rabbit_types:ok_pid_or_error()). + pid(), pid()) -> rabbit_types:ok_pid_or_error()). -spec(do/2 :: (pid(), rabbit_framing:amqp_method_record()) -> 'ok'). -spec(do/3 :: (pid(), rabbit_framing:amqp_method_record(), rabbit_types:maybe(rabbit_types:content())) -> 'ok'). @@ -180,7 +180,7 @@ force_event_refresh() -> %%--------------------------------------------------------------------------- init([Channel, ReaderPid, WriterPid, ConnPid, ConnName, Protocol, User, VHost, - Capabilities, CollectorPid, Limiter]) -> + Capabilities, CollectorPid, LimiterPid]) -> process_flag(trap_exit, true), ok = pg_local:join(rabbit_channels, self()), State = #ch{state = starting, @@ -190,7 +190,7 @@ init([Channel, ReaderPid, WriterPid, ConnPid, ConnName, Protocol, User, VHost, writer_pid = WriterPid, conn_pid = ConnPid, conn_name = ConnName, - limiter = Limiter, + limiter = rabbit_limiter:new(LimiterPid), tx = none, next_tag = 1, unacked_message_q = queue:new(), @@ -804,18 +804,10 @@ handle_method(#'basic.qos'{prefetch_size = Size}, _, _State) when Size /= 0 -> "prefetch_size!=0 (~w)", [Size]); handle_method(#'basic.qos'{prefetch_count = PrefetchCount}, _, - State = #ch{limiter = Limiter}) -> - Limiter1 = case {rabbit_limiter:is_enabled(Limiter), PrefetchCount} of - {false, 0} -> Limiter; - {false, _} -> enable_limiter(State); - {_, _} -> Limiter - end, - Limiter3 = case rabbit_limiter:limit(Limiter1, PrefetchCount) of - ok -> Limiter1; - {disabled, Limiter2} -> ok = limit_queues(Limiter2, State), - Limiter2 - end, - {reply, #'basic.qos_ok'{}, State#ch{limiter = Limiter3}}; + State = #ch{limiter = Limiter, unacked_message_q = UAMQ}) -> + Limiter1 = rabbit_limiter:limit(Limiter, PrefetchCount, queue:len(UAMQ)), + {reply, #'basic.qos_ok'{}, + maybe_limit_queues(Limiter, Limiter1, State#ch{limiter = Limiter1})}; handle_method(#'basic.recover_async'{requeue = true}, _, State = #ch{unacked_message_q = UAMQ, @@ -1078,25 +1070,23 @@ handle_method(#'confirm.select'{nowait = NoWait}, _, State) -> handle_method(#'channel.flow'{active = true}, _, State = #ch{limiter = Limiter}) -> - Limiter2 = case rabbit_limiter:unblock(Limiter) of - ok -> Limiter; - {disabled, Limiter1} -> ok = limit_queues(Limiter1, State), - Limiter1 - end, - {reply, #'channel.flow_ok'{active = true}, State#ch{limiter = Limiter2}}; + Limiter1 = rabbit_limiter:unblock(Limiter), + {reply, #'channel.flow_ok'{active = true}, + maybe_limit_queues(Limiter, Limiter1, State#ch{limiter = Limiter1})}; handle_method(#'channel.flow'{active = false}, _, State = #ch{consumer_mapping = Consumers, limiter = Limiter}) -> - Limiter1 = case rabbit_limiter:is_enabled(Limiter) of - true -> Limiter; - false -> enable_limiter(State) - end, - State1 = State#ch{limiter = Limiter1}, - ok = rabbit_limiter:block(Limiter1), - QPids = consumer_queues(Consumers), - ok = rabbit_amqqueue:flush_all(QPids, self()), - {noreply, maybe_send_flow_ok(State1#ch{blocking = sets:from_list(QPids)})}; + case rabbit_limiter:is_blocked(Limiter) of + true -> {noreply, maybe_send_flow_ok(State)}; + false -> Limiter1 = rabbit_limiter:block(Limiter), + State1 = maybe_limit_queues(Limiter, Limiter1, + State#ch{limiter = Limiter1}), + QPids = consumer_queues(Consumers), + ok = rabbit_amqqueue:flush_all(QPids, self()), + {noreply, maybe_send_flow_ok( + State1#ch{blocking = sets:from_list(QPids)})} + end; handle_method(_MethodRecord, _Content, _State) -> rabbit_misc:protocol_error( @@ -1332,14 +1322,14 @@ foreach_per_queue(F, UAL) -> end, gb_trees:empty(), UAL), rabbit_misc:gb_trees_foreach(F, T). -enable_limiter(State = #ch{unacked_message_q = UAMQ, - limiter = Limiter}) -> - Limiter1 = rabbit_limiter:enable(Limiter, queue:len(UAMQ)), - ok = limit_queues(Limiter1, State), - Limiter1. - -limit_queues(Limiter, #ch{consumer_mapping = Consumers}) -> - rabbit_amqqueue:limit_all(consumer_queues(Consumers), self(), Limiter). +maybe_limit_queues(OldLimiter, NewLimiter, State) -> + case ((not rabbit_limiter:is_active(OldLimiter)) andalso + rabbit_limiter:is_active(NewLimiter)) of + true -> Queues = consumer_queues(State#ch.consumer_mapping), + rabbit_amqqueue:limit_all(Queues, self(), NewLimiter); + false -> ok + end, + State. consumer_queues(Consumers) -> lists:usort([QPid || @@ -1350,7 +1340,7 @@ consumer_queues(Consumers) -> %% messages sent in a response to a basic.get (identified by their %% 'none' consumer tag) notify_limiter(Limiter, Acked) -> - case rabbit_limiter:is_enabled(Limiter) of + case rabbit_limiter:is_limited(Limiter) of false -> ok; true -> case lists:foldl(fun ({_, none, _}, Acc) -> Acc; ({_, _, _}, Acc) -> Acc + 1 diff --git a/src/rabbit_channel_sup.erl b/src/rabbit_channel_sup.erl index 8ea44a81..a0c7624b 100644 --- a/src/rabbit_channel_sup.erl +++ b/src/rabbit_channel_sup.erl @@ -58,7 +58,7 @@ start_link({tcp, Sock, Channel, FrameMax, ReaderPid, ConnName, Protocol, User, {channel, {rabbit_channel, start_link, [Channel, ReaderPid, WriterPid, ReaderPid, ConnName, Protocol, User, VHost, Capabilities, Collector, - rabbit_limiter:make_token(LimiterPid)]}, + LimiterPid]}, intrinsic, ?MAX_WAIT, worker, [rabbit_channel]}), {ok, AState} = rabbit_command_assembler:init(Protocol), {ok, SupPid, {ChannelPid, AState}}; @@ -72,7 +72,7 @@ start_link({direct, Channel, ClientChannelPid, ConnPid, ConnName, Protocol, {channel, {rabbit_channel, start_link, [Channel, ClientChannelPid, ClientChannelPid, ConnPid, ConnName, Protocol, User, VHost, Capabilities, Collector, - rabbit_limiter:make_token(LimiterPid)]}, + LimiterPid]}, intrinsic, ?MAX_WAIT, worker, [rabbit_channel]}), {ok, SupPid, {ChannelPid, none}}. diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index 8a7d14fe..ae656328 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -18,38 +18,43 @@ -behaviour(gen_server2). +-export([start_link/0]). +-export([new/1, limit/3, unlimit/1, block/1, unblock/1, + is_limited/1, is_blocked/1, is_active/1, get_limit/1, ack/2]). +-export([can_send/2, register/1, unregister/1]). + -export([init/1, terminate/2, code_change/3, handle_call/3, handle_cast/2, handle_info/2, prioritise_call/3]). --export([start_link/0, make_token/0, make_token/1, is_enabled/1, enable/2, - disable/1]). --export([limit/2, can_send/3, ack/2, register/2, unregister/2]). --export([get_limit/1, block/1, unblock/1, is_blocked/1]). %%---------------------------------------------------------------------------- --record(token, {pid, enabled}). +-record(lstate, {pid, limited, blocked}). -ifdef(use_specs). --export_type([token/0]). +-export_type([lstate/0]). --opaque(token() :: #token{}). +-opaque(lstate() :: #lstate {pid :: pid(), + limited :: boolean(), + blocked :: boolean()}). -spec(start_link/0 :: () -> rabbit_types:ok_pid_or_error()). --spec(make_token/0 :: () -> token()). --spec(make_token/1 :: ('undefined' | pid()) -> token()). --spec(is_enabled/1 :: (token()) -> boolean()). --spec(enable/2 :: (token(), non_neg_integer()) -> token()). --spec(disable/1 :: (token()) -> token()). --spec(limit/2 :: (token(), non_neg_integer()) -> 'ok' | {'disabled', token()}). --spec(can_send/3 :: (token(), pid(), boolean()) -> boolean()). --spec(ack/2 :: (token(), non_neg_integer()) -> 'ok'). --spec(register/2 :: (token(), pid()) -> 'ok'). --spec(unregister/2 :: (token(), pid()) -> 'ok'). --spec(get_limit/1 :: (token()) -> non_neg_integer()). --spec(block/1 :: (token()) -> 'ok'). --spec(unblock/1 :: (token()) -> 'ok' | {'disabled', token()}). --spec(is_blocked/1 :: (token()) -> boolean()). +-spec(new/1 :: (pid()) -> lstate()). + +-spec(limit/3 :: (lstate(), non_neg_integer(), non_neg_integer()) -> + lstate()). +-spec(unlimit/1 :: (lstate()) -> lstate()). +-spec(block/1 :: (lstate()) -> lstate()). +-spec(unblock/1 :: (lstate()) -> lstate()). +-spec(is_limited/1 :: (lstate()) -> boolean()). +-spec(is_blocked/1 :: (lstate()) -> boolean()). +-spec(is_active/1 :: (lstate()) -> boolean()). +-spec(get_limit/1 :: (lstate()) -> non_neg_integer()). +-spec(ack/2 :: (lstate(), non_neg_integer()) -> 'ok'). + +-spec(can_send/2 :: (lstate(), boolean()) -> boolean()). +-spec(register/1 :: (lstate()) -> 'ok'). +-spec(unregister/1 :: (lstate()) -> 'ok'). -endif. @@ -70,65 +75,95 @@ start_link() -> gen_server2:start_link(?MODULE, [], []). -make_token() -> make_token(undefined). -make_token(Pid) -> #token{pid = Pid, enabled = false}. +new(Pid) -> + %% this a 'call' to ensure that it is invoked at most once. + ok = gen_server:call(Pid, {new, self()}), + #lstate{pid = Pid, limited = false, blocked = false}. -is_enabled(#token{enabled = Enabled}) -> Enabled. +limit(L, PrefetchCount, UnackedCount) when PrefetchCount > 0 -> + ok = gen_server:call(L#lstate.pid, {limit, PrefetchCount, UnackedCount}), + L#lstate{limited = true}. -enable(#token{pid = Pid} = Token, Volume) -> - gen_server2:call(Pid, {enable, Token, self(), Volume}, infinity). +unlimit(L) -> + ok = gen_server:call(L#lstate.pid, unlimit), + L#lstate{limited = false}. -disable(#token{pid = Pid} = Token) -> - gen_server2:call(Pid, {disable, Token}, infinity). +block(L) -> + ok = gen_server:call(L#lstate.pid, block), + L#lstate{blocked = true}. -limit(Limiter, PrefetchCount) -> - maybe_call(Limiter, {limit, PrefetchCount, Limiter}, ok). +unblock(L) -> + ok = gen_server:call(L#lstate.pid, unblock), + L#lstate{blocked = false}. -%% Ask the limiter whether the queue can deliver a message without -%% breaching a limit. Note that we don't use maybe_call here in order -%% to avoid always going through with_exit_handler/2, even when the -%% limiter is disabled. -can_send(#token{pid = Pid, enabled = true}, QPid, AckRequired) -> - rabbit_misc:with_exit_handler( - fun () -> true end, - fun () -> - gen_server2:call(Pid, {can_send, QPid, AckRequired}, infinity) - end); -can_send(_, _, _) -> - true. +is_limited(#lstate{limited = Limited}) -> Limited. -%% Let the limiter know that the channel has received some acks from a -%% consumer -ack(Limiter, Count) -> maybe_cast(Limiter, {ack, Count}). +is_blocked(#lstate{blocked = Blocked}) -> Blocked. -register(Limiter, QPid) -> maybe_cast(Limiter, {register, QPid}). +is_active(L) -> is_limited(L) orelse is_blocked(L). -unregister(Limiter, QPid) -> maybe_cast(Limiter, {unregister, QPid}). +get_limit(#lstate{limited = false}) -> 0; +get_limit(L) -> gen_server:call(L#lstate.pid, get_limit). -get_limit(Limiter) -> - rabbit_misc:with_exit_handler( - fun () -> 0 end, - fun () -> maybe_call(Limiter, get_limit, 0) end). +ack(#lstate{limited = false}, _AckCount) -> ok; +ack(L, AckCount) -> gen_server:cast(L#lstate.pid, {ack, AckCount}). -block(Limiter) -> - maybe_call(Limiter, block, ok). +%% Ask the limiter whether the queue can deliver a message without +%% breaching a limit. +can_send(L, AckRequired) -> + case is_active(L) of + false -> true; + true -> rabbit_misc:with_exit_handler( + fun () -> true end, + fun () -> Msg = {can_send, self(), AckRequired}, + gen_server2:call(L#lstate.pid, Msg, infinity) + end) + end. -unblock(Limiter) -> - maybe_call(Limiter, {unblock, Limiter}, ok). +register(L) -> + case is_active(L) of + false -> ok; + true -> gen_server:cast(L#lstate.pid, {register, self()}) + end. -is_blocked(Limiter) -> - maybe_call(Limiter, is_blocked, false). +unregister(L) -> + case is_active(L) of + false -> ok; + true -> gen_server:cast(L#lstate.pid, {unregister, self()}) + end. %%---------------------------------------------------------------------------- %% gen_server callbacks %%---------------------------------------------------------------------------- -init([]) -> - {ok, #lim{}}. +init([]) -> {ok, #lim{}}. prioritise_call(get_limit, _From, _State) -> 9; prioritise_call(_Msg, _From, _State) -> 0. +handle_call({new, ChPid}, _From, State = #lim{ch_pid = undefined}) -> + {reply, ok, State#lim{ch_pid = ChPid}}; + +handle_call({limit, PrefetchCount, UnackedCount}, _From, State) -> + %% assertion + true = State#lim.prefetch_count == 0 orelse + State#lim.volume == UnackedCount, + {reply, ok, maybe_notify(State, State#lim{prefetch_count = PrefetchCount, + volume = UnackedCount})}; + +handle_call(unlimit, _From, State) -> + {reply, ok, maybe_notify(State, State#lim{prefetch_count = 0, + volume = 0})}; + +handle_call(block, _From, State) -> + {reply, ok, State#lim{blocked = true}}; + +handle_call(unblock, _From, State) -> + {reply, ok, maybe_notify(State, State#lim{blocked = false})}; + +handle_call(get_limit, _From, State = #lim{prefetch_count = PrefetchCount}) -> + {reply, PrefetchCount, State}; + handle_call({can_send, QPid, _AckRequired}, _From, State = #lim{blocked = true}) -> {reply, false, limit_queue(QPid, State)}; @@ -139,45 +174,13 @@ handle_call({can_send, QPid, AckRequired}, _From, false -> {reply, true, State#lim{volume = if AckRequired -> Volume + 1; true -> Volume end}} - end; - -handle_call(get_limit, _From, State = #lim{prefetch_count = PrefetchCount}) -> - {reply, PrefetchCount, State}; - -handle_call({limit, PrefetchCount, Token}, _From, State) -> - case maybe_notify(State, State#lim{prefetch_count = PrefetchCount}) of - {cont, State1} -> - {reply, ok, State1}; - {stop, State1} -> - {reply, {disabled, Token#token{enabled = false}}, State1} - end; - -handle_call(block, _From, State) -> - {reply, ok, State#lim{blocked = true}}; - -handle_call({unblock, Token}, _From, State) -> - case maybe_notify(State, State#lim{blocked = false}) of - {cont, State1} -> - {reply, ok, State1}; - {stop, State1} -> - {reply, {disabled, Token#token{enabled = false}}, State1} - end; - -handle_call(is_blocked, _From, State) -> - {reply, blocked(State), State}; - -handle_call({enable, Token, Channel, Volume}, _From, State) -> - {reply, Token#token{enabled = true}, - State#lim{ch_pid = Channel, volume = Volume}}; -handle_call({disable, Token}, _From, State) -> - {reply, Token#token{enabled = false}, State}. + end. handle_cast({ack, Count}, State = #lim{volume = Volume}) -> NewVolume = if Volume == 0 -> 0; true -> Volume - Count end, - {cont, State1} = maybe_notify(State, State#lim{volume = NewVolume}), - {noreply, State1}; + {noreply, maybe_notify(State, State#lim{volume = NewVolume})}; handle_cast({register, QPid}, State) -> {noreply, remember_queue(QPid, State)}; @@ -201,24 +204,10 @@ code_change(_, State, _) -> maybe_notify(OldState, NewState) -> case (limit_reached(OldState) orelse blocked(OldState)) andalso not (limit_reached(NewState) orelse blocked(NewState)) of - true -> NewState1 = notify_queues(NewState), - {case NewState1#lim.prefetch_count of - 0 -> stop; - _ -> cont - end, NewState1}; - false -> {cont, NewState} + true -> notify_queues(NewState); + false -> NewState end. -maybe_call(#token{pid = Pid, enabled = true}, Call, _Default) -> - gen_server2:call(Pid, Call, infinity); -maybe_call(_, _Call, Default) -> - Default. - -maybe_cast(#token{pid = Pid, enabled = true}, Cast) -> - gen_server2:cast(Pid, Cast); -maybe_cast(_, _Call) -> - ok. - limit_reached(#lim{prefetch_count = Limit, volume = Volume}) -> Limit =/= 0 andalso Volume >= Limit. -- cgit v1.2.1 From 7122822f79823b2e6fe80aa16c99aeb1af5d0e6b Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Tue, 19 Mar 2013 11:56:41 +0000 Subject: Perform route decoration differently --- src/rabbit_exchange.erl | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index c5a6309a..c2c7d947 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -320,34 +320,34 @@ route(#exchange{name = #resource{virtual_host = VHost, %% Optimisation [rabbit_misc:r(VHost, queue, RK) || RK <- lists:usort(RKs)]; {Decorators, _} -> - QNames = route1(Delivery, {[X], XName, []}), - lists:usort(decorate_route(Decorators, X, Delivery, QNames)) + lists:usort(route1(Delivery, Decorators, {[X], XName, []})) end. -decorate_route([], _X, _Delivery, QNames) -> +route1(_, _, {[], _, QNames}) -> QNames; -decorate_route(Decorators, X, Delivery, QNames) -> - QNames ++ - lists:append([Decorator:route(X, Delivery) || Decorator <- Decorators]). - -route1(_, {[], _, QNames}) -> - QNames; -route1(Delivery, {[X = #exchange{type = Type} | WorkList], SeenXs, QNames}) -> - DstNames = process_alternate( - X, ((type_to_module(Type)):route(X, Delivery))), - route1(Delivery, +route1(Delivery, Decorators, + {[X = #exchange{type = Type} | WorkList], SeenXs, QNames}) -> + ExchangeDests = (type_to_module(Type)):route(X, Delivery), + AlternateDest = process_alternate(X, ExchangeDests), + DecorateDests = process_decorators(Delivery, Decorators, X), + route1(Delivery, Decorators, lists:foldl(fun process_route/2, {WorkList, SeenXs, QNames}, - DstNames)). + AlternateDest ++ DecorateDests ++ ExchangeDests)). -process_alternate(#exchange{arguments = []}, Results) -> %% optimisation - Results; +process_alternate(#exchange{arguments = []}, _Results) -> %% optimisation + []; process_alternate(#exchange{name = XName, arguments = Args}, []) -> case rabbit_misc:r_arg(XName, exchange, Args, <<"alternate-exchange">>) of undefined -> []; AName -> [AName] end; -process_alternate(_X, Results) -> - Results. +process_alternate(_X, _Results) -> + []. + +process_decorators(_Delivery, [], _X) -> + []; +process_decorators(Delivery, Decorators, X) -> + lists:append([Decorator:route(X, Delivery) || Decorator <- Decorators]). process_route(#resource{kind = exchange} = XName, {_WorkList, XName, _QNames} = Acc) -> -- cgit v1.2.1 From f593d0719d3bfcbf61c607a40d8f5a9767bc3836 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Tue, 19 Mar 2013 12:49:57 +0000 Subject: Cosmetic --- src/rabbit_exchange.erl | 4 ++-- src/rabbit_registry.erl | 25 ++++++++----------------- 2 files changed, 10 insertions(+), 19 deletions(-) diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index d0504591..a4a88661 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -334,11 +334,11 @@ route1(_, _, {[], _, QNames}) -> route1(Delivery, Decorators, {[X = #exchange{type = Type} | WorkList], SeenXs, QNames}) -> ExchangeDests = (type_to_module(Type)):route(X, Delivery), - AlternateDest = process_alternate(X, ExchangeDests), + AlternateDests = process_alternate(X, ExchangeDests), DecorateDests = process_decorators(Delivery, Decorators, X), route1(Delivery, Decorators, lists:foldl(fun process_route/2, {WorkList, SeenXs, QNames}, - AlternateDest ++ DecorateDests ++ ExchangeDests)). + AlternateDests ++ DecorateDests ++ ExchangeDests)). process_alternate(#exchange{arguments = []}, _Results) -> %% optimisation []; diff --git a/src/rabbit_registry.erl b/src/rabbit_registry.erl index db07dcdb..41b82ba5 100644 --- a/src/rabbit_registry.erl +++ b/src/rabbit_registry.erl @@ -95,29 +95,20 @@ internal_unregister(Class, TypeName) -> true = ets:delete(?ETS_NAME, UnregArg), ok. -conditional_register({{exchange_decorator, Type}, ModuleName}) -> +conditional_register({{exchange_decorator, _Type}, ModuleName} = RegArg) -> case erlang:function_exported(ModuleName, route, 2) of - true -> - true = ets:insert(?ETS_NAME, {{exchange_decorator_route, Type}, - ModuleName}), - ok; - false -> - ok + true -> true = ets:insert(?ETS_NAME, RegArg); + false -> ok end; conditional_register(_) -> ok. -conditional_unregister({exchange_decorator, Type}) -> +conditional_unregister({exchange_decorator, Type} = UnregArg) -> case lookup_module(exchange_decorator, Type) of - {ok, ModuleName} -> - case erlang:function_exported(ModuleName, route, 2) of - true -> - true = ets:delete(?ETS_NAME, {exchange_decorator_route, - Type}), - ok; - false -> - ok - end; + {ok, ModName} -> case erlang:function_exported(ModName, route, 2) of + true -> true = ets:delete(?ETS_NAME, UnregArg); + false -> ok + end; {error, not_found} -> ok end; -- cgit v1.2.1 From 0a9d0322fa7de0f277730aa955edaada77861947 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 19 Mar 2013 13:38:27 +0000 Subject: limiter API revision - part 2/2 - queue-side API --- src/rabbit_amqqueue.erl | 29 +++++++------- src/rabbit_amqqueue_process.erl | 86 ++++++++++++++++++++-------------------- src/rabbit_channel.erl | 10 +++-- src/rabbit_limiter.erl | 87 ++++++++++++++++++++++++----------------- src/rabbit_tests.erl | 19 ++++----- 5 files changed, 123 insertions(+), 108 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index bd5de239..be8ab385 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -26,9 +26,9 @@ -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/3, basic_consume/7, basic_cancel/4]). --export([notify_sent/2, notify_sent_queue_down/1, unblock/2, flush_all/2]). --export([notify_down_all/2, limit_all/3]). +-export([basic_get/3, basic_consume/8, 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]). -export([on_node_down/1]). -export([update/2, store_queue/1, policy_changed/2]). -export([start_mirroring/1, stop_mirroring/1, sync_mirrors/1, @@ -144,19 +144,18 @@ -spec(ack/3 :: (pid(), [msg_id()], pid()) -> 'ok'). -spec(reject/4 :: (pid(), [msg_id()], boolean(), pid()) -> 'ok'). -spec(notify_down_all/2 :: (qpids(), pid()) -> ok_or_errors()). --spec(limit_all/3 :: (qpids(), pid(), rabbit_limiter:lstate()) -> - ok_or_errors()). +-spec(activate_limit_all/2 :: (qpids(), pid()) -> ok_or_errors()). -spec(basic_get/3 :: (rabbit_types:amqqueue(), pid(), boolean()) -> {'ok', non_neg_integer(), qmsg()} | 'empty'). --spec(basic_consume/7 :: - (rabbit_types:amqqueue(), boolean(), pid(), - rabbit_limiter:token(), rabbit_types:ctag(), boolean(), any()) +-spec(basic_consume/8 :: + (rabbit_types:amqqueue(), boolean(), pid(), pid(), boolean(), + rabbit_types:ctag(), boolean(), any()) -> rabbit_types:ok_or_error('exclusive_consume_unavailable')). -spec(basic_cancel/4 :: (rabbit_types:amqqueue(), pid(), rabbit_types:ctag(), any()) -> 'ok'). -spec(notify_sent/2 :: (pid(), pid()) -> 'ok'). -spec(notify_sent_queue_down/1 :: (pid()) -> 'ok'). --spec(unblock/2 :: (pid(), pid()) -> 'ok'). +-spec(resume/2 :: (pid(), pid()) -> 'ok'). -spec(flush_all/2 :: (qpids(), pid()) -> 'ok'). -spec(internal_delete/1 :: (name()) -> rabbit_types:ok_or_error('not_found') | @@ -538,16 +537,16 @@ notify_down_all(QPids, ChPid) -> Bads1 -> {error, Bads1} end. -limit_all(QPids, ChPid, Limiter) -> - delegate:cast(QPids, {limit, ChPid, Limiter}). +activate_limit_all(QPids, ChPid) -> + delegate:cast(QPids, {activate_limit, ChPid}). basic_get(#amqqueue{pid = QPid}, ChPid, NoAck) -> delegate:call(QPid, {basic_get, ChPid, NoAck}). -basic_consume(#amqqueue{pid = QPid}, NoAck, ChPid, Limiter, +basic_consume(#amqqueue{pid = QPid}, NoAck, ChPid, LimiterPid, LimiterActive, ConsumerTag, ExclusiveConsume, OkMsg) -> - delegate:call(QPid, {basic_consume, NoAck, ChPid, - Limiter, ConsumerTag, ExclusiveConsume, OkMsg}). + delegate:call(QPid, {basic_consume, NoAck, ChPid, LimiterPid, LimiterActive, + ConsumerTag, ExclusiveConsume, OkMsg}). basic_cancel(#amqqueue{pid = QPid}, ChPid, ConsumerTag, OkMsg) -> delegate:call(QPid, {basic_cancel, ChPid, ConsumerTag, OkMsg}). @@ -569,7 +568,7 @@ notify_sent_queue_down(QPid) -> erase({consumer_credit_to, QPid}), ok. -unblock(QPid, ChPid) -> delegate:cast(QPid, {unblock, ChPid}). +resume(QPid, ChPid) -> delegate:cast(QPid, {resume, ChPid}). flush_all(QPids, ChPid) -> delegate:cast(QPids, {flush, ChPid}). diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 0ddc9eba..37daa0df 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -68,7 +68,6 @@ consumer_count, blocked_consumers, limiter, - is_limit_active, unsent_message_count}). %%---------------------------------------------------------------------------- @@ -371,7 +370,6 @@ ch_record(ChPid) -> acktags = queue:new(), consumer_count = 0, blocked_consumers = queue:new(), - is_limit_active = false, limiter = undefined, unsent_message_count = 0}, put(Key, C), @@ -392,30 +390,18 @@ store_ch_record(C = #cr{ch_pid = ChPid}) -> put({ch, ChPid}, C), ok. -erase_ch_record(#cr{ch_pid = ChPid, - limiter = Limiter, - monitor_ref = MonitorRef}) -> - ok = rabbit_limiter:unregister(Limiter), +erase_ch_record(#cr{ch_pid = ChPid, monitor_ref = MonitorRef}) -> erlang:demonitor(MonitorRef), erase({ch, ChPid}), ok. -update_consumer_count(C = #cr{consumer_count = 0, limiter = Limiter}, +1) -> - ok = rabbit_limiter:register(Limiter), - update_ch_record(C#cr{consumer_count = 1}); -update_consumer_count(C = #cr{consumer_count = 1, limiter = Limiter}, -1) -> - ok = rabbit_limiter:unregister(Limiter), - update_ch_record(C#cr{consumer_count = 0, limiter = undefined}); -update_consumer_count(C = #cr{consumer_count = Count}, Delta) -> - update_ch_record(C#cr{consumer_count = Count + Delta}). - 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)}). -is_ch_blocked(#cr{unsent_message_count = Count, is_limit_active = Limited}) -> - Limited orelse Count >= ?UNSENT_MESSAGE_LIMIT. +is_ch_blocked(#cr{unsent_message_count = Count, limiter = Limiter}) -> + Count >= ?UNSENT_MESSAGE_LIMIT orelse rabbit_limiter:is_suspended(Limiter). ch_record_state_transition(OldCR, NewCR) -> case {is_ch_blocked(OldCR), is_ch_blocked(NewCR)} of @@ -439,18 +425,20 @@ deliver_msgs_to_consumers(DeliverFun, false, end. deliver_msg_to_consumer(DeliverFun, E = {ChPid, Consumer}, State) -> - C = ch_record(ChPid), + C = lookup_ch(ChPid), case is_ch_blocked(C) of true -> block_consumer(C, E), {false, State}; false -> case rabbit_limiter:can_send(C#cr.limiter, Consumer#consumer.ack_required) of - false -> block_consumer(C#cr{is_limit_active = true}, E), - {false, State}; - true -> AC1 = queue:in(E, State#q.active_consumers), - deliver_msg_to_consumer( - DeliverFun, Consumer, C, - State#q{active_consumers = AC1}) + {suspend, Limiter} -> + block_consumer(C#cr{limiter = Limiter}, E), + {false, State}; + {continue, Limiter} -> + AC1 = queue:in(E, State#q.active_consumers), + deliver_msg_to_consumer( + DeliverFun, Consumer, C#cr{limiter = Limiter}, + State#q{active_consumers = AC1}) end end. @@ -1127,15 +1115,25 @@ handle_call({basic_get, ChPid, NoAck}, _From, reply({ok, BQ:len(BQS), Msg}, State3) end; -handle_call({basic_consume, NoAck, ChPid, Limiter, +handle_call({basic_consume, NoAck, ChPid, LimiterPid, LimiterActive, ConsumerTag, ExclusiveConsume, OkMsg}, _From, State = #q{exclusive_consumer = Holder}) -> case check_exclusive_access(Holder, ExclusiveConsume, State) of in_use -> reply({error, exclusive_consume_unavailable}, State); ok -> - C = ch_record(ChPid), - update_consumer_count(C#cr{limiter = Limiter}, +1), + C = #cr{consumer_count = Count, + limiter = Limiter0} = ch_record(ChPid), + Limiter = case Limiter0 of + undefined -> rabbit_limiter:client(LimiterPid); + _ -> Limiter0 + end, + Limiter1 = case LimiterActive of + true -> rabbit_limiter:activate(Limiter); + false -> Limiter + end, + update_ch_record(C#cr{consumer_count = Count + 1, + limiter = Limiter1}), Consumer = #consumer{tag = ConsumerTag, ack_required = not NoAck}, ExclusiveConsumer = if ExclusiveConsume -> {ChPid, ConsumerTag}; @@ -1156,10 +1154,18 @@ handle_call({basic_cancel, ChPid, ConsumerTag, OkMsg}, From, case lookup_ch(ChPid) of not_found -> reply(ok, State); - C = #cr{blocked_consumers = Blocked} -> + C = #cr{consumer_count = Count, + limiter = Limiter, + blocked_consumers = Blocked} -> emit_consumer_deleted(ChPid, ConsumerTag, qname(State)), Blocked1 = remove_consumer(ChPid, ConsumerTag, Blocked), - update_consumer_count(C#cr{blocked_consumers = Blocked1}, -1), + Limiter1 = case Count of + 1 -> rabbit_limiter:forget(Limiter); + _ -> Limiter + end, + update_ch_record(C#cr{consumer_count = Count - 1, + limiter = Limiter1, + blocked_consumers = Blocked1}), State1 = State#q{ exclusive_consumer = case Holder of {ChPid, ConsumerTag} -> none; @@ -1287,10 +1293,13 @@ handle_cast({reject, AckTags, false, ChPid}, State) -> handle_cast(delete_immediately, State) -> stop(State); -handle_cast({unblock, ChPid}, State) -> +handle_cast({resume, ChPid}, State) -> noreply( possibly_unblock(State, ChPid, - fun (C) -> C#cr{is_limit_active = false} end)); + fun (C = #cr{limiter = undefined}) -> C; + (C = #cr{limiter = Limiter}) -> + C#cr{limiter = rabbit_limiter:resume(Limiter)} + end)); handle_cast({notify_sent, ChPid, Credit}, State) -> noreply( @@ -1299,20 +1308,13 @@ handle_cast({notify_sent, ChPid, Credit}, State) -> C#cr{unsent_message_count = Count - Credit} end)); -handle_cast({limit, ChPid, Limiter}, State) -> +handle_cast({activate_limit, ChPid}, State) -> noreply( possibly_unblock( State, ChPid, - fun (C = #cr{consumer_count = ConsumerCount, - limiter = OldLimiter, - is_limit_active = OldLimited}) -> - case (ConsumerCount =/= 0 andalso - not rabbit_limiter:is_active(OldLimiter)) of - true -> ok = rabbit_limiter:register(Limiter); - false -> ok - end, - Limited = OldLimited andalso rabbit_limiter:is_active(Limiter), - C#cr{limiter = Limiter, is_limit_active = Limited} + fun (C = #cr{limiter = Limiter, consumer_count = Count}) -> + true = Limiter =/= undefined andalso Count =/= 0, %% assertion + C#cr{limiter = rabbit_limiter:activate(Limiter)} end)); handle_cast({flush, ChPid}, State) -> diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index cda5747a..005200f8 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -81,8 +81,8 @@ -spec(start_link/11 :: (channel_number(), pid(), pid(), pid(), string(), rabbit_types:protocol(), rabbit_types:user(), rabbit_types:vhost(), - rabbit_framing:amqp_table(), - pid(), pid()) -> rabbit_types:ok_pid_or_error()). + rabbit_framing:amqp_table(), pid(), pid()) -> + rabbit_types:ok_pid_or_error()). -spec(do/2 :: (pid(), rabbit_framing:amqp_method_record()) -> 'ok'). -spec(do/3 :: (pid(), rabbit_framing:amqp_method_record(), rabbit_types:maybe(rabbit_types:content())) -> 'ok'). @@ -728,7 +728,9 @@ handle_method(#'basic.consume'{queue = QueueNameBin, QueueName, ConnPid, fun (Q) -> {rabbit_amqqueue:basic_consume( - Q, NoAck, self(), Limiter, + Q, NoAck, self(), + rabbit_limiter:pid(Limiter), + rabbit_limiter:is_active(Limiter), ActualConsumerTag, ExclusiveConsume, ok_msg(NoWait, #'basic.consume_ok'{ consumer_tag = ActualConsumerTag})), @@ -1326,7 +1328,7 @@ maybe_limit_queues(OldLimiter, NewLimiter, State) -> case ((not rabbit_limiter:is_active(OldLimiter)) andalso rabbit_limiter:is_active(NewLimiter)) of true -> Queues = consumer_queues(State#ch.consumer_mapping), - rabbit_amqqueue:limit_all(Queues, self(), NewLimiter); + rabbit_amqqueue:activate_limit_all(Queues, self()); false -> ok end, State. diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index ae656328..235c69c2 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -19,24 +19,27 @@ -behaviour(gen_server2). -export([start_link/0]). +%% channel API -export([new/1, limit/3, unlimit/1, block/1, unblock/1, - is_limited/1, is_blocked/1, is_active/1, get_limit/1, ack/2]). --export([can_send/2, register/1, unregister/1]). - + is_limited/1, is_blocked/1, is_active/1, get_limit/1, ack/2, pid/1]). +%% queue API +-export([client/1, activate/1, can_send/2, resume/1, forget/1, is_suspended/1]). +%% callbacks -export([init/1, terminate/2, code_change/3, handle_call/3, handle_cast/2, handle_info/2, prioritise_call/3]). %%---------------------------------------------------------------------------- -record(lstate, {pid, limited, blocked}). +-record(qstate, {pid, state}). -ifdef(use_specs). --export_type([lstate/0]). - --opaque(lstate() :: #lstate {pid :: pid(), - limited :: boolean(), - blocked :: boolean()}). +-type(lstate() :: #lstate{pid :: pid(), + limited :: boolean(), + blocked :: boolean()}). +-type(qstate() :: #qstate{pid :: pid(), + state :: 'dormant' | 'active' | 'suspended'}). -spec(start_link/0 :: () -> rabbit_types:ok_pid_or_error()). -spec(new/1 :: (pid()) -> lstate()). @@ -51,10 +54,15 @@ -spec(is_active/1 :: (lstate()) -> boolean()). -spec(get_limit/1 :: (lstate()) -> non_neg_integer()). -spec(ack/2 :: (lstate(), non_neg_integer()) -> 'ok'). +-spec(pid/1 :: (lstate()) -> pid()). --spec(can_send/2 :: (lstate(), boolean()) -> boolean()). --spec(register/1 :: (lstate()) -> 'ok'). --spec(unregister/1 :: (lstate()) -> 'ok'). +-spec(client/1 :: (pid()) -> qstate()). +-spec(activate/1 :: (qstate()) -> qstate()). +-spec(can_send/2 :: (qstate(), boolean()) -> + {'continue' | 'suspend', qstate()}). +-spec(resume/1 :: (qstate()) -> qstate()). +-spec(forget/1 :: (qstate()) -> undefined). +-spec(is_suspended/1 :: (qstate()) -> boolean()). -endif. @@ -108,29 +116,37 @@ get_limit(L) -> gen_server:call(L#lstate.pid, get_limit). ack(#lstate{limited = false}, _AckCount) -> ok; ack(L, AckCount) -> gen_server:cast(L#lstate.pid, {ack, AckCount}). -%% Ask the limiter whether the queue can deliver a message without -%% breaching a limit. -can_send(L, AckRequired) -> - case is_active(L) of - false -> true; - true -> rabbit_misc:with_exit_handler( - fun () -> true end, - fun () -> Msg = {can_send, self(), AckRequired}, - gen_server2:call(L#lstate.pid, Msg, infinity) - end) - end. +pid(#lstate{pid = Pid}) -> Pid. -register(L) -> - case is_active(L) of - false -> ok; - true -> gen_server:cast(L#lstate.pid, {register, self()}) - end. +client(Pid) -> #qstate{pid = Pid, state = dormant}. -unregister(L) -> - case is_active(L) of - false -> ok; - true -> gen_server:cast(L#lstate.pid, {unregister, self()}) - end. +activate(L = #qstate{state = dormant}) -> + ok = gen_server:cast(L#qstate.pid, {register, self()}), + L#qstate{state = active}; +activate(L) -> L. + +%% Ask the limiter whether the queue can deliver a message without +%% breaching a limit. +can_send(L = #qstate{state = active}, AckRequired) -> + rabbit_misc:with_exit_handler( + fun () -> {continue, L} end, + fun () -> Msg = {can_send, self(), AckRequired}, + case gen_server2:call(L#qstate.pid, Msg, infinity) of + true -> {continue, L}; + false -> {suspend, L#qstate{state = suspended}} + end + end); +can_send(L, _AckRequired) -> {continue, L}. + +resume(L) -> L#qstate{state = active}. + +forget(#qstate{state = dormant}) -> undefined; +forget(L) -> + ok = gen_server:cast(L#qstate.pid, {unregister, self()}), + undefined. + +is_suspended(#qstate{state = suspended}) -> true; +is_suspended(#qstate{}) -> false. %%---------------------------------------------------------------------------- %% gen_server callbacks @@ -220,10 +236,9 @@ remember_queue(QPid, State = #lim{queues = Queues}) -> true -> State end. -forget_queue(QPid, State = #lim{ch_pid = ChPid, queues = Queues}) -> +forget_queue(QPid, State = #lim{queues = Queues}) -> case orddict:find(QPid, Queues) of {ok, {MRef, _}} -> true = erlang:demonitor(MRef), - ok = rabbit_amqqueue:unblock(QPid, ChPid), State#lim{queues = orddict:erase(QPid, Queues)}; error -> State end. @@ -240,13 +255,13 @@ notify_queues(State = #lim{ch_pid = ChPid, queues = Queues}) -> end, {[], Queues}, Queues), case length(QList) of 0 -> ok; - 1 -> ok = rabbit_amqqueue:unblock(hd(QList), ChPid); %% common case + 1 -> ok = rabbit_amqqueue:resume(hd(QList), ChPid); %% common case L -> %% We randomly vary the position of queues in the list, %% thus ensuring that each queue has an equal chance of %% being notified first. {L1, L2} = lists:split(random:uniform(L), QList), - [[ok = rabbit_amqqueue:unblock(Q, ChPid) || Q <- L3] + [[ok = rabbit_amqqueue:resume(Q, ChPid) || Q <- L3] || L3 <- [L2, L1]], ok end, diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 1188c554..31a56ac8 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -1100,20 +1100,14 @@ test_policy_validation() -> test_server_status() -> %% create a few things so there is some useful information to list - Writer = spawn(fun test_writer/0), - {ok, Ch} = rabbit_channel:start_link( - 1, self(), Writer, self(), "", rabbit_framing_amqp_0_9_1, - user(<<"user">>), <<"/">>, [], self(), - rabbit_limiter:make_token(self())), + {_Writer, Limiter, Ch} = test_channel(), [Q, Q2] = [Queue || Name <- [<<"foo">>, <<"bar">>], {new, Queue = #amqqueue{}} <- [rabbit_amqqueue:declare( rabbit_misc:r(<<"/">>, queue, Name), false, false, [], none)]], - ok = rabbit_amqqueue:basic_consume( - Q, true, Ch, rabbit_limiter:make_token(), - <<"ctag">>, true, undefined), + Q, true, Ch, Limiter, false, <<"ctag">>, true, undefined), %% list queues ok = info_action(list_queues, rabbit_amqqueue:info_keys(), true), @@ -1191,8 +1185,6 @@ find_listener() -> N =:= node()], {H, P}. -test_writer() -> test_writer(none). - test_writer(Pid) -> receive {'$gen_call', From, flush} -> gen_server:reply(From, ok), @@ -1202,13 +1194,18 @@ test_writer(Pid) -> shutdown -> ok end. -test_spawn() -> +test_channel() -> Me = self(), Writer = spawn(fun () -> test_writer(Me) end), + {ok, Limiter} = rabbit_limiter:start_link(), {ok, Ch} = rabbit_channel:start_link( 1, Me, Writer, Me, "", rabbit_framing_amqp_0_9_1, user(<<"guest">>), <<"/">>, [], Me, rabbit_limiter:make_token(self())), + {Writer, Limiter, Ch}. + +test_spawn() -> + {Writer, _Limiter, Ch} = test_channel(), ok = rabbit_channel:do(Ch, #'channel.open'{}), receive #'channel.open_ok'{} -> ok after ?TIMEOUT -> throw(failed_to_receive_channel_open_ok) -- cgit v1.2.1 From 96350c751fe4f112783dcb1497a7e48aedae6f9f Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 19 Mar 2013 14:08:50 +0000 Subject: correctness++ --- src/rabbit_tests.erl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 31a56ac8..b67be544 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -1200,8 +1200,7 @@ test_channel() -> {ok, Limiter} = rabbit_limiter:start_link(), {ok, Ch} = rabbit_channel:start_link( 1, Me, Writer, Me, "", rabbit_framing_amqp_0_9_1, - user(<<"guest">>), <<"/">>, [], Me, - rabbit_limiter:make_token(self())), + user(<<"guest">>), <<"/">>, [], Me, Limiter), {Writer, Limiter, Ch}. test_spawn() -> -- cgit v1.2.1 From f9edb57bd0b3ac55471f4cc9af74024896bab265 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 19 Mar 2013 14:45:32 +0000 Subject: niggles-- --- src/rabbit_amqqueue_process.erl | 29 ++++++++++++----------------- src/rabbit_channel.erl | 5 +++++ 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 37daa0df..d9264736 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -401,14 +401,9 @@ block_consumer(C = #cr{blocked_consumers = Blocked}, QEntry) -> update_ch_record(C#cr{blocked_consumers = queue:in(QEntry, Blocked)}). is_ch_blocked(#cr{unsent_message_count = Count, limiter = Limiter}) -> - Count >= ?UNSENT_MESSAGE_LIMIT orelse rabbit_limiter:is_suspended(Limiter). - -ch_record_state_transition(OldCR, NewCR) -> - case {is_ch_blocked(OldCR), is_ch_blocked(NewCR)} of - {true, false} -> unblock; - {false, true} -> block; - {_, _} -> ok - end. + Count >= ?UNSENT_MESSAGE_LIMIT + orelse (Limiter =/= undefined andalso + rabbit_limiter:is_suspended(Limiter)). deliver_msgs_to_consumers(_DeliverFun, true, State) -> {true, State}; @@ -629,15 +624,15 @@ possibly_unblock(State, ChPid, Update) -> State; C -> C1 = Update(C), - case ch_record_state_transition(C, C1) of - ok -> update_ch_record(C1), - State; - unblock -> #cr{blocked_consumers = Consumers} = C1, - update_ch_record( - C1#cr{blocked_consumers = queue:new()}), - AC1 = queue:join(State#q.active_consumers, - Consumers), - run_message_queue(State#q{active_consumers = AC1}) + case is_ch_blocked(C) andalso not is_ch_blocked(C1) of + false -> update_ch_record(C1), + State; + true -> #cr{blocked_consumers = Consumers} = C1, + update_ch_record( + C1#cr{blocked_consumers = queue:new()}), + AC1 = queue:join(State#q.active_consumers, + Consumers), + run_message_queue(State#q{active_consumers = AC1}) end end. diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 005200f8..eb248a4c 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -805,6 +805,11 @@ handle_method(#'basic.qos'{prefetch_size = Size}, _, _State) when Size /= 0 -> rabbit_misc:protocol_error(not_implemented, "prefetch_size!=0 (~w)", [Size]); +handle_method(#'basic.qos'{prefetch_count = 0}, _, + State = #ch{limiter = Limiter}) -> + Limiter1 = rabbit_limiter:unlimit(Limiter), + {reply, #'basic.qos_ok'{}, State#ch{limiter = Limiter1}}; + handle_method(#'basic.qos'{prefetch_count = PrefetchCount}, _, State = #ch{limiter = Limiter, unacked_message_q = UAMQ}) -> Limiter1 = rabbit_limiter:limit(Limiter, PrefetchCount, queue:len(UAMQ)), -- cgit v1.2.1 From feb1d94405e7cba7d70ae54b2e308e99158ee261 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 19 Mar 2013 14:57:36 +0000 Subject: These log messages do not necessarily make their implications clear enough; I can imagine people's eyes glazing over them. Try to deal with that. --- src/rabbit_alarm.erl | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/rabbit_alarm.erl b/src/rabbit_alarm.erl index 25357b6e..b6b2d4b7 100644 --- a/src/rabbit_alarm.erl +++ b/src/rabbit_alarm.erl @@ -209,11 +209,19 @@ internal_register(Pid, {M, F, A} = HighMemMFA, State#alarms{alertees = NewAlertees}. handle_set_alarm({{resource_limit, Source, Node}, []}, State) -> - rabbit_log:warning("~s resource limit alarm set on node ~p~n", - [Source, Node]), + rabbit_log:warning( + "~s resource limit alarm set on node ~p.~n~n" + "**********************************************************~n" + "*** Publishers will be blocked until this alarm clears ***~n" + "**********************************************************~n", + [Source, Node]), {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"), + 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) -> rabbit_log:warning("alarm '~p' set~n", [Alarm]), -- cgit v1.2.1 From 7045b90b2e9a2be678b6781db6815f20af120e87 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 19 Mar 2013 17:15:07 +0000 Subject: First pass at autohealing. --- src/rabbit_node_monitor.erl | 132 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 121 insertions(+), 11 deletions(-) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index de53b7f0..aac2bf84 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -33,7 +33,7 @@ -define(SERVER, ?MODULE). -define(RABBIT_UP_RPC_TIMEOUT, 2000). --record(state, {monitors, partitions, subscribers}). +-record(state, {monitors, partitions, subscribers, autoheal}). %%---------------------------------------------------------------------------- @@ -196,7 +196,8 @@ init([]) -> {ok, _} = mnesia:subscribe(system), {ok, #state{monitors = pmon:new(), subscribers = pmon:new(), - partitions = []}}. + partitions = [], + autoheal = not_healing}}. handle_call(partitions, _From, State = #state{partitions = Partitions}) -> {reply, {node(), Partitions}, State}; @@ -268,9 +269,60 @@ handle_info({mnesia_system_event, monitors = pmon:monitor({rabbit, Node}, Monitors)} end, ok = handle_live_rabbit(Node), + State2 = case application:get_env(rabbit, cluster_partition_handling) of + {ok, autoheal} -> case ratio() of + 1.0 -> autoheal(State1); + _ -> State1 + end; + _ -> State1 + end, Partitions1 = ordsets:to_list( ordsets:add_element(Node, ordsets:from_list(Partitions))), - {noreply, State1#state{partitions = Partitions1}}; + {noreply, State2#state{partitions = Partitions1}}; + +handle_info({autoheal_request_winner, Node}, + State = #state{autoheal = {wait_for_winner_reqs,[Node], Notify}}) -> + %% TODO actually do something sensible to figure out who the winner is + Winner = self(), + [{?MODULE, N} ! {autoheal_winner, Winner} || N <- Notify], + {noreply, State#state{autoheal = wait_for_winner}}; + +handle_info({autoheal_request_winner, Node}, + State = #state{autoheal = {wait_for_winner_reqs, Nodes, Notify}}) -> + {noreply, State#state{autoheal = {wait_for_winner_reqs, + Nodes -- [Node], Notify}}}; + +handle_info({autoheal_winner, Winner}, + State = #state{autoheal = wait_for_winner, + partitions = Partitions}) -> + Node = node(Winner), + case lists:member(Node, Partitions) of + false -> case node() of + Node -> {noreply, + State#state{autoheal = {wait_for, Partitions, + Partitions}}}; + _ -> {noreply, State#state{autoheal = not_healing}} + end; + true -> autoheal_restart(Winner), + {noreply, State} + end; + +handle_info({autoheal_winner, _Winner}, State) -> + %% ignore, we already cancelled the autoheal process + {noreply, State}; + +handle_info({autoheal_node_stopped, Node}, + State = #state{autoheal = {wait_for, [Node], Notify}}) -> + [{rabbit_outside_app_process, N} ! autoheal_safe_to_start || N <- Notify], + {noreply, State#state{autoheal = not_healing}}; + +handle_info({autoheal_node_stopped, Node}, + State = #state{autoheal = {wait_for, WaitFor, Notify}}) -> + {noreply, State#state{autoheal = {wait_for, WaitFor -- [Node], Notify}}}; + +handle_info({autoheal_node_stopped, _Node}, State) -> + %% ignore, we already cancelled the autoheal process + {noreply, State}; handle_info(_Info, State) -> {noreply, State}. @@ -301,6 +353,8 @@ handle_dead_rabbit(Node) -> end; {ok, ignore} -> ok; + {ok, autoheal} -> + ok; {ok, Term} -> rabbit_log:warning("cluster_partition_handling ~p unrecognised, " "assuming 'ignore'~n", [Term]), @@ -308,8 +362,8 @@ handle_dead_rabbit(Node) -> end, ok. -majority() -> - length(alive_nodes()) / length(rabbit_mnesia:cluster_nodes(all)) > 0.5. +majority() -> ratio() > 0.5. +ratio() -> length(alive_nodes()) / length(rabbit_mnesia:cluster_nodes(all)). %% mnesia:system_info(db_nodes) (and hence %% rabbit_mnesia:cluster_nodes(running)) does not give reliable results @@ -322,15 +376,20 @@ await_cluster_recovery() -> rabbit_log:warning("Cluster minority status detected - awaiting recovery~n", []), Nodes = rabbit_mnesia:cluster_nodes(all), + run_outside_applications(fun () -> + rabbit:stop(), + wait_for_cluster_recovery(Nodes) + end). + +run_outside_applications(Fun) -> spawn(fun () -> %% If our group leader is inside an application we are about %% to stop, application:stop/1 does not return. group_leader(whereis(init), self()), - %% Ensure only one restarting process at a time, will + %% Ensure only one such process at a time, will %% exit(badarg) (harmlessly) if one is already running - register(rabbit_restarting_process, self()), - rabbit:stop(), - wait_for_cluster_recovery(Nodes) + register(rabbit_outside_app_process, self()), + Fun() end). wait_for_cluster_recovery(Nodes) -> @@ -340,7 +399,54 @@ wait_for_cluster_recovery(Nodes) -> wait_for_cluster_recovery(Nodes) end. -handle_dead_rabbit_state(State = #state{partitions = Partitions}) -> +%% In order to autoheal we want to: +%% +%% * Find the winning partition +%% * Stop all nodes in other partitions +%% * Wait for them all to be stopped +%% * Start them again +%% +%% To keep things simple, we assume all nodes are up. We don't start +%% unless all nodes are up, and if a node goes down we abandon the +%% whole process. To further keep things simple we also defer the +%% decision as to the winning node to the "leader" - arbitrarily +%% selected as the first node in the cluster. +%% +%% To coordinate the restarting nodes we pick a special node from the +%% winning partition - the "winner". Restarting nodes then stop, tell +%% the winner they have done so, and wait for it to tell them it is +%% safe to start again. +%% +%% The winner and the leader are not necessarily the same node! Since +%% the leader may end up restarting, we also make sure that it does +%% not announce its decision (and thus cue other nodes to restart) +%% until it has seen a request from every node. +autoheal(State) -> + [Leader | _] = All = lists:usort(rabbit_mnesia:cluster_nodes(all)), + {?MODULE, Leader} ! {autoheal_request_winner, node()}, + State#state{autoheal = case node() of + Leader -> {wait_for_winner_reqs, All, All}; + _ -> wait_for_winner + end}. + +autoheal_restart(Winner) -> + rabbit_log:warning( + "Cluster partition healed; we were selected to restart~n", []), + run_outside_applications( + fun () -> + MRef = erlang:monitor(process, Winner), + rabbit:stop(), + Winner ! {autoheal_node_stopped, node()}, + receive + {'DOWN', MRef, process, Winner, _Reason} -> ok; + autoheal_safe_to_start -> ok + end, + erlang:demonitor(MRef, [flush]), + rabbit:start() + end). + +handle_dead_rabbit_state(State = #state{partitions = Partitions, + autoheal = Autoheal}) -> %% If we have been partitioned, and we are now in the only remaining %% partition, we no longer care about partitions - forget them. Note %% that we do not attempt to deal with individual (other) partitions @@ -350,7 +456,11 @@ handle_dead_rabbit_state(State = #state{partitions = Partitions}) -> [] -> []; _ -> Partitions end, - State#state{partitions = Partitions1}. + State#state{partitions = Partitions1, + autoheal = case Autoheal of + {wait_for, _Nodes, _Notify} -> Autoheal; + _ -> not_healing + end}. handle_live_rabbit(Node) -> ok = rabbit_alarm:on_node_up(Node), -- cgit v1.2.1 From 00eb6aefa5f0bdacad56aeacd53420a86a479350 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 19 Mar 2013 17:41:53 +0000 Subject: A node counts as down if it is not running rabbit in this case. --- src/rabbit_node_monitor.erl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index de53b7f0..3872f3df 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -318,6 +318,9 @@ alive_nodes() -> Nodes = rabbit_mnesia:cluster_nodes(all), [N || N <- Nodes, pong =:= net_adm:ping(N)]. +alive_rabbit_nodes() -> + [N || N <- alive_nodes(), rabbit_nodes:is_running(N, rabbit)]. + await_cluster_recovery() -> rabbit_log:warning("Cluster minority status detected - awaiting recovery~n", []), @@ -346,7 +349,7 @@ handle_dead_rabbit_state(State = #state{partitions = Partitions}) -> %% that we do not attempt to deal with individual (other) partitions %% going away. It's only safe to forget anything about partitions when %% there are no partitions. - Partitions1 = case Partitions -- (Partitions -- alive_nodes()) of + Partitions1 = case Partitions -- (Partitions -- alive_rabbit_nodes()) of [] -> []; _ -> Partitions end, -- cgit v1.2.1 From 1f4d7608d714a376d57b5d3144ce682c6bcbe695 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 19 Mar 2013 20:21:50 +0000 Subject: some documentation --- src/rabbit_channel.erl | 15 +++++++++- src/rabbit_limiter.erl | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 1 deletion(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index eb248a4c..17bf5c83 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -1089,6 +1089,17 @@ handle_method(#'channel.flow'{active = false}, _, false -> Limiter1 = rabbit_limiter:block(Limiter), State1 = maybe_limit_queues(Limiter, Limiter1, State#ch{limiter = Limiter1}), + %% The semantics of channel.flow{active=false} + %% require that no messages are delivered after the + %% channel.flow_ok has been sent. We accomplish that + %% by "flushing" all messages in flight from the + %% consumer queues to us. To do this we tell all the + %% queues to invoke rabbit_channel:flushed/2, which + %% will send us a {flushed, ...} message that appears + %% *after* all the {deliver, ...} messages. We keep + %% track of all the QPids thus asked, and once all of + %% them have responded (or died) we send the + %% channel.flow_ok. QPids = consumer_queues(Consumers), ok = rabbit_amqqueue:flush_all(QPids, self()), {noreply, maybe_send_flow_ok( @@ -1347,7 +1358,9 @@ consumer_queues(Consumers) -> %% messages sent in a response to a basic.get (identified by their %% 'none' consumer tag) notify_limiter(Limiter, Acked) -> - case rabbit_limiter:is_limited(Limiter) of + %% optimisation: avoid the potentially expensive 'foldl' in the + %% common case. + case rabbit_limiter:is_limited(Limiter) of false -> ok; true -> case lists:foldl(fun ({_, none, _}, Acc) -> Acc; ({_, _, _}, Acc) -> Acc + 1 diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index 235c69c2..4059fdb0 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -14,6 +14,85 @@ %% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. %% +%% The purpose of the limiter is to stem the flow of messages from +%% queues to channels, in order to act upon various protocol-level +%% flow control mechanisms, specifically AMQP's basic.qos +%% prefetch_count and channel.flow. +%% +%% Each channel has an associated limiter process, created with +%% start_link/1, which it passes to queues on consumer creation with +%% rabbit_amqqueue:basic_consume/8. This process holds state that is, +%% in effect, shared between the channel and all queues from which the +%% channel is consuming. Essentially all these queues are competing +%% for access to a single, limited resource - the ability to deliver +%% messages via the channel - and it is the job of the limiter process +%% to mediate that access. +%% +%% The limiter process is separate from the channel process for two +%% reasons: separation of concerns, and efficiency. Channels can get +%% very busy, particularly if they are also dealing with publishes. +%% With a separate limiter process all the aforementioned access +%% mediation can take place without touching the channel. +%% +%% For efficiency, both the channel and the queues keep some local +%% state, initialised from the limiter pid with new/1 and client/1, +%% respectively. In particular this allows them to avoid any +%% interaction with the limiter process when it is 'inactive', i.e. no +%% protocol-level flow control is taking place. +%% +%% This optimisation does come at the cost of some complexity though: +%% when a limiter becomes active, the channel needs to inform all its +%% consumer queues of this change in status. It does this by invoking +%% rabbit_amqqueue:activate_limit_all/2. Note that there is no inverse +%% transition, i.e. once a queue has been told about an active +%% limiter, it is not subsequently told when that limiter becomes +%% inactive. In practice it is rare for that to happen, though we +%% could optimise this case in the future. +%% +%% The interactions with the limiter are as follows: +%% +%% 1. Channels tell the limiter about basic.qos prefetch counts - +%% that's what the limit/3, unlimit/1, is_limited/1, get_limit/1 +%% API functions are about - and channel.flow blocking - that's +%% what block/1, unblock/1 and is_blocked/1 are for. +%% +%% 2. Queues register with the limiter - this happens as part of +%% activate/1. +%% +%% 4. The limiter process maintains an internal counter of 'messages +%% sent but not yet acknowledged', called the 'volume'. +%% +%% 5. Queues ask the limiter for permission (with can_send/2) whenever +%% they want to deliver a message to a channel. The limiter checks +%% whether a) the channel isn't blocked by channel.flow, and b) the +%% volume has not yet reached the prefetch limit. If so it +%% increments the volume and tells the queue to proceed. Otherwise +%% it marks the queue as requiring notification (see below) and +%% tells the queue not to proceed. +%% +%% 6. A queue that has told to proceed (by the return value of +%% can_send/2) sends the message to the channel. Conversely, a +%% queue that has been told not to proceed, will not attempt to +%% deliver that message, or any future messages, to the +%% channel. This is accomplished by can_send/2 capturing the +%% outcome in the local state, where it can be accessed with +%% is_suspended/1. +%% +%% 7. When a channel receives an ack it tells the limiter (via ack/2) +%% how many messages were ack'ed. The limiter process decrements +%% the volume and if it falls below the prefetch_count then it +%% notifies (through rabbit_amqqueue:resume/2) all the queues +%% requiring notification, i.e. all those that had a can_send/2 +%% request denied. +%% +%% 8. Upon receipt of such a notification, queues resume delivery to +%% the channel, i.e. they will once again start asking limiter, as +%% described in (5). +%% +%% 9. When a queues has no more consumers associated with a particular +%% channel, it unregisters with the limiter and forgets about it - +%% all via forget/1. + -module(rabbit_limiter). -behaviour(gen_server2). -- cgit v1.2.1 From 964f43befe1a48e5256ee1bfb77bc6b7422e4c05 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 20 Mar 2013 11:08:17 +0000 Subject: "source" is also an architecture - and a very important one. --- packaging/debs/apt-repository/distributions | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/debs/apt-repository/distributions b/packaging/debs/apt-repository/distributions index 61fd778a..75b9fe46 100644 --- a/packaging/debs/apt-repository/distributions +++ b/packaging/debs/apt-repository/distributions @@ -2,6 +2,6 @@ Origin: RabbitMQ Label: RabbitMQ Repository for Debian / Ubuntu etc Suite: testing Codename: kitten -Architectures: AVR32 alpha amd64 arm armel armhf hppa hurd-i386 i386 ia64 kfreebsd-amd64 kfreebsd-i386 m32 m68k mips mipsel netbsd-alpha netbsd-i386 powerpc s390 s390x sh sparc +Architectures: AVR32 alpha amd64 arm armel armhf hppa hurd-i386 i386 ia64 kfreebsd-amd64 kfreebsd-i386 m32 m68k mips mipsel netbsd-alpha netbsd-i386 powerpc s390 s390x sh sparc source Components: main Description: RabbitMQ Repository for Debian / Ubuntu etc -- cgit v1.2.1 From d9c5b6057c48fbf971e8fd5dd07ba9ff2c5e4c26 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 20 Mar 2013 15:05:58 +0000 Subject: always hold a valid limiter in queue's channel records This is less fiddly, but does mean we have to pass the limiter pid in basic_get. --- src/rabbit_amqqueue.erl | 8 ++++---- src/rabbit_amqqueue_process.erl | 35 ++++++++++++++--------------------- src/rabbit_channel.erl | 5 ++++- src/rabbit_limiter.erl | 34 +++++++++++++++++++++------------- src/rabbit_tests.erl | 7 +++++-- 5 files changed, 48 insertions(+), 41 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index be8ab385..3f0a7f9c 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/3, basic_consume/8, basic_cancel/4]). +-export([basic_get/4, basic_consume/8, 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]). -export([on_node_down/1]). @@ -145,7 +145,7 @@ -spec(reject/4 :: (pid(), [msg_id()], boolean(), pid()) -> 'ok'). -spec(notify_down_all/2 :: (qpids(), pid()) -> ok_or_errors()). -spec(activate_limit_all/2 :: (qpids(), pid()) -> ok_or_errors()). --spec(basic_get/3 :: (rabbit_types:amqqueue(), pid(), boolean()) -> +-spec(basic_get/4 :: (rabbit_types:amqqueue(), pid(), boolean(), pid()) -> {'ok', non_neg_integer(), qmsg()} | 'empty'). -spec(basic_consume/8 :: (rabbit_types:amqqueue(), boolean(), pid(), pid(), boolean(), @@ -540,8 +540,8 @@ notify_down_all(QPids, ChPid) -> activate_limit_all(QPids, ChPid) -> delegate:cast(QPids, {activate_limit, ChPid}). -basic_get(#amqqueue{pid = QPid}, ChPid, NoAck) -> - delegate:call(QPid, {basic_get, ChPid, NoAck}). +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, OkMsg) -> diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index d9264736..6fc79dca 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -361,16 +361,17 @@ lookup_ch(ChPid) -> C -> C end. -ch_record(ChPid) -> +ch_record(ChPid, LimiterPid) -> Key = {ch, ChPid}, case get(Key) of undefined -> MonitorRef = erlang:monitor(process, ChPid), + Limiter = rabbit_limiter:client(LimiterPid), C = #cr{ch_pid = ChPid, monitor_ref = MonitorRef, acktags = queue:new(), consumer_count = 0, blocked_consumers = queue:new(), - limiter = undefined, + limiter = Limiter, unsent_message_count = 0}, put(Key, C), C; @@ -401,9 +402,7 @@ block_consumer(C = #cr{blocked_consumers = Blocked}, QEntry) -> update_ch_record(C#cr{blocked_consumers = queue:in(QEntry, Blocked)}). is_ch_blocked(#cr{unsent_message_count = Count, limiter = Limiter}) -> - Count >= ?UNSENT_MESSAGE_LIMIT - orelse (Limiter =/= undefined andalso - rabbit_limiter:is_suspended(Limiter)). + Count >= ?UNSENT_MESSAGE_LIMIT orelse rabbit_limiter:is_suspended(Limiter). deliver_msgs_to_consumers(_DeliverFun, true, State) -> {true, State}; @@ -1090,7 +1089,7 @@ handle_call({notify_down, ChPid}, From, State) -> {stop, State1} -> stop(From, ok, State1) end; -handle_call({basic_get, ChPid, NoAck}, _From, +handle_call({basic_get, ChPid, NoAck, LimiterPid}, _From, State = #q{q = #amqqueue{name = QName}}) -> AckRequired = not NoAck, State1 = ensure_expiry_timer(State), @@ -1100,7 +1099,8 @@ handle_call({basic_get, ChPid, NoAck}, _From, {{Message, IsDelivered, AckTag}, State2} -> State3 = #q{backing_queue = BQ, backing_queue_state = BQS} = case AckRequired of - true -> C = #cr{acktags = ChAckTags} = ch_record(ChPid), + true -> C = #cr{acktags = ChAckTags} = + ch_record(ChPid, LimiterPid), ChAckTags1 = queue:in(AckTag, ChAckTags), update_ch_record(C#cr{acktags = ChAckTags1}), State2; @@ -1118,11 +1118,7 @@ handle_call({basic_consume, NoAck, ChPid, LimiterPid, LimiterActive, reply({error, exclusive_consume_unavailable}, State); ok -> C = #cr{consumer_count = Count, - limiter = Limiter0} = ch_record(ChPid), - Limiter = case Limiter0 of - undefined -> rabbit_limiter:client(LimiterPid); - _ -> Limiter0 - end, + limiter = Limiter} = ch_record(ChPid, LimiterPid), Limiter1 = case LimiterActive of true -> rabbit_limiter:activate(Limiter); false -> Limiter @@ -1155,7 +1151,7 @@ handle_call({basic_cancel, ChPid, ConsumerTag, OkMsg}, From, emit_consumer_deleted(ChPid, ConsumerTag, qname(State)), Blocked1 = remove_consumer(ChPid, ConsumerTag, Blocked), Limiter1 = case Count of - 1 -> rabbit_limiter:forget(Limiter); + 1 -> rabbit_limiter:deactivate(Limiter); _ -> Limiter end, update_ch_record(C#cr{consumer_count = Count - 1, @@ -1291,8 +1287,7 @@ handle_cast(delete_immediately, State) -> handle_cast({resume, ChPid}, State) -> noreply( possibly_unblock(State, ChPid, - fun (C = #cr{limiter = undefined}) -> C; - (C = #cr{limiter = Limiter}) -> + fun (C = #cr{limiter = Limiter}) -> C#cr{limiter = rabbit_limiter:resume(Limiter)} end)); @@ -1305,12 +1300,10 @@ handle_cast({notify_sent, ChPid, Credit}, State) -> handle_cast({activate_limit, ChPid}, State) -> noreply( - possibly_unblock( - State, ChPid, - fun (C = #cr{limiter = Limiter, consumer_count = Count}) -> - true = Limiter =/= undefined andalso Count =/= 0, %% assertion - C#cr{limiter = rabbit_limiter:activate(Limiter)} - end)); + possibly_unblock(State, ChPid, + fun (C = #cr{limiter = Limiter}) -> + C#cr{limiter = rabbit_limiter:activate(Limiter)} + end)); handle_cast({flush, ChPid}, State) -> ok = rabbit_channel:flushed(ChPid, self()), diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 17bf5c83..67cabcfb 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -676,12 +676,15 @@ handle_method(#'basic.get'{queue = QueueNameBin, no_ack = NoAck}, _, State = #ch{writer_pid = WriterPid, conn_pid = ConnPid, + limiter = Limiter, next_tag = DeliveryTag}) -> QueueName = expand_queue_name_shortcut(QueueNameBin, State), check_read_permitted(QueueName, State), case rabbit_amqqueue:with_exclusive_access_or_die( QueueName, ConnPid, - fun (Q) -> rabbit_amqqueue:basic_get(Q, self(), NoAck) end) of + fun (Q) -> rabbit_amqqueue:basic_get( + Q, self(), NoAck, rabbit_limiter:pid(Limiter)) + end) of {ok, MessageCount, Msg = {QName, QPid, _MsgId, Redelivered, #basic_message{exchange_name = ExchangeName, diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index 4059fdb0..b914306b 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -21,12 +21,17 @@ %% %% Each channel has an associated limiter process, created with %% start_link/1, which it passes to queues on consumer creation with -%% rabbit_amqqueue:basic_consume/8. This process holds state that is, -%% in effect, shared between the channel and all queues from which the -%% channel is consuming. Essentially all these queues are competing -%% for access to a single, limited resource - the ability to deliver -%% messages via the channel - and it is the job of the limiter process -%% to mediate that access. +%% rabbit_amqqueue:basic_consume/8, and rabbit_amqqueue:basic_get/4. +%% The latter isn't strictly necessary, since basic.get is not +%% subject to limiting, but it means that whenever a queue knows about +%% a channel, it also knows about its limiter, which is less fiddly. +%% +%% Th limiter process holds state that is, in effect, shared between +%% the channel and all queues from which the channel is +%% consuming. Essentially all these queues are competing for access to +%% a single, limited resource - the ability to deliver messages via +%% the channel - and it is the job of the limiter process to mediate +%% that access. %% %% The limiter process is separate from the channel process for two %% reasons: separation of concerns, and efficiency. Channels can get @@ -90,8 +95,10 @@ %% described in (5). %% %% 9. When a queues has no more consumers associated with a particular -%% channel, it unregisters with the limiter and forgets about it - -%% all via forget/1. +%% channel, it deactivates use of the limiter with deactivate/1, +%% which alters the local state such that no further interactions +%% with the limiter process take place until a subsequent +%% activate/1. -module(rabbit_limiter). @@ -102,7 +109,8 @@ -export([new/1, limit/3, unlimit/1, block/1, unblock/1, is_limited/1, is_blocked/1, is_active/1, get_limit/1, ack/2, pid/1]). %% queue API --export([client/1, activate/1, can_send/2, resume/1, forget/1, is_suspended/1]). +-export([client/1, activate/1, can_send/2, resume/1, deactivate/1, + is_suspended/1]). %% callbacks -export([init/1, terminate/2, code_change/3, handle_call/3, handle_cast/2, handle_info/2, prioritise_call/3]). @@ -140,7 +148,7 @@ -spec(can_send/2 :: (qstate(), boolean()) -> {'continue' | 'suspend', qstate()}). -spec(resume/1 :: (qstate()) -> qstate()). --spec(forget/1 :: (qstate()) -> undefined). +-spec(deactivate/1 :: (qstate()) -> qstate()). -spec(is_suspended/1 :: (qstate()) -> boolean()). -endif. @@ -219,10 +227,10 @@ can_send(L, _AckRequired) -> {continue, L}. resume(L) -> L#qstate{state = active}. -forget(#qstate{state = dormant}) -> undefined; -forget(L) -> +deactivate(L = #qstate{state = dormant}) -> L; +deactivate(L) -> ok = gen_server:cast(L#qstate.pid, {unregister, self()}), - undefined. + L#qstate{state = dormant}. is_suspended(#qstate{state = suspended}) -> true; is_suspended(#qstate{}) -> false. diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index b67be544..d1ae38be 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -2718,12 +2718,13 @@ test_queue_recover() -> end, rabbit_amqqueue:stop(), rabbit_amqqueue:start(rabbit_amqqueue:recover()), + {ok, Limiter} = rabbit_limiter:start_link(), rabbit_amqqueue:with_or_die( QName, fun (Q1 = #amqqueue { pid = QPid1 }) -> CountMinusOne = Count - 1, {ok, CountMinusOne, {QName, QPid1, _AckTag, true, _Msg}} = - rabbit_amqqueue:basic_get(Q1, self(), false), + rabbit_amqqueue:basic_get(Q1, self(), false, Limiter), exit(QPid1, shutdown), VQ1 = variable_queue_init(Q, true), {{_Msg1, true, _AckTag1}, VQ2} = @@ -2744,9 +2745,11 @@ test_variable_queue_delete_msg_store_files_callback() -> rabbit_amqqueue:set_ram_duration_target(QPid, 0), + {ok, Limiter} = rabbit_limiter:start_link(), + CountMinusOne = Count - 1, {ok, CountMinusOne, {QName, QPid, _AckTag, false, _Msg}} = - rabbit_amqqueue:basic_get(Q, self(), true), + rabbit_amqqueue:basic_get(Q, self(), true, Limiter), {ok, CountMinusOne} = rabbit_amqqueue:purge(Q), %% give the queue a second to receive the close_fds callback msg -- cgit v1.2.1 From e5a86b2fd17a316327975ce99bb9a6e81aadef37 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 20 Mar 2013 16:08:48 +0000 Subject: better names for the prefetch related part of the limiter API --- src/rabbit_channel.erl | 9 +++--- src/rabbit_limiter.erl | 88 ++++++++++++++++++++++++++------------------------ 2 files changed, 50 insertions(+), 47 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 67cabcfb..1787d688 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -810,12 +810,13 @@ handle_method(#'basic.qos'{prefetch_size = Size}, _, _State) when Size /= 0 -> handle_method(#'basic.qos'{prefetch_count = 0}, _, State = #ch{limiter = Limiter}) -> - Limiter1 = rabbit_limiter:unlimit(Limiter), + Limiter1 = rabbit_limiter:unlimit_prefetch(Limiter), {reply, #'basic.qos_ok'{}, State#ch{limiter = Limiter1}}; handle_method(#'basic.qos'{prefetch_count = PrefetchCount}, _, State = #ch{limiter = Limiter, unacked_message_q = UAMQ}) -> - Limiter1 = rabbit_limiter:limit(Limiter, PrefetchCount, queue:len(UAMQ)), + Limiter1 = rabbit_limiter:limit_prefetch(Limiter, + PrefetchCount, queue:len(UAMQ)), {reply, #'basic.qos_ok'{}, maybe_limit_queues(Limiter, Limiter1, State#ch{limiter = Limiter1})}; @@ -1363,7 +1364,7 @@ consumer_queues(Consumers) -> notify_limiter(Limiter, Acked) -> %% optimisation: avoid the potentially expensive 'foldl' in the %% common case. - case rabbit_limiter:is_limited(Limiter) of + case rabbit_limiter:is_prefetch_limited(Limiter) of false -> ok; true -> case lists:foldl(fun ({_, none, _}, Acc) -> Acc; ({_, _, _}, Acc) -> Acc + 1 @@ -1528,7 +1529,7 @@ i(messages_uncommitted, #ch{}) -> 0; i(acks_uncommitted, #ch{tx = {_Msgs, Acks}}) -> ack_len(Acks); i(acks_uncommitted, #ch{}) -> 0; i(prefetch_count, #ch{limiter = Limiter}) -> - rabbit_limiter:get_limit(Limiter); + rabbit_limiter:get_prefetch_limit(Limiter); i(client_flow_blocked, #ch{limiter = Limiter}) -> rabbit_limiter:is_blocked(Limiter); i(Item, _) -> diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index b914306b..430c2716 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -57,9 +57,10 @@ %% The interactions with the limiter are as follows: %% %% 1. Channels tell the limiter about basic.qos prefetch counts - -%% that's what the limit/3, unlimit/1, is_limited/1, get_limit/1 -%% API functions are about - and channel.flow blocking - that's -%% what block/1, unblock/1 and is_blocked/1 are for. +%% that's what the limit_prefetch/3, unlimit_prefetch/1, +%% is_prefetch_limited/1, get_prefetch_limit/1 API functions are +%% about - and channel.flow blocking - that's what block/1, +%% unblock/1 and is_blocked/1 are for. %% %% 2. Queues register with the limiter - this happens as part of %% activate/1. @@ -106,8 +107,9 @@ -export([start_link/0]). %% channel API --export([new/1, limit/3, unlimit/1, block/1, unblock/1, - is_limited/1, is_blocked/1, is_active/1, get_limit/1, ack/2, pid/1]). +-export([new/1, limit_prefetch/3, unlimit_prefetch/1, block/1, unblock/1, + is_prefetch_limited/1, is_blocked/1, is_active/1, + get_prefetch_limit/1, ack/2, pid/1]). %% queue API -export([client/1, activate/1, can_send/2, resume/1, deactivate/1, is_suspended/1]). @@ -117,31 +119,31 @@ %%---------------------------------------------------------------------------- --record(lstate, {pid, limited, blocked}). +-record(lstate, {pid, prefetch_limited, blocked}). -record(qstate, {pid, state}). -ifdef(use_specs). --type(lstate() :: #lstate{pid :: pid(), - limited :: boolean(), - blocked :: boolean()}). +-type(lstate() :: #lstate{pid :: pid(), + prefetch_limited :: boolean(), + blocked :: boolean()}). -type(qstate() :: #qstate{pid :: pid(), state :: 'dormant' | 'active' | 'suspended'}). -spec(start_link/0 :: () -> rabbit_types:ok_pid_or_error()). -spec(new/1 :: (pid()) -> lstate()). --spec(limit/3 :: (lstate(), non_neg_integer(), non_neg_integer()) -> - lstate()). --spec(unlimit/1 :: (lstate()) -> lstate()). --spec(block/1 :: (lstate()) -> lstate()). --spec(unblock/1 :: (lstate()) -> lstate()). --spec(is_limited/1 :: (lstate()) -> boolean()). --spec(is_blocked/1 :: (lstate()) -> boolean()). --spec(is_active/1 :: (lstate()) -> boolean()). --spec(get_limit/1 :: (lstate()) -> non_neg_integer()). --spec(ack/2 :: (lstate(), non_neg_integer()) -> 'ok'). --spec(pid/1 :: (lstate()) -> pid()). +-spec(limit_prefetch/3 :: (lstate(), non_neg_integer(), non_neg_integer()) + -> lstate()). +-spec(unlimit_prefetch/1 :: (lstate()) -> lstate()). +-spec(block/1 :: (lstate()) -> lstate()). +-spec(unblock/1 :: (lstate()) -> lstate()). +-spec(is_prefetch_limited/1 :: (lstate()) -> boolean()). +-spec(is_blocked/1 :: (lstate()) -> boolean()). +-spec(is_active/1 :: (lstate()) -> boolean()). +-spec(get_prefetch_limit/1 :: (lstate()) -> non_neg_integer()). +-spec(ack/2 :: (lstate(), non_neg_integer()) -> 'ok'). +-spec(pid/1 :: (lstate()) -> pid()). -spec(client/1 :: (pid()) -> qstate()). -spec(activate/1 :: (qstate()) -> qstate()). @@ -173,15 +175,16 @@ start_link() -> gen_server2:start_link(?MODULE, [], []). new(Pid) -> %% this a 'call' to ensure that it is invoked at most once. ok = gen_server:call(Pid, {new, self()}), - #lstate{pid = Pid, limited = false, blocked = false}. + #lstate{pid = Pid, prefetch_limited = false, blocked = false}. -limit(L, PrefetchCount, UnackedCount) when PrefetchCount > 0 -> - ok = gen_server:call(L#lstate.pid, {limit, PrefetchCount, UnackedCount}), - L#lstate{limited = true}. +limit_prefetch(L, PrefetchCount, UnackedCount) when PrefetchCount > 0 -> + ok = gen_server:call(L#lstate.pid, + {limit_prefetch, PrefetchCount, UnackedCount}), + L#lstate{prefetch_limited = true}. -unlimit(L) -> - ok = gen_server:call(L#lstate.pid, unlimit), - L#lstate{limited = false}. +unlimit_prefetch(L) -> + ok = gen_server:call(L#lstate.pid, unlimit_prefetch), + L#lstate{prefetch_limited = false}. block(L) -> ok = gen_server:call(L#lstate.pid, block), @@ -191,16 +194,16 @@ unblock(L) -> ok = gen_server:call(L#lstate.pid, unblock), L#lstate{blocked = false}. -is_limited(#lstate{limited = Limited}) -> Limited. +is_prefetch_limited(#lstate{prefetch_limited = Limited}) -> Limited. is_blocked(#lstate{blocked = Blocked}) -> Blocked. -is_active(L) -> is_limited(L) orelse is_blocked(L). +is_active(L) -> is_prefetch_limited(L) orelse is_blocked(L). -get_limit(#lstate{limited = false}) -> 0; -get_limit(L) -> gen_server:call(L#lstate.pid, get_limit). +get_prefetch_limit(#lstate{prefetch_limited = false}) -> 0; +get_prefetch_limit(L) -> gen_server:call(L#lstate.pid, get_prefetch_limit). -ack(#lstate{limited = false}, _AckCount) -> ok; +ack(#lstate{prefetch_limited = false}, _AckCount) -> ok; ack(L, AckCount) -> gen_server:cast(L#lstate.pid, {ack, AckCount}). pid(#lstate{pid = Pid}) -> Pid. @@ -212,8 +215,6 @@ activate(L = #qstate{state = dormant}) -> L#qstate{state = active}; activate(L) -> L. -%% Ask the limiter whether the queue can deliver a message without -%% breaching a limit. can_send(L = #qstate{state = active}, AckRequired) -> rabbit_misc:with_exit_handler( fun () -> {continue, L} end, @@ -241,20 +242,20 @@ is_suspended(#qstate{}) -> false. init([]) -> {ok, #lim{}}. -prioritise_call(get_limit, _From, _State) -> 9; -prioritise_call(_Msg, _From, _State) -> 0. +prioritise_call(get_prefetch_limit, _From, _State) -> 9; +prioritise_call(_Msg, _From, _State) -> 0. handle_call({new, ChPid}, _From, State = #lim{ch_pid = undefined}) -> {reply, ok, State#lim{ch_pid = ChPid}}; -handle_call({limit, PrefetchCount, UnackedCount}, _From, State) -> +handle_call({limit_prefetch, PrefetchCount, UnackedCount}, _From, State) -> %% assertion true = State#lim.prefetch_count == 0 orelse State#lim.volume == UnackedCount, {reply, ok, maybe_notify(State, State#lim{prefetch_count = PrefetchCount, volume = UnackedCount})}; -handle_call(unlimit, _From, State) -> +handle_call(unlimit_prefetch, _From, State) -> {reply, ok, maybe_notify(State, State#lim{prefetch_count = 0, volume = 0})}; @@ -264,7 +265,8 @@ handle_call(block, _From, State) -> handle_call(unblock, _From, State) -> {reply, ok, maybe_notify(State, State#lim{blocked = false})}; -handle_call(get_limit, _From, State = #lim{prefetch_count = PrefetchCount}) -> +handle_call(get_prefetch_limit, _From, + State = #lim{prefetch_count = PrefetchCount}) -> {reply, PrefetchCount, State}; handle_call({can_send, QPid, _AckRequired}, _From, @@ -272,7 +274,7 @@ handle_call({can_send, QPid, _AckRequired}, _From, {reply, false, limit_queue(QPid, State)}; handle_call({can_send, QPid, AckRequired}, _From, State = #lim{volume = Volume}) -> - case limit_reached(State) of + case prefetch_limit_reached(State) of true -> {reply, false, limit_queue(QPid, State)}; false -> {reply, true, State#lim{volume = if AckRequired -> Volume + 1; true -> Volume @@ -305,13 +307,13 @@ code_change(_, State, _) -> %%---------------------------------------------------------------------------- maybe_notify(OldState, NewState) -> - case (limit_reached(OldState) orelse blocked(OldState)) andalso - not (limit_reached(NewState) orelse blocked(NewState)) of + case (prefetch_limit_reached(OldState) orelse blocked(OldState)) andalso + not (prefetch_limit_reached(NewState) orelse blocked(NewState)) of true -> notify_queues(NewState); false -> NewState end. -limit_reached(#lim{prefetch_count = Limit, volume = Volume}) -> +prefetch_limit_reached(#lim{prefetch_count = Limit, volume = Volume}) -> Limit =/= 0 andalso Volume >= Limit. blocked(#lim{blocked = Blocked}) -> Blocked. -- cgit v1.2.1 From b5cc5097783245010a335f6a8f06c86eb55d2d62 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 20 Mar 2013 16:24:45 +0000 Subject: First attempt at pinging that doesn't really work well. --- src/rabbit_node_monitor.erl | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index de53b7f0..4aaf634e 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -32,8 +32,9 @@ -define(SERVER, ?MODULE). -define(RABBIT_UP_RPC_TIMEOUT, 2000). +-define(RABBIT_DOWN_PING_INTERVAL, 1000). --record(state, {monitors, partitions, subscribers}). +-record(state, {monitors, partitions, subscribers, down_ping_timer}). %%---------------------------------------------------------------------------- @@ -272,10 +273,22 @@ handle_info({mnesia_system_event, ordsets:add_element(Node, ordsets:from_list(Partitions))), {noreply, State1#state{partitions = Partitions1}}; +handle_info(ping_nodes, State) -> + %% We ping nodes when some are down to ensure that we find out + %% about healed partitions quickly. We ping all nodes rather than + %% just the ones we know are down for simplicity; it's not expensive. + State1 = State#state{down_ping_timer = undefined}, + %% ratio() both pings all the nodes and tells us if we need to again :-) + {noreply, case ratio() of + 1.0 -> State1; + _ -> ensure_ping_timer(State1) + end}; + handle_info(_Info, State) -> {noreply, State}. -terminate(_Reason, _State) -> +terminate(_Reason, State) -> + rabbit_misc:stop_timer(State, #state.down_ping_timer), ok. code_change(_OldVsn, State, _Extra) -> @@ -308,8 +321,8 @@ handle_dead_rabbit(Node) -> end, ok. -majority() -> - length(alive_nodes()) / length(rabbit_mnesia:cluster_nodes(all)) > 0.5. +majority() -> ratio() > 0.5. +ratio() -> length(alive_nodes()) / length(rabbit_mnesia:cluster_nodes(all)). %% mnesia:system_info(db_nodes) (and hence %% rabbit_mnesia:cluster_nodes(running)) does not give reliable results @@ -350,7 +363,11 @@ handle_dead_rabbit_state(State = #state{partitions = Partitions}) -> [] -> []; _ -> Partitions end, - State#state{partitions = Partitions1}. + ensure_ping_timer(State#state{partitions = Partitions1}). + +ensure_ping_timer(State) -> + rabbit_misc:ensure_timer( + State, #state.down_ping_timer, ?RABBIT_DOWN_PING_INTERVAL, ping_nodes). handle_live_rabbit(Node) -> ok = rabbit_alarm:on_node_up(Node), -- cgit v1.2.1 From ecc36c55bfd41a5eaf252057c87f5edc986bc35a Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Thu, 21 Mar 2013 10:02:31 +0000 Subject: Exchange decorator routing improvements --- src/rabbit_exchange.erl | 6 +++--- src/rabbit_exchange_decorator.erl | 7 +++++++ src/rabbit_registry.erl | 21 ++++++++++++++------- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index a4a88661..a3b32c7b 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -335,7 +335,7 @@ route1(Delivery, Decorators, {[X = #exchange{type = Type} | WorkList], SeenXs, QNames}) -> ExchangeDests = (type_to_module(Type)):route(X, Delivery), AlternateDests = process_alternate(X, ExchangeDests), - DecorateDests = process_decorators(Delivery, Decorators, X), + DecorateDests = process_decorators(X, Decorators, Delivery), route1(Delivery, Decorators, lists:foldl(fun process_route/2, {WorkList, SeenXs, QNames}, AlternateDests ++ DecorateDests ++ ExchangeDests)). @@ -350,9 +350,9 @@ process_alternate(#exchange{name = XName, arguments = Args}, []) -> process_alternate(_X, _Results) -> []. -process_decorators(_Delivery, [], _X) -> +process_decorators(_, [], _) -> []; -process_decorators(Delivery, Decorators, X) -> +process_decorators(X, Decorators, Delivery) -> lists:append([Decorator:route(X, Delivery) || Decorator <- Decorators]). process_route(#resource{kind = exchange} = XName, diff --git a/src/rabbit_exchange_decorator.erl b/src/rabbit_exchange_decorator.erl index bf4add73..d8600835 100644 --- a/src/rabbit_exchange_decorator.erl +++ b/src/rabbit_exchange_decorator.erl @@ -57,6 +57,13 @@ -callback remove_bindings(serial(), rabbit_types:exchange(), [rabbit_types:binding()]) -> 'ok'. +%% %% called after exchange routing +%% %% return value is a list of queues to be added to the list of +%% %% destination queues. decorators must register separately for +%% %% this callback using exchange_decorator_route. +%% -callback route(rabbit_types:exchange(), rabbit_types:delivery()) -> +%% [rabbit_amqqueue:name()]. + -else. -export([behaviour_info/1]). diff --git a/src/rabbit_registry.erl b/src/rabbit_registry.erl index 41b82ba5..22700a7c 100644 --- a/src/rabbit_registry.erl +++ b/src/rabbit_registry.erl @@ -95,20 +95,27 @@ internal_unregister(Class, TypeName) -> true = ets:delete(?ETS_NAME, UnregArg), ok. -conditional_register({{exchange_decorator, _Type}, ModuleName} = RegArg) -> + +%% (un)register exchange decorator route callback only when implemented +%% to avoid decorators being called unnecessarily on the fast publishing path +conditional_register({{exchange_decorator, _Type}, ModuleName}) -> case erlang:function_exported(ModuleName, route, 2) of - true -> true = ets:insert(?ETS_NAME, RegArg); + true -> true = ets:insert(?ETS_NAME, + {{exchange_decorator_route, _Type}, + ModuleName}); false -> ok end; conditional_register(_) -> ok. -conditional_unregister({exchange_decorator, Type} = UnregArg) -> +conditional_unregister({exchange_decorator, Type}) -> case lookup_module(exchange_decorator, Type) of - {ok, ModName} -> case erlang:function_exported(ModName, route, 2) of - true -> true = ets:delete(?ETS_NAME, UnregArg); - false -> ok - end; + {ok, ModName} -> + case erlang:function_exported(ModName, route, 2) of + true -> true = ets:delete(?ETS_NAME, + {exchange_decorator_route, Type}); + false -> ok + end; {error, not_found} -> ok end; -- cgit v1.2.1 From 30965c881b81a27880c1a81fcc735f79dd1f8982 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 21 Mar 2013 10:41:42 +0000 Subject: Merge the two can_sends and tidy up. --- src/rabbit_amqqueue_process.erl | 11 +++----- src/rabbit_limiter.erl | 56 ++++++++++++++++------------------------- 2 files changed, 25 insertions(+), 42 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 8f325e5c..22bbbd00 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -447,16 +447,13 @@ deliver_msg_to_consumer(DeliverFun, E = {ChPid, Consumer}, State) -> false -> case rabbit_limiter:can_send(C#cr.limiter, Consumer#consumer.ack_required, Consumer#consumer.tag) of - consumer_blocked -> - block_consumer(C, E), + {suspend, Limiter} -> + block_consumer(C#cr{limiter = Limiter}, E), {false, State}; - channel_blocked -> - block_consumer(C, E), - {false, State}; - Limiter2 -> + {continue, Limiter} -> AC1 = queue:in(E, State#q.active_consumers), deliver_msg_to_consumer( - DeliverFun, Consumer, C#cr{limiter = Limiter2}, + DeliverFun, Consumer, C#cr{limiter = Limiter}, State#q{active_consumers = AC1}) end end. diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index 71ed2e73..6825622d 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -111,14 +111,13 @@ is_prefetch_limited/1, is_blocked/1, is_active/1, get_prefetch_limit/1, ack/2, pid/1]). %% queue API --export([client/1, activate/1, can_send/2, resume/1, deactivate/1, - is_suspended/1]). +-export([client/1, activate/1, can_send/3, resume/1, deactivate/1, + is_suspended/1, is_consumer_blocked/2, credit/4, drained/1, + forget_consumer/2]). %% callbacks -export([init/1, terminate/2, code_change/3, handle_call/3, handle_cast/2, handle_info/2, prioritise_call/3]). --export([is_consumer_blocked/2, credit/4, drained/1, forget_consumer/2]). - -import(rabbit_misc, [serial_add/2, serial_diff/2]). %%---------------------------------------------------------------------------- @@ -141,8 +140,6 @@ -> lstate()). -spec(unlimit_prefetch/1 :: (lstate()) -> lstate()). -spec(block/1 :: (lstate()) -> lstate()). --spec(can_send/4 :: (qstate(), pid(), boolean(), rabbit_types:ctag()) - -> qstate() | 'consumer_blocked' | 'channel_blocked'). -spec(unblock/1 :: (lstate()) -> lstate()). -spec(is_prefetch_limited/1 :: (lstate()) -> boolean()). -spec(is_blocked/1 :: (lstate()) -> boolean()). @@ -153,7 +150,7 @@ -spec(client/1 :: (pid()) -> qstate()). -spec(activate/1 :: (qstate()) -> qstate()). --spec(can_send/2 :: (qstate(), boolean()) -> +-spec(can_send/3 :: (qstate(), boolean(), rabbit_types:ctag()) -> {'continue' | 'suspend', qstate()}). -spec(resume/1 :: (qstate()) -> qstate()). -spec(deactivate/1 :: (qstate()) -> qstate()). @@ -210,24 +207,6 @@ unblock(L) -> is_prefetch_limited(#lstate{prefetch_limited = Limited}) -> Limited. -can_send(Token = #qstate{pid = Pid, state = State, credits = Credits}, - QPid, AckReq, CTag) -> - case is_consumer_blocked(Token, CTag) of - false -> case State =/= active orelse call_can_send(Pid, QPid, AckReq) of - true -> Token#qstate{ - credits = record_send_q(CTag, Credits)}; - false -> channel_blocked - end; - true -> consumer_blocked - end. - -call_can_send(Pid, QPid, AckRequired) -> - rabbit_misc:with_exit_handler( - fun () -> true end, - fun () -> - gen_server2:call(Pid, {can_send, QPid, AckRequired}, infinity) - end). - is_blocked(#lstate{blocked = Blocked}) -> Blocked. is_active(L) -> is_prefetch_limited(L) orelse is_blocked(L). @@ -247,16 +226,24 @@ activate(L = #qstate{state = dormant}) -> L#qstate{state = active}; activate(L) -> L. -can_send(L = #qstate{state = active}, AckRequired) -> +can_send(L = #qstate{pid = Pid, state = State, credits = Credits}, + AckReq, CTag) -> + case is_consumer_blocked(L, CTag) of + false -> case State =/= active orelse call_can_send( + Pid, self(), AckReq) of + true -> {continue, L#qstate{ + credits = record_send_q(CTag, Credits)}}; + false -> {suspend, L#qstate{state = suspended}} + end; + true -> {suspend, L#qstate{state = suspended}} + end. + +call_can_send(Pid, QPid, AckRequired) -> rabbit_misc:with_exit_handler( - fun () -> {continue, L} end, - fun () -> Msg = {can_send, self(), AckRequired}, - case gen_server2:call(L#qstate.pid, Msg, infinity) of - true -> {continue, L}; - false -> {suspend, L#qstate{state = suspended}} - end - end); -can_send(L, _AckRequired) -> {continue, L}. + fun () -> true end, + fun () -> + gen_server2:call(Pid, {can_send, QPid, AckRequired}, infinity) + end). resume(L) -> L#qstate{state = active}. @@ -275,7 +262,6 @@ is_consumer_blocked(#qstate{credits = Credits}, CTag) -> none -> false end. - credit(Limiter = #qstate{credits = Credits}, CTag, Credit, Drain) -> Limiter#qstate{credits = update_credit(CTag, Credit, Drain, Credits)}. -- cgit v1.2.1 From e169405354ca9567de824c561c97d3fda9cb8a45 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 21 Mar 2013 11:10:52 +0000 Subject: oops --- src/rabbit_tests.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index f56fe8ee..7ecea035 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -1131,7 +1131,7 @@ test_server_status() -> rabbit_misc:r(<<"/">>, queue, Name), false, false, [], none)]], ok = rabbit_amqqueue:basic_consume( - Q, true, Ch, Limiter, false, <<"ctag">>, true, 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 e54051656c68e06b7e4833e8a44ceb204f8d37be Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 21 Mar 2013 11:22:14 +0000 Subject: Cosmetic, reduce distance to bug25461, remove dead comment --- src/rabbit_amqqueue.erl | 1 - src/rabbit_amqqueue_process.erl | 26 +++++++++++--------------- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 2dfed21d..8c00c85c 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -548,7 +548,6 @@ 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, ConsumerTag, ExclusiveConsume, CreditArgs, OkMsg) -> delegate:call(QPid, {basic_consume, NoAck, ChPid, LimiterPid, LimiterActive, diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 22bbbd00..bff762f3 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -71,8 +71,6 @@ blocked_consumers, %% The limiter itself limiter, - %% Has the limiter imposed a channel-wide block, either - %% because of qos or channel flow? %% Internal flow control for queue -> writer unsent_message_count}). @@ -1157,16 +1155,14 @@ handle_call({basic_consume, NoAck, ChPid, LimiterPid, LimiterActive, ok -> C = #cr{consumer_count = Count, limiter = Limiter} = ch_record(ChPid, LimiterPid), - Limiter1 = case CreditArgs of - none -> - Limiter; - {Credit, Drain} -> - rabbit_limiter:credit( - Limiter, ConsumerTag, Credit, Drain) + Limiter1 = case LimiterActive of + true -> rabbit_limiter:activate(Limiter); + false -> Limiter end, - Limiter2 = case LimiterActive of - true -> rabbit_limiter:activate(Limiter1); - false -> Limiter1 + Limiter2 = case CreditArgs of + none -> Limiter1; + {Crd, Drain} -> rabbit_limiter:credit( + Limiter1, ConsumerTag, Crd, Drain) end, C1 = update_ch_record(C#cr{consumer_count = Count + 1, limiter = Limiter2}), @@ -1198,12 +1194,12 @@ handle_call({basic_cancel, ChPid, ConsumerTag, OkMsg}, From, limiter = Limiter, blocked_consumers = Blocked} -> emit_consumer_deleted(ChPid, ConsumerTag, qname(State)), - Limiter1 = rabbit_limiter:forget_consumer(Limiter, ConsumerTag), Blocked1 = remove_consumer(ChPid, ConsumerTag, Blocked), - Limiter2 = case Count of - 1 -> rabbit_limiter:deactivate(Limiter1); - _ -> Limiter1 + Limiter1 = case Count of + 1 -> rabbit_limiter:deactivate(Limiter); + _ -> Limiter end, + Limiter2 = rabbit_limiter:forget_consumer(Limiter1, ConsumerTag), update_ch_record(C#cr{consumer_count = Count - 1, limiter = Limiter2, blocked_consumers = Blocked1}), -- cgit v1.2.1 From 4fd233bd49868359f55c50a509f7b87b8f55a8c5 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Thu, 21 Mar 2013 11:33:40 +0000 Subject: Modify decorator routing comment, variable and whitespace --- src/rabbit_exchange_decorator.erl | 8 +++----- src/rabbit_registry.erl | 5 ++--- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/rabbit_exchange_decorator.erl b/src/rabbit_exchange_decorator.erl index d8600835..45d0cbf5 100644 --- a/src/rabbit_exchange_decorator.erl +++ b/src/rabbit_exchange_decorator.erl @@ -57,12 +57,10 @@ -callback remove_bindings(serial(), rabbit_types:exchange(), [rabbit_types:binding()]) -> 'ok'. -%% %% called after exchange routing -%% %% return value is a list of queues to be added to the list of -%% %% destination queues. decorators must register separately for -%% %% this callback using exchange_decorator_route. +%% Decorators can optionally implement route/2 which allows additional +%% queues to be added to the routing decision. %% -callback route(rabbit_types:exchange(), rabbit_types:delivery()) -> -%% [rabbit_amqqueue:name()]. +%% [rabbit_amqqueue:name() | rabbit_types:exchange()]. -else. diff --git a/src/rabbit_registry.erl b/src/rabbit_registry.erl index 22700a7c..f6bc4071 100644 --- a/src/rabbit_registry.erl +++ b/src/rabbit_registry.erl @@ -95,13 +95,12 @@ internal_unregister(Class, TypeName) -> true = ets:delete(?ETS_NAME, UnregArg), ok. - %% (un)register exchange decorator route callback only when implemented %% to avoid decorators being called unnecessarily on the fast publishing path -conditional_register({{exchange_decorator, _Type}, ModuleName}) -> +conditional_register({{exchange_decorator, Type}, ModuleName}) -> case erlang:function_exported(ModuleName, route, 2) of true -> true = ets:insert(?ETS_NAME, - {{exchange_decorator_route, _Type}, + {{exchange_decorator_route, Type}, ModuleName}); false -> ok end; -- cgit v1.2.1 From 5a5ff913f7bdbc081689e6ef235e828178a2ce47 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Thu, 21 Mar 2013 11:40:42 +0000 Subject: Comment --- src/rabbit_exchange_decorator.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_exchange_decorator.erl b/src/rabbit_exchange_decorator.erl index 45d0cbf5..040b55db 100644 --- a/src/rabbit_exchange_decorator.erl +++ b/src/rabbit_exchange_decorator.erl @@ -58,9 +58,9 @@ [rabbit_types:binding()]) -> 'ok'. %% Decorators can optionally implement route/2 which allows additional -%% queues to be added to the routing decision. +%% destinations to be added to the routing decision. %% -callback route(rabbit_types:exchange(), rabbit_types:delivery()) -> -%% [rabbit_amqqueue:name() | rabbit_types:exchange()]. +%% [rabbit_amqqueue:name() | rabbit_exchange:name()]. -else. -- cgit v1.2.1 From dc22fedbe7689b64cc35bbb03def0fc295540c05 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 21 Mar 2013 11:57:00 +0000 Subject: cosmetic --- src/rabbit_exchange.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index a3b32c7b..9e98448d 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -333,9 +333,9 @@ route1(_, _, {[], _, QNames}) -> QNames; route1(Delivery, Decorators, {[X = #exchange{type = Type} | WorkList], SeenXs, QNames}) -> - ExchangeDests = (type_to_module(Type)):route(X, Delivery), + ExchangeDests = (type_to_module(Type)):route(X, Delivery), + DecorateDests = process_decorators(X, Decorators, Delivery), AlternateDests = process_alternate(X, ExchangeDests), - DecorateDests = process_decorators(X, Decorators, Delivery), route1(Delivery, Decorators, lists:foldl(fun process_route/2, {WorkList, SeenXs, QNames}, AlternateDests ++ DecorateDests ++ ExchangeDests)). @@ -350,7 +350,7 @@ process_alternate(#exchange{name = XName, arguments = Args}, []) -> process_alternate(_X, _Results) -> []. -process_decorators(_, [], _) -> +process_decorators(_, [], _) -> %% optimisation []; process_decorators(X, Decorators, Delivery) -> lists:append([Decorator:route(X, Delivery) || Decorator <- Decorators]). -- cgit v1.2.1 From 1b0febe01dae9aca674393cf708c768f80822e59 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 21 Mar 2013 12:03:31 +0000 Subject: simplify --- src/rabbit_registry.erl | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/rabbit_registry.erl b/src/rabbit_registry.erl index f6bc4071..9f1b52e6 100644 --- a/src/rabbit_registry.erl +++ b/src/rabbit_registry.erl @@ -108,16 +108,8 @@ conditional_register(_) -> ok. conditional_unregister({exchange_decorator, Type}) -> - case lookup_module(exchange_decorator, Type) of - {ok, ModName} -> - case erlang:function_exported(ModName, route, 2) of - true -> true = ets:delete(?ETS_NAME, - {exchange_decorator_route, Type}); - false -> ok - end; - {error, not_found} -> - ok - end; + true = ets:delete(?ETS_NAME, {exchange_decorator_route, Type}), + ok; conditional_unregister(_) -> ok. -- cgit v1.2.1 From ae743ee4ea4e6cec41fb43710b7f908c8f9ed71f Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 21 Mar 2013 12:05:20 +0000 Subject: correct comment --- src/rabbit_registry.erl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/rabbit_registry.erl b/src/rabbit_registry.erl index 9f1b52e6..acdc2cff 100644 --- a/src/rabbit_registry.erl +++ b/src/rabbit_registry.erl @@ -95,8 +95,9 @@ internal_unregister(Class, TypeName) -> true = ets:delete(?ETS_NAME, UnregArg), ok. -%% (un)register exchange decorator route callback only when implemented -%% to avoid decorators being called unnecessarily on the fast publishing path +%% register exchange decorator route callback only when implemented, +%% in order to avoid unnecessary decorator calls on the fast +%% publishing path conditional_register({{exchange_decorator, Type}, ModuleName}) -> case erlang:function_exported(ModuleName, route, 2) of true -> true = ets:insert(?ETS_NAME, -- cgit v1.2.1 From 530c1203d73101eb781bf514b848cd613f42a857 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 21 Mar 2013 12:27:25 +0000 Subject: Make CheckVHost actually do something. --- src/rabbit_tests.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 1188c554..cd8fa720 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -1580,7 +1580,7 @@ control_action(Command, Node, Args, Opts) -> info_action(Command, Args, CheckVHost) -> ok = control_action(Command, []), - if CheckVHost -> ok = control_action(Command, []); + if CheckVHost -> ok = control_action(Command, [], ["-p", "/"]); true -> ok end, ok = control_action(Command, lists:map(fun atom_to_list/1, Args)), -- cgit v1.2.1 From d7a121de182f25c906e5c2326b080421d016c88c Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 21 Mar 2013 13:22:03 +0000 Subject: suspended =/= has run out of credit. --- src/rabbit_limiter.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index 6825622d..a187fd7b 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -235,7 +235,7 @@ can_send(L = #qstate{pid = Pid, state = State, credits = Credits}, credits = record_send_q(CTag, Credits)}}; false -> {suspend, L#qstate{state = suspended}} end; - true -> {suspend, L#qstate{state = suspended}} + true -> {suspend, L} end. call_can_send(Pid, QPid, AckRequired) -> -- cgit v1.2.1 From 54a5439a4ef96335d75b20d8807b24bc8c69ac3d Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 21 Mar 2013 15:32:34 +0000 Subject: Do the pinging in a separate process so we don't block the node monitor. --- src/rabbit_node_monitor.erl | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index 4aaf634e..16b52a4d 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -276,13 +276,25 @@ handle_info({mnesia_system_event, handle_info(ping_nodes, State) -> %% We ping nodes when some are down to ensure that we find out %% about healed partitions quickly. We ping all nodes rather than - %% just the ones we know are down for simplicity; it's not expensive. + %% just the ones we know are down for simplicity; it's not expensive + %% to ping the nodes that are up, after all. State1 = State#state{down_ping_timer = undefined}, - %% ratio() both pings all the nodes and tells us if we need to again :-) - {noreply, case ratio() of - 1.0 -> State1; - _ -> ensure_ping_timer(State1) - end}; + Self = self(), + %% ratio() both pings all the nodes and tells us if we need to again. + %% + %% We ping in a separate process since in a partition it might + %% take some noticeable length of time and we don't want to block + %% the node monitor for that long. + spawn_link(fun () -> + case ratio() of + 1.0 -> ok; + _ -> Self ! ping_again + end + end), + {noreply, State1}; + +handle_info(ping_again, State) -> + {noreply, ensure_ping_timer(State)}; handle_info(_Info, State) -> {noreply, State}. -- cgit v1.2.1 From 42d7a9385fd422e618124666369adadc7b9ac430 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 21 Mar 2013 17:01:54 +0000 Subject: re-introduce state-transition optimisation for possibly_unblock --- src/rabbit_amqqueue_process.erl | 50 ++++++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index c6a8bf2f..e24568bb 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -644,28 +644,28 @@ remove_consumers(ChPid, Queue, QName) -> possibly_unblock(State, ChPid, Update) -> case lookup_ch(ChPid) of not_found -> State; - C -> possibly_unblock(State, Update(C)) + C -> C1 = Update(C), + case is_ch_blocked(C) andalso not is_ch_blocked(C1) of + false -> update_ch_record(C1), + State; + true -> unblock(State, C1) + end end. -possibly_unblock(State, C = #cr{limiter = Limiter}) -> - case is_ch_blocked(C) of - true -> update_ch_record(C), - State; - false -> case lists:partition( - fun({_ChPid, #consumer{tag = CTag}}) -> - rabbit_limiter:is_consumer_blocked( - Limiter, CTag) - end, 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), - update_ch_record(C#cr{blocked_consumers = BlockedQ}), - AC1 = queue:join(State#q.active_consumers, UnblockedQ), - run_message_queue(State#q{active_consumers = AC1}) - end +unblock(State, C = #cr{limiter = Limiter}) -> + case lists:partition( + fun({_ChPid, #consumer{tag = CTag}}) -> + rabbit_limiter:is_consumer_blocked(Limiter, CTag) + end, 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), + update_ch_record(C#cr{blocked_consumers = BlockedQ}), + AC1 = queue:join(State#q.active_consumers, UnblockedQ), + run_message_queue(State#q{active_consumers = AC1}) end. should_auto_delete(#q{q = #amqqueue{auto_delete = false}}) -> false; @@ -1389,13 +1389,17 @@ handle_cast({credit, ChPid, CTag, Credit, Drain}, backing_queue_state = BQS}) -> Len = BQ:len(BQS), rabbit_channel:send_credit_reply(ChPid, Len), - C = #cr{limiter = Lim} = lookup_ch(ChPid), - C1 = C#cr{limiter = rabbit_limiter:credit(Lim, CTag, Credit, Drain)}, + C = #cr{limiter = Limiter} = lookup_ch(ChPid), + C1 = C#cr{limiter = rabbit_limiter:credit(Limiter, CTag, Credit, Drain)}, noreply(case Drain andalso Len == 0 of true -> update_ch_record(C1), send_drained(C1), State; - false -> possibly_unblock(State, C1) + false -> case is_ch_blocked(C1) of + true -> update_ch_record(C1), + State; + false -> unblock(State, C1) + end end); handle_cast(wake_up, State) -> -- cgit v1.2.1 From 209677404df56137c57d33b6a566ab4555792ae4 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 21 Mar 2013 17:22:31 +0000 Subject: cosmetic(ish) --- src/rabbit_channel.erl | 3 +-- src/rabbit_limiter.erl | 22 ++++++++++------------ 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 79a71b8d..39bd375a 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -330,8 +330,7 @@ handle_cast({send_credit_reply, Len}, State = #ch{writer_pid = WriterPid}) -> WriterPid, #'basic.credit_ok'{available = Len}), noreply(State); -handle_cast({send_drained, CTagCredit}, - State = #ch{writer_pid = WriterPid}) -> +handle_cast({send_drained, CTagCredit}, State = #ch{writer_pid = WriterPid}) -> [ok = rabbit_writer:send_command( WriterPid, #'basic.credit_drained'{consumer_tag = ConsumerTag, credit_drained = CreditDrained}) diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index a187fd7b..602681e5 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -68,7 +68,7 @@ %% 4. The limiter process maintains an internal counter of 'messages %% sent but not yet acknowledged', called the 'volume'. %% -%% 5. Queues ask the limiter for permission (with can_send/2) whenever +%% 5. Queues ask the limiter for permission (with can_send/3) whenever %% they want to deliver a message to a channel. The limiter checks %% whether a) the channel isn't blocked by channel.flow, and b) the %% volume has not yet reached the prefetch limit. If so it @@ -77,10 +77,10 @@ %% tells the queue not to proceed. %% %% 6. A queue that has told to proceed (by the return value of -%% can_send/2) sends the message to the channel. Conversely, a +%% can_send/3) sends the message to the channel. Conversely, a %% queue that has been told not to proceed, will not attempt to %% deliver that message, or any future messages, to the -%% channel. This is accomplished by can_send/2 capturing the +%% channel. This is accomplished by can_send/3 capturing the %% outcome in the local state, where it can be accessed with %% is_suspended/1. %% @@ -88,7 +88,7 @@ %% how many messages were ack'ed. The limiter process decrements %% the volume and if it falls below the prefetch_count then it %% notifies (through rabbit_amqqueue:resume/2) all the queues -%% requiring notification, i.e. all those that had a can_send/2 +%% requiring notification, i.e. all those that had a can_send/3 %% request denied. %% %% 8. Upon receipt of such a notification, queues resume delivery to @@ -227,10 +227,10 @@ activate(L = #qstate{state = dormant}) -> activate(L) -> L. can_send(L = #qstate{pid = Pid, state = State, credits = Credits}, - AckReq, CTag) -> + AckRequired, CTag) -> case is_consumer_blocked(L, CTag) of - false -> case State =/= active orelse call_can_send( - Pid, self(), AckReq) of + false -> case (State =/= active orelse + safe_call(Pid, {can_send, self(), AckRequired}, true)) of true -> {continue, L#qstate{ credits = record_send_q(CTag, Credits)}}; false -> {suspend, L#qstate{state = suspended}} @@ -238,12 +238,10 @@ can_send(L = #qstate{pid = Pid, state = State, credits = Credits}, true -> {suspend, L} end. -call_can_send(Pid, QPid, AckRequired) -> +safe_call(Pid, Msg, ExitValue) -> rabbit_misc:with_exit_handler( - fun () -> true end, - fun () -> - gen_server2:call(Pid, {can_send, QPid, AckRequired}, infinity) - end). + fun () -> ExitValue end, + fun () -> gen_server2:call(Pid, Msg, infinity) end). resume(L) -> L#qstate{state = active}. -- cgit v1.2.1 From 3c54a951b3c29ad252eab9c6ae887fafed300a86 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 21 Mar 2013 17:28:32 +0000 Subject: fix typo --- src/rabbit_limiter.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index 602681e5..dfed5cb4 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -21,7 +21,7 @@ %% %% Each channel has an associated limiter process, created with %% start_link/1, which it passes to queues on consumer creation with -%% rabbit_amqqueue:basic_consume/8, and rabbit_amqqueue:basic_get/4. +%% rabbit_amqqueue:basic_consume/9, and rabbit_amqqueue:basic_get/4. %% The latter isn't strictly necessary, since basic.get is not %% subject to limiting, but it means that whenever a queue knows about %% a channel, it also knows about its limiter, which is less fiddly. -- cgit v1.2.1 From b357f2876a2a3de329aa67c9ef239bdfa26b1e96 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 21 Mar 2013 17:29:33 +0000 Subject: get rid of unused imports --- src/rabbit_limiter.erl | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index dfed5cb4..489cfe37 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -118,8 +118,6 @@ -export([init/1, terminate/2, code_change/3, handle_call/3, handle_cast/2, handle_info/2, prioritise_call/3]). --import(rabbit_misc, [serial_add/2, serial_diff/2]). - %%---------------------------------------------------------------------------- -record(lstate, {pid, prefetch_limited, blocked}). -- cgit v1.2.1 From 9ab1619d08614af98fd0ccc48373470d37424530 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 22 Mar 2013 11:17:33 +0000 Subject: Move serial arithmetic out of the broker. --- src/rabbit_misc.erl | 51 --------------------------------------------------- src/rabbit_tests.erl | 24 ------------------------ 2 files changed, 75 deletions(-) diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 135f6443..c36fb147 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -69,7 +69,6 @@ -export([interval_operation/4]). -export([ensure_timer/4, stop_timer/2]). -export([get_parent/0]). --export([serial_add/2, serial_compare/2, serial_diff/2]). %% Horrible macro to use in guards -define(IS_BENIGN_EXIT(R), @@ -84,7 +83,6 @@ -ifdef(use_specs). -export_type([resource_name/0, thunk/1]). --export_type([serial_number/0]). -type(ok_or_error() :: rabbit_types:ok_or_error(any())). -type(thunk(T) :: fun(() -> T)). @@ -97,8 +95,6 @@ fun ((atom(), [term()]) -> [{digraph:vertex(), digraph_label()}])). -type(graph_edge_fun() :: fun ((atom(), [term()]) -> [{digraph:vertex(), digraph:vertex()}])). --type(serial_number() :: non_neg_integer()). --type(serial_compare_result() :: 'equal' | 'less' | 'greater'). -spec(method_record_type/1 :: (rabbit_framing:amqp_method_record()) -> rabbit_framing:amqp_method_name()). @@ -250,12 +246,6 @@ -spec(ensure_timer/4 :: (A, non_neg_integer(), non_neg_integer(), any()) -> A). -spec(stop_timer/2 :: (A, non_neg_integer()) -> A). -spec(get_parent/0 :: () -> pid()). --spec(serial_add/2 :: (serial_number(), non_neg_integer()) -> - serial_number()). --spec(serial_compare/2 :: (serial_number(), serial_number()) -> - serial_compare_result()). --spec(serial_diff/2 :: (serial_number(), serial_number()) -> - integer()). -endif. %%---------------------------------------------------------------------------- @@ -1109,44 +1099,3 @@ whereis_name(Name) -> %% End copypasta from gen_server2.erl %% ------------------------------------------------------------------------- - -%% Serial arithmetic for unsigned ints. -%% http://www.faqs.org/rfcs/rfc1982.html -%% SERIAL_BITS = 32 - -%% 2 ^ SERIAL_BITS --define(SERIAL_MAX, 16#100000000). -%% 2 ^ (SERIAL_BITS - 1) - 1 --define(SERIAL_MAX_ADDEND, 16#7fffffff). - -serial_add(S, N) when N =< ?SERIAL_MAX_ADDEND -> - (S + N) rem ?SERIAL_MAX; -serial_add(S, N) -> - exit({out_of_bound_serial_addition, S, N}). - -serial_compare(A, B) -> - if A =:= B -> - equal; - (A < B andalso B - A < ?SERIAL_MAX_ADDEND) orelse - (A > B andalso A - B > ?SERIAL_MAX_ADDEND) -> - less; - (A < B andalso B - A > ?SERIAL_MAX_ADDEND) orelse - (A > B andalso B - A < ?SERIAL_MAX_ADDEND) -> - greater; - true -> exit({indeterminate_serial_comparison, A, B}) - end. - --define(SERIAL_DIFF_BOUND, 16#80000000). - -serial_diff(A, B) -> - Diff = A - B, - if Diff > (?SERIAL_DIFF_BOUND) -> - %% B is actually greater than A - - (?SERIAL_MAX - Diff); - Diff < - (?SERIAL_DIFF_BOUND) -> - ?SERIAL_MAX + Diff; - Diff < ?SERIAL_DIFF_BOUND andalso Diff > -?SERIAL_DIFF_BOUND -> - Diff; - true -> - exit({indeterminate_serial_diff, A, B}) - end. diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 44b7fc4a..e7b69879 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -50,7 +50,6 @@ all_tests() -> passed = test_table_codec(), passed = test_content_framing(), passed = test_content_transcoding(), - passed = test_serial_arithmetic(), passed = test_topic_matching(), passed = test_log_management(), passed = test_app_management(), @@ -560,29 +559,6 @@ sequence_with_content(Sequence) -> rabbit_framing_amqp_0_9_1), Sequence). -test_serial_arithmetic() -> - 1 = rabbit_misc:serial_add(0, 1), - 16#7fffffff = rabbit_misc:serial_add(0, 16#7fffffff), - 0 = rabbit_misc:serial_add(16#ffffffff, 1), - %% Cannot add more than 2 ^ 31 - 1 - case catch rabbit_misc:serial_add(200, 16#80000000) of - {'EXIT', {out_of_bound_serial_addition, _, _}} -> ok; - _ -> exit(fail_out_of_bound_serial_addition) - end, - - 1 = rabbit_misc:serial_diff(1, 0), - 2 = rabbit_misc:serial_diff(1, 16#ffffffff), - -2 = rabbit_misc:serial_diff(16#ffffffff, 1), - case catch rabbit_misc:serial_diff(0, 16#80000000) of - {'EXIT', {indeterminate_serial_diff, _, _}} -> ok; - _ -> exit(fail_indeterminate_serial_difference) - end, - case catch rabbit_misc:serial_diff(16#ffffffff, 16#7fffffff) of - {'EXIT', {indeterminate_serial_diff, _, _}} -> ok; - _ -> exit(fail_indeterminate_serial_difference) - end, - passed. - test_topic_matching() -> XName = #resource{virtual_host = <<"/">>, kind = exchange, -- cgit v1.2.1 From 7aeba22413273a33eb3693f4d216d6d17425019d Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 22 Mar 2013 12:05:17 +0000 Subject: Update essay for credit (and fix a few typos I couldn't be bothered to separate out). --- src/rabbit_limiter.erl | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index 489cfe37..3a279940 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -16,8 +16,9 @@ %% The purpose of the limiter is to stem the flow of messages from %% queues to channels, in order to act upon various protocol-level -%% flow control mechanisms, specifically AMQP's basic.qos -%% prefetch_count and channel.flow. +%% flow control mechanisms, specifically AMQP 0-9-1's basic.qos +%% prefetch_count and channel.flow, and AMQP 1.0's link (aka consumer) +%% credit mechanism. %% %% Each channel has an associated limiter process, created with %% start_link/1, which it passes to queues on consumer creation with @@ -26,7 +27,7 @@ %% subject to limiting, but it means that whenever a queue knows about %% a channel, it also knows about its limiter, which is less fiddly. %% -%% Th limiter process holds state that is, in effect, shared between +%% The limiter process holds state that is, in effect, shared between %% the channel and all queues from which the channel is %% consuming. Essentially all these queues are competing for access to %% a single, limited resource - the ability to deliver messages via @@ -54,15 +55,27 @@ %% inactive. In practice it is rare for that to happen, though we %% could optimise this case in the future. %% +%% In addition, the consumer credit bookkeeping is local to queues, so +%% it is not necessary to store information about it in the limiter +%% process. But for abstraction we hide it from the queue behind the +%% limiter API, and it therefore becomes part of the queue local +%% state. +%% %% The interactions with the limiter are as follows: %% %% 1. Channels tell the limiter about basic.qos prefetch counts - %% that's what the limit_prefetch/3, unlimit_prefetch/1, %% is_prefetch_limited/1, get_prefetch_limit/1 API functions are %% about - and channel.flow blocking - that's what block/1, -%% unblock/1 and is_blocked/1 are for. +%% unblock/1 and is_blocked/1 are for. They also tell the limiter +%% queue state (via the queue) about consumer credit changes - +%% that's what credit/4 is for. +%% +%% 2. Queues also tell the limiter queue state about the queue +%% becoming empty (via drained/1) and consumers leaving (via +%% forget_consumer/2). %% -%% 2. Queues register with the limiter - this happens as part of +%% 3. Queues register with the limiter - this happens as part of %% activate/1. %% %% 4. The limiter process maintains an internal counter of 'messages @@ -70,13 +83,14 @@ %% %% 5. Queues ask the limiter for permission (with can_send/3) whenever %% they want to deliver a message to a channel. The limiter checks -%% whether a) the channel isn't blocked by channel.flow, and b) the -%% volume has not yet reached the prefetch limit. If so it -%% increments the volume and tells the queue to proceed. Otherwise -%% it marks the queue as requiring notification (see below) and -%% tells the queue not to proceed. +%% whether a) the channel isn't blocked by channel.flow, b) the +%% volume has not yet reached the prefetch limit, and c) whether +%% the consumer has enough credit. If so it increments the volume +%% and tells the queue to proceed. Otherwise it marks the queue as +%% requiring notification (see below) and tells the queue not to +%% proceed. %% -%% 6. A queue that has told to proceed (by the return value of +%% 6. A queue that has been told to proceed (by the return value of %% can_send/3) sends the message to the channel. Conversely, a %% queue that has been told not to proceed, will not attempt to %% deliver that message, or any future messages, to the @@ -95,7 +109,7 @@ %% the channel, i.e. they will once again start asking limiter, as %% described in (5). %% -%% 9. When a queues has no more consumers associated with a particular +%% 9. When a queue has no more consumers associated with a particular %% channel, it deactivates use of the limiter with deactivate/1, %% which alters the local state such that no further interactions %% with the limiter process take place until a subsequent -- cgit v1.2.1 From b48a5cd44111cfb3d8bdac86d2d0063a1027b1e9 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Fri, 22 Mar 2013 12:47:21 +0000 Subject: Move decorators to exchange record --- include/rabbit.hrl | 2 +- src/rabbit_exchange.erl | 57 +++++++++++++++++---------------------- src/rabbit_exchange_decorator.erl | 39 ++++++++++++++++++++++----- src/rabbit_policy.erl | 19 ++++++++----- src/rabbit_tests.erl | 5 ++-- 5 files changed, 73 insertions(+), 49 deletions(-) diff --git a/include/rabbit.hrl b/include/rabbit.hrl index eeee799e..4282755d 100644 --- a/include/rabbit.hrl +++ b/include/rabbit.hrl @@ -40,7 +40,7 @@ -record(resource, {virtual_host, kind, name}). -record(exchange, {name, type, durable, auto_delete, internal, arguments, - scratches, policy}). + scratches, policy, decorators}). -record(exchange_serial, {name, next}). -record(amqqueue, {name, durable, auto_delete, exclusive_owner = none, diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index 9e98448d..16cfa8e3 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -115,23 +115,23 @@ recover() -> rabbit_durable_exchange), [XName || #exchange{name = XName} <- Xs]. -callback(X = #exchange{type = XType}, Fun, Serial0, Args) -> +callback(X = #exchange{type = XType, + decorators = Decorators}, Fun, Serial0, Args) -> Serial = if is_function(Serial0) -> Serial0; is_atom(Serial0) -> fun (_Bool) -> Serial0 end end, [ok = apply(M, Fun, [Serial(M:serialise_events(X)) | Args]) || - M <- registry_lookup(exchange_decorator)], + M <- rabbit_exchange_decorator:select(all, Decorators)], Module = type_to_module(XType), apply(Module, Fun, [Serial(Module:serialise_events()) | Args]). policy_changed(X = #exchange{type = XType}, X1) -> - [ok = M:policy_changed(X, X1) || - M <- [type_to_module(XType) | registry_lookup(exchange_decorator)]], - ok. + ok = (type_to_module(XType)):policy_changed(X, X1). -serialise_events(X = #exchange{type = Type}) -> - lists:any(fun (M) -> M:serialise_events(X) end, - registry_lookup(exchange_decorator)) +serialise_events(X = #exchange{type = Type, decorators = Decorators}) -> + lists:any(fun (M) -> + M:serialise_events(X) + end, rabbit_exchange_decorator:select(all, Decorators)) orelse (type_to_module(Type)):serialise_events(). serial(#exchange{name = XName} = X) -> @@ -143,23 +143,14 @@ serial(#exchange{name = XName} = X) -> (false) -> none end. -registry_lookup(exchange_decorator_route = Class) -> - case get(exchange_decorator_route_modules) of - undefined -> Mods = [M || {_, M} <- rabbit_registry:lookup_all(Class)], - put(exchange_decorator_route_modules, Mods), - Mods; - Mods -> Mods - end; -registry_lookup(Class) -> - [M || {_, M} <- rabbit_registry:lookup_all(Class)]. - declare(XName, Type, Durable, AutoDelete, Internal, Args) -> - X = rabbit_policy:set(#exchange{name = XName, - type = Type, - durable = Durable, - auto_delete = AutoDelete, - internal = Internal, - arguments = Args}), + X0 = rabbit_policy:set(#exchange{name = XName, + type = Type, + durable = Durable, + auto_delete = AutoDelete, + internal = Internal, + arguments = Args}), + X = rabbit_exchange_decorator:record(X0, rabbit_exchange_decorator:list()), XT = type_to_module(Type), %% We want to upset things if it isn't ok ok = XT:validate(X), @@ -318,25 +309,25 @@ info_all(VHostPath) -> map(VHostPath, fun (X) -> info(X) end). info_all(VHostPath, Items) -> map(VHostPath, fun (X) -> info(X, Items) end). -route(#exchange{name = #resource{virtual_host = VHost, - name = RName} = XName} = X, +route(#exchange{name = #resource{virtual_host = VHost, name = RName} = XName, + decorators = Decorators} = X, #delivery{message = #basic_message{routing_keys = RKs}} = Delivery) -> - case {registry_lookup(exchange_decorator_route), RName == <<"">>} of - {[], true} -> + case {RName, rabbit_exchange_decorator:select(route, Decorators)} of + {<<"">>, []} -> %% Optimisation [rabbit_misc:r(VHost, queue, RK) || RK <- lists:usort(RKs)]; - {Decorators, _} -> - lists:usort(route1(Delivery, Decorators, {[X], XName, []})) + {_, RDecorators} -> + lists:usort(route1(Delivery, RDecorators, {[X], XName, []})) end. route1(_, _, {[], _, QNames}) -> QNames; -route1(Delivery, Decorators, +route1(Delivery, RDecorators, {[X = #exchange{type = Type} | WorkList], SeenXs, QNames}) -> ExchangeDests = (type_to_module(Type)):route(X, Delivery), - DecorateDests = process_decorators(X, Decorators, Delivery), + DecorateDests = process_decorators(X, RDecorators, Delivery), AlternateDests = process_alternate(X, ExchangeDests), - route1(Delivery, Decorators, + route1(Delivery, RDecorators, lists:foldl(fun process_route/2, {WorkList, SeenXs, QNames}, AlternateDests ++ DecorateDests ++ ExchangeDests)). diff --git a/src/rabbit_exchange_decorator.erl b/src/rabbit_exchange_decorator.erl index 040b55db..240ddd9a 100644 --- a/src/rabbit_exchange_decorator.erl +++ b/src/rabbit_exchange_decorator.erl @@ -16,6 +16,10 @@ -module(rabbit_exchange_decorator). +-include("rabbit.hrl"). + +-export([list/0, select/2, record/2]). + %% This is like an exchange type except that: %% %% 1) It applies to all exchanges as soon as it is installed, therefore @@ -45,10 +49,6 @@ -callback delete(tx(), rabbit_types:exchange(), [rabbit_types:binding()]) -> 'ok'. -%% called when the policy attached to this exchange changes. --callback policy_changed(rabbit_types:exchange(), rabbit_types:exchange()) -> - 'ok'. - %% called after a binding has been added or recovered -callback add_binding(serial(), rabbit_types:exchange(), rabbit_types:binding()) -> 'ok'. @@ -59,8 +59,12 @@ %% Decorators can optionally implement route/2 which allows additional %% destinations to be added to the routing decision. -%% -callback route(rabbit_types:exchange(), rabbit_types:delivery()) -> -%% [rabbit_amqqueue:name() | rabbit_exchange:name()]. +-callback route(rabbit_types:exchange(), rabbit_types:delivery()) -> + [rabbit_amqqueue:name() | rabbit_exchange:name()] | ok. + +%% Whether the decorator wishes to receive callbacks for the exchange +%% none:no callbacks, noroute:all callbacks except route, all:all callbacks +-callback active_for(rabbit_types:exchange()) -> 'none' | 'noroute' | 'all'. -else. @@ -68,8 +72,29 @@ behaviour_info(callbacks) -> [{description, 0}, {serialise_events, 1}, {create, 2}, {delete, 3}, - {policy_changed, 2}, {add_binding, 3}, {remove_bindings, 3}]; + {policy_changed, 2}, {add_binding, 3}, {remove_bindings, 3}, + {active_for, 1}]; behaviour_info(_Other) -> undefined. -endif. + +%%---------------------------------------------------------------------------- + +list() -> [M || {_, M} <- rabbit_registry:lookup_all(exchange_decorator)]. + +%% select a subset of active decorators +select(all, {Route, NoRoute}) -> Route ++ NoRoute; +select(route, {Route, _NoRoute}) -> Route. + +%% record active decorators in an exchange +record(X, Decorators) -> + X#exchange{decorators = + lists:foldl(fun (D, {Route, NoRoute}) -> + Callbacks = D:active_for(X), + {cons_if_eq(all, Callbacks, D, Route), + cons_if_eq(noroute, Callbacks, D, NoRoute)} + end, {[], []}, Decorators)}. + +cons_if_eq(Select, Select, Item, List) -> [Item | List]; +cons_if_eq(_Select, _Other, _Item, List) -> List. diff --git a/src/rabbit_policy.erl b/src/rabbit_policy.erl index 7398cd2d..22e9bdac 100644 --- a/src/rabbit_policy.erl +++ b/src/rabbit_policy.erl @@ -156,9 +156,10 @@ notify_clear(VHost, <<"policy">>, _Name) -> update_policies(VHost) -> Policies = list(VHost), + Decorators = rabbit_exchange_decorator:list(), {Xs, Qs} = rabbit_misc:execute_mnesia_transaction( fun() -> - {[update_exchange(X, Policies) || + {[update_exchange(X, Policies, Decorators) || X <- rabbit_exchange:list(VHost)], [update_queue(Q, Policies) || Q <- rabbit_amqqueue:list(VHost)]} @@ -167,12 +168,18 @@ update_policies(VHost) -> [catch notify(Q) || Q <- Qs], ok. -update_exchange(X = #exchange{name = XName, policy = OldPolicy}, Policies) -> +update_exchange(X = #exchange{name = XName, policy = OldPolicy}, + Policies, Decorators) -> case match(XName, Policies) of - OldPolicy -> no_change; - NewPolicy -> rabbit_exchange:update( - XName, fun(X1) -> X1#exchange{policy = NewPolicy} end), - {X, X#exchange{policy = NewPolicy}} + OldPolicy -> + no_change; + NewPolicy -> + rabbit_exchange:update( + XName, fun(X1) -> + rabbit_exchange_decorator:record( + X1#exchange{policy = NewPolicy}, Decorators) + end), + {X, X#exchange{policy = NewPolicy}} end. update_queue(Q = #amqqueue{name = QName, policy = OldPolicy}, Policies) -> diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index b2c80364..91f560cb 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -563,8 +563,9 @@ test_topic_matching() -> XName = #resource{virtual_host = <<"/">>, kind = exchange, name = <<"test_exchange">>}, - X = #exchange{name = XName, type = topic, durable = false, - auto_delete = false, arguments = []}, + X0 = #exchange{name = XName, type = topic, durable = false, + auto_delete = false, arguments = []}, + X = rabbit_exchange_decorator:record(X0, []), %% create rabbit_exchange_type_topic:validate(X), exchange_op_callback(X, create, []), -- cgit v1.2.1 From 4dbd221c2a2486676d7ee031c9b4b1ce8d5355aa Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 22 Mar 2013 13:43:07 +0000 Subject: Backout e2d962df4128 until we can make that work properly... --- src/rabbit_node_monitor.erl | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index 3872f3df..de53b7f0 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -318,9 +318,6 @@ alive_nodes() -> Nodes = rabbit_mnesia:cluster_nodes(all), [N || N <- Nodes, pong =:= net_adm:ping(N)]. -alive_rabbit_nodes() -> - [N || N <- alive_nodes(), rabbit_nodes:is_running(N, rabbit)]. - await_cluster_recovery() -> rabbit_log:warning("Cluster minority status detected - awaiting recovery~n", []), @@ -349,7 +346,7 @@ handle_dead_rabbit_state(State = #state{partitions = Partitions}) -> %% that we do not attempt to deal with individual (other) partitions %% going away. It's only safe to forget anything about partitions when %% there are no partitions. - Partitions1 = case Partitions -- (Partitions -- alive_rabbit_nodes()) of + Partitions1 = case Partitions -- (Partitions -- alive_nodes()) of [] -> []; _ -> Partitions end, -- cgit v1.2.1 From 24667bfce161c25bfca584e85d3ebec65156930e Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 22 Mar 2013 14:40:47 +0000 Subject: Check if the rabbit process is running and thus avoid deadlocks in the application controller. --- src/rabbit_node_monitor.erl | 2 +- src/rabbit_nodes.erl | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index 3872f3df..263f960d 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -319,7 +319,7 @@ alive_nodes() -> [N || N <- Nodes, pong =:= net_adm:ping(N)]. alive_rabbit_nodes() -> - [N || N <- alive_nodes(), rabbit_nodes:is_running(N, rabbit)]. + [N || N <- alive_nodes(), rabbit_nodes:is_process_running(N, rabbit)]. await_cluster_recovery() -> rabbit_log:warning("Cluster minority status detected - awaiting recovery~n", diff --git a/src/rabbit_nodes.erl b/src/rabbit_nodes.erl index c92e5963..5640f12a 100644 --- a/src/rabbit_nodes.erl +++ b/src/rabbit_nodes.erl @@ -16,7 +16,8 @@ -module(rabbit_nodes). --export([names/1, diagnostics/1, make/1, parts/1, cookie_hash/0, is_running/2]). +-export([names/1, diagnostics/1, make/1, parts/1, cookie_hash/0, + is_running/2, is_process_running/2]). -define(EPMD_TIMEOUT, 30000). @@ -33,6 +34,7 @@ -spec(parts/1 :: (node() | string()) -> {string(), string()}). -spec(cookie_hash/0 :: () -> string()). -spec(is_running/2 :: (node(), atom()) -> boolean()). +-spec(is_process_running/2 :: (node(), atom()) -> boolean()). -endif. @@ -98,3 +100,10 @@ is_running(Node, Application) -> {badrpc, _} -> false; Apps -> proplists:is_defined(Application, Apps) end. + +is_process_running(Node, Process) -> + case rpc:call(Node, erlang, whereis, [Process]) of + {badrpc, _} -> false; + undefined -> false; + P when is_pid(P) -> true + end. -- cgit v1.2.1 From 96de9093402fb2fb789ebd7166ba578787590e9d Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Mon, 25 Mar 2013 11:12:54 +0000 Subject: Cosmetic --- src/rabbit_exchange.erl | 4 ++-- src/rabbit_exchange_decorator.erl | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index 16cfa8e3..aa697f07 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -325,11 +325,11 @@ route1(_, _, {[], _, QNames}) -> route1(Delivery, RDecorators, {[X = #exchange{type = Type} | WorkList], SeenXs, QNames}) -> ExchangeDests = (type_to_module(Type)):route(X, Delivery), - DecorateDests = process_decorators(X, RDecorators, Delivery), + RDecorateDests = process_decorators(X, RDecorators, Delivery), AlternateDests = process_alternate(X, ExchangeDests), route1(Delivery, RDecorators, lists:foldl(fun process_route/2, {WorkList, SeenXs, QNames}, - AlternateDests ++ DecorateDests ++ ExchangeDests)). + AlternateDests ++ RDecorateDests ++ ExchangeDests)). process_alternate(#exchange{arguments = []}, _Results) -> %% optimisation []; diff --git a/src/rabbit_exchange_decorator.erl b/src/rabbit_exchange_decorator.erl index 240ddd9a..cdbc42bb 100644 --- a/src/rabbit_exchange_decorator.erl +++ b/src/rabbit_exchange_decorator.erl @@ -94,7 +94,7 @@ record(X, Decorators) -> Callbacks = D:active_for(X), {cons_if_eq(all, Callbacks, D, Route), cons_if_eq(noroute, Callbacks, D, NoRoute)} - end, {[], []}, Decorators)}. + end, {[], []}, Decorators)}. cons_if_eq(Select, Select, Item, List) -> [Item | List]; cons_if_eq(_Select, _Other, _Item, List) -> List. -- cgit v1.2.1 From e3938b32731757085de356093f9cc76835a575e9 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 25 Mar 2013 14:24:59 +0000 Subject: Use a macro --- src/rabbit_node_monitor.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index 16b52a4d..dd59a0c7 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -361,7 +361,7 @@ await_cluster_recovery() -> wait_for_cluster_recovery(Nodes) -> case majority() of true -> rabbit:start(); - false -> timer:sleep(1000), + false -> timer:sleep(?RABBIT_DOWN_PING_INTERVAL), wait_for_cluster_recovery(Nodes) end. -- cgit v1.2.1 From ededc010f092f61aca0336abeadffa27c46703ee Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 25 Mar 2013 14:39:53 +0000 Subject: Don't call rabbit_mnesia:cluster_nodes(all) as much, don't use floating point if we don't have to. --- src/rabbit_node_monitor.erl | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index dd59a0c7..ead0c661 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -280,15 +280,15 @@ handle_info(ping_nodes, State) -> %% to ping the nodes that are up, after all. State1 = State#state{down_ping_timer = undefined}, Self = self(), - %% ratio() both pings all the nodes and tells us if we need to again. + %% all_nodes_up() both pings all the nodes and tells us if we need to again. %% %% We ping in a separate process since in a partition it might %% take some noticeable length of time and we don't want to block %% the node monitor for that long. spawn_link(fun () -> - case ratio() of - 1.0 -> ok; - _ -> Self ! ping_again + case all_nodes_up() of + true -> ok; + false -> Self ! ping_again end end), {noreply, State1}; @@ -333,15 +333,20 @@ handle_dead_rabbit(Node) -> end, ok. -majority() -> ratio() > 0.5. -ratio() -> length(alive_nodes()) / length(rabbit_mnesia:cluster_nodes(all)). +majority() -> + Nodes = rabbit_mnesia:cluster_nodes(all), + length(alive_nodes(Nodes)) / length(Nodes) > 0.5. + +all_nodes_up() -> + Nodes = rabbit_mnesia:cluster_nodes(all), + length(alive_nodes(Nodes)) =:= length(Nodes). %% mnesia:system_info(db_nodes) (and hence %% rabbit_mnesia:cluster_nodes(running)) does not give reliable results %% when partitioned. -alive_nodes() -> - Nodes = rabbit_mnesia:cluster_nodes(all), - [N || N <- Nodes, pong =:= net_adm:ping(N)]. +alive_nodes() -> alive_nodes(rabbit_mnesia:cluster_nodes(all)). + +alive_nodes(Nodes) -> [N || N <- Nodes, pong =:= net_adm:ping(N)]. await_cluster_recovery() -> rabbit_log:warning("Cluster minority status detected - awaiting recovery~n", -- cgit v1.2.1 From 137e6b0568eef1603cdfc9921f906e85548ff79a Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 26 Mar 2013 16:48:57 +0000 Subject: Add some logging --- src/rabbit_node_monitor.erl | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index aac2bf84..ca39440b 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -284,6 +284,7 @@ handle_info({autoheal_request_winner, Node}, State = #state{autoheal = {wait_for_winner_reqs,[Node], Notify}}) -> %% TODO actually do something sensible to figure out who the winner is Winner = self(), + rabbit_log:info("Autoheal: winner is ~p~n", [Winner]), [{?MODULE, N} ! {autoheal_winner, Winner} || N <- Notify], {noreply, State#state{autoheal = wait_for_winner}}; @@ -298,10 +299,15 @@ handle_info({autoheal_winner, Winner}, Node = node(Winner), case lists:member(Node, Partitions) of false -> case node() of - Node -> {noreply, + Node -> rabbit_log:info( + "Autoheal: waiting for nodes to stop: ~p~n", + [Partitions]), + {noreply, State#state{autoheal = {wait_for, Partitions, Partitions}}}; - _ -> {noreply, State#state{autoheal = not_healing}} + _ -> rabbit_log:info( + "Autoheal: nothing to do~n", []), + {noreply, State#state{autoheal = not_healing}} end; true -> autoheal_restart(Winner), {noreply, State} @@ -313,6 +319,7 @@ handle_info({autoheal_winner, _Winner}, State) -> handle_info({autoheal_node_stopped, Node}, State = #state{autoheal = {wait_for, [Node], Notify}}) -> + rabbit_log:info("Autoheal: final node has stopped, starting...~n",[]), [{rabbit_outside_app_process, N} ! autoheal_safe_to_start || N <- Notify], {noreply, State#state{autoheal = not_healing}}; @@ -423,6 +430,7 @@ wait_for_cluster_recovery(Nodes) -> %% until it has seen a request from every node. autoheal(State) -> [Leader | _] = All = lists:usort(rabbit_mnesia:cluster_nodes(all)), + rabbit_log:info("Autoheal: leader is ~p~n", [Leader]), {?MODULE, Leader} ! {autoheal_request_winner, node()}, State#state{autoheal = case node() of Leader -> {wait_for_winner_reqs, All, All}; @@ -431,7 +439,7 @@ autoheal(State) -> autoheal_restart(Winner) -> rabbit_log:warning( - "Cluster partition healed; we were selected to restart~n", []), + "Autoheal: we were selected to restart; winner is ~p~n", [node(Winner)]), run_outside_applications( fun () -> MRef = erlang:monitor(process, Winner), -- cgit v1.2.1 From 81ef7e1ba1ad331d29d697dd1fadde6dc7fc6f01 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 26 Mar 2013 16:49:15 +0000 Subject: Figure out a global view of partitions. --- src/rabbit_node_monitor.erl | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index ca39440b..c2c81545 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -281,8 +281,10 @@ handle_info({mnesia_system_event, {noreply, State2#state{partitions = Partitions1}}; handle_info({autoheal_request_winner, Node}, - State = #state{autoheal = {wait_for_winner_reqs,[Node], Notify}}) -> + State = #state{autoheal = {wait_for_winner_reqs,[Node], Notify}, + partitions = Partitions}) -> %% TODO actually do something sensible to figure out who the winner is + AllPartitions = all_partitions(Partitions), Winner = self(), rabbit_log:info("Autoheal: winner is ~p~n", [Winner]), [{?MODULE, N} ! {autoheal_winner, Winner} || N <- Notify], @@ -453,6 +455,24 @@ autoheal_restart(Winner) -> rabbit:start() end). +%% We have our local understanding of what partitions exist; but we +%% only know which nodes we have been partitioned from, not which +%% nodes are partitioned from each other. +%% +%% Note that here we assume that partition information is +%% consistent. If it isn't, what can we do? +all_partitions(PartitionedWith) -> + All = rabbit_mnesia:cluster_nodes(all), + OurPartition = All -- PartitionedWith, + all_partitions([OurPartition], PartitionedWith, All). + +all_partitions(AllPartitions, [], _) -> + AllPartitions; +all_partitions(AllPartitions, [One | _] = ToDo, All) -> + {One, PartitionedFrom} = rpc:call(One, rabbit_node_monitor, partitions, []), + Partition = All -- PartitionedFrom, + all_partitions([Partition | AllPartitions], ToDo -- Partition, All). + handle_dead_rabbit_state(State = #state{partitions = Partitions, autoheal = Autoheal}) -> %% If we have been partitioned, and we are now in the only remaining -- cgit v1.2.1 From 46eb23b9f4b1c693c37abfdf9e55203e688f50e8 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 26 Mar 2013 17:54:00 +0000 Subject: Simplistic way of selecting a winner: order by the number of connections, then the size of the partition. --- src/rabbit_node_monitor.erl | 47 +++++++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index c2c81545..957a9025 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -283,9 +283,7 @@ handle_info({mnesia_system_event, handle_info({autoheal_request_winner, Node}, State = #state{autoheal = {wait_for_winner_reqs,[Node], Notify}, partitions = Partitions}) -> - %% TODO actually do something sensible to figure out who the winner is - AllPartitions = all_partitions(Partitions), - Winner = self(), + Winner = autoheal_select_winner(all_partitions(Partitions)), rabbit_log:info("Autoheal: winner is ~p~n", [Winner]), [{?MODULE, N} ! {autoheal_winner, Winner} || N <- Notify], {noreply, State#state{autoheal = wait_for_winner}}; @@ -298,18 +296,17 @@ handle_info({autoheal_request_winner, Node}, handle_info({autoheal_winner, Winner}, State = #state{autoheal = wait_for_winner, partitions = Partitions}) -> - Node = node(Winner), - case lists:member(Node, Partitions) of + case lists:member(Winner, Partitions) of false -> case node() of - Node -> rabbit_log:info( - "Autoheal: waiting for nodes to stop: ~p~n", - [Partitions]), - {noreply, - State#state{autoheal = {wait_for, Partitions, - Partitions}}}; - _ -> rabbit_log:info( - "Autoheal: nothing to do~n", []), - {noreply, State#state{autoheal = not_healing}} + Winner -> rabbit_log:info( + "Autoheal: waiting for nodes to stop: ~p~n", + [Partitions]), + {noreply, + State#state{autoheal = {wait_for, Partitions, + Partitions}}}; + _ -> rabbit_log:info( + "Autoheal: nothing to do~n", []), + {noreply, State#state{autoheal = not_healing}} end; true -> autoheal_restart(Winner), {noreply, State} @@ -439,17 +436,29 @@ autoheal(State) -> _ -> wait_for_winner end}. +autoheal_select_winner(AllPartitions) -> + {_, [Winner | _]} = hd(lists:sort( + [{autoheal_value(P), P} || P <- AllPartitions])), + Winner. + +autoheal_value(Partition) -> + Connections = [Res || Node <- Partition, + Res <- [rpc:call(Node, rabbit_networking, + connections_local, [])], + is_list(Res)], + {length(lists:append(Connections)), length(Partition)}. + autoheal_restart(Winner) -> rabbit_log:warning( - "Autoheal: we were selected to restart; winner is ~p~n", [node(Winner)]), + "Autoheal: we were selected to restart; winner is ~p~n", [Winner]), run_outside_applications( fun () -> - MRef = erlang:monitor(process, Winner), + MRef = erlang:monitor(process, {?MODULE, Winner}), rabbit:stop(), - Winner ! {autoheal_node_stopped, node()}, + {?MODULE, Winner} ! {autoheal_node_stopped, node()}, receive - {'DOWN', MRef, process, Winner, _Reason} -> ok; - autoheal_safe_to_start -> ok + {'DOWN', MRef, process, {?MODULE, Winner}, _Reason} -> ok; + autoheal_safe_to_start -> ok end, erlang:demonitor(MRef, [flush]), rabbit:start() -- cgit v1.2.1 From c1039e1e1d64228b8ae93acc3acb3d919997bd88 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 27 Mar 2013 15:29:51 +0000 Subject: Cosmetic --- src/rabbit_node_monitor.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index 843ccb1f..aaacc200 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -285,7 +285,8 @@ handle_info({autoheal_request_winner, Node}, State = #state{autoheal = {wait_for_winner_reqs,[Node], Notify}, partitions = Partitions}) -> Winner = autoheal_select_winner(all_partitions(Partitions)), - rabbit_log:info("Autoheal request winner from ~p: winner is ~p~n", [Node, Winner]), + rabbit_log:info("Autoheal request winner from ~p: winner is ~p~n", + [Node, Winner]), [{?MODULE, N} ! {autoheal_winner, Winner} || N <- Notify], {noreply, State#state{autoheal = wait_for_winner}}; -- cgit v1.2.1 From f722ead2119d5ffc9baf131999d783723e24190d Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 28 Mar 2013 13:26:30 +0000 Subject: WIP commit: start to deal with non-consistent partitions. all_partitions/1 needs attention though. --- src/rabbit_node_monitor.erl | 70 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 54 insertions(+), 16 deletions(-) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index ffd02fc4..edb197fa 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -252,6 +252,7 @@ handle_info({'DOWN', _MRef, process, {rabbit, Node}, _Reason}, ok = handle_dead_rabbit(Node), [P ! {node_down, Node} || P <- pmon:monitored(Subscribers)], {noreply, handle_dead_rabbit_state( + Node, State#state{monitors = pmon:erase({rabbit, Node}, Monitors)})}; handle_info({'DOWN', _MRef, process, Pid, _Reason}, @@ -282,11 +283,40 @@ handle_info({mnesia_system_event, {noreply, State2#state{partitions = Partitions1}}; handle_info({autoheal_request_winner, Node}, - State = #state{autoheal = {wait_for_winner_reqs,[Node], Notify}, + State = #state{autoheal = not_healing, partitions = Partitions}) -> - Winner = autoheal_select_winner(all_partitions(Partitions)), - rabbit_log:info("Autoheal request winner from ~p: winner is ~p~n", - [Node, Winner]), + case all_nodes_up() of + false -> {noreply, State}; + true -> Nodes = rabbit_mnesia:cluster_nodes(all), + Partitioned = + [N || N <- Nodes -- [node()], + P <- [begin + {_, R} = rpc:call(N, rabbit_node_monitor, + partitions, []), + R + end], + is_list(P) andalso length(P) > 0], + Partitioned1 = case Partitions of + [] -> Partitioned; + _ -> [node() | Partitioned] + end, + rabbit_log:info( + "Autoheal leader start; partitioned nodes are ~p~n", + [Partitioned1]), + Autoheal = {wait_for_winner_reqs, Partitioned1, Partitioned1}, + handle_info({autoheal_request_winner, Node}, + State#state{autoheal = Autoheal}) + end; + +handle_info({autoheal_request_winner, Node}, + State = #state{autoheal = {wait_for_winner_reqs, [Node], Notify}, + partitions = Partitions}) -> + AllPartitions = all_partitions(Partitions), + io:format("all partitions: ~p~n", [AllPartitions]), + Winner = autoheal_select_winner(AllPartitions), + rabbit_log:info("Autoheal request winner from ~p~n" + " Partitions were determined to be ~p~n" + " Winner is ~p~n", [Node, AllPartitions, Winner]), [{?MODULE, N} ! {autoheal_winner, Winner} || N <- Notify], {noreply, State#state{autoheal = wait_for_winner}}; @@ -299,6 +329,7 @@ handle_info({autoheal_request_winner, Node}, handle_info({autoheal_winner, Winner}, State = #state{autoheal = wait_for_winner, partitions = Partitions}) -> + io:format("got winner ~p~n", [[Winner, Partitions]]), case lists:member(Winner, Partitions) of false -> case node() of Winner -> rabbit_log:info( @@ -461,13 +492,14 @@ wait_for_cluster_recovery(Nodes) -> %% The winner and the leader are not necessarily the same node! Since %% the leader may end up restarting, we also make sure that it does %% not announce its decision (and thus cue other nodes to restart) -%% until it has seen a request from every node. +%% until it has seen a request from every node that has experienced a +%% partition. autoheal(State = #state{autoheal = not_healing}) -> - [Leader | _] = All = lists:usort(rabbit_mnesia:cluster_nodes(all)), + [Leader | _] = lists:usort(rabbit_mnesia:cluster_nodes(all)), rabbit_log:info("Autoheal: leader is ~p~n", [Leader]), {?MODULE, Leader} ! {autoheal_request_winner, node()}, State#state{autoheal = case node() of - Leader -> {wait_for_winner_reqs, All, All}; + Leader -> not_healing; _ -> wait_for_winner end}; autoheal(State) -> @@ -506,7 +538,7 @@ autoheal_restart(Winner) -> %% nodes are partitioned from each other. %% %% Note that here we assume that partition information is -%% consistent. If it isn't, what can we do? +%% consistent - which it isn't. TODO fix this. all_partitions(PartitionedWith) -> All = rabbit_mnesia:cluster_nodes(all), OurPartition = All -- PartitionedWith, @@ -519,8 +551,8 @@ all_partitions(AllPartitions, [One | _] = ToDo, All) -> Partition = All -- PartitionedFrom, all_partitions([Partition | AllPartitions], ToDo -- Partition, All). -handle_dead_rabbit_state(State = #state{partitions = Partitions, - autoheal = Autoheal}) -> +handle_dead_rabbit_state(Node, State = #state{partitions = Partitions, + autoheal = Autoheal}) -> %% If we have been partitioned, and we are now in the only remaining %% partition, we no longer care about partitions - forget them. Note %% that we do not attempt to deal with individual (other) partitions @@ -530,12 +562,18 @@ handle_dead_rabbit_state(State = #state{partitions = Partitions, [] -> []; _ -> Partitions end, - ensure_ping_timer( - State#state{partitions = Partitions1, - autoheal = case Autoheal of - {wait_for, _Nodes, _Notify} -> Autoheal; - _ -> not_healing - end}). + Autoheal1 = case Autoheal of + {wait_for, _Nodes, _Notify} -> + Autoheal; + not_healing -> + not_healing; + _ -> + rabbit_log:info( + "Autoheal: aborting - ~p went down~n", [Node]), + not_healing + end, + ensure_ping_timer(State#state{partitions = Partitions1, + autoheal = Autoheal1}). ensure_ping_timer(State) -> rabbit_misc:ensure_timer( -- cgit v1.2.1 From 0a385f2fab060154f3700fcbd7036d055d1413b8 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Thu, 28 Mar 2013 17:24:13 +0000 Subject: Mnesia upgrade for exchange decorators --- src/rabbit_upgrade_functions.erl | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/rabbit_upgrade_functions.erl b/src/rabbit_upgrade_functions.erl index 457b1567..5211987f 100644 --- a/src/rabbit_upgrade_functions.erl +++ b/src/rabbit_upgrade_functions.erl @@ -43,6 +43,7 @@ -rabbit_upgrade({sync_slave_pids, mnesia, [policy]}). -rabbit_upgrade({no_mirror_nodes, mnesia, [sync_slave_pids]}). -rabbit_upgrade({gm_pids, mnesia, [no_mirror_nodes]}). +-rabbit_upgrade({exchange_decorators, mnesia, [policy]}). %% ------------------------------------------------------------------- @@ -68,6 +69,7 @@ -spec(sync_slave_pids/0 :: () -> 'ok'). -spec(no_mirror_nodes/0 :: () -> 'ok'). -spec(gm_pids/0 :: () -> 'ok'). +-spec(exchange_decorators/0 :: () -> 'ok'). -endif. @@ -282,6 +284,19 @@ gm_pids() -> || T <- Tables], ok. +exchange_decorators() -> + ok = exchange_decorators(rabbit_exchange), + ok = exchange_decorators(rabbit_durable_exchange). + +exchange_decorators(Table) -> + transform( + Table, + fun ({exchange, Name, Type, Dur, AutoDel, Int, Args, Scratches, + Policy}) -> + {exchange, Name, Type, Dur, AutoDel, Int, Args, Scratches, Policy, + {[], []}} + end, + [name, type, durable, auto_delete, internal, arguments, scratches, policy, decorators]). %%-------------------------------------------------------------------- -- cgit v1.2.1 From 1b2653094fadfd005b035fb2e31238b08ff881bb Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Thu, 28 Mar 2013 17:26:07 +0000 Subject: Wrap --- src/rabbit_upgrade_functions.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rabbit_upgrade_functions.erl b/src/rabbit_upgrade_functions.erl index 5211987f..b7b1635b 100644 --- a/src/rabbit_upgrade_functions.erl +++ b/src/rabbit_upgrade_functions.erl @@ -296,7 +296,8 @@ exchange_decorators(Table) -> {exchange, Name, Type, Dur, AutoDel, Int, Args, Scratches, Policy, {[], []}} end, - [name, type, durable, auto_delete, internal, arguments, scratches, policy, decorators]). + [name, type, durable, auto_delete, internal, arguments, scratches, policy, + decorators]). %%-------------------------------------------------------------------- -- cgit v1.2.1 From e91e370baa19d18a7b2fbcf7450aa694330d0b11 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Thu, 4 Apr 2013 12:45:41 +0100 Subject: DLX without confirms --- src/rabbit_amqqueue_process.erl | 126 ++++++++-------------------------------- 1 file changed, 23 insertions(+), 103 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index b016c4d2..1a70af0d 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -49,9 +49,6 @@ ttl_timer_ref, ttl_timer_expiry, senders, - publish_seqno, - unconfirmed, - delayed_stop, queue_monitors, dlx, dlx_routing_key, @@ -151,9 +148,6 @@ init_state(Q) -> has_had_consumers = false, active_consumers = queue:new(), senders = pmon:new(), - publish_seqno = 1, - unconfirmed = dtree:empty(), - queue_monitors = pmon:new(), msg_id_to_channel = gb_trees:empty(), status = running}, rabbit_event:init_stats_timer(State, #q.stats_timer). @@ -820,31 +814,20 @@ dead_letter_maxlen_msgs(X, Excess, State = #q{backing_queue = BQ}) -> State1. dead_letter_msgs(Fun, Reason, X, State = #q{dlx_routing_key = RK, - publish_seqno = SeqNo0, - unconfirmed = UC0, - queue_monitors = QMons0, backing_queue_state = BQS, backing_queue = BQ}) -> QName = qname(State), - {Res, {AckImm1, SeqNo1, UC1, QMons1}, BQS1} = - Fun(fun (Msg, AckTag, {AckImm, SeqNo, UC, QMons}) -> - case dead_letter_publish(Msg, Reason, - X, RK, SeqNo, QName) of - [] -> {[AckTag | AckImm], SeqNo, UC, QMons}; - QPids -> {AckImm, SeqNo + 1, - dtree:insert(SeqNo, QPids, AckTag, UC), - pmon:monitor_all(QPids, QMons)} - end - end, {[], SeqNo0, UC0, QMons0}, BQS), - {_Guids, BQS2} = BQ:ack(AckImm1, BQS1), - {Res, State#q{publish_seqno = SeqNo1, - unconfirmed = UC1, - queue_monitors = QMons1, - backing_queue_state = BQS2}}. - -dead_letter_publish(Msg, Reason, X, RK, MsgSeqNo, QName) -> + {Res, Acks1, BQS1} = + Fun(fun (Msg, AckTag, Acks) -> + dead_letter_publish(Msg, Reason, X, RK, QName), + [AckTag | Acks] + end, [], BQS), + {_Guids, BQS2} = BQ:ack(Acks1, BQS1), + {Res, State#q{backing_queue_state = BQS2}}. + +dead_letter_publish(Msg, Reason, X, RK, QName) -> DLMsg = make_dead_letter_msg(Msg, Reason, X#exchange.name, RK, QName), - Delivery = rabbit_basic:delivery(false, DLMsg, MsgSeqNo), + Delivery = rabbit_basic:delivery(false, DLMsg, undefined), {Queues, Cycles} = detect_dead_letter_cycles( DLMsg, rabbit_exchange:route(X, Delivery)), lists:foreach(fun log_cycle_once/1, Cycles), @@ -852,48 +835,11 @@ dead_letter_publish(Msg, Reason, X, RK, MsgSeqNo, QName) -> rabbit_amqqueue:lookup(Queues), Delivery), DeliveredQPids. -handle_queue_down(QPid, Reason, State = #q{queue_monitors = QMons, - unconfirmed = UC}) -> - case pmon:is_monitored(QPid, QMons) of - false -> noreply(State); - true -> case rabbit_misc:is_abnormal_exit(Reason) of - true -> {Lost, _UC1} = dtree:take_all(QPid, UC), - QNameS = rabbit_misc:rs(qname(State)), - rabbit_log:warning("DLQ ~p for ~s died with " - "~p unconfirmed messages~n", - [QPid, QNameS, length(Lost)]); - false -> ok - end, - {MsgSeqNoAckTags, UC1} = dtree:take(QPid, UC), - cleanup_after_confirm( - [AckTag || {_MsgSeqNo, AckTag} <- MsgSeqNoAckTags], - State#q{queue_monitors = pmon:erase(QPid, QMons), - unconfirmed = UC1}) - end. +stop(State) -> stop(noreply, State). -stop(State) -> stop(undefined, noreply, State). - -stop(From, Reply, State = #q{unconfirmed = UC}) -> - case {dtree:is_empty(UC), Reply} of - {true, noreply} -> {stop, normal, State}; - {true, _} -> {stop, normal, Reply, State}; - {false, _} -> noreply(State#q{delayed_stop = {From, Reply}}) - end. +stop(noreply, State) -> {stop, normal, State}; +stop(Reply, State) -> {stop, normal, Reply, State}. -cleanup_after_confirm(AckTags, State = #q{delayed_stop = DS, - unconfirmed = UC, - backing_queue = BQ, - backing_queue_state = BQS}) -> - {_Guids, BQS1} = BQ:ack(AckTags, BQS), - State1 = State#q{backing_queue_state = BQS1}, - case dtree:is_empty(UC) andalso DS =/= undefined of - true -> case DS of - {_, noreply} -> ok; - {From, Reply} -> gen_server2:reply(From, Reply) - end, - {stop, normal, State1}; - false -> noreply(State1) - end. detect_dead_letter_cycles(#basic_message{content = Content}, Queues) -> #content{properties = #'P_basic'{headers = Headers}} = @@ -1073,9 +1019,6 @@ prioritise_info(Msg, _Len, #q{q = #amqqueue{exclusive_owner = DownPid}}) -> _ -> 0 end. -handle_call(_, _, State = #q{delayed_stop = DS}) when DS =/= undefined -> - noreply(State); - handle_call({init, Recover}, From, State = #q{q = #amqqueue{exclusive_owner = none}}) -> declare(Recover, From, State); @@ -1115,16 +1058,15 @@ handle_call({deliver, Delivery, Delivered}, From, State) -> gen_server2:reply(From, ok), noreply(deliver_or_enqueue(Delivery, Delivered, State)); -handle_call({notify_down, ChPid}, From, State) -> +handle_call({notify_down, ChPid}, _From, State) -> %% we want to do this synchronously, so that auto_deleted queues %% are no longer visible by the time we send a response to the %% client. The queue is ultimately deleted in terminate/2; if we %% return stop with a reply, terminate/2 will be called by - %% gen_server2 *before* the reply is sent. FIXME: in case of a - %% delayed stop the reply is sent earlier. + %% gen_server2 *before* the reply is sent. case handle_ch_down(ChPid, State) of {ok, State1} -> reply(ok, State1); - {stop, State1} -> stop(From, ok, State1) + {stop, State1} -> stop(ok, State1) end; handle_call({basic_get, ChPid, NoAck, LimiterPid}, _From, @@ -1186,7 +1128,7 @@ handle_call({basic_consume, NoAck, ChPid, LimiterPid, LimiterActive, reply(ok, run_message_queue(State1#q{active_consumers = AC1})) end; -handle_call({basic_cancel, ChPid, ConsumerTag, OkMsg}, From, +handle_call({basic_cancel, ChPid, ConsumerTag, OkMsg}, _From, State = #q{exclusive_consumer = Holder}) -> ok = maybe_send_reply(ChPid, OkMsg), case lookup_ch(ChPid) of @@ -1215,7 +1157,7 @@ handle_call({basic_cancel, ChPid, ConsumerTag, OkMsg}, From, State#q.active_consumers)}, case should_auto_delete(State1) of false -> reply(ok, ensure_expiry_timer(State1)); - true -> stop(From, ok, State1) + true -> stop(ok, State1) end end; @@ -1224,14 +1166,14 @@ handle_call(stat, _From, State) -> ensure_expiry_timer(State), reply({ok, BQ:len(BQS), consumer_count()}, State1); -handle_call({delete, IfUnused, IfEmpty}, From, +handle_call({delete, IfUnused, IfEmpty}, _From, State = #q{backing_queue_state = BQS, backing_queue = BQ}) -> IsEmpty = BQ:is_empty(BQS), IsUnused = is_unused(State), if IfEmpty and not(IsEmpty) -> reply({error, not_empty}, State); IfUnused and not(IsUnused) -> reply({error, in_use}, State); - true -> stop(From, {ok, BQ:len(BQS)}, State) + true -> stop({ok, BQ:len(BQS)}, State) end; handle_call(purge, _From, State = #q{backing_queue = BQ, @@ -1286,19 +1228,6 @@ handle_call(force_event_refresh, _From, end, reply(ok, State). -handle_cast({confirm, MsgSeqNos, QPid}, State = #q{unconfirmed = UC}) -> - {MsgSeqNoAckTags, UC1} = dtree:take(MsgSeqNos, QPid, UC), - State1 = case dtree:is_defined(QPid, UC1) of - false -> QMons = State#q.queue_monitors, - State#q{queue_monitors = pmon:demonitor(QPid, QMons)}; - true -> State - end, - cleanup_after_confirm([AckTag || {_MsgSeqNo, AckTag} <- MsgSeqNoAckTags], - State1#q{unconfirmed = UC1}); - -handle_cast(_, State = #q{delayed_stop = DS}) when DS =/= undefined -> - noreply(State); - handle_cast({run_backing_queue, Mod, Fun}, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> noreply(State#q{backing_queue_state = BQ:invoke(Mod, Fun, BQS)}); @@ -1405,15 +1334,6 @@ handle_cast({credit, ChPid, CTag, Credit, Drain}, handle_cast(wake_up, State) -> noreply(State). -%% We need to not ignore this as we need to remove outstanding -%% confirms due to queue death. -handle_info({'DOWN', _MonitorRef, process, DownPid, Reason}, - State = #q{delayed_stop = DS}) when DS =/= undefined -> - handle_queue_down(DownPid, Reason, State); - -handle_info(_, State = #q{delayed_stop = DS}) when DS =/= undefined -> - noreply(State); - handle_info(maybe_expire, State) -> case is_unused(State) of true -> stop(State); @@ -1442,10 +1362,10 @@ handle_info({'DOWN', _MonitorRef, process, DownPid, _Reason}, %% unexpectedly. stop(State); -handle_info({'DOWN', _MonitorRef, process, DownPid, Reason}, State) -> +handle_info({'DOWN', _MonitorRef, process, DownPid, _Reason}, State) -> case handle_ch_down(DownPid, State) of - {ok, State1} -> handle_queue_down(DownPid, Reason, State1); - {stop, State1} -> stop(State1) + {ok, State1} -> noreply(State1); + {stop, State1} -> {stop, normal, State1} end; handle_info(update_ram_duration, State = #q{backing_queue = BQ, -- cgit v1.2.1 From 2d420464faa7c904a800c5dc9bd540e188988acc Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Thu, 4 Apr 2013 16:47:29 +0100 Subject: Change client_sup API --- src/rabbit_client_sup.erl | 24 +++++++++++++----------- src/rabbit_direct.erl | 2 +- src/rabbit_networking.erl | 2 +- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/rabbit_client_sup.erl b/src/rabbit_client_sup.erl index 54bb8671..1a15b0cb 100644 --- a/src/rabbit_client_sup.erl +++ b/src/rabbit_client_sup.erl @@ -18,7 +18,7 @@ -behaviour(supervisor2). --export([start_link/1, start_link/2]). +-export([start_link/1, start_link/2, start_link/3]). -export([init/1]). @@ -37,16 +37,18 @@ %%---------------------------------------------------------------------------- -start_link(CallbackOpts) -> - supervisor2:start_link(?MODULE, CallbackOpts). +start_link(Callback) -> + supervisor2:start_link(?MODULE, Callback). -start_link(SupName, CallbackOpts) -> - supervisor2:start_link(SupName, ?MODULE, CallbackOpts). +start_link(SupName, Callback) -> + supervisor2:start_link(SupName, ?MODULE, Callback). -init({{M,F,A},Opts}) -> - {Shutdown, Type} = case rabbit_misc:pget(worker_type, Opts, supervisor) of - supervisor -> {infinity, supervisor}; - worker -> {?MAX_WAIT, worker} - end, +start_link(SupName, Callback, worker) -> + supervisor2:start_link(SupName, ?MODULE, {Callback, worker}). + +init({M,F,A}) -> + {ok, {{simple_one_for_one_terminate, 0, 1}, + [{client, {M,F,A}, temporary, infinity, supervisor, [M]}]}}; +init({{M,F,A}, worker}) -> {ok, {{simple_one_for_one_terminate, 0, 1}, - [{client, {M,F,A}, temporary, Shutdown, Type, [M]}]}}. + [{client, {M,F,A}, temporary, ?MAX_WAIT, worker, [M]}]}}. diff --git a/src/rabbit_direct.erl b/src/rabbit_direct.erl index 036f354b..53144f3f 100644 --- a/src/rabbit_direct.erl +++ b/src/rabbit_direct.erl @@ -50,7 +50,7 @@ boot() -> rabbit_sup:start_supervisor_child( rabbit_direct_client_sup, rabbit_client_sup, [{local, rabbit_direct_client_sup}, - {{rabbit_channel_sup, start_link, []}, []}]). + {rabbit_channel_sup, start_link, []}]). force_event_refresh() -> [Pid ! force_event_refresh || Pid<- list()], diff --git a/src/rabbit_networking.erl b/src/rabbit_networking.erl index 517fa360..0a0e51c5 100644 --- a/src/rabbit_networking.erl +++ b/src/rabbit_networking.erl @@ -139,7 +139,7 @@ boot_ssl() -> start() -> rabbit_sup:start_supervisor_child( rabbit_tcp_client_sup, rabbit_client_sup, [{local, rabbit_tcp_client_sup}, - {{rabbit_connection_sup,start_link,[]}, []}]). + {rabbit_connection_sup,start_link,[]}]). ensure_ssl() -> ok = app_utils:start_applications([crypto, public_key, ssl]), -- cgit v1.2.1 From 68b870a5c85124f1bcf7657129e3c1cb58713815 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Fri, 5 Apr 2013 12:41:28 +0100 Subject: client_sup API change --- src/rabbit_client_sup.erl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/rabbit_client_sup.erl b/src/rabbit_client_sup.erl index 1a15b0cb..7cc11fef 100644 --- a/src/rabbit_client_sup.erl +++ b/src/rabbit_client_sup.erl @@ -18,7 +18,7 @@ -behaviour(supervisor2). --export([start_link/1, start_link/2, start_link/3]). +-export([start_link/1, start_link/2, start_link_worker/2]). -export([init/1]). @@ -32,6 +32,8 @@ rabbit_types:ok_pid_or_error()). -spec(start_link/2 :: ({'local', atom()}, rabbit_types:mfargs()) -> rabbit_types:ok_pid_or_error()). +-spec(start_link_worker/2 :: ({'local', atom()}, rabbit_types:mfargs()) -> + rabbit_types:ok_pid_or_error()). -endif. @@ -43,7 +45,7 @@ start_link(Callback) -> start_link(SupName, Callback) -> supervisor2:start_link(SupName, ?MODULE, Callback). -start_link(SupName, Callback, worker) -> +start_link_worker(SupName, Callback) -> supervisor2:start_link(SupName, ?MODULE, {Callback, worker}). init({M,F,A}) -> -- cgit v1.2.1 From 8347524e1bac0c8331b4fd55947eb92ed7a040b7 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Fri, 5 Apr 2013 13:24:10 +0100 Subject: Minimise diff --- src/rabbit_amqqueue_process.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 1a70af0d..7ab0d774 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1365,7 +1365,7 @@ handle_info({'DOWN', _MonitorRef, process, DownPid, _Reason}, handle_info({'DOWN', _MonitorRef, process, DownPid, _Reason}, State) -> case handle_ch_down(DownPid, State) of {ok, State1} -> noreply(State1); - {stop, State1} -> {stop, normal, State1} + {stop, State1} -> stop(State1) end; handle_info(update_ram_duration, State = #q{backing_queue = BQ, -- cgit v1.2.1 From e2c1ff54a9db9c92c3c9e732c21cd2a5ccbccbc5 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 5 Apr 2013 17:26:01 +0100 Subject: Only queue up for GC at most two pairs of files at once, since we lock files as soon as they are queued; no point in having them locked for ages as they wait to get GCed. --- src/rabbit_msg_store.erl | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/rabbit_msg_store.erl b/src/rabbit_msg_store.erl index c2d8e06e..112d1ce2 100644 --- a/src/rabbit_msg_store.erl +++ b/src/rabbit_msg_store.erl @@ -51,6 +51,9 @@ -define(HANDLE_CACHE_BUFFER_SIZE, 1048576). %% 1MB + %% i.e. two pairs, so GC does not go idle when busy +-define(MAXIMUM_SIMULTANEOUS_GC_FILES, 4). + %%---------------------------------------------------------------------------- -record(msstate, @@ -1731,10 +1734,12 @@ maybe_compact(State = #msstate { sum_valid_data = SumValid, (SumFileSize - SumValid) / SumFileSize > ?GARBAGE_FRACTION -> %% TODO: the algorithm here is sub-optimal - it may result in a %% complete traversal of FileSummaryEts. - case ets:first(FileSummaryEts) of - '$end_of_table' -> + First = ets:first(FileSummaryEts), + case First =:= '$end_of_table' orelse + orddict:size(Pending) >= ?MAXIMUM_SIMULTANEOUS_GC_FILES of + true -> State; - First -> + false -> case find_files_to_combine(FileSummaryEts, FileSizeLimit, ets:lookup(FileSummaryEts, First)) of not_found -> -- cgit v1.2.1 From 79ecab90cfaded68f596cf3783f907ffe8c106f2 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 8 Apr 2013 15:56:01 +0100 Subject: Umm, order by *best* partition first! --- src/rabbit_node_monitor.erl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index edb197fa..7fd20f8c 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -506,8 +506,9 @@ autoheal(State) -> State. autoheal_select_winner(AllPartitions) -> - {_, [Winner | _]} = hd(lists:sort( - [{autoheal_value(P), P} || P <- AllPartitions])), + {_, [Winner | _]} = + hd(lists:reverse( + lists:sort([{autoheal_value(P), P} || P <- AllPartitions]))), Winner. autoheal_value(Partition) -> -- cgit v1.2.1 From c9bac515bb06c2a4a1514baba21ccbca28f525ee Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 8 Apr 2013 15:56:18 +0100 Subject: Deal with partial partitions. --- src/rabbit_node_monitor.erl | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index 7fd20f8c..07898d34 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -537,20 +537,26 @@ autoheal_restart(Winner) -> %% We have our local understanding of what partitions exist; but we %% only know which nodes we have been partitioned from, not which %% nodes are partitioned from each other. -%% -%% Note that here we assume that partition information is -%% consistent - which it isn't. TODO fix this. all_partitions(PartitionedWith) -> - All = rabbit_mnesia:cluster_nodes(all), - OurPartition = All -- PartitionedWith, - all_partitions([OurPartition], PartitionedWith, All). - -all_partitions(AllPartitions, [], _) -> - AllPartitions; -all_partitions(AllPartitions, [One | _] = ToDo, All) -> - {One, PartitionedFrom} = rpc:call(One, rabbit_node_monitor, partitions, []), - Partition = All -- PartitionedFrom, - all_partitions([Partition | AllPartitions], ToDo -- Partition, All). + Nodes = rabbit_mnesia:cluster_nodes(all), + Partitions = [{node(), PartitionedWith} | + [rpc:call(Node, rabbit_node_monitor, partitions, []) + || Node <- Nodes -- [node()]]], + all_partitions(Partitions, [Nodes]). + +all_partitions([], Partitions) -> + Partitions; +all_partitions([{Node, CantSee} | Rest], Partitions) -> + {[Containing], Others} = + lists:partition(fun (Part) -> lists:member(Node, Part) end, Partitions), + A = Containing -- CantSee, + B = Containing -- A, + Partitions1 = case {A, B} of + {[], _} -> Partitions; + {_, []} -> Partitions; + _ -> [A, B | Others] + end, + all_partitions(Rest, Partitions1). handle_dead_rabbit_state(Node, State = #state{partitions = Partitions, autoheal = Autoheal}) -> -- cgit v1.2.1 From d3f6a2dc0ecce0ff7ced9f4bbfea0276c8f53043 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 8 Apr 2013 15:56:29 +0100 Subject: Remove io:formats --- src/rabbit_node_monitor.erl | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index 07898d34..2cdde14e 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -312,7 +312,6 @@ handle_info({autoheal_request_winner, Node}, State = #state{autoheal = {wait_for_winner_reqs, [Node], Notify}, partitions = Partitions}) -> AllPartitions = all_partitions(Partitions), - io:format("all partitions: ~p~n", [AllPartitions]), Winner = autoheal_select_winner(AllPartitions), rabbit_log:info("Autoheal request winner from ~p~n" " Partitions were determined to be ~p~n" @@ -329,7 +328,6 @@ handle_info({autoheal_request_winner, Node}, handle_info({autoheal_winner, Winner}, State = #state{autoheal = wait_for_winner, partitions = Partitions}) -> - io:format("got winner ~p~n", [[Winner, Partitions]]), case lists:member(Winner, Partitions) of false -> case node() of Winner -> rabbit_log:info( -- cgit v1.2.1 From 16959fe509d2d36c85fb5a2a6b14d7ab0211a74a Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 9 Apr 2013 17:19:43 +0100 Subject: Replace all uses of io_lib:format/2 with rabbit_misc:format/2 - we want something that returns a flat list. --- src/rabbit_ssl.erl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/rabbit_ssl.erl b/src/rabbit_ssl.erl index b1238623..7c9e6027 100644 --- a/src/rabbit_ssl.erl +++ b/src/rabbit_ssl.erl @@ -165,12 +165,12 @@ format_rdn(#'AttributeTypeAndValue'{type = T, value = V}) -> {?'street-address' , "STREET"}], case proplists:lookup(T, Fmts) of {_, Fmt} -> - io_lib:format(Fmt ++ "=~s", [FV]); + rabbit_misc:format(Fmt ++ "=~s", [FV]); none when is_tuple(T) -> - TypeL = [io_lib:format("~w", [X]) || X <- tuple_to_list(T)], - io_lib:format("~s:~s", [string:join(TypeL, "."), FV]); + TypeL = [rabbit_misc:format("~w", [X]) || X <- tuple_to_list(T)], + rabbit_misc:format("~s:~s", [string:join(TypeL, "."), FV]); none -> - io_lib:format("~p:~s", [T, FV]) + rabbit_misc:format("~p:~s", [T, FV]) end. %% Escape a string as per RFC4514. @@ -204,14 +204,14 @@ format_asn1_value({ST, S}) when ST =:= teletexString; ST =:= printableString; format_directory_string(ST, S); format_asn1_value({utcTime, [Y1, Y2, M1, M2, D1, D2, H1, H2, Min1, Min2, S1, S2, $Z]}) -> - io_lib:format("20~c~c-~c~c-~c~cT~c~c:~c~c:~c~cZ", - [Y1, Y2, M1, M2, D1, D2, H1, H2, Min1, Min2, S1, S2]); + rabbit_misc:format("20~c~c-~c~c-~c~cT~c~c:~c~c:~c~cZ", + [Y1, Y2, M1, M2, D1, D2, H1, H2, Min1, Min2, S1, S2]); %% We appear to get an untagged value back for an ia5string %% (e.g. domainComponent). format_asn1_value(V) when is_list(V) -> V; format_asn1_value(V) -> - io_lib:format("~p", [V]). + rabbit_misc:format("~p", [V]). %% DirectoryString { INTEGER : maxSize } ::= CHOICE { %% teletexString TeletexString (SIZE (1..maxSize)), -- cgit v1.2.1 From 1e731ffa29684325053caf4b4ce88f4d4be52f5b Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 9 Apr 2013 17:48:31 +0100 Subject: Insert a hack. --- src/rabbit_ssl.erl | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/rabbit_ssl.erl b/src/rabbit_ssl.erl index 7c9e6027..c769f35d 100644 --- a/src/rabbit_ssl.erl +++ b/src/rabbit_ssl.erl @@ -210,6 +210,16 @@ format_asn1_value({utcTime, [Y1, Y2, M1, M2, D1, D2, H1, H2, %% (e.g. domainComponent). format_asn1_value(V) when is_list(V) -> V; +format_asn1_value(V) when is_binary(V) -> + %% OTP does not decode some values when combined with an unknown + %% type. That's probably wrong, so as a last ditch effort let's + %% try manually decoding. This is certainly not guaranteed to work + %% in all cases, but if we have a printableString we're in luck. + try + public_key:der_decode('CommonName', V) + catch _:_ -> + rabbit_misc:format("~p", [V]) + end; format_asn1_value(V) -> rabbit_misc:format("~p", [V]). -- cgit v1.2.1 From 939f5838af410660bc25dd02edb40ce86f369e75 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 9 Apr 2013 17:55:11 +0100 Subject: Also RFC4514 suggests we always use = even if we don't recognise an OID. --- src/rabbit_ssl.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_ssl.erl b/src/rabbit_ssl.erl index c769f35d..4db0ea7e 100644 --- a/src/rabbit_ssl.erl +++ b/src/rabbit_ssl.erl @@ -168,9 +168,9 @@ format_rdn(#'AttributeTypeAndValue'{type = T, value = V}) -> rabbit_misc:format(Fmt ++ "=~s", [FV]); none when is_tuple(T) -> TypeL = [rabbit_misc:format("~w", [X]) || X <- tuple_to_list(T)], - rabbit_misc:format("~s:~s", [string:join(TypeL, "."), FV]); + rabbit_misc:format("~s=~s", [string:join(TypeL, "."), FV]); none -> - rabbit_misc:format("~p:~s", [T, FV]) + rabbit_misc:format("~p=~s", [T, FV]) end. %% Escape a string as per RFC4514. -- cgit v1.2.1 From dc19bbcbeaf4bcd4a735eab344eff84d4955cd55 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 9 Apr 2013 18:04:28 +0100 Subject: Add support for UID. No, it's not in public_key.hrl. Grrr. --- src/rabbit_ssl.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rabbit_ssl.erl b/src/rabbit_ssl.erl index 4db0ea7e..68593413 100644 --- a/src/rabbit_ssl.erl +++ b/src/rabbit_ssl.erl @@ -162,7 +162,8 @@ format_rdn(#'AttributeTypeAndValue'{type = T, value = V}) -> {?'id-at-pseudonym' , "PSEUDONYM"}, {?'id-domainComponent' , "DC"}, {?'id-emailAddress' , "EMAILADDRESS"}, - {?'street-address' , "STREET"}], + {?'street-address' , "STREET"}, + {{0,9,2342,19200300,100,1,1} , "UID"}], case proplists:lookup(T, Fmts) of {_, Fmt} -> rabbit_misc:format(Fmt ++ "=~s", [FV]); -- cgit v1.2.1 From a74670bbb63046bdb6e2f4e6d417c8711c25b44d Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 9 Apr 2013 18:04:52 +0100 Subject: Explain a bit more WTF we're doing. --- src/rabbit_ssl.erl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/rabbit_ssl.erl b/src/rabbit_ssl.erl index 68593413..8ef31fb6 100644 --- a/src/rabbit_ssl.erl +++ b/src/rabbit_ssl.erl @@ -216,6 +216,8 @@ format_asn1_value(V) when is_binary(V) -> %% type. That's probably wrong, so as a last ditch effort let's %% try manually decoding. This is certainly not guaranteed to work %% in all cases, but if we have a printableString we're in luck. + %% 'CommonName' is somewhat arbitrary - we need a valid type, and + %% der_decode/2 will do some type checking against it. try public_key:der_decode('CommonName', V) catch _:_ -> -- cgit v1.2.1 From 7e1c3ef4831ca32b38522e1913d3c0a3a18e1dda Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 9 Apr 2013 18:08:23 +0100 Subject: Excuse --- src/rabbit_ssl.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_ssl.erl b/src/rabbit_ssl.erl index 8ef31fb6..0bcbc5f1 100644 --- a/src/rabbit_ssl.erl +++ b/src/rabbit_ssl.erl @@ -163,7 +163,7 @@ format_rdn(#'AttributeTypeAndValue'{type = T, value = V}) -> {?'id-domainComponent' , "DC"}, {?'id-emailAddress' , "EMAILADDRESS"}, {?'street-address' , "STREET"}, - {{0,9,2342,19200300,100,1,1} , "UID"}], + {{0,9,2342,19200300,100,1,1} , "UID"}], %% Not in public_key.hrl case proplists:lookup(T, Fmts) of {_, Fmt} -> rabbit_misc:format(Fmt ++ "=~s", [FV]); -- cgit v1.2.1 From d5f090520c771a70e0349923cc973512142293a7 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 9 Apr 2013 18:17:05 +0100 Subject: Be rather less arbitrary. But still a bit. --- src/rabbit_ssl.erl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/rabbit_ssl.erl b/src/rabbit_ssl.erl index 0bcbc5f1..96277b68 100644 --- a/src/rabbit_ssl.erl +++ b/src/rabbit_ssl.erl @@ -214,12 +214,12 @@ format_asn1_value(V) when is_list(V) -> format_asn1_value(V) when is_binary(V) -> %% OTP does not decode some values when combined with an unknown %% type. That's probably wrong, so as a last ditch effort let's - %% try manually decoding. This is certainly not guaranteed to work - %% in all cases, but if we have a printableString we're in luck. - %% 'CommonName' is somewhat arbitrary - we need a valid type, and - %% der_decode/2 will do some type checking against it. + %% try manually decoding. 'DirectoryString' is semi-arbitrary - + %% but it is the type which covers the various string types we + %% handle below. try - public_key:der_decode('CommonName', V) + {ST, S} = public_key:der_decode('DirectoryString', V), + format_directory_string(ST, S) catch _:_ -> rabbit_misc:format("~p", [V]) end; -- cgit v1.2.1 From 52f53d9390ceceee28d0a0216c240b3a754e1bab Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 10 Apr 2013 11:57:29 +0100 Subject: Allow use of esl-erlang --- packaging/debs/Debian/debian/control | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/debs/Debian/debian/control b/packaging/debs/Debian/debian/control index d4526d87..3a15c4b6 100644 --- a/packaging/debs/Debian/debian/control +++ b/packaging/debs/Debian/debian/control @@ -9,7 +9,7 @@ Standards-Version: 3.9.2 Package: rabbitmq-server Architecture: all -Depends: erlang-nox (>= 1:12.b.3), adduser, logrotate, ${misc:Depends} +Depends: erlang-nox (>= 1:12.b.3) | esl-erlang, adduser, logrotate, ${misc:Depends} Description: AMQP server written in Erlang RabbitMQ is an implementation of AMQP, the emerging standard for high performance enterprise messaging. The RabbitMQ server is a robust and -- cgit v1.2.1 From 4f68ebd4e31effe000208ef1b836575427663a36 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Wed, 10 Apr 2013 13:59:47 +0100 Subject: Alternative implementation of bytecode compatibility check --- src/rabbit_mnesia.erl | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index c39e898c..52af28ab 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -403,7 +403,9 @@ cluster_status(WhichNodes) -> end. node_info() -> - {erlang:system_info(otp_release), rabbit_misc:version(), + DelegateBeamLocation = code:which(delegate), + {ok, {delegate, DelegateBeamHash}} = beam_lib:md5(DelegateBeamLocation), + {erlang:system_info(otp_release), rabbit_misc:version(), DelegateBeamHash, cluster_status_from_mnesia()}. node_type() -> @@ -562,10 +564,10 @@ check_cluster_consistency(Node) -> case rpc:call(Node, rabbit_mnesia, node_info, []) of {badrpc, _Reason} -> {error, not_found}; - {_OTP, _Rabbit, {error, _}} -> + {_OTP, _Rabbit, _Hash, {error, _}} -> {error, not_found}; - {OTP, Rabbit, {ok, Status}} -> - case check_consistency(OTP, Rabbit, Node, Status) of + {OTP, Rabbit, Hash, {ok, Status}} -> + case check_consistency(OTP, Rabbit, Hash, Node, Status) of {error, _} = E -> E; {ok, Res} -> {ok, Res} end @@ -732,14 +734,17 @@ change_extra_db_nodes(ClusterNodes0, CheckOtherNodes) -> Nodes end. -check_consistency(OTP, Rabbit) -> +check_consistency(OTP, Rabbit, Hash) -> rabbit_misc:sequence_error( - [check_otp_consistency(OTP), check_rabbit_consistency(Rabbit)]). + [check_otp_consistency(OTP), + check_rabbit_consistency(Rabbit), + check_beam_compatibility(Hash)]). -check_consistency(OTP, Rabbit, Node, Status) -> +check_consistency(OTP, Rabbit, Hash, 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, _, _}) -> @@ -780,6 +785,15 @@ check_rabbit_consistency(Remote) -> rabbit_misc:version(), Remote, "Rabbit", fun rabbit_misc:version_minor_equivalent/2). +check_beam_compatibility(RemoteHash) -> + DelegateBeamLocation = code:which(delegate), + {ok, {delegate, LocalHash}} = beam_lib:md5(DelegateBeamLocation), + case RemoteHash == LocalHash of + true -> ok; + false -> {error, {incompatible_bytecode, + "Incompatible Erlang bytecode found on nodes"}} + end. + %% 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 @@ -806,10 +820,10 @@ find_good_node([]) -> find_good_node([Node | Nodes]) -> case rpc:call(Node, rabbit_mnesia, node_info, []) of {badrpc, _Reason} -> find_good_node(Nodes); - {OTP, Rabbit, _} -> case check_consistency(OTP, Rabbit) of - {error, _} -> find_good_node(Nodes); - ok -> {ok, Node} - end + {OTP, Rabbit, Hash, _} -> case check_consistency(OTP, Rabbit, Hash) of + {error, _} -> find_good_node(Nodes); + ok -> {ok, Node} + end end. is_only_clustered_disc_node() -> -- cgit v1.2.1 From 58ee0ccec52aff1e31e82d9c2f28570dbc9d672e Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Wed, 10 Apr 2013 18:13:38 +0100 Subject: Changes --- src/rabbit_exchange.erl | 25 ++++++++++++------------- src/rabbit_exchange_decorator.erl | 22 +++++++++++----------- src/rabbit_policy.erl | 13 ++++++------- src/rabbit_tests.erl | 2 +- 4 files changed, 30 insertions(+), 32 deletions(-) diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index aa697f07..f5fd9a65 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -144,13 +144,12 @@ serial(#exchange{name = XName} = X) -> end. declare(XName, Type, Durable, AutoDelete, Internal, Args) -> - X0 = rabbit_policy:set(#exchange{name = XName, - type = Type, - durable = Durable, - auto_delete = AutoDelete, - internal = Internal, - arguments = Args}), - X = rabbit_exchange_decorator:record(X0, rabbit_exchange_decorator:list()), + X = rabbit_policy:set(#exchange{name = XName, + type = Type, + durable = Durable, + auto_delete = AutoDelete, + internal = Internal, + arguments = Args}), XT = type_to_module(Type), %% We want to upset things if it isn't ok ok = XT:validate(X), @@ -316,20 +315,20 @@ route(#exchange{name = #resource{virtual_host = VHost, name = RName} = XName, {<<"">>, []} -> %% Optimisation [rabbit_misc:r(VHost, queue, RK) || RK <- lists:usort(RKs)]; - {_, RDecorators} -> - lists:usort(route1(Delivery, RDecorators, {[X], XName, []})) + {_, SelectedDecorators} -> + lists:usort(route1(Delivery, SelectedDecorators, {[X], XName, []})) end. route1(_, _, {[], _, QNames}) -> QNames; -route1(Delivery, RDecorators, +route1(Delivery, Decorators, {[X = #exchange{type = Type} | WorkList], SeenXs, QNames}) -> ExchangeDests = (type_to_module(Type)):route(X, Delivery), - RDecorateDests = process_decorators(X, RDecorators, Delivery), + DecorateDests = process_decorators(X, Decorators, Delivery), AlternateDests = process_alternate(X, ExchangeDests), - route1(Delivery, RDecorators, + route1(Delivery, Decorators, lists:foldl(fun process_route/2, {WorkList, SeenXs, QNames}, - AlternateDests ++ RDecorateDests ++ ExchangeDests)). + AlternateDests ++ DecorateDests ++ ExchangeDests)). process_alternate(#exchange{arguments = []}, _Results) -> %% optimisation []; diff --git a/src/rabbit_exchange_decorator.erl b/src/rabbit_exchange_decorator.erl index cdbc42bb..3748a844 100644 --- a/src/rabbit_exchange_decorator.erl +++ b/src/rabbit_exchange_decorator.erl @@ -18,7 +18,7 @@ -include("rabbit.hrl"). --export([list/0, select/2, record/2]). +-export([list/0, select/2, set/1]). %% This is like an exchange type except that: %% @@ -59,8 +59,8 @@ %% Decorators can optionally implement route/2 which allows additional %% destinations to be added to the routing decision. --callback route(rabbit_types:exchange(), rabbit_types:delivery()) -> - [rabbit_amqqueue:name() | rabbit_exchange:name()] | ok. +%% -callback route(rabbit_types:exchange(), rabbit_types:delivery()) -> +%% [rabbit_amqqueue:name() | rabbit_exchange:name()] | ok. %% Whether the decorator wishes to receive callbacks for the exchange %% none:no callbacks, noroute:all callbacks except route, all:all callbacks @@ -87,14 +87,14 @@ list() -> [M || {_, M} <- rabbit_registry:lookup_all(exchange_decorator)]. select(all, {Route, NoRoute}) -> Route ++ NoRoute; select(route, {Route, _NoRoute}) -> Route. -%% record active decorators in an exchange -record(X, Decorators) -> - X#exchange{decorators = - lists:foldl(fun (D, {Route, NoRoute}) -> - Callbacks = D:active_for(X), - {cons_if_eq(all, Callbacks, D, Route), - cons_if_eq(noroute, Callbacks, D, NoRoute)} - end, {[], []}, Decorators)}. +set(X) -> + X#exchange{ + decorators = + lists:foldl(fun (D, {Route, NoRoute}) -> + Callbacks = D:active_for(X), + {cons_if_eq(all, Callbacks, D, Route), + cons_if_eq(noroute, Callbacks, D, NoRoute)} + end, {[], []}, rabbit_exchange_decorator:list())}. cons_if_eq(Select, Select, Item, List) -> [Item | List]; cons_if_eq(_Select, _Other, _Item, List) -> List. diff --git a/src/rabbit_policy.erl b/src/rabbit_policy.erl index 22e9bdac..d276c2fb 100644 --- a/src/rabbit_policy.erl +++ b/src/rabbit_policy.erl @@ -46,7 +46,8 @@ name0(undefined) -> none; name0(Policy) -> pget(name, Policy). set(Q = #amqqueue{name = Name}) -> Q#amqqueue{policy = set0(Name)}; -set(X = #exchange{name = Name}) -> X#exchange{policy = set0(Name)}. +set(X = #exchange{name = Name}) -> rabbit_exchange_decorator:set( + X#exchange{policy = set0(Name)}). set0(Name = #resource{virtual_host = VHost}) -> match(Name, list(VHost)). @@ -156,10 +157,9 @@ notify_clear(VHost, <<"policy">>, _Name) -> update_policies(VHost) -> Policies = list(VHost), - Decorators = rabbit_exchange_decorator:list(), {Xs, Qs} = rabbit_misc:execute_mnesia_transaction( fun() -> - {[update_exchange(X, Policies, Decorators) || + {[update_exchange(X, Policies) || X <- rabbit_exchange:list(VHost)], [update_queue(Q, Policies) || Q <- rabbit_amqqueue:list(VHost)]} @@ -168,16 +168,15 @@ update_policies(VHost) -> [catch notify(Q) || Q <- Qs], ok. -update_exchange(X = #exchange{name = XName, policy = OldPolicy}, - Policies, Decorators) -> +update_exchange(X = #exchange{name = XName, policy = OldPolicy}, Policies) -> case match(XName, Policies) of OldPolicy -> no_change; NewPolicy -> rabbit_exchange:update( XName, fun(X1) -> - rabbit_exchange_decorator:record( - X1#exchange{policy = NewPolicy}, Decorators) + rabbit_exchange_decorator:set( + X1#exchange{policy = NewPolicy}) end), {X, X#exchange{policy = NewPolicy}} end. diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 91f560cb..93e2d046 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -565,7 +565,7 @@ test_topic_matching() -> name = <<"test_exchange">>}, X0 = #exchange{name = XName, type = topic, durable = false, auto_delete = false, arguments = []}, - X = rabbit_exchange_decorator:record(X0, []), + X = rabbit_exchange_decorator:set(X0), %% create rabbit_exchange_type_topic:validate(X), exchange_op_callback(X, create, []), -- cgit v1.2.1 From 1d82b4ffcd52d2f9b8ed3de776d2080d07366674 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 11 Apr 2013 15:37:59 +0100 Subject: Move mirrored queue modes into a behaviour and implementations thereof. --- src/rabbit_mirror_queue_misc.erl | 137 ++++++++++--------------------- src/rabbit_mirror_queue_mode.erl | 55 +++++++++++++ src/rabbit_mirror_queue_mode_all.erl | 41 +++++++++ src/rabbit_mirror_queue_mode_exactly.erl | 56 +++++++++++++ src/rabbit_mirror_queue_mode_nodes.erl | 70 ++++++++++++++++ src/rabbit_policy_validator.erl | 2 + src/rabbit_registry.erl | 3 +- src/rabbit_tests.erl | 9 +- 8 files changed, 276 insertions(+), 97 deletions(-) create mode 100644 src/rabbit_mirror_queue_mode.erl create mode 100644 src/rabbit_mirror_queue_mode_all.erl create mode 100644 src/rabbit_mirror_queue_mode_exactly.erl create mode 100644 src/rabbit_mirror_queue_mode_nodes.erl diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 4fb1fc3b..8787e966 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -22,7 +22,7 @@ is_mirrored/1, update_mirrors/2, validate_policy/1]). %% for testing only --export([suggested_queue_nodes/4]). +-export([module/1]). -include("rabbit.hrl"). @@ -237,14 +237,17 @@ 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, PossibleNodes) -> +suggested_queue_nodes(Q, All) -> {MNode0, SNodes, SSNodes} = actual_queue_nodes(Q), MNode = case MNode0 of none -> node(); _ -> MNode0 end, - suggested_queue_nodes(policy(<<"ha-mode">>, Q), policy(<<"ha-params">>, Q), - {MNode, SNodes, SSNodes}, PossibleNodes). + Params = policy(<<"ha-params">>, Q), + case module(Q) of + {ok, M} -> M:suggested_queue_nodes(Params, MNode, SNodes, SSNodes, All); + _ -> {MNode, []} + end. policy(Policy, Q) -> case rabbit_policy:get(Policy, Q) of @@ -252,52 +255,26 @@ policy(Policy, Q) -> _ -> none end. -suggested_queue_nodes(<<"all">>, _Params, {MNode, _SNodes, _SSNodes}, Poss) -> - {MNode, Poss -- [MNode]}; -suggested_queue_nodes(<<"nodes">>, Nodes0, {MNode, _SNodes, SSNodes}, Poss) -> - Nodes1 = [list_to_atom(binary_to_list(Node)) || Node <- Nodes0], - %% If the current master is not in the nodes specified, then what we want - %% to do depends on whether there are any synchronised slaves. If there - %% are then we can just kill the current master - the admin has asked for - %% a migration and we should give it to them. If there are not however - %% then we must keep the master around so as not to lose messages. - Nodes = case SSNodes of - [] -> lists:usort([MNode | Nodes1]); - _ -> Nodes1 - end, - Unavailable = Nodes -- Poss, - Available = Nodes -- Unavailable, - case Available of - [] -> %% We have never heard of anything? Not much we can do but - %% keep the master alive. - {MNode, []}; - _ -> case lists:member(MNode, Available) of - true -> {MNode, Available -- [MNode]}; - false -> %% Make sure the new master is synced! In order to - %% get here SSNodes must not be empty. - [NewMNode | _] = SSNodes, - {NewMNode, Available -- [NewMNode]} - end +module(#amqqueue{} = Q) -> + case rabbit_policy:get(<<"ha-mode">>, Q) of + {ok, Mode} -> module(Mode); + _ -> not_mirrored end; -%% When we need to add nodes, we randomise our candidate list as a -%% crude form of load-balancing. TODO it would also be nice to -%% randomise the list of ones to remove when we have too many - we -%% would have to take account of synchronisation though. -suggested_queue_nodes(<<"exactly">>, Count, {MNode, SNodes, _SSNodes}, Poss) -> - SCount = Count - 1, - {MNode, case SCount > length(SNodes) of - true -> Cand = shuffle((Poss -- [MNode]) -- SNodes), - SNodes ++ lists:sublist(Cand, SCount - length(SNodes)); - false -> lists:sublist(SNodes, SCount) - end}; -suggested_queue_nodes(_, _, {MNode, _, _}, _) -> - {MNode, []}. - -shuffle(L) -> - {A1,A2,A3} = now(), - random:seed(A1, A2, A3), - {_, L1} = lists:unzip(lists:keysort(1, [{random:uniform(), N} || N <- L])), - L1. + +module(Mode) when is_binary(Mode) -> + case rabbit_registry:binary_to_type(Mode) of + {error, not_found} -> not_mirrored; + T -> case rabbit_registry:lookup_module(ha_mode, T) of + {ok, Module} -> {ok, Module}; + _ -> not_mirrored + end + end. + +is_mirrored(Q) -> + case module(Q) of + {ok, _} -> true; + _ -> false + end. actual_queue_nodes(#amqqueue{pid = MPid, slave_pids = SPids, @@ -308,14 +285,6 @@ actual_queue_nodes(#amqqueue{pid = MPid, _ -> node(MPid) end, Nodes(SPids), Nodes(SSPids)}. -is_mirrored(Q) -> - case policy(<<"ha-mode">>, Q) of - <<"all">> -> true; - <<"nodes">> -> true; - <<"exactly">> -> true; - _ -> false - end. - maybe_auto_sync(Q = #amqqueue{pid = QPid}) -> case policy(<<"ha-sync-mode">>, Q) of <<"automatic">> -> @@ -347,40 +316,24 @@ update_mirrors0(OldQ = #amqqueue{name = QName}, %%---------------------------------------------------------------------------- validate_policy(KeyList) -> - case validate_policy( - proplists:get_value(<<"ha-mode">>, KeyList), - proplists:get_value(<<"ha-params">>, KeyList, none)) of - ok -> case proplists:get_value( - <<"ha-sync-mode">>, KeyList, <<"manual">>) of - <<"automatic">> -> ok; - <<"manual">> -> ok; - Mode -> {error, "ha-sync-mode must be \"manual\" " - "or \"automatic\", got ~p", [Mode]} - end; - E -> E + Mode = proplists:get_value(<<"ha-mode">>, KeyList), + Params = proplists:get_value(<<"ha-params">>, KeyList, none), + case Mode of + undefined -> ok; + _ -> case module(Mode) of + {ok, M} -> case M:validate_policy(Params) of + ok -> validate_sync_mode(KeyList); + E -> E + end; + _ -> {error, + "~p is not a valid ha-mode value", [Mode]} + end end. -validate_policy(<<"all">>, none) -> - ok; -validate_policy(<<"all">>, _Params) -> - {error, "ha-mode=\"all\" does not take parameters", []}; - -validate_policy(<<"nodes">>, []) -> - {error, "ha-mode=\"nodes\" list must be non-empty", []}; -validate_policy(<<"nodes">>, Nodes) when is_list(Nodes) -> - case [I || I <- Nodes, not is_binary(I)] of - [] -> ok; - Invalid -> {error, "ha-mode=\"nodes\" takes a list of strings, " - "~p was not a string", [Invalid]} - end; -validate_policy(<<"nodes">>, Params) -> - {error, "ha-mode=\"nodes\" takes a list, ~p given", [Params]}; - -validate_policy(<<"exactly">>, N) when is_integer(N) andalso N > 0 -> - ok; -validate_policy(<<"exactly">>, Params) -> - {error, "ha-mode=\"exactly\" takes an integer, ~p given", [Params]}; - -validate_policy(Mode, _Params) -> - {error, "~p is not a valid ha-mode value", [Mode]}. - +validate_sync_mode(KeyList) -> + case proplists:get_value(<<"ha-sync-mode">>, KeyList, <<"manual">>) of + <<"automatic">> -> ok; + <<"manual">> -> ok; + Mode -> {error, "ha-sync-mode must be \"manual\" " + "or \"automatic\", got ~p", [Mode]} + end. diff --git a/src/rabbit_mirror_queue_mode.erl b/src/rabbit_mirror_queue_mode.erl new file mode 100644 index 00000000..de1c35e2 --- /dev/null +++ b/src/rabbit_mirror_queue_mode.erl @@ -0,0 +1,55 @@ +%% 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 VMware, Inc. +%% Copyright (c) 2010-2013 VMware, Inc. All rights reserved. +%% + +-module(rabbit_mirror_queue_mode). + +-ifdef(use_specs). + +-type(master() :: node()). +-type(slave() :: node()). +-type(params() :: any()). + +-callback description() -> [proplists:property()]. + +%% Called whenever we think we might need to change nodes for a +%% mirrored queue. +%% +%% Takes: parameters set in the policy, +%% current master, +%% current slaves, +%% current synchronised slaves, +%% all nodes to consider +%% +%% Returns: tuple of new master, new slaves +%% +-callback suggested_queue_nodes( + params(), master(), [slave()], [slave()], [node()]) -> + {master(), [slave()]}. + +%% Are the parameters valid for this mode? +-callback validate_policy(params()) -> + rabbit_policy_validator:validate_results(). + +-else. + +-export([behaviour_info/1]). + +behaviour_info(callbacks) -> + [{description, 0}, {suggested_queue_nodes, 5}, {validate_policy, 1}]. +behaviour_info(_Other) -> + undefined. + +-endif. diff --git a/src/rabbit_mirror_queue_mode_all.erl b/src/rabbit_mirror_queue_mode_all.erl new file mode 100644 index 00000000..84d75b9a --- /dev/null +++ b/src/rabbit_mirror_queue_mode_all.erl @@ -0,0 +1,41 @@ +%% 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 VMware, Inc. +%% Copyright (c) 2010-2013 VMware, Inc. All rights reserved. +%% + +-module(rabbit_mirror_queue_mode_all). + +-include("rabbit.hrl"). + +-behaviour(rabbit_mirror_queue_mode). + +-export([description/0, suggested_queue_nodes/5, validate_policy/1]). + +-rabbit_boot_step({?MODULE, + [{description, "mirror mode all"}, + {mfa, {rabbit_registry, register, + [ha_mode, <<"all">>, ?MODULE]}}, + {requires, rabbit_registry}, + {enables, kernel_ready}]}). + +description() -> + [{description, <<"Mirror queue to all nodes">>}]. + +suggested_queue_nodes(_Params, MNode, _SNodes, _SSNodes, Poss) -> + {MNode, Poss -- [MNode]}. + +validate_policy(none) -> + ok; +validate_policy(_Params) -> + {error, "ha-mode=\"all\" does not take parameters", []}. diff --git a/src/rabbit_mirror_queue_mode_exactly.erl b/src/rabbit_mirror_queue_mode_exactly.erl new file mode 100644 index 00000000..2a42c383 --- /dev/null +++ b/src/rabbit_mirror_queue_mode_exactly.erl @@ -0,0 +1,56 @@ +%% 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 VMware, Inc. +%% Copyright (c) 2010-2013 VMware, Inc. All rights reserved. +%% + +-module(rabbit_mirror_queue_mode_exactly). + +-include("rabbit.hrl"). + +-behaviour(rabbit_mirror_queue_mode). + +-export([description/0, suggested_queue_nodes/5, validate_policy/1]). + +-rabbit_boot_step({?MODULE, + [{description, "mirror mode exactly"}, + {mfa, {rabbit_registry, register, + [ha_mode, <<"exactly">>, ?MODULE]}}, + {requires, rabbit_registry}, + {enables, kernel_ready}]}). + +description() -> + [{description, <<"Mirror queue to a specified number of nodes">>}]. + +%% When we need to add nodes, we randomise our candidate list as a +%% crude form of load-balancing. TODO it would also be nice to +%% randomise the list of ones to remove when we have too many - we +%% would have to take account of synchronisation though. +suggested_queue_nodes(Count, MNode, SNodes, _SSNodes, Poss) -> + SCount = Count - 1, + {MNode, case SCount > length(SNodes) of + true -> Cand = shuffle((Poss -- [MNode]) -- SNodes), + SNodes ++ lists:sublist(Cand, SCount - length(SNodes)); + false -> lists:sublist(SNodes, SCount) + end}. + +shuffle(L) -> + {A1,A2,A3} = now(), + random:seed(A1, A2, A3), + {_, L1} = lists:unzip(lists:keysort(1, [{random:uniform(), N} || N <- L])), + L1. + +validate_policy(N) when is_integer(N) andalso N > 0 -> + ok; +validate_policy(Params) -> + {error, "ha-mode=\"exactly\" takes an integer, ~p given", [Params]}. diff --git a/src/rabbit_mirror_queue_mode_nodes.erl b/src/rabbit_mirror_queue_mode_nodes.erl new file mode 100644 index 00000000..aa62ad33 --- /dev/null +++ b/src/rabbit_mirror_queue_mode_nodes.erl @@ -0,0 +1,70 @@ +%% 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 VMware, Inc. +%% Copyright (c) 2010-2013 VMware, Inc. All rights reserved. +%% + +-module(rabbit_mirror_queue_mode_nodes). + +-include("rabbit.hrl"). + +-behaviour(rabbit_mirror_queue_mode). + +-export([description/0, suggested_queue_nodes/5, validate_policy/1]). + +-rabbit_boot_step({?MODULE, + [{description, "mirror mode nodes"}, + {mfa, {rabbit_registry, register, + [ha_mode, <<"nodes">>, ?MODULE]}}, + {requires, rabbit_registry}, + {enables, kernel_ready}]}). + +description() -> + [{description, <<"Mirror queue to specified nodes">>}]. + +suggested_queue_nodes(Nodes0, MNode, _SNodes, SSNodes, Poss) -> + Nodes1 = [list_to_atom(binary_to_list(Node)) || Node <- Nodes0], + %% If the current master is not in the nodes specified, then what we want + %% to do depends on whether there are any synchronised slaves. If there + %% are then we can just kill the current master - the admin has asked for + %% a migration and we should give it to them. If there are not however + %% then we must keep the master around so as not to lose messages. + Nodes = case SSNodes of + [] -> lists:usort([MNode | Nodes1]); + _ -> Nodes1 + end, + Unavailable = Nodes -- Poss, + Available = Nodes -- Unavailable, + case Available of + [] -> %% We have never heard of anything? Not much we can do but + %% keep the master alive. + {MNode, []}; + _ -> case lists:member(MNode, Available) of + true -> {MNode, Available -- [MNode]}; + false -> %% Make sure the new master is synced! In order to + %% get here SSNodes must not be empty. + [NewMNode | _] = SSNodes, + {NewMNode, Available -- [NewMNode]} + end + end. + +validate_policy([]) -> + {error, "ha-mode=\"nodes\" list must be non-empty", []}; +validate_policy(Nodes) when is_list(Nodes) -> + case [I || I <- Nodes, not is_binary(I)] of + [] -> ok; + Invalid -> {error, "ha-mode=\"nodes\" takes a list of strings, " + "~p was not a string", [Invalid]} + end; +validate_policy(Params) -> + {error, "ha-mode=\"nodes\" takes a list, ~p given", [Params]}. diff --git a/src/rabbit_policy_validator.erl b/src/rabbit_policy_validator.erl index 75b88c39..f0bc1a30 100644 --- a/src/rabbit_policy_validator.erl +++ b/src/rabbit_policy_validator.erl @@ -18,6 +18,8 @@ -ifdef(use_specs). +-export_type([validate_results/0]). + -type(validate_results() :: 'ok' | {error, string(), [term()]} | [validate_results()]). diff --git a/src/rabbit_registry.erl b/src/rabbit_registry.erl index acdc2cff..6aae8de6 100644 --- a/src/rabbit_registry.erl +++ b/src/rabbit_registry.erl @@ -130,7 +130,8 @@ 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(policy_validator) -> rabbit_policy_validator. +class_module(policy_validator) -> rabbit_policy_validator; +class_module(ha_mode) -> rabbit_mirror_queue_mode. %%--------------------------------------------------------------------------- diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index e7b69879..fc529797 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -912,10 +912,11 @@ test_arguments_parser() -> test_dynamic_mirroring() -> %% Just unit tests of the node selection logic, see multi node %% tests for the rest... - Test = fun ({NewM, NewSs, ExtraSs}, Policy, Params, CurrentState, All) -> - {NewM, NewSs0} = - rabbit_mirror_queue_misc:suggested_queue_nodes( - Policy, Params, CurrentState, All), + Test = fun ({NewM, NewSs, ExtraSs}, Policy, Params, + {MNode, SNodes, SSNodes}, All) -> + {ok, M} = rabbit_mirror_queue_misc:module(Policy), + {NewM, NewSs0} = M:suggested_queue_nodes( + Params, MNode, SNodes, SSNodes, All), NewSs1 = lists:sort(NewSs0), case dm_list_match(NewSs, NewSs1, ExtraSs) of ok -> ok; -- cgit v1.2.1 From d28654b6d985242ea3d6fc80c426f1d89dd59b1d Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Thu, 11 Apr 2013 17:53:15 +0100 Subject: Changes --- src/rabbit_exchange.erl | 10 +++++++--- src/rabbit_exchange_decorator.erl | 17 ++++++++++------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index f5fd9a65..c924f53a 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -125,8 +125,12 @@ callback(X = #exchange{type = XType, Module = type_to_module(XType), apply(Module, Fun, [Serial(Module:serialise_events()) | Args]). -policy_changed(X = #exchange{type = XType}, X1) -> - ok = (type_to_module(XType)):policy_changed(X, X1). +policy_changed(X = #exchange{type = XType, + decorators = Decorators}, X1) -> + [ok = M:policy_changed(X, X1) || + M <- [type_to_module(XType) | + rabbit_exchange_decorator:select(all, Decorators)]], + ok. serialise_events(X = #exchange{type = Type, decorators = Decorators}) -> lists:any(fun (M) -> @@ -324,7 +328,7 @@ route1(_, _, {[], _, QNames}) -> route1(Delivery, Decorators, {[X = #exchange{type = Type} | WorkList], SeenXs, QNames}) -> ExchangeDests = (type_to_module(Type)):route(X, Delivery), - DecorateDests = process_decorators(X, Decorators, Delivery), + DecorateDests = process_decorators(X, Decorators, Delivery), AlternateDests = process_alternate(X, ExchangeDests), route1(Delivery, Decorators, lists:foldl(fun process_route/2, {WorkList, SeenXs, QNames}, diff --git a/src/rabbit_exchange_decorator.erl b/src/rabbit_exchange_decorator.erl index 3748a844..3bc9de1e 100644 --- a/src/rabbit_exchange_decorator.erl +++ b/src/rabbit_exchange_decorator.erl @@ -18,7 +18,7 @@ -include("rabbit.hrl"). --export([list/0, select/2, set/1]). +-export([select/2, set/1]). %% This is like an exchange type except that: %% @@ -49,6 +49,10 @@ -callback delete(tx(), rabbit_types:exchange(), [rabbit_types:binding()]) -> 'ok'. +%% called when the policy attached to this exchange changes. +-callback policy_changed(rabbit_types:exchange(), rabbit_types:exchange()) -> + 'ok'. + %% called after a binding has been added or recovered -callback add_binding(serial(), rabbit_types:exchange(), rabbit_types:binding()) -> 'ok'. @@ -57,10 +61,9 @@ -callback remove_bindings(serial(), rabbit_types:exchange(), [rabbit_types:binding()]) -> 'ok'. -%% Decorators can optionally implement route/2 which allows additional -%% destinations to be added to the routing decision. -%% -callback route(rabbit_types:exchange(), rabbit_types:delivery()) -> -%% [rabbit_amqqueue:name() | rabbit_exchange:name()] | ok. +%% Allows additional destinations to be added to the routing decision. +-callback route(rabbit_types:exchange(), rabbit_types:delivery()) -> + [rabbit_amqqueue:name() | rabbit_exchange:name()] | ok. %% Whether the decorator wishes to receive callbacks for the exchange %% none:no callbacks, noroute:all callbacks except route, all:all callbacks @@ -73,7 +76,7 @@ behaviour_info(callbacks) -> [{description, 0}, {serialise_events, 1}, {create, 2}, {delete, 3}, {policy_changed, 2}, {add_binding, 3}, {remove_bindings, 3}, - {active_for, 1}]; + {route, 2}, {active_for, 1}]; behaviour_info(_Other) -> undefined. @@ -94,7 +97,7 @@ set(X) -> Callbacks = D:active_for(X), {cons_if_eq(all, Callbacks, D, Route), cons_if_eq(noroute, Callbacks, D, NoRoute)} - end, {[], []}, rabbit_exchange_decorator:list())}. + end, {[], []}, list())}. cons_if_eq(Select, Select, Item, List) -> [Item | List]; cons_if_eq(_Select, _Other, _Item, List) -> List. -- cgit v1.2.1 From b382e07f8ef319dfbe46e4d874aa97e01233694c Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 12 Apr 2013 14:42:11 +0100 Subject: Only ban cycles that are entirely due to expiry --- src/rabbit_amqqueue_process.erl | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index b016c4d2..b0b37bb2 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -846,7 +846,7 @@ dead_letter_publish(Msg, Reason, X, RK, MsgSeqNo, QName) -> DLMsg = make_dead_letter_msg(Msg, Reason, X#exchange.name, RK, QName), Delivery = rabbit_basic:delivery(false, DLMsg, MsgSeqNo), {Queues, Cycles} = detect_dead_letter_cycles( - DLMsg, rabbit_exchange:route(X, Delivery)), + Reason, DLMsg, rabbit_exchange:route(X, Delivery)), lists:foreach(fun log_cycle_once/1, Cycles), {_, DeliveredQPids} = rabbit_amqqueue:deliver( rabbit_amqqueue:lookup(Queues), Delivery), @@ -895,7 +895,8 @@ cleanup_after_confirm(AckTags, State = #q{delayed_stop = DS, false -> noreply(State1) end. -detect_dead_letter_cycles(#basic_message{content = Content}, Queues) -> +detect_dead_letter_cycles(expired, + #basic_message{content = Content}, Queues) -> #content{properties = #'P_basic'{headers = Headers}} = rabbit_binary_parser:ensure_content_decoded(Content), NoCycles = {Queues, []}, @@ -904,22 +905,38 @@ detect_dead_letter_cycles(#basic_message{content = Content}, Queues) -> NoCycles; _ -> case rabbit_misc:table_lookup(Headers, <<"x-death">>) of - {array, DeathTables} -> - OldQueues = [rabbit_misc:table_lookup(D, <<"queue">>) || - {table, D} <- DeathTables], - OldQueues1 = [QName || {longstr, QName} <- OldQueues], - OldQueuesSet = ordsets:from_list(OldQueues1), + {array, Deaths} -> {Cycling, NotCycling} = lists:partition( - fun(Queue) -> - ordsets:is_element(Queue#resource.name, - OldQueuesSet) + fun (#resource{name = Queue}) -> + is_dead_letter_cycle(Queue, Deaths) end, Queues), + OldQueues = [rabbit_misc:table_lookup(D, <<"queue">>) || + {table, D} <- Deaths], + OldQueues1 = [QName || {longstr, QName} <- OldQueues], {NotCycling, [[QName | OldQueues1] || #resource{name = QName} <- Cycling]}; _ -> NoCycles end + end; +detect_dead_letter_cycles(_Reason, _Msg, Queues) -> + {Queues, []}. + +is_dead_letter_cycle(Queue, Deaths) -> + {Cycle, Rest} = + lists:splitwith( + fun ({table, D}) -> + {longstr, Queue} =/= rabbit_misc:table_lookup(D, <<"queue">>); + (_) -> + true + end, lists:reverse(Deaths)), + %% Is there a cycle, and if so, is it entirely due to expiry? + case Rest of + [] -> false; + [H|_] -> [] =:= [D || {table, D} <- Cycle ++ [H], + {longstr, <<"expired">>} =/= + rabbit_misc:table_lookup(D, <<"reason">>)] end. make_dead_letter_msg(Msg = #basic_message{content = Content, -- cgit v1.2.1 From 569cc88471263357776c316f7d6dfeff370bf687 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 12 Apr 2013 15:42:27 +0100 Subject: No, the newest dead letterings are at the head. --- src/rabbit_amqqueue_process.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index b0b37bb2..e6bc71fe 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -930,7 +930,7 @@ is_dead_letter_cycle(Queue, Deaths) -> {longstr, Queue} =/= rabbit_misc:table_lookup(D, <<"queue">>); (_) -> true - end, lists:reverse(Deaths)), + end, Deaths), %% Is there a cycle, and if so, is it entirely due to expiry? case Rest of [] -> false; -- cgit v1.2.1 From 97dc8b329186bc528034c7846a8c1244e138f802 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 12 Apr 2013 15:44:55 +0100 Subject: Maybe clearer? --- src/rabbit_amqqueue_process.erl | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index e6bc71fe..fb2c16c8 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -934,9 +934,13 @@ is_dead_letter_cycle(Queue, Deaths) -> %% Is there a cycle, and if so, is it entirely due to expiry? case Rest of [] -> false; - [H|_] -> [] =:= [D || {table, D} <- Cycle ++ [H], - {longstr, <<"expired">>} =/= - rabbit_misc:table_lookup(D, <<"reason">>)] + [H|_] -> lists:all( + fun ({table, D}) -> + {longstr, <<"expired">>} =:= + rabbit_misc:table_lookup(D, <<"reason">>); + (_) -> + false + end, Cycle ++ [H]) end. make_dead_letter_msg(Msg = #basic_message{content = Content, -- cgit v1.2.1 From 59664eeaa335252c7b4bdb0c6fbc33a3ec52c302 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 15 Apr 2013 10:58:24 +0100 Subject: Cosmetic --- src/rabbit_exchange.erl | 5 ++--- src/rabbit_policy.erl | 16 +++++++--------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index c924f53a..6d111b83 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -133,9 +133,8 @@ policy_changed(X = #exchange{type = XType, ok. serialise_events(X = #exchange{type = Type, decorators = Decorators}) -> - lists:any(fun (M) -> - M:serialise_events(X) - end, rabbit_exchange_decorator:select(all, Decorators)) + lists:any(fun (M) -> M:serialise_events(X) end, + rabbit_exchange_decorator:select(all, Decorators)) orelse (type_to_module(Type)):serialise_events(). serial(#exchange{name = XName} = X) -> diff --git a/src/rabbit_policy.erl b/src/rabbit_policy.erl index d276c2fb..ad2949b8 100644 --- a/src/rabbit_policy.erl +++ b/src/rabbit_policy.erl @@ -170,15 +170,13 @@ update_policies(VHost) -> update_exchange(X = #exchange{name = XName, policy = OldPolicy}, Policies) -> case match(XName, Policies) of - OldPolicy -> - no_change; - NewPolicy -> - rabbit_exchange:update( - XName, fun(X1) -> - rabbit_exchange_decorator:set( - X1#exchange{policy = NewPolicy}) - end), - {X, X#exchange{policy = NewPolicy}} + OldPolicy -> no_change; + NewPolicy -> rabbit_exchange:update( + XName, fun(X1) -> + rabbit_exchange_decorator:set( + X1#exchange{policy = NewPolicy}) + end), + {X, X#exchange{policy = NewPolicy}} end. update_queue(Q = #amqqueue{name = QName, policy = OldPolicy}, Policies) -> -- cgit v1.2.1 From e4cfae04a643a3a960dbd53272649c32cd2b13cb Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Mon, 15 Apr 2013 13:49:55 +0100 Subject: Ignore missing exchange decorators --- src/rabbit_exchange.erl | 9 +++++++++ src/rabbit_exchange_decorator.erl | 12 ++++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index 6d111b83..e7847673 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -113,8 +113,17 @@ recover() -> callback(X, create, map_create_tx(Tx), [X]) end, rabbit_durable_exchange), + report_missing_decorators(Xs), [XName || #exchange{name = XName} <- Xs]. +report_missing_decorators(Xs) -> + Mods = lists:usort(lists:append([rabbit_exchange_decorator:select(raw, D) || + #exchange{decorators = D} <- Xs])), + case [M || M <- Mods, code:which(M) =:= non_existing] of + [] -> ok; + M -> rabbit_log:warning("Missing exchange decorators: ~p~n", [M]) + end. + callback(X = #exchange{type = XType, decorators = Decorators}, Fun, Serial0, Args) -> Serial = if is_function(Serial0) -> Serial0; diff --git a/src/rabbit_exchange_decorator.erl b/src/rabbit_exchange_decorator.erl index 3bc9de1e..19cbf92f 100644 --- a/src/rabbit_exchange_decorator.erl +++ b/src/rabbit_exchange_decorator.erl @@ -84,11 +84,13 @@ behaviour_info(_Other) -> %%---------------------------------------------------------------------------- -list() -> [M || {_, M} <- rabbit_registry:lookup_all(exchange_decorator)]. - %% select a subset of active decorators -select(all, {Route, NoRoute}) -> Route ++ NoRoute; -select(route, {Route, _NoRoute}) -> Route. +select(all, {Route, NoRoute}) -> filter(Route ++ NoRoute); +select(route, {Route, _NoRoute}) -> filter(Route); +select(raw, {Route, NoRoute}) -> Route ++ NoRoute. + +filter(Modules) -> + [M || M <- Modules, code:which(M) =/= non_existing]. set(X) -> X#exchange{ @@ -99,5 +101,7 @@ set(X) -> cons_if_eq(noroute, Callbacks, D, NoRoute)} end, {[], []}, list())}. +list() -> [M || {_, M} <- rabbit_registry:lookup_all(exchange_decorator)]. + cons_if_eq(Select, Select, Item, List) -> [Item | List]; cons_if_eq(_Select, _Other, _Item, List) -> List. -- cgit v1.2.1 From 8089c02708b9e19725f9ed68b335c0d1cbeb1bca Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Tue, 16 Apr 2013 11:59:44 +0100 Subject: Updates --- src/rabbit_exchange.erl | 18 +++++++++++------- src/rabbit_policy.erl | 14 ++++++++------ 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index e7847673..891caea2 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -134,11 +134,13 @@ callback(X = #exchange{type = XType, Module = type_to_module(XType), apply(Module, Fun, [Serial(Module:serialise_events()) | Args]). -policy_changed(X = #exchange{type = XType, - decorators = Decorators}, X1) -> - [ok = M:policy_changed(X, X1) || - M <- [type_to_module(XType) | - rabbit_exchange_decorator:select(all, Decorators)]], +policy_changed(X = #exchange{type = XType, + decorators = Decorators}, + X1 = #exchange{decorators = Decorators1}) -> + D = rabbit_exchange_decorator:select(all, Decorators), + D1 = rabbit_exchange_decorator:select(all, Decorators1), + Diff = (D -- D1) ++ (D1 -- D), + [ok = M:policy_changed(X, X1) || M <- [type_to_module(XType) | Diff]], ok. serialise_events(X = #exchange{type = Type, decorators = Decorators}) -> @@ -275,7 +277,8 @@ update_scratch(Name, App, Fun) -> Scratches2 = orddict:store( App, Fun(Scratch), Scratches1), X#exchange{scratches = Scratches2} - end) + end), + ok end). update(Name, Fun) -> @@ -286,7 +289,8 @@ update(Name, Fun) -> case Durable of true -> ok = mnesia:write(rabbit_durable_exchange, X1, write); _ -> ok - end; + end, + X1; [] -> ok end. diff --git a/src/rabbit_policy.erl b/src/rabbit_policy.erl index ad2949b8..ae8d21b5 100644 --- a/src/rabbit_policy.erl +++ b/src/rabbit_policy.erl @@ -171,12 +171,14 @@ update_policies(VHost) -> update_exchange(X = #exchange{name = XName, policy = OldPolicy}, Policies) -> case match(XName, Policies) of OldPolicy -> no_change; - NewPolicy -> rabbit_exchange:update( - XName, fun(X1) -> - rabbit_exchange_decorator:set( - X1#exchange{policy = NewPolicy}) - end), - {X, X#exchange{policy = NewPolicy}} + NewPolicy -> case rabbit_exchange:update( + XName, fun (X0) -> + rabbit_exchange_decorator:set( + X0 #exchange{policy = NewPolicy}) + end) of + #exchange{} = X1 -> {X, X1}; + ok -> {X, X } + end end. update_queue(Q = #amqqueue{name = QName, policy = OldPolicy}, Policies) -> -- 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(-) 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 3afa3660873ba2affc9cc402c07cc126e2a78302 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Tue, 16 Apr 2013 12:45:12 +0100 Subject: Further excisions --- src/rabbit_amqqueue_process.erl | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 7ab0d774..3712a625 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -49,7 +49,6 @@ ttl_timer_ref, ttl_timer_expiry, senders, - queue_monitors, dlx, dlx_routing_key, max_length, @@ -831,9 +830,8 @@ dead_letter_publish(Msg, Reason, X, RK, QName) -> {Queues, Cycles} = detect_dead_letter_cycles( DLMsg, rabbit_exchange:route(X, Delivery)), lists:foreach(fun log_cycle_once/1, Cycles), - {_, DeliveredQPids} = rabbit_amqqueue:deliver( - rabbit_amqqueue:lookup(Queues), Delivery), - DeliveredQPids. + rabbit_amqqueue:deliver( rabbit_amqqueue:lookup(Queues), Delivery), + ok. stop(State) -> stop(noreply, State). -- cgit v1.2.1 From 08957ca51b5fc40f54227350a38f258e10a3a4a9 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 16 Apr 2013 13:35:02 +0100 Subject: first stab at summarising processes by their ancestor chain --- src/rabbit_vm.erl | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/rabbit_vm.erl b/src/rabbit_vm.erl index b3e9ec66..3d3ef59b 100644 --- a/src/rabbit_vm.erl +++ b/src/rabbit_vm.erl @@ -138,3 +138,45 @@ plugin_memory(App) -> is_plugin("rabbitmq_" ++ _) -> true; is_plugin(App) -> lists:member(App, ?MAGIC_PLUGINS). + +%% TODO support Pids, not just Names - need this for plugins +sum_processes(Names, Acc0) -> + sum_processes(Names, fun (_, X, Y) -> X + Y end, + [{Item, 0} || Item <- Acc0]). + +sum_processes(Names, Fun, Acc0) -> + Items = [Item || {Item, _Val0} <- Acc0], + Acc0Dict = orddict:from_list(Acc0), + NameAccs0 = orddict:from_list([{Name, Acc0Dict} || Name <- Names]), + {NameAccs, OtherAcc} = + lists:foldl(fun (Pid, Acc) -> + case process_info(Pid, [dictionary | Items]) of + undefined -> + Acc; + [{dictionary, D} | Vals] -> + ValsDict = orddict:from_list(Vals), + accumulate(find_ancestor(D, Names), Fun, + ValsDict, Acc) + end + end, {NameAccs0, Acc0Dict}, processes()), + %% these conversions aren't strictly necessary; we do them simply + %% for the sake of encapsulating the representation. + {[orddict:to_list(NameAcc) || NameAcc <- orddict:to_list(NameAccs)], + orddict:to_list(OtherAcc)}. + +find_ancestor(D, Names) -> + case lists:keyfind('$ancestors', 1, D) of + false -> undefined; + {_, Ancestors} -> case lists:splitwith( + fun (A) -> not lists:member(A, Names) end, + Ancestors) of + {_, []} -> undefined; + {_, [Name | _]} -> Name + end + end. + +accumulate(undefined, Fun, ValsDict, {NameAccs, OtherAcc}) -> + {NameAccs, orddict:merge(Fun, ValsDict, OtherAcc)}; +accumulate(Name, Fun, ValsDict, {NameAccs, OtherAcc}) -> + F = fun (NameAcc) -> orddict:merge(Fun, ValsDict, NameAcc) end, + {orddict:update(Name, F, NameAccs), OtherAcc}. -- cgit v1.2.1 From 6846869eeade78fccc3b5b9bdf4a881f63d80530 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Tue, 16 Apr 2013 14:57:48 +0100 Subject: Further tweaks --- src/rabbit_mnesia.erl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 52af28ab..7775aa3f 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -566,6 +566,9 @@ check_cluster_consistency(Node) -> {error, not_found}; {_OTP, _Rabbit, _Hash, {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 {error, _} = E -> E; @@ -819,8 +822,8 @@ 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, Hash, _} -> case check_consistency(OTP, Rabbit, Hash) of + {badrpc, _Reason} -> find_good_node(Nodes); + {OTP, Rabbit, Hash, _} -> case check_consistency(OTP, Rabbit, Hash) of {error, _} -> find_good_node(Nodes); ok -> {ok, Node} end -- cgit v1.2.1 From 7a3f0d0c44c6f37f12a36e624c5de73d269f78ea Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Tue, 16 Apr 2013 15:08:51 +0100 Subject: Handle potential incompatibility --- src/rabbit_mnesia.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 7775aa3f..5bef1277 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -823,6 +823,7 @@ find_good_node([]) -> 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} -- 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(-) 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(-) 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(-) 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(-) 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 edd2b1784af98f759e2d1cedafd05993824bdce7 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 16 Apr 2013 15:50:11 +0100 Subject: Cosmetic --- src/rabbit_mnesia.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 5bef1277..e331f62c 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -827,7 +827,7 @@ find_good_node([Node | Nodes]) -> {OTP, Rabbit, Hash, _} -> case check_consistency(OTP, Rabbit, Hash) of {error, _} -> find_good_node(Nodes); ok -> {ok, Node} - end + end end. is_only_clustered_disc_node() -> -- 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(-) 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 074fdf03ac7b9245d150cc03aa89c403001d8526 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Tue, 16 Apr 2013 17:36:34 +0100 Subject: Further modifications implementing feedback --- src/rabbit_exchange.erl | 13 ++++++++----- src/rabbit_exchange_decorator.erl | 2 +- src/rabbit_policy.erl | 5 +++-- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index 891caea2..a1759c08 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -68,7 +68,10 @@ -spec(update_scratch/3 :: (name(), atom(), fun((any()) -> any())) -> 'ok'). -spec(update/2 :: (name(), - fun((rabbit_types:exchange()) -> rabbit_types:exchange())) -> 'ok'). + fun((rabbit_types:exchange()) -> rabbit_types:exchange())) + -> {exchange_not_found, rabbit_exchange:name()} | + {exchange_not_durable, rabbit_exchange:name()} | + rabbit_types:exchange()). -spec(info_keys/0 :: () -> rabbit_types:info_keys()). -spec(info/1 :: (rabbit_types:exchange()) -> rabbit_types:infos()). -spec(info/2 :: @@ -139,8 +142,8 @@ policy_changed(X = #exchange{type = XType, X1 = #exchange{decorators = Decorators1}) -> D = rabbit_exchange_decorator:select(all, Decorators), D1 = rabbit_exchange_decorator:select(all, Decorators1), - Diff = (D -- D1) ++ (D1 -- D), - [ok = M:policy_changed(X, X1) || M <- [type_to_module(XType) | Diff]], + DAll = lists:usort(D ++ D1), + [ok = M:policy_changed(X, X1) || M <- [type_to_module(XType) | DAll]], ok. serialise_events(X = #exchange{type = Type, decorators = Decorators}) -> @@ -288,11 +291,11 @@ update(Name, Fun) -> ok = mnesia:write(rabbit_exchange, X1, write), case Durable of true -> ok = mnesia:write(rabbit_durable_exchange, X1, write); - _ -> ok + _ -> {exchange_not_durable, Name} end, X1; [] -> - ok + {exchange_not_found, Name} end. info_keys() -> ?INFO_KEYS. diff --git a/src/rabbit_exchange_decorator.erl b/src/rabbit_exchange_decorator.erl index 19cbf92f..79ea212f 100644 --- a/src/rabbit_exchange_decorator.erl +++ b/src/rabbit_exchange_decorator.erl @@ -63,7 +63,7 @@ %% Allows additional destinations to be added to the routing decision. -callback route(rabbit_types:exchange(), rabbit_types:delivery()) -> - [rabbit_amqqueue:name() | rabbit_exchange:name()] | ok. + [rabbit_amqqueue:name() | rabbit_exchange:name()]. %% Whether the decorator wishes to receive callbacks for the exchange %% none:no callbacks, noroute:all callbacks except route, all:all callbacks diff --git a/src/rabbit_policy.erl b/src/rabbit_policy.erl index ae8d21b5..184e1c33 100644 --- a/src/rabbit_policy.erl +++ b/src/rabbit_policy.erl @@ -176,8 +176,9 @@ update_exchange(X = #exchange{name = XName, policy = OldPolicy}, Policies) -> rabbit_exchange_decorator:set( X0 #exchange{policy = NewPolicy}) end) of - #exchange{} = X1 -> {X, X1}; - ok -> {X, X } + #exchange{} = X1 -> {X, X1}; + {exchange_not_found, _} -> {X, X }; + {exchange_not_durable, _} -> {X, X } end end. -- cgit v1.2.1 From e9e634e40fc21fb53a69c69038a78c62b743f379 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Wed, 17 Apr 2013 08:17:53 +0100 Subject: Change exchange update return type --- src/rabbit_exchange.erl | 7 +++---- src/rabbit_policy.erl | 6 +++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index a1759c08..1bed9344 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -69,8 +69,7 @@ -spec(update/2 :: (name(), fun((rabbit_types:exchange()) -> rabbit_types:exchange())) - -> {exchange_not_found, rabbit_exchange:name()} | - {exchange_not_durable, rabbit_exchange:name()} | + -> exchange_not_found | exchange_not_durable | rabbit_types:exchange()). -spec(info_keys/0 :: () -> rabbit_types:info_keys()). -spec(info/1 :: (rabbit_types:exchange()) -> rabbit_types:infos()). @@ -291,11 +290,11 @@ update(Name, Fun) -> ok = mnesia:write(rabbit_exchange, X1, write), case Durable of true -> ok = mnesia:write(rabbit_durable_exchange, X1, write); - _ -> {exchange_not_durable, Name} + _ -> exchange_not_durable end, X1; [] -> - {exchange_not_found, Name} + exchange_not_found end. info_keys() -> ?INFO_KEYS. diff --git a/src/rabbit_policy.erl b/src/rabbit_policy.erl index 184e1c33..ca2837f5 100644 --- a/src/rabbit_policy.erl +++ b/src/rabbit_policy.erl @@ -176,9 +176,9 @@ update_exchange(X = #exchange{name = XName, policy = OldPolicy}, Policies) -> rabbit_exchange_decorator:set( X0 #exchange{policy = NewPolicy}) end) of - #exchange{} = X1 -> {X, X1}; - {exchange_not_found, _} -> {X, X }; - {exchange_not_durable, _} -> {X, X } + #exchange{} = X1 -> {X, X1}; + exchange_not_found -> {X, X }; + exchange_not_durable -> {X, X } end end. -- cgit v1.2.1 From 73b7ccda425668ebd26debc6458b265af4f599a8 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Wed, 17 Apr 2013 09:46:29 +0100 Subject: Abstract --- src/rabbit_mnesia.erl | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index e331f62c..5d902b5d 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -403,10 +403,8 @@ cluster_status(WhichNodes) -> end. node_info() -> - DelegateBeamLocation = code:which(delegate), - {ok, {delegate, DelegateBeamHash}} = beam_lib:md5(DelegateBeamLocation), - {erlang:system_info(otp_release), rabbit_misc:version(), DelegateBeamHash, - cluster_status_from_mnesia()}. + {erlang:system_info(otp_release), rabbit_misc:version(), + delegate_beam_hash(), cluster_status_from_mnesia()}. node_type() -> DiscNodes = cluster_nodes(disc), @@ -789,14 +787,17 @@ check_rabbit_consistency(Remote) -> fun rabbit_misc:version_minor_equivalent/2). check_beam_compatibility(RemoteHash) -> - DelegateBeamLocation = code:which(delegate), - {ok, {delegate, LocalHash}} = beam_lib:md5(DelegateBeamLocation), - case RemoteHash == LocalHash of + case RemoteHash == delegate_beam_hash() of true -> ok; false -> {error, {incompatible_bytecode, "Incompatible Erlang bytecode found on nodes"}} end. +delegate_beam_hash() -> + DelegateBeamLocation = code:which(delegate), + {ok, {delegate, Hash}} = beam_lib:md5(DelegateBeamLocation), + 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 -- cgit v1.2.1 From cfa98cd3073cac972721c0884ae05189dda6a0f1 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 17 Apr 2013 10:41:20 +0100 Subject: Explain why we are doing this. --- src/rabbit_mnesia.erl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 5d902b5d..95182a7c 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -793,6 +793,9 @@ check_beam_compatibility(RemoteHash) -> "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() -> DelegateBeamLocation = code:which(delegate), {ok, {delegate, Hash}} = beam_lib:md5(DelegateBeamLocation), -- cgit v1.2.1 From ace238a2125fc42a64cd227eb9833db1a6785361 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 17 Apr 2013 11:16:28 +0100 Subject: Magic call handler to debug the state. --- src/gen_server2.erl | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/gen_server2.erl b/src/gen_server2.erl index 9109febd..84536cb6 100644 --- a/src/gen_server2.erl +++ b/src/gen_server2.erl @@ -883,6 +883,13 @@ common_become(_Name, _Mod, _NState, [] = _Debug) -> common_become(Name, Mod, NState, Debug) -> sys:handle_debug(Debug, fun print_event/3, Name, {become, Mod, NState}). +handle_msg({'$gen_call', From, {debug_state, Fun}}, + GS2State = #gs2_state{state = State, + name = Name, + debug = Debug}) -> + Debug1 = common_reply(Name, From, catch Fun(State), State, Debug), + loop(GS2State #gs2_state { state = State, + debug = Debug1 }); handle_msg({'$gen_call', From, Msg}, GS2State = #gs2_state { mod = Mod, state = State, name = Name, -- cgit v1.2.1 From 4f58d9a56ff2f72f5524b898a079cc6ee8041acf Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 17 Apr 2013 12:08:05 +0100 Subject: Remove exchange_not_durable as, err, it would never be returned. And strip leading exchange_ since this is rabbit_exchange. --- src/rabbit_exchange.erl | 7 +++---- src/rabbit_policy.erl | 5 ++--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index 1bed9344..b4bdd348 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -69,8 +69,7 @@ -spec(update/2 :: (name(), fun((rabbit_types:exchange()) -> rabbit_types:exchange())) - -> exchange_not_found | exchange_not_durable | - rabbit_types:exchange()). + -> not_found | rabbit_types:exchange()). -spec(info_keys/0 :: () -> rabbit_types:info_keys()). -spec(info/1 :: (rabbit_types:exchange()) -> rabbit_types:infos()). -spec(info/2 :: @@ -290,11 +289,11 @@ update(Name, Fun) -> ok = mnesia:write(rabbit_exchange, X1, write), case Durable of true -> ok = mnesia:write(rabbit_durable_exchange, X1, write); - _ -> exchange_not_durable + _ -> ok end, X1; [] -> - exchange_not_found + not_found end. info_keys() -> ?INFO_KEYS. diff --git a/src/rabbit_policy.erl b/src/rabbit_policy.erl index ca2837f5..0990c662 100644 --- a/src/rabbit_policy.erl +++ b/src/rabbit_policy.erl @@ -176,9 +176,8 @@ update_exchange(X = #exchange{name = XName, policy = OldPolicy}, Policies) -> rabbit_exchange_decorator:set( X0 #exchange{policy = NewPolicy}) end) of - #exchange{} = X1 -> {X, X1}; - exchange_not_found -> {X, X }; - exchange_not_durable -> {X, X } + #exchange{} = X1 -> {X, X1}; + not_found -> {X, X } end end. -- cgit v1.2.1 From 08477ab54edd3b4652fd4d8abd84a2ac3f0bf3ba Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 17 Apr 2013 12:33:40 +0100 Subject: Cosmetic --- src/rabbit_exchange_decorator.erl | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/rabbit_exchange_decorator.erl b/src/rabbit_exchange_decorator.erl index 79ea212f..3abaa48c 100644 --- a/src/rabbit_exchange_decorator.erl +++ b/src/rabbit_exchange_decorator.erl @@ -93,13 +93,12 @@ filter(Modules) -> [M || M <- Modules, code:which(M) =/= non_existing]. set(X) -> - X#exchange{ - decorators = - lists:foldl(fun (D, {Route, NoRoute}) -> - Callbacks = D:active_for(X), - {cons_if_eq(all, Callbacks, D, Route), - cons_if_eq(noroute, Callbacks, D, NoRoute)} - end, {[], []}, list())}. + Decs = lists:foldl(fun (D, {Route, NoRoute}) -> + ActiveFor = D:active_for(X), + {cons_if_eq(all, ActiveFor, D, Route), + cons_if_eq(noroute, ActiveFor, D, NoRoute)} + end, {[], []}, list()), + X#exchange{decorators = Decs}. list() -> [M || {_, M} <- rabbit_registry:lookup_all(exchange_decorator)]. -- cgit v1.2.1 From c3291ec23ade84322093c7f67ca17f14353c9ca6 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 17 Apr 2013 16:26:34 +0100 Subject: First pass at splitting all the autoheal stuff out into a separate module. --- src/rabbit_autoheal.erl | 208 ++++++++++++++++++++++++++++++++++++++++++++ src/rabbit_node_monitor.erl | 203 ++++-------------------------------------- 2 files changed, 223 insertions(+), 188 deletions(-) create mode 100644 src/rabbit_autoheal.erl diff --git a/src/rabbit_autoheal.erl b/src/rabbit_autoheal.erl new file mode 100644 index 00000000..4a6307fc --- /dev/null +++ b/src/rabbit_autoheal.erl @@ -0,0 +1,208 @@ +%% 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 VMware, Inc. +%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% + +-module(rabbit_autoheal). + +-export([init/0, maybe_start/1, node_down/2, handle_msg/3]). + +%% The named process we are running in. +-define(SERVER, rabbit_node_monitor). + +%%---------------------------------------------------------------------------- + +%% In order to autoheal we want to: +%% +%% * Find the winning partition +%% * Stop all nodes in other partitions +%% * Wait for them all to be stopped +%% * Start them again +%% +%% To keep things simple, we assume all nodes are up. We don't start +%% unless all nodes are up, and if a node goes down we abandon the +%% whole process. To further keep things simple we also defer the +%% decision as to the winning node to the "leader" - arbitrarily +%% selected as the first node in the cluster. +%% +%% To coordinate the restarting nodes we pick a special node from the +%% winning partition - the "winner". Restarting nodes then stop, tell +%% the winner they have done so, and wait for it to tell them it is +%% safe to start again. +%% +%% The winner and the leader are not necessarily the same node! Since +%% the leader may end up restarting, we also make sure that it does +%% not announce its decision (and thus cue other nodes to restart) +%% until it has seen a request from every node that has experienced a +%% partition. + +%%---------------------------------------------------------------------------- + +init() -> not_healing. + +maybe_start(not_healing) -> + case enabled() andalso rabbit_node_monitor:all_nodes_up() of + true -> [Leader | _] = lists:usort(rabbit_mnesia:cluster_nodes(all)), + rabbit_log:info("Autoheal: leader is ~p~n", [Leader]), + send(Leader, {request_winner, node()}), + case node() of + Leader -> not_healing; + _ -> wait_for_winner + end; + false -> not_healing + end; +maybe_start(State) -> + State. + +enabled() -> + {ok, autoheal} =:= application:get_env(rabbit, cluster_partition_handling). + +node_down(_Node, {wait_for, _Nodes, _Notify} = Autoheal) -> + Autoheal; +node_down(_Node, not_healing) -> + not_healing; +node_down(Node, _State) -> + rabbit_log:info("Autoheal: aborting - ~p went down~n", [Node]), + not_healing. + +handle_msg({request_winner, Node}, + not_healing, Partitions) -> + case rabbit_node_monitor:all_nodes_up() of + false -> not_healing; + true -> Nodes = rabbit_mnesia:cluster_nodes(all), + Partitioned = + [N || N <- Nodes -- [node()], + P <- [begin + {_, R} = rpc:call(N, rabbit_node_monitor, + partitions, []), + R + end], + is_list(P) andalso length(P) > 0], + Partitioned1 = case Partitions of + [] -> Partitioned; + _ -> [node() | Partitioned] + end, + rabbit_log:info( + "Autoheal leader start; partitioned nodes are ~p~n", + [Partitioned1]), + handle_msg({request_winner, Node}, + {wait_for_winner_reqs, Partitioned1, Partitioned1}, + Partitions) + end; + +handle_msg({request_winner, Node}, + {wait_for_winner_reqs, [Node], Notify}, Partitions) -> + AllPartitions = all_partitions(Partitions), + Winner = select_winner(AllPartitions), + rabbit_log:info("Autoheal request winner from ~p~n" + " Partitions were determined to be ~p~n" + " Winner is ~p~n", [Node, AllPartitions, Winner]), + [send(N, {winner, Winner}) || N <- Notify], + wait_for_winner; + +handle_msg({request_winner, Node}, + {wait_for_winner_reqs, Nodes, Notify}, _Partitions) -> + rabbit_log:info("Autoheal request winner from ~p~n", [Node]), + {wait_for_winner_reqs, Nodes -- [Node], Notify}; + +handle_msg({winner, Winner}, + wait_for_winner, Partitions) -> + case lists:member(Winner, Partitions) of + false -> case node() of + Winner -> rabbit_log:info( + "Autoheal: waiting for nodes to stop: ~p~n", + [Partitions]), + {wait_for, Partitions, Partitions}; + _ -> rabbit_log:info( + "Autoheal: nothing to do~n", []), + not_healing + end; + true -> restart_me(Winner), + restarting + end; + +handle_msg({winner, _Winner}, State, _Partitions) -> + %% ignore, we already cancelled the autoheal process + State; + +handle_msg({node_stopped, Node}, + {wait_for, [Node], Notify}, _Partitions) -> + rabbit_log:info("Autoheal: final node has stopped, starting...~n",[]), + [{rabbit_outside_app_process, N} ! autoheal_safe_to_start || N <- Notify], + not_healing; + +handle_msg({node_stopped, Node}, + {wait_for, WaitFor, Notify}, _Partitions) -> + {wait_for, WaitFor -- [Node], Notify}; + +handle_msg({node_stopped, _Node}, State, _Partitions) -> + %% ignore, we already cancelled the autoheal process + State. + +%%---------------------------------------------------------------------------- + +send(Node, Msg) -> {?SERVER, Node} ! {autoheal_msg, Msg}. + +select_winner(AllPartitions) -> + {_, [Winner | _]} = + hd(lists:reverse( + lists:sort([{partition_value(P), P} || P <- AllPartitions]))), + Winner. + +partition_value(Partition) -> + Connections = [Res || Node <- Partition, + Res <- [rpc:call(Node, rabbit_networking, + connections_local, [])], + is_list(Res)], + {length(lists:append(Connections)), length(Partition)}. + +restart_me(Winner) -> + rabbit_log:warning( + "Autoheal: we were selected to restart; winner is ~p~n", [Winner]), + rabbit_node_monitor:run_outside_applications( + fun () -> + MRef = erlang:monitor(process, {?SERVER, Winner}), + rabbit:stop(), + send(Winner, {node_stopped, node()}), + receive + {'DOWN', MRef, process, {?SERVER, Winner}, _Reason} -> ok; + autoheal_safe_to_start -> ok + end, + erlang:demonitor(MRef, [flush]), + rabbit:start() + end). + +%% We have our local understanding of what partitions exist; but we +%% only know which nodes we have been partitioned from, not which +%% nodes are partitioned from each other. +all_partitions(PartitionedWith) -> + Nodes = rabbit_mnesia:cluster_nodes(all), + Partitions = [{node(), PartitionedWith} | + [rpc:call(Node, rabbit_node_monitor, partitions, []) + || Node <- Nodes -- [node()]]], + all_partitions(Partitions, [Nodes]). + +all_partitions([], Partitions) -> + Partitions; +all_partitions([{Node, CantSee} | Rest], Partitions) -> + {[Containing], Others} = + lists:partition(fun (Part) -> lists:member(Node, Part) end, Partitions), + A = Containing -- CantSee, + B = Containing -- A, + Partitions1 = case {A, B} of + {[], _} -> Partitions; + {_, []} -> Partitions; + _ -> [A, B | Others] + end, + all_partitions(Rest, Partitions1). diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index 2cdde14e..61df9f66 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -30,6 +30,9 @@ -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). + %% Utils +-export([all_nodes_up/0, run_outside_applications/1]). + -define(SERVER, ?MODULE). -define(RABBIT_UP_RPC_TIMEOUT, 2000). -define(RABBIT_DOWN_PING_INTERVAL, 1000). @@ -198,7 +201,7 @@ init([]) -> {ok, #state{monitors = pmon:new(), subscribers = pmon:new(), partitions = [], - autoheal = not_healing}}. + autoheal = rabbit_autoheal:init()}}. handle_call(partitions, _From, State = #state{partitions = Partitions}) -> {reply, {node(), Partitions}, State}; @@ -262,7 +265,8 @@ handle_info({'DOWN', _MRef, process, Pid, _Reason}, handle_info({mnesia_system_event, {inconsistent_database, running_partitioned_network, Node}}, State = #state{partitions = Partitions, - monitors = Monitors}) -> + monitors = Monitors, + autoheal = AState}) -> %% We will not get a node_up from this node - yet we should treat it as %% up (mostly). State1 = case pmon:is_monitored({rabbit, Node}, Monitors) of @@ -271,96 +275,15 @@ handle_info({mnesia_system_event, monitors = pmon:monitor({rabbit, Node}, Monitors)} end, ok = handle_live_rabbit(Node), - State2 = case application:get_env(rabbit, cluster_partition_handling) of - {ok, autoheal} -> case all_nodes_up() of - true -> autoheal(State1); - false -> State1 - end; - _ -> State1 - end, Partitions1 = ordsets:to_list( ordsets:add_element(Node, ordsets:from_list(Partitions))), - {noreply, State2#state{partitions = Partitions1}}; - -handle_info({autoheal_request_winner, Node}, - State = #state{autoheal = not_healing, - partitions = Partitions}) -> - case all_nodes_up() of - false -> {noreply, State}; - true -> Nodes = rabbit_mnesia:cluster_nodes(all), - Partitioned = - [N || N <- Nodes -- [node()], - P <- [begin - {_, R} = rpc:call(N, rabbit_node_monitor, - partitions, []), - R - end], - is_list(P) andalso length(P) > 0], - Partitioned1 = case Partitions of - [] -> Partitioned; - _ -> [node() | Partitioned] - end, - rabbit_log:info( - "Autoheal leader start; partitioned nodes are ~p~n", - [Partitioned1]), - Autoheal = {wait_for_winner_reqs, Partitioned1, Partitioned1}, - handle_info({autoheal_request_winner, Node}, - State#state{autoheal = Autoheal}) - end; - -handle_info({autoheal_request_winner, Node}, - State = #state{autoheal = {wait_for_winner_reqs, [Node], Notify}, - partitions = Partitions}) -> - AllPartitions = all_partitions(Partitions), - Winner = autoheal_select_winner(AllPartitions), - rabbit_log:info("Autoheal request winner from ~p~n" - " Partitions were determined to be ~p~n" - " Winner is ~p~n", [Node, AllPartitions, Winner]), - [{?MODULE, N} ! {autoheal_winner, Winner} || N <- Notify], - {noreply, State#state{autoheal = wait_for_winner}}; - -handle_info({autoheal_request_winner, Node}, - State = #state{autoheal = {wait_for_winner_reqs, Nodes, Notify}}) -> - rabbit_log:info("Autoheal request winner from ~p~n", [Node]), - {noreply, State#state{autoheal = {wait_for_winner_reqs, - Nodes -- [Node], Notify}}}; - -handle_info({autoheal_winner, Winner}, - State = #state{autoheal = wait_for_winner, - partitions = Partitions}) -> - case lists:member(Winner, Partitions) of - false -> case node() of - Winner -> rabbit_log:info( - "Autoheal: waiting for nodes to stop: ~p~n", - [Partitions]), - {noreply, - State#state{autoheal = {wait_for, Partitions, - Partitions}}}; - _ -> rabbit_log:info( - "Autoheal: nothing to do~n", []), - {noreply, State#state{autoheal = not_healing}} - end; - true -> autoheal_restart(Winner), - {noreply, State} - end; - -handle_info({autoheal_winner, _Winner}, State) -> - %% ignore, we already cancelled the autoheal process - {noreply, State}; - -handle_info({autoheal_node_stopped, Node}, - State = #state{autoheal = {wait_for, [Node], Notify}}) -> - rabbit_log:info("Autoheal: final node has stopped, starting...~n",[]), - [{rabbit_outside_app_process, N} ! autoheal_safe_to_start || N <- Notify], - {noreply, State#state{autoheal = not_healing}}; - -handle_info({autoheal_node_stopped, Node}, - State = #state{autoheal = {wait_for, WaitFor, Notify}}) -> - {noreply, State#state{autoheal = {wait_for, WaitFor -- [Node], Notify}}}; + {noreply, State1#state{partitions = Partitions1, + autoheal = rabbit_autoheal:maybe_start(AState)}}; -handle_info({autoheal_node_stopped, _Node}, State) -> - %% ignore, we already cancelled the autoheal process - {noreply, State}; +handle_info({autoheal_msg, Msg}, State = #state{autoheal = AState, + partitions = Partitions}) -> + AState1 = rabbit_autoheal:handle_msg(Msg, AState, Partitions), + {noreply, State#state{autoheal = AState1}}; handle_info(ping_nodes, State) -> %% We ping nodes when some are down to ensure that we find out @@ -469,93 +392,6 @@ wait_for_cluster_recovery(Nodes) -> wait_for_cluster_recovery(Nodes) end. -%% In order to autoheal we want to: -%% -%% * Find the winning partition -%% * Stop all nodes in other partitions -%% * Wait for them all to be stopped -%% * Start them again -%% -%% To keep things simple, we assume all nodes are up. We don't start -%% unless all nodes are up, and if a node goes down we abandon the -%% whole process. To further keep things simple we also defer the -%% decision as to the winning node to the "leader" - arbitrarily -%% selected as the first node in the cluster. -%% -%% To coordinate the restarting nodes we pick a special node from the -%% winning partition - the "winner". Restarting nodes then stop, tell -%% the winner they have done so, and wait for it to tell them it is -%% safe to start again. -%% -%% The winner and the leader are not necessarily the same node! Since -%% the leader may end up restarting, we also make sure that it does -%% not announce its decision (and thus cue other nodes to restart) -%% until it has seen a request from every node that has experienced a -%% partition. -autoheal(State = #state{autoheal = not_healing}) -> - [Leader | _] = lists:usort(rabbit_mnesia:cluster_nodes(all)), - rabbit_log:info("Autoheal: leader is ~p~n", [Leader]), - {?MODULE, Leader} ! {autoheal_request_winner, node()}, - State#state{autoheal = case node() of - Leader -> not_healing; - _ -> wait_for_winner - end}; -autoheal(State) -> - State. - -autoheal_select_winner(AllPartitions) -> - {_, [Winner | _]} = - hd(lists:reverse( - lists:sort([{autoheal_value(P), P} || P <- AllPartitions]))), - Winner. - -autoheal_value(Partition) -> - Connections = [Res || Node <- Partition, - Res <- [rpc:call(Node, rabbit_networking, - connections_local, [])], - is_list(Res)], - {length(lists:append(Connections)), length(Partition)}. - -autoheal_restart(Winner) -> - rabbit_log:warning( - "Autoheal: we were selected to restart; winner is ~p~n", [Winner]), - run_outside_applications( - fun () -> - MRef = erlang:monitor(process, {?MODULE, Winner}), - rabbit:stop(), - {?MODULE, Winner} ! {autoheal_node_stopped, node()}, - receive - {'DOWN', MRef, process, {?MODULE, Winner}, _Reason} -> ok; - autoheal_safe_to_start -> ok - end, - erlang:demonitor(MRef, [flush]), - rabbit:start() - end). - -%% We have our local understanding of what partitions exist; but we -%% only know which nodes we have been partitioned from, not which -%% nodes are partitioned from each other. -all_partitions(PartitionedWith) -> - Nodes = rabbit_mnesia:cluster_nodes(all), - Partitions = [{node(), PartitionedWith} | - [rpc:call(Node, rabbit_node_monitor, partitions, []) - || Node <- Nodes -- [node()]]], - all_partitions(Partitions, [Nodes]). - -all_partitions([], Partitions) -> - Partitions; -all_partitions([{Node, CantSee} | Rest], Partitions) -> - {[Containing], Others} = - lists:partition(fun (Part) -> lists:member(Node, Part) end, Partitions), - A = Containing -- CantSee, - B = Containing -- A, - Partitions1 = case {A, B} of - {[], _} -> Partitions; - {_, []} -> Partitions; - _ -> [A, B | Others] - end, - all_partitions(Rest, Partitions1). - handle_dead_rabbit_state(Node, State = #state{partitions = Partitions, autoheal = Autoheal}) -> %% If we have been partitioned, and we are now in the only remaining @@ -567,18 +403,9 @@ handle_dead_rabbit_state(Node, State = #state{partitions = Partitions, [] -> []; _ -> Partitions end, - Autoheal1 = case Autoheal of - {wait_for, _Nodes, _Notify} -> - Autoheal; - not_healing -> - not_healing; - _ -> - rabbit_log:info( - "Autoheal: aborting - ~p went down~n", [Node]), - not_healing - end, - ensure_ping_timer(State#state{partitions = Partitions1, - autoheal = Autoheal1}). + ensure_ping_timer( + State#state{partitions = Partitions1, + autoheal = rabbit_autoheal:node_down(Node, Autoheal)}). ensure_ping_timer(State) -> rabbit_misc:ensure_timer( -- cgit v1.2.1 From adf6677168f7ecdf92020cddb5d702a7b5620bf8 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 17 Apr 2013 16:55:39 +0100 Subject: Rename states to hopefully be clearer; add more comments. --- src/rabbit_autoheal.erl | 54 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 42 insertions(+), 12 deletions(-) diff --git a/src/rabbit_autoheal.erl b/src/rabbit_autoheal.erl index 4a6307fc..fc3ca1e8 100644 --- a/src/rabbit_autoheal.erl +++ b/src/rabbit_autoheal.erl @@ -46,6 +46,28 @@ %% not announce its decision (and thus cue other nodes to restart) %% until it has seen a request from every node that has experienced a %% partition. +%% +%% Possible states: +%% +%% not_healing +%% - the default +%% +%% {leader_wait_for_winner_requests, OutstandingRequests, Notify} +%% - we are the leader and are waiting to hear requests from all +%% other partitioned nodes +%% +%% wait_for_winner +%% - we are not the leader and are waiting to see what it has to say +%% +%% {winner_wait_for_stops, OutstandingStops, Notify} +%% - we are the winner and are waiting for all losing nodes to stop +%% before telling them they can restart +%% +%% restarting +%% - we are restarting. Of course the node monitor immediately dies +%% then so this state does not last long. We therefore send the +%% autoheal_safe_to_start message to the rabbit_outside_app_process +%% instead. %%---------------------------------------------------------------------------- @@ -68,7 +90,7 @@ maybe_start(State) -> enabled() -> {ok, autoheal} =:= application:get_env(rabbit, cluster_partition_handling). -node_down(_Node, {wait_for, _Nodes, _Notify} = Autoheal) -> +node_down(_Node, {winner_wait_for_stops, _Nodes, _Notify} = Autoheal) -> Autoheal; node_down(_Node, not_healing) -> not_healing; @@ -76,6 +98,7 @@ node_down(Node, _State) -> rabbit_log:info("Autoheal: aborting - ~p went down~n", [Node]), not_healing. +%% By receiving this message we become the leader handle_msg({request_winner, Node}, not_healing, Partitions) -> case rabbit_node_monitor:all_nodes_up() of @@ -97,33 +120,38 @@ handle_msg({request_winner, Node}, "Autoheal leader start; partitioned nodes are ~p~n", [Partitioned1]), handle_msg({request_winner, Node}, - {wait_for_winner_reqs, Partitioned1, Partitioned1}, + {leader_wait_for_winner_requests, + Partitioned1, Partitioned1}, Partitions) end; +%% This is the leader receiving its last winner request - all +%% partitioned nodes have checked in handle_msg({request_winner, Node}, - {wait_for_winner_reqs, [Node], Notify}, Partitions) -> + {leader_wait_for_winner_requests, [Node], Notify}, Partitions) -> AllPartitions = all_partitions(Partitions), Winner = select_winner(AllPartitions), rabbit_log:info("Autoheal request winner from ~p~n" " Partitions were determined to be ~p~n" " Winner is ~p~n", [Node, AllPartitions, Winner]), - [send(N, {winner, Winner}) || N <- Notify], + [send(N, {winner_is, Winner}) || N <- Notify], wait_for_winner; +%% This is the leader receiving any other winner request handle_msg({request_winner, Node}, - {wait_for_winner_reqs, Nodes, Notify}, _Partitions) -> + {leader_wait_for_winner_requests, Nodes, Notify}, _Partitions) -> rabbit_log:info("Autoheal request winner from ~p~n", [Node]), - {wait_for_winner_reqs, Nodes -- [Node], Notify}; + {leader_wait_for_winner_requests, Nodes -- [Node], Notify}; -handle_msg({winner, Winner}, +handle_msg({winner_is, Winner}, wait_for_winner, Partitions) -> case lists:member(Winner, Partitions) of false -> case node() of Winner -> rabbit_log:info( "Autoheal: waiting for nodes to stop: ~p~n", [Partitions]), - {wait_for, Partitions, Partitions}; + {winner_wait_for_stops, + Partitions, Partitions}; _ -> rabbit_log:info( "Autoheal: nothing to do~n", []), not_healing @@ -132,19 +160,21 @@ handle_msg({winner, Winner}, restarting end; -handle_msg({winner, _Winner}, State, _Partitions) -> +handle_msg({winner_is, _Winner}, State, _Partitions) -> %% ignore, we already cancelled the autoheal process State; +%% This is the winner receiving its last notification that a node has +%% stopped - all nodes can now start again handle_msg({node_stopped, Node}, - {wait_for, [Node], Notify}, _Partitions) -> + {winner_wait_for_stops, [Node], Notify}, _Partitions) -> rabbit_log:info("Autoheal: final node has stopped, starting...~n",[]), [{rabbit_outside_app_process, N} ! autoheal_safe_to_start || N <- Notify], not_healing; handle_msg({node_stopped, Node}, - {wait_for, WaitFor, Notify}, _Partitions) -> - {wait_for, WaitFor -- [Node], Notify}; + {winner_wait_for_stops, WaitFor, Notify}, _Partitions) -> + {winner_wait_for_stops, WaitFor -- [Node], Notify}; handle_msg({node_stopped, _Node}, State, _Partitions) -> %% ignore, we already cancelled the autoheal process -- cgit v1.2.1 From 10557c85d7635e923511f5be47058637a5693a32 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Thu, 18 Apr 2013 10:30:54 +0100 Subject: Obtain beam hash reliably --- src/rabbit_mnesia.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 95182a7c..8cd976fa 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -797,8 +797,8 @@ check_beam_compatibility(RemoteHash) -> %% 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() -> - DelegateBeamLocation = code:which(delegate), - {ok, {delegate, Hash}} = beam_lib:md5(DelegateBeamLocation), + {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 -- cgit v1.2.1 From aea1d035dc499f3f4d4bc13ea53ab23186ef8316 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 18 Apr 2013 15:14:15 +0100 Subject: Have the leader decide what to do and then just tell other nodes (rather than have them request a winner). Substantially more reliable and shorter than previously. --- src/rabbit_autoheal.erl | 155 ++++++++++++++++++------------------------------ 1 file changed, 58 insertions(+), 97 deletions(-) diff --git a/src/rabbit_autoheal.erl b/src/rabbit_autoheal.erl index fc3ca1e8..82f26634 100644 --- a/src/rabbit_autoheal.erl +++ b/src/rabbit_autoheal.erl @@ -52,14 +52,7 @@ %% not_healing %% - the default %% -%% {leader_wait_for_winner_requests, OutstandingRequests, Notify} -%% - we are the leader and are waiting to hear requests from all -%% other partitioned nodes -%% -%% wait_for_winner -%% - we are not the leader and are waiting to see what it has to say -%% -%% {winner_wait_for_stops, OutstandingStops, Notify} +%% {winner_waiting, OutstandingStops, Notify} %% - we are the winner and are waiting for all losing nodes to stop %% before telling them they can restart %% @@ -74,14 +67,11 @@ init() -> not_healing. maybe_start(not_healing) -> - case enabled() andalso rabbit_node_monitor:all_nodes_up() of + case enabled() of true -> [Leader | _] = lists:usort(rabbit_mnesia:cluster_nodes(all)), - rabbit_log:info("Autoheal: leader is ~p~n", [Leader]), - send(Leader, {request_winner, node()}), - case node() of - Leader -> not_healing; - _ -> wait_for_winner - end; + send(Leader, {request_start, node()}), + rabbit_log:info("Autoheal request sent to ~p~n", [Leader]), + not_healing; false -> not_healing end; maybe_start(State) -> @@ -90,7 +80,7 @@ maybe_start(State) -> enabled() -> {ok, autoheal} =:= application:get_env(rabbit, cluster_partition_handling). -node_down(_Node, {winner_wait_for_stops, _Nodes, _Notify} = Autoheal) -> +node_down(_Node, {winner_waiting, _Nodes, _Notify} = Autoheal) -> Autoheal; node_down(_Node, not_healing) -> not_healing; @@ -99,82 +89,70 @@ node_down(Node, _State) -> not_healing. %% By receiving this message we become the leader -handle_msg({request_winner, Node}, +%% TODO should we try to debounce this? +handle_msg({request_start, Node}, not_healing, Partitions) -> + rabbit_log:info("Autoheal request received from ~p~n", [Node]), case rabbit_node_monitor:all_nodes_up() of false -> not_healing; - true -> Nodes = rabbit_mnesia:cluster_nodes(all), - Partitioned = - [N || N <- Nodes -- [node()], - P <- [begin - {_, R} = rpc:call(N, rabbit_node_monitor, - partitions, []), - R - end], - is_list(P) andalso length(P) > 0], - Partitioned1 = case Partitions of - [] -> Partitioned; - _ -> [node() | Partitioned] - end, - rabbit_log:info( - "Autoheal leader start; partitioned nodes are ~p~n", - [Partitioned1]), - handle_msg({request_winner, Node}, - {leader_wait_for_winner_requests, - Partitioned1, Partitioned1}, - Partitions) + true -> AllPartitions = all_partitions(Partitions), + {Winner, Losers} = make_decision(AllPartitions), + rabbit_log:info("Autoheal decision~n" + " * Partitions: ~p~n" + " * Winner: ~p~n" + " * Losers: ~p~n", + [AllPartitions, Winner, Losers]), + send(Winner, {become_winner, Losers}), + [send(L, {winner_is, Winner}) || L <- Losers], + not_healing end; -%% This is the leader receiving its last winner request - all -%% partitioned nodes have checked in -handle_msg({request_winner, Node}, - {leader_wait_for_winner_requests, [Node], Notify}, Partitions) -> - AllPartitions = all_partitions(Partitions), - Winner = select_winner(AllPartitions), - rabbit_log:info("Autoheal request winner from ~p~n" - " Partitions were determined to be ~p~n" - " Winner is ~p~n", [Node, AllPartitions, Winner]), - [send(N, {winner_is, Winner}) || N <- Notify], - wait_for_winner; - -%% This is the leader receiving any other winner request -handle_msg({request_winner, Node}, - {leader_wait_for_winner_requests, Nodes, Notify}, _Partitions) -> - rabbit_log:info("Autoheal request winner from ~p~n", [Node]), - {leader_wait_for_winner_requests, Nodes -- [Node], Notify}; +handle_msg({become_winner, Losers}, + not_healing, _Partitions) -> + rabbit_log:info("Autoheal: I am the winner, waiting for ~p to stop~n", + [Losers]), + {winner_waiting, Losers, Losers}; -handle_msg({winner_is, Winner}, - wait_for_winner, Partitions) -> - case lists:member(Winner, Partitions) of - false -> case node() of - Winner -> rabbit_log:info( - "Autoheal: waiting for nodes to stop: ~p~n", - [Partitions]), - {winner_wait_for_stops, - Partitions, Partitions}; - _ -> rabbit_log:info( - "Autoheal: nothing to do~n", []), - not_healing - end; - true -> restart_me(Winner), - restarting - end; +handle_msg({become_winner, Losers}, + {winner_waiting, WaitFor, Notify}, _Partitions) -> + rabbit_log:info("Autoheal: I am the winner, waiting additionally for " + "~p to stop~n", [Losers]), + {winner_waiting, lists:usort(Losers ++ WaitFor), + lists:usort(Losers ++ Notify)}; -handle_msg({winner_is, _Winner}, State, _Partitions) -> - %% ignore, we already cancelled the autoheal process - State; +handle_msg({winner_is, Winner}, + not_healing, _Partitions) -> + rabbit_log:warning( + "Autoheal: we were selected to restart; winner is ~p~n", [Winner]), + rabbit_node_monitor:run_outside_applications( + fun () -> + MRef = erlang:monitor(process, {?SERVER, Winner}), + rabbit:stop(), + send(Winner, {node_stopped, node()}), + receive + {'DOWN', MRef, process, {?SERVER, Winner}, _Reason} -> ok; + autoheal_safe_to_start -> ok + end, + erlang:demonitor(MRef, [flush]), + rabbit:start() + end), + restarting; %% This is the winner receiving its last notification that a node has %% stopped - all nodes can now start again handle_msg({node_stopped, Node}, - {winner_wait_for_stops, [Node], Notify}, _Partitions) -> + {winner_waiting, [Node], Notify}, _Partitions) -> rabbit_log:info("Autoheal: final node has stopped, starting...~n",[]), [{rabbit_outside_app_process, N} ! autoheal_safe_to_start || N <- Notify], not_healing; handle_msg({node_stopped, Node}, - {winner_wait_for_stops, WaitFor, Notify}, _Partitions) -> - {winner_wait_for_stops, WaitFor -- [Node], Notify}; + {winner_waiting, WaitFor, Notify}, _Partitions) -> + {winner_waiting, WaitFor -- [Node], Notify}; + +handle_msg(_, restarting, _Partitions) -> + %% ignore, we can contribute no further + restarting; handle_msg({node_stopped, _Node}, State, _Partitions) -> %% ignore, we already cancelled the autoheal process @@ -184,11 +162,10 @@ handle_msg({node_stopped, _Node}, State, _Partitions) -> send(Node, Msg) -> {?SERVER, Node} ! {autoheal_msg, Msg}. -select_winner(AllPartitions) -> - {_, [Winner | _]} = - hd(lists:reverse( - lists:sort([{partition_value(P), P} || P <- AllPartitions]))), - Winner. +make_decision(AllPartitions) -> + Sorted = lists:sort([{partition_value(P), P} || P <- AllPartitions]), + [[Winner | _] | Rest] = lists:reverse([P || {_, P} <- Sorted]), + {Winner, lists:append(Rest)}. partition_value(Partition) -> Connections = [Res || Node <- Partition, @@ -197,22 +174,6 @@ partition_value(Partition) -> is_list(Res)], {length(lists:append(Connections)), length(Partition)}. -restart_me(Winner) -> - rabbit_log:warning( - "Autoheal: we were selected to restart; winner is ~p~n", [Winner]), - rabbit_node_monitor:run_outside_applications( - fun () -> - MRef = erlang:monitor(process, {?SERVER, Winner}), - rabbit:stop(), - send(Winner, {node_stopped, node()}), - receive - {'DOWN', MRef, process, {?SERVER, Winner}, _Reason} -> ok; - autoheal_safe_to_start -> ok - end, - erlang:demonitor(MRef, [flush]), - rabbit:start() - end). - %% We have our local understanding of what partitions exist; but we %% only know which nodes we have been partitioned from, not which %% nodes are partitioned from each other. -- cgit v1.2.1 From 6435a6cbcf9ad1782fbef8eec9daab9ecc3d8eef Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 18 Apr 2013 15:18:07 +0100 Subject: I suppose it would be polite to add specs here. --- src/rabbit_node_monitor.erl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index 61df9f66..2d237020 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -60,6 +60,9 @@ -spec(partitions/0 :: () -> {node(), [node()]}). -spec(subscribe/1 :: (pid()) -> 'ok'). +-spec(all_nodes_up/0 :: () -> boolean()). +-spec(run_outside_applications/1 :: (fun (() -> any())) -> pid()). + -endif. %%---------------------------------------------------------------------------- -- cgit v1.2.1 From 30421a90bf7a55e4be3c9cece04da44723fed70f Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 18 Apr 2013 17:30:44 +0100 Subject: Less magic, more essay. --- src/gen_server2.erl | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/gen_server2.erl b/src/gen_server2.erl index 84536cb6..ec67c04f 100644 --- a/src/gen_server2.erl +++ b/src/gen_server2.erl @@ -75,6 +75,12 @@ %% format_message_queue/2 which is the equivalent of format_status/2 %% but where the second argument is specifically the priority_queue %% which contains the prioritised message_queue. +%% +%% 9) The function with_state/2 can be used to debug a process with +%% heavyweight state (without needing to copy the entire state out of +%% process as sys:get_status/1 would). Pass through a function which +%% can be invoked on the state, get back the result. The state is not +%% modified. %% All modifications are (C) 2009-2013 VMware, Inc. @@ -180,7 +186,7 @@ %% API -export([start/3, start/4, start_link/3, start_link/4, - call/2, call/3, + call/2, call/3, with_state/2, cast/2, reply/2, abcast/2, abcast/3, multi_call/2, multi_call/3, multi_call/4, @@ -319,6 +325,14 @@ call(Name, Request, Timeout) -> exit({Reason, {?MODULE, call, [Name, Request, Timeout]}}) end. +with_state(Name, Fun) -> + case catch gen:call(Name, '$with_state', Fun, infinity) of + {ok,Res} -> + Res; + {'EXIT',Reason} -> + exit({Reason, {?MODULE, with_state, [Name, Fun]}}) + end. + %% ----------------------------------------------------------------- %% Make a cast to a generic server. %% ----------------------------------------------------------------- @@ -645,6 +659,8 @@ in({'$gen_cast', Msg} = Input, in({'$gen_call', From, Msg} = Input, GS2State = #gs2_state { prioritisers = {F, _, _} }) -> in(Input, F(Msg, From, GS2State), GS2State); +in({'$with_state', _From, _Fun} = Input, GS2State) -> + in(Input, 0, GS2State); in({'EXIT', Parent, _R} = Input, GS2State = #gs2_state { parent = Parent }) -> in(Input, infinity, GS2State); in({system, _From, _Req} = Input, GS2State) -> @@ -883,7 +899,7 @@ common_become(_Name, _Mod, _NState, [] = _Debug) -> common_become(Name, Mod, NState, Debug) -> sys:handle_debug(Debug, fun print_event/3, Name, {become, Mod, NState}). -handle_msg({'$gen_call', From, {debug_state, Fun}}, +handle_msg({'$with_state', From, Fun}, GS2State = #gs2_state{state = State, name = Name, debug = Debug}) -> -- cgit v1.2.1 From 3d5cef2e53d73f62a795aca8d43df3654125c58d Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 18 Apr 2013 20:44:53 +0100 Subject: cosmetic --- src/rabbit_vm.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_vm.erl b/src/rabbit_vm.erl index 3d3ef59b..d09b1c9d 100644 --- a/src/rabbit_vm.erl +++ b/src/rabbit_vm.erl @@ -140,9 +140,9 @@ is_plugin("rabbitmq_" ++ _) -> true; is_plugin(App) -> lists:member(App, ?MAGIC_PLUGINS). %% TODO support Pids, not just Names - need this for plugins -sum_processes(Names, Acc0) -> +sum_processes(Names, Items) -> sum_processes(Names, fun (_, X, Y) -> X + Y end, - [{Item, 0} || Item <- Acc0]). + [{Item, 0} || Item <- Items]). sum_processes(Names, Fun, Acc0) -> Items = [Item || {Item, _Val0} <- Acc0], -- cgit v1.2.1 From e827185233fc97579f50c917e5d3721b92f7c0a4 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 18 Apr 2013 20:45:44 +0100 Subject: include the named processes in the sums --- src/rabbit_vm.erl | 45 ++++++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/src/rabbit_vm.erl b/src/rabbit_vm.erl index d09b1c9d..f3508cae 100644 --- a/src/rabbit_vm.erl +++ b/src/rabbit_vm.erl @@ -149,30 +149,37 @@ sum_processes(Names, Fun, Acc0) -> Acc0Dict = orddict:from_list(Acc0), NameAccs0 = orddict:from_list([{Name, Acc0Dict} || Name <- Names]), {NameAccs, OtherAcc} = - lists:foldl(fun (Pid, Acc) -> - case process_info(Pid, [dictionary | Items]) of - undefined -> - Acc; - [{dictionary, D} | Vals] -> - ValsDict = orddict:from_list(Vals), - accumulate(find_ancestor(D, Names), Fun, - ValsDict, Acc) - end - end, {NameAccs0, Acc0Dict}, processes()), + lists:foldl( + fun (Pid, Acc) -> + InfoItems = [registered_name, dictionary | Items], + case process_info(Pid, InfoItems) of + undefined -> + Acc; + [{registered_name, RegName}, {dictionary, D} | Vals] -> + %% see docs for process_info/2 for the + %% special handling of 'registered_name' + %% info items + Extra = case RegName of + [] -> []; + N -> [N] + end, + accumulate(find_ancestor(Extra, D, Names), Fun, + orddict:from_list(Vals), Acc) + end + end, {NameAccs0, Acc0Dict}, processes()), %% these conversions aren't strictly necessary; we do them simply %% for the sake of encapsulating the representation. {[orddict:to_list(NameAcc) || NameAcc <- orddict:to_list(NameAccs)], orddict:to_list(OtherAcc)}. -find_ancestor(D, Names) -> - case lists:keyfind('$ancestors', 1, D) of - false -> undefined; - {_, Ancestors} -> case lists:splitwith( - fun (A) -> not lists:member(A, Names) end, - Ancestors) of - {_, []} -> undefined; - {_, [Name | _]} -> Name - end +find_ancestor(Extra, D, Names) -> + case lists:splitwith(fun (A) -> not lists:member(A, Names) end, + Extra ++ case lists:keyfind('$ancestors', 1, D) of + false -> []; + {_, Ancestors} -> Ancestors + end) of + {_, []} -> undefined; + {_, [Name | _]} -> Name end. accumulate(undefined, Fun, ValsDict, {NameAccs, OtherAcc}) -> -- cgit v1.2.1 From 7cf2a89beaecb43661247cd62adc5851e4cfda68 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 18 Apr 2013 21:52:02 +0100 Subject: wire in sum_processes for all non-plugin process memory --- src/rabbit_vm.erl | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/src/rabbit_vm.erl b/src/rabbit_vm.erl index f3508cae..16294ccd 100644 --- a/src/rabbit_vm.erl +++ b/src/rabbit_vm.erl @@ -33,17 +33,22 @@ %% Like erlang:memory(), but with awareness of rabbit-y things memory() -> - Conns = (sup_memory(rabbit_tcp_client_sup) + - sup_memory(ssl_connection_sup) + - sup_memory(amqp_sup)), - Qs = (sup_memory(rabbit_amqqueue_sup) + - sup_memory(rabbit_mirror_queue_slave_sup)), + ConnProcs = [rabbit_tcp_client_sup, ssl_connection_sup, amqp_sup], + QProcs = [rabbit_amqqueue_sup, rabbit_mirror_queue_slave_sup], + MsgIndexProcs = [msg_store_transient, msg_store_persistent], + MgmtDbProcs = [rabbit_mgmt_sup], + %% TODO: plug-ins + + All = [ConnProcs, QProcs, MsgIndexProcs, MgmtDbProcs], + + {Sums, _Other} = sum_processes(lists:append(All), [memory]), + + [Conns, Qs, MsgIndexProc, MgmtDbProc] = + [aggregate_memory(Names, Sums) || Names <- All], + Mnesia = mnesia_memory(), MsgIndexETS = ets_memory(rabbit_msg_store_ets_index), - MsgIndexProc = (pid_memory(msg_store_transient) + - pid_memory(msg_store_persistent)), MgmtDbETS = ets_memory(rabbit_mgmt_db), - MgmtDbProc = sup_memory(rabbit_mgmt_sup), Plugins = plugin_memory() - MgmtDbProc, [{total, Total}, @@ -55,6 +60,8 @@ memory() -> {system, System}] = erlang:memory([total, processes, ets, atom, binary, code, system]), + %% TODO: should we replace this with the value extracted from + %% 'Other'? OtherProc = Processes - Conns - Qs - MsgIndexProc - MgmtDbProc - Plugins, [{total, Total}, @@ -139,7 +146,16 @@ plugin_memory(App) -> is_plugin("rabbitmq_" ++ _) -> true; is_plugin(App) -> lists:member(App, ?MAGIC_PLUGINS). +aggregate_memory(Names, Sums) -> + lists:sum([extract_memory(Name, Sums) || Name <- Names]). + +extract_memory(Name, Sums) -> + {_, Accs} = lists:keyfind(Name, 1, Sums), + {memory, V} = lists:keyfind(memory, 1, Accs), + V. + %% TODO support Pids, not just Names - need this for plugins +%% NB: this code is non-rabbit specific sum_processes(Names, Items) -> sum_processes(Names, fun (_, X, Y) -> X + Y end, [{Item, 0} || Item <- Items]). -- cgit v1.2.1 From b36e166e1ada8c6337a58220afe55a4a47e953d6 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 18 Apr 2013 22:39:53 +0100 Subject: deal with plug-ins ...and rip out the now unused sup traversal code --- src/rabbit_vm.erl | 64 +++++++++++++++++-------------------------------------- 1 file changed, 20 insertions(+), 44 deletions(-) diff --git a/src/rabbit_vm.erl b/src/rabbit_vm.erl index 16294ccd..a772d9c4 100644 --- a/src/rabbit_vm.erl +++ b/src/rabbit_vm.erl @@ -37,19 +37,19 @@ memory() -> QProcs = [rabbit_amqqueue_sup, rabbit_mirror_queue_slave_sup], MsgIndexProcs = [msg_store_transient, msg_store_persistent], MgmtDbProcs = [rabbit_mgmt_sup], - %% TODO: plug-ins + PluginProcs = plugin_sups(), - All = [ConnProcs, QProcs, MsgIndexProcs, MgmtDbProcs], + All = [ConnProcs, QProcs, MsgIndexProcs, MgmtDbProcs, PluginProcs], {Sums, _Other} = sum_processes(lists:append(All), [memory]), - [Conns, Qs, MsgIndexProc, MgmtDbProc] = + [Conns, Qs, MsgIndexProc, MgmtDbProc, AllPlugins] = [aggregate_memory(Names, Sums) || Names <- All], Mnesia = mnesia_memory(), MsgIndexETS = ets_memory(rabbit_msg_store_ets_index), MgmtDbETS = ets_memory(rabbit_mgmt_db), - Plugins = plugin_memory() - MgmtDbProc, + Plugins = AllPlugins - MgmtDbProc, [{total, Total}, {processes, Processes}, @@ -62,7 +62,7 @@ memory() -> %% TODO: should we replace this with the value extracted from %% 'Other'? - OtherProc = Processes - Conns - Qs - MsgIndexProc - MgmtDbProc - Plugins, + OtherProc = Processes - Conns - Qs - MsgIndexProc - AllPlugins, [{total, Total}, {connection_procs, Conns}, @@ -85,35 +85,6 @@ memory() -> %%---------------------------------------------------------------------------- -sup_memory(Sup) -> - lists:sum([child_memory(P, T) || {_, P, T, _} <- sup_children(Sup)]) + - pid_memory(Sup). - -sup_children(Sup) -> - rabbit_misc:with_exit_handler( - rabbit_misc:const([]), - fun () -> - %% Just in case we end up talking to something that is - %% not a supervisor by mistake. - case supervisor:which_children(Sup) of - L when is_list(L) -> L; - _ -> [] - end - end). - -pid_memory(Pid) when is_pid(Pid) -> case process_info(Pid, memory) of - {memory, M} -> M; - _ -> 0 - end; -pid_memory(Name) when is_atom(Name) -> case whereis(Name) of - P when is_pid(P) -> pid_memory(P); - _ -> 0 - end. - -child_memory(Pid, worker) when is_pid (Pid) -> pid_memory(Pid); -child_memory(Pid, supervisor) when is_pid (Pid) -> sup_memory(Pid); -child_memory(_, _) -> 0. - mnesia_memory() -> case mnesia:system_info(is_running) of yes -> lists:sum([bytes(mnesia:table_info(Tab, memory)) || @@ -128,21 +99,27 @@ ets_memory(Name) -> bytes(Words) -> Words * erlang:system_info(wordsize). -plugin_memory() -> - lists:sum([plugin_memory(App) || - {App, _, _} <- application:which_applications(), - is_plugin(atom_to_list(App))]). +plugin_sups() -> + lists:append([plugin_sup(App) || + {App, _, _} <- application:which_applications(), + is_plugin(atom_to_list(App))]). -plugin_memory(App) -> +plugin_sup(App) -> case application_controller:get_master(App) of - undefined -> 0; + undefined -> []; Master -> case application_master:get_child(Master) of - {Pid, _} when is_pid(Pid) -> sup_memory(Pid); - Pid when is_pid(Pid) -> sup_memory(Pid); - _ -> 0 + {Pid, _} when is_pid(Pid) -> [process_name(Pid)]; + Pid when is_pid(Pid) -> [process_name(Pid)]; + _ -> [] end end. +process_name(Pid) -> + case process_info(Pid, registered_name) of + {registered_name, Name} -> Name; + _ -> Pid + end. + is_plugin("rabbitmq_" ++ _) -> true; is_plugin(App) -> lists:member(App, ?MAGIC_PLUGINS). @@ -154,7 +131,6 @@ extract_memory(Name, Sums) -> {memory, V} = lists:keyfind(memory, 1, Accs), V. -%% TODO support Pids, not just Names - need this for plugins %% NB: this code is non-rabbit specific sum_processes(Names, Items) -> sum_processes(Names, fun (_, X, Y) -> X + Y end, -- cgit v1.2.1 From 018d8b70943ce8455d96b716c2352e651562d252 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 19 Apr 2013 06:31:51 +0100 Subject: lists:keyfind/3 is not in R12B-3. --- src/rabbit_vm.erl | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/rabbit_vm.erl b/src/rabbit_vm.erl index a772d9c4..afd8921b 100644 --- a/src/rabbit_vm.erl +++ b/src/rabbit_vm.erl @@ -127,8 +127,8 @@ aggregate_memory(Names, Sums) -> lists:sum([extract_memory(Name, Sums) || Name <- Names]). extract_memory(Name, Sums) -> - {_, Accs} = lists:keyfind(Name, 1, Sums), - {memory, V} = lists:keyfind(memory, 1, Accs), + {value, {_, Accs}} = lists:keysearch(Name, 1, Sums), + {value, {memory, V}} = lists:keysearch(memory, 1, Accs), V. %% NB: this code is non-rabbit specific @@ -165,11 +165,12 @@ sum_processes(Names, Fun, Acc0) -> orddict:to_list(OtherAcc)}. find_ancestor(Extra, D, Names) -> + Ancestors = case lists:keysearch('$ancestors', 1, D) of + {value, {_, Ancs}} -> Ancs; + false -> [] + end, case lists:splitwith(fun (A) -> not lists:member(A, Names) end, - Extra ++ case lists:keyfind('$ancestors', 1, D) of - false -> []; - {_, Ancestors} -> Ancestors - end) of + Extra ++ Ancestors) of {_, []} -> undefined; {_, [Name | _]} -> Name end. -- cgit v1.2.1 From ec0cf0fc5c621500854125fd41631414192d4d53 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 19 Apr 2013 11:44:03 +0100 Subject: Move to near the other system message and simplify. --- src/gen_server2.erl | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/gen_server2.erl b/src/gen_server2.erl index ec67c04f..99fa272a 100644 --- a/src/gen_server2.erl +++ b/src/gen_server2.erl @@ -679,6 +679,10 @@ process_msg({system, From, Req}, %% gen_server puts Hib on the end as the 7th arg, but that version %% of the fun seems not to be documented so leaving out for now. sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug, GS2State); +process_msg({'$with_state', From, Fun}, + GS2State = #gs2_state{state = State}) -> + reply(From, catch Fun(State)), + loop(GS2State); process_msg({'EXIT', Parent, Reason} = Msg, GS2State = #gs2_state { parent = Parent }) -> terminate(Reason, Msg, GS2State); @@ -899,13 +903,6 @@ common_become(_Name, _Mod, _NState, [] = _Debug) -> common_become(Name, Mod, NState, Debug) -> sys:handle_debug(Debug, fun print_event/3, Name, {become, Mod, NState}). -handle_msg({'$with_state', From, Fun}, - GS2State = #gs2_state{state = State, - name = Name, - debug = Debug}) -> - Debug1 = common_reply(Name, From, catch Fun(State), State, Debug), - loop(GS2State #gs2_state { state = State, - debug = Debug1 }); handle_msg({'$gen_call', From, Msg}, GS2State = #gs2_state { mod = Mod, state = State, name = Name, -- cgit v1.2.1 From 0cc0fccd7797ec1fa269b485527ea923fbe14fc2 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 19 Apr 2013 12:03:45 +0100 Subject: Tiny test --- src/rabbit_tests.erl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index e7b69879..bcb3bf19 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -63,6 +63,7 @@ all_tests() -> passed = test_server_status(), passed = test_amqp_connection_refusal(), passed = test_confirms(), + passed = test_with_state(), passed = do_if_secondary_node( fun run_cluster_dependent_tests/1, @@ -1298,6 +1299,11 @@ test_confirms() -> passed. +test_with_state() -> + fhc_state = gen_server2:with_state(file_handle_cache, + fun (S) -> element(1, S) end), + passed. + test_statistics_event_receiver(Pid) -> receive Foo -> Pid ! Foo, test_statistics_event_receiver(Pid) -- cgit v1.2.1 From 92b4d9f240b4eef2502537f08251409fd4d168d0 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 19 Apr 2013 12:05:21 +0100 Subject: cosmetic --- src/gen_server2.erl | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/gen_server2.erl b/src/gen_server2.erl index 99fa272a..507d1cda 100644 --- a/src/gen_server2.erl +++ b/src/gen_server2.erl @@ -186,10 +186,11 @@ %% API -export([start/3, start/4, start_link/3, start_link/4, - call/2, call/3, with_state/2, + call/2, call/3, cast/2, reply/2, abcast/2, abcast/3, multi_call/2, multi_call/3, multi_call/4, + with_state/2, enter_loop/3, enter_loop/4, enter_loop/5, enter_loop/6, wake_hib/1]). %% System exports @@ -325,14 +326,6 @@ call(Name, Request, Timeout) -> exit({Reason, {?MODULE, call, [Name, Request, Timeout]}}) end. -with_state(Name, Fun) -> - case catch gen:call(Name, '$with_state', Fun, infinity) of - {ok,Res} -> - Res; - {'EXIT',Reason} -> - exit({Reason, {?MODULE, with_state, [Name, Fun]}}) - end. - %% ----------------------------------------------------------------- %% Make a cast to a generic server. %% ----------------------------------------------------------------- @@ -396,6 +389,16 @@ multi_call(Nodes, Name, Req, Timeout) when is_list(Nodes), is_atom(Name), is_integer(Timeout), Timeout >= 0 -> do_multi_call(Nodes, Name, Req, Timeout). +%% ----------------------------------------------------------------- +%% Apply a function to a generic server's state. +%% ----------------------------------------------------------------- +with_state(Name, Fun) -> + case catch gen:call(Name, '$with_state', Fun, infinity) of + {ok,Res} -> + Res; + {'EXIT',Reason} -> + exit({Reason, {?MODULE, with_state, [Name, Fun]}}) + end. %%----------------------------------------------------------------- %% enter_loop(Mod, Options, State, , , ) ->_ -- 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(-) 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(-) 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 a8858534212a396b46bafaa3794ad877c58d0e97 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 19 Apr 2013 16:30:37 +0100 Subject: fix typo --- docs/rabbitmqctl.1.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/rabbitmqctl.1.xml b/docs/rabbitmqctl.1.xml index 34947b66..08395107 100644 --- a/docs/rabbitmqctl.1.xml +++ b/docs/rabbitmqctl.1.xml @@ -289,7 +289,7 @@ - join_cluster clusternode--ram + join_cluster clusternode --ram -- cgit v1.2.1 From bb7cf97480b0f524e9660e53ea0b6b6e6aced461 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 22 Apr 2013 09:45:09 +0100 Subject: leave OtherProc info as is --- src/rabbit_vm.erl | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/rabbit_vm.erl b/src/rabbit_vm.erl index afd8921b..49172adb 100644 --- a/src/rabbit_vm.erl +++ b/src/rabbit_vm.erl @@ -60,8 +60,6 @@ memory() -> {system, System}] = erlang:memory([total, processes, ets, atom, binary, code, system]), - %% TODO: should we replace this with the value extracted from - %% 'Other'? OtherProc = Processes - Conns - Qs - MsgIndexProc - AllPlugins, [{total, Total}, -- cgit v1.2.1 From a566245a15d4551c6df60debb2892c2a8b6f3ebe Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 22 Apr 2013 09:47:09 +0100 Subject: fix a (harmless) bug spotted by dialyzer the original code only worked since orddict:to_list is implemented as the identity function. --- src/rabbit_vm.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rabbit_vm.erl b/src/rabbit_vm.erl index 49172adb..1e7bd383 100644 --- a/src/rabbit_vm.erl +++ b/src/rabbit_vm.erl @@ -159,7 +159,8 @@ sum_processes(Names, Fun, Acc0) -> end, {NameAccs0, Acc0Dict}, processes()), %% these conversions aren't strictly necessary; we do them simply %% for the sake of encapsulating the representation. - {[orddict:to_list(NameAcc) || NameAcc <- orddict:to_list(NameAccs)], + {[{Name, orddict:to_list(Accs)} || + {Name, Accs} <- orddict:to_list(NameAccs)], orddict:to_list(OtherAcc)}. find_ancestor(Extra, D, Names) -> -- cgit v1.2.1 From e870642153f73f5cdafb9188f4f7a1cad9b0db52 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 22 Apr 2013 09:47:26 +0100 Subject: add some specs and docs --- src/rabbit_vm.erl | 49 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/src/rabbit_vm.erl b/src/rabbit_vm.erl index 1e7bd383..c28b0cd5 100644 --- a/src/rabbit_vm.erl +++ b/src/rabbit_vm.erl @@ -129,11 +129,58 @@ extract_memory(Name, Sums) -> {value, {memory, V}} = lists:keysearch(memory, 1, Accs), V. -%% NB: this code is non-rabbit specific +%%---------------------------------------------------------------------------- + +%% NB: this code is non-rabbit specific. + +-ifdef(use_specs). +-type(process() :: pid() | atom()). +-type(info_key() :: atom()). +-type(info_value() :: any()). +-type(info_item() :: {info_key(), info_value()}). +-type(accumulate() :: fun ((info_key(), info_value(), info_value()) -> + info_value())). +-spec(sum_processes/2 :: ([process()], [info_key()]) -> + {[{process(), [info_item()]}], [info_item()]}). +-spec(sum_processes/3 :: ([process()], accumulate(), [info_item()]) -> + {[{process(), [info_item()]}], [info_item()]}). +-endif. + sum_processes(Names, Items) -> sum_processes(Names, fun (_, X, Y) -> X + Y end, [{Item, 0} || Item <- Items]). +%% summarize the process_info of all processes based on their +%% '$ancestor' hierarchy, recorded in their process dictionary. +%% +%% The function takes +%% +%% 1) a list of names/pids of processes that are accumulation points +%% in the hierarchy. +%% +%% 2) a function that aggregates individual info items -taking the +%% info item key, value and accumulated value as the input and +%% producing a new accumulated value. +%% +%% 3) a list of info item key / initial accumulator value pairs. +%% +%% The process_info of a process is accumulated at the nearest of its +%% ancestors that is mentioned in the first argument, or, if no such +%% ancestor exists or the ancestor information is absent, in a special +%% 'other' bucket. +%% +%% The result is a pair consisting of +%% +%% 1) a k/v list, containing for each of the accumulation names/pids a +%% list of info items, containing the accumulated data, and +%% +%% 2) the 'other' bucket - a list of info items containing the +%% accumulated data of all processes with no matching ancestors +%% +%% Note that this function operates on names as well as pids, but +%% these must match whatever is contained in the '$ancestor' process +%% dictionary entry. Generally that means for all registered processes +%% the name should be used. sum_processes(Names, Fun, Acc0) -> Items = [Item || {Item, _Val0} <- Acc0], Acc0Dict = orddict:from_list(Acc0), -- cgit v1.2.1 From 8b7df16ae4b8014181c32b29db2cba53c35b2c24 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 22 Apr 2013 16:10:51 +0100 Subject: Move those functions to their own place, and replace the autoheal all_nodes_up check with all_rabbit_nodes_up since it will depend on the rabbit application running to DTRT. --- src/rabbit_autoheal.erl | 2 +- src/rabbit_node_monitor.erl | 49 +++++++++++++++++++++++++++------------------ 2 files changed, 30 insertions(+), 21 deletions(-) diff --git a/src/rabbit_autoheal.erl b/src/rabbit_autoheal.erl index 82f26634..c00c2dd6 100644 --- a/src/rabbit_autoheal.erl +++ b/src/rabbit_autoheal.erl @@ -93,7 +93,7 @@ node_down(Node, _State) -> handle_msg({request_start, Node}, not_healing, Partitions) -> rabbit_log:info("Autoheal request received from ~p~n", [Node]), - case rabbit_node_monitor:all_nodes_up() of + case rabbit_node_monitor:all_rabbit_nodes_up() of false -> not_healing; true -> AllPartitions = all_partitions(Partitions), {Winner, Losers} = make_decision(AllPartitions), diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index 2d237020..ca8e6dbd 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -31,7 +31,7 @@ code_change/3]). %% Utils --export([all_nodes_up/0, run_outside_applications/1]). +-export([all_rabbit_nodes_up/0, run_outside_applications/1]). -define(SERVER, ?MODULE). -define(RABBIT_UP_RPC_TIMEOUT, 2000). @@ -60,7 +60,7 @@ -spec(partitions/0 :: () -> {node(), [node()]}). -spec(subscribe/1 :: (pid()) -> 'ok'). --spec(all_nodes_up/0 :: () -> boolean()). +-spec(all_rabbit_nodes_up/0 :: () -> boolean()). -spec(run_outside_applications/1 :: (fun (() -> any())) -> pid()). -endif. @@ -350,24 +350,6 @@ handle_dead_rabbit(Node) -> end, ok. -majority() -> - Nodes = rabbit_mnesia:cluster_nodes(all), - length(alive_nodes(Nodes)) / length(Nodes) > 0.5. - -all_nodes_up() -> - Nodes = rabbit_mnesia:cluster_nodes(all), - length(alive_nodes(Nodes)) =:= length(Nodes). - -%% mnesia:system_info(db_nodes) (and hence -%% rabbit_mnesia:cluster_nodes(running)) does not give reliable results -%% when partitioned. -alive_nodes() -> alive_nodes(rabbit_mnesia:cluster_nodes(all)). - -alive_nodes(Nodes) -> [N || N <- Nodes, pong =:= net_adm:ping(N)]. - -alive_rabbit_nodes() -> - [N || N <- alive_nodes(), rabbit_nodes:is_process_running(N, rabbit)]. - await_cluster_recovery() -> rabbit_log:warning("Cluster minority status detected - awaiting recovery~n", []), @@ -441,3 +423,30 @@ legacy_should_be_disc_node(DiscNodes) -> add_node(Node, Nodes) -> lists:usort([Node | Nodes]). del_node(Node, Nodes) -> Nodes -- [Node]. + +%%-------------------------------------------------------------------- + +%% mnesia:system_info(db_nodes) (and hence +%% rabbit_mnesia:cluster_nodes(running)) does not give reliable +%% results when partitioned. So we have a small set of replacement +%% functions here. "rabbit" in a function's name implies we test if +%% the rabbit application is up, not just the node. + +majority() -> + Nodes = rabbit_mnesia:cluster_nodes(all), + length(alive_nodes(Nodes)) / length(Nodes) > 0.5. + +all_nodes_up() -> + Nodes = rabbit_mnesia:cluster_nodes(all), + length(alive_nodes(Nodes)) =:= length(Nodes). + +all_rabbit_nodes_up() -> + Nodes = rabbit_mnesia:cluster_nodes(all), + length(alive_rabbit_nodes(Nodes)) =:= length(Nodes). + +alive_nodes(Nodes) -> [N || N <- Nodes, pong =:= net_adm:ping(N)]. + +alive_rabbit_nodes() -> alive_rabbit_nodes(rabbit_mnesia:cluster_nodes(all)). + +alive_rabbit_nodes(Nodes) -> + [N || N <- alive_nodes(Nodes), rabbit_nodes:is_process_running(N, rabbit)]. -- cgit v1.2.1 From d35534e6a7a0a04fc2ed68061d9c2508864a0865 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 22 Apr 2013 16:23:54 +0100 Subject: Switch pause_minority mode to making its decisions entirely based on node-upness, not rabbit-application-upness and explain why. --- src/rabbit_node_monitor.erl | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index ca8e6dbd..7d844c72 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -200,6 +200,7 @@ init([]) -> %% writing out the cluster status files - bad things can then %% happen. process_flag(trap_exit, true), + net_kernel:monitor_nodes(true), {ok, _} = mnesia:subscribe(system), {ok, #state{monitors = pmon:new(), subscribers = pmon:new(), @@ -265,6 +266,10 @@ handle_info({'DOWN', _MRef, process, Pid, _Reason}, State = #state{subscribers = Subscribers}) -> {noreply, State#state{subscribers = pmon:erase(Pid, Subscribers)}}; +handle_info({nodedown, Node}, State) -> + ok = handle_dead_node(Node), + {noreply, State}; + handle_info({mnesia_system_event, {inconsistent_database, running_partitioned_network, Node}}, State = #state{partitions = Partitions, @@ -333,6 +338,18 @@ handle_dead_rabbit(Node) -> ok = rabbit_amqqueue:on_node_down(Node), ok = rabbit_alarm:on_node_down(Node), ok = rabbit_mnesia:on_node_down(Node), + ok. + +handle_dead_node(_Node) -> + %% In general in rabbit_node_monitor we care about whether the + %% rabbit application is up rather than the node; we do this so + %% that we can respond in the same way to "rabbitmqctl stop_app" + %% and "rabbitmqctl stop" as much as possible. + %% + %% However, for pause_minority mode we can't do this, since we + %% depend on looking at whether other nodes are up to decide + %% whether to come back up ourselves - if we decide that based on + %% the rabbit application we would go down and never come back. case application:get_env(rabbit, cluster_partition_handling) of {ok, pause_minority} -> case majority() of @@ -347,8 +364,7 @@ handle_dead_rabbit(Node) -> rabbit_log:warning("cluster_partition_handling ~p unrecognised, " "assuming 'ignore'~n", [Term]), ok - end, - ok. + end. await_cluster_recovery() -> rabbit_log:warning("Cluster minority status detected - awaiting recovery~n", -- cgit v1.2.1 From 7f4b87411bc8e5d83a4a17740db76fbcb52eec54 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 24 Apr 2013 12:15:43 +0100 Subject: Add explanations to pmon:is_monitored/2 invocations. --- src/rabbit_mirror_queue_slave.erl | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 22edfcb6..63a53e2b 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -605,6 +605,10 @@ 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 }) -> + %% 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 + %% essay). ok = case pmon:is_monitored(ChPid, KS) of false -> ok; true -> credit_flow:peer_down(ChPid), @@ -621,6 +625,10 @@ confirm_sender_death(Pid) -> fun (?MODULE, State = #state { known_senders = KS, gm = GM }) -> %% We're running still as a slave + %% + %% See comment in local_sender_death/2; we might have + %% received a sender_death in the meanwhile so check + %% again. ok = case pmon:is_monitored(Pid, KS) of false -> ok; true -> gm:broadcast(GM, {ensure_monitoring, [Pid]}), @@ -766,6 +774,9 @@ process_instruction({sender_death, ChPid}, State = #state { sender_queues = SQ, msg_id_status = MS, 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 -- cgit v1.2.1 From 7bfc8223b1d8403787c6081c7a8c9acd6eb2b989 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Wed, 24 Apr 2013 14:45:14 +0100 Subject: Move the peer_down handling to sender_death, since that's when the slave really gives up on a sender. Transplant of 55f7c6afabe0 --- src/rabbit_mirror_queue_slave.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 222457c6..17337d9a 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -595,8 +595,7 @@ ensure_monitoring(ChPid, State = #state { known_senders = KS }) -> local_sender_death(ChPid, State = #state { known_senders = KS }) -> ok = case pmon:is_monitored(ChPid, KS) of false -> ok; - true -> credit_flow:peer_down(ChPid), - confirm_sender_death(ChPid) + true -> confirm_sender_death(ChPid) end, State. @@ -779,6 +778,7 @@ process_instruction({sender_death, ChPid}, 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) } -- cgit v1.2.1 From bb6fb516738dbc030afe968c62e0ed628707fc8b Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Thu, 25 Apr 2013 13:19:19 +0100 Subject: Receive fixed number of bytes from socket synchronously --- src/rabbit_net.erl | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/rabbit_net.erl b/src/rabbit_net.erl index b8b03f56..8b6895f5 100644 --- a/src/rabbit_net.erl +++ b/src/rabbit_net.erl @@ -18,9 +18,9 @@ -include("rabbit.hrl"). -export([is_ssl/1, ssl_info/1, controlling_process/2, getstat/2, - recv/1, async_recv/3, port_command/2, getopts/2, setopts/2, send/2, - close/1, fast_close/1, sockname/1, peername/1, peercert/1, - tune_buffer_size/1, connection_string/2, socket_ends/2]). + recv/1, sync_recv/2, async_recv/3, port_command/2, getopts/2, + setopts/2, send/2, close/1, fast_close/1, sockname/1, peername/1, + peercert/1, tune_buffer_size/1, connection_string/2, socket_ends/2]). %%--------------------------------------------------------------------------- @@ -48,6 +48,8 @@ -spec(recv/1 :: (socket()) -> {'data', [char()] | binary()} | 'closed' | rabbit_types:error(any()) | {'other', any()}). +-spec(sync_recv/2 :: (socket(), integer()) -> rabbit_types:ok(binary()) | + rabbit_types:error(any())). -spec(async_recv/3 :: (socket(), integer(), timeout()) -> rabbit_types:ok(any())). -spec(port_command/2 :: (socket(), iolist()) -> 'true'). @@ -115,6 +117,11 @@ recv(S, {DataTag, ClosedTag, ErrorTag}) -> Other -> {other, Other} end. +sync_recv(Sock, Length) when ?IS_SSL(Sock) -> + ssl:recv(Sock#ssl_socket.ssl, Length); +sync_recv(Sock, Length) -> + gen_tcp:recv(Sock, Length). + async_recv(Sock, Length, Timeout) when ?IS_SSL(Sock) -> Pid = self(), Ref = make_ref(), -- 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(-) 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 cfdc7a1968e4933afea26c9a9332442090dcef27 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 26 Apr 2013 13:38:25 +0100 Subject: Use the credit extension. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index bf33b931..449d1edd 100644 --- a/Makefile +++ b/Makefile @@ -64,7 +64,7 @@ TARGET_SRC_DIR=dist/$(TARBALL_NAME) SIBLING_CODEGEN_DIR=../rabbitmq-codegen/ AMQP_CODEGEN_DIR=$(shell [ -d $(SIBLING_CODEGEN_DIR) ] && echo $(SIBLING_CODEGEN_DIR) || echo codegen) -AMQP_SPEC_JSON_FILES_0_9_1=$(AMQP_CODEGEN_DIR)/amqp-rabbitmq-0.9.1.json +AMQP_SPEC_JSON_FILES_0_9_1=$(AMQP_CODEGEN_DIR)/amqp-rabbitmq-0.9.1.json $(AMQP_CODEGEN_DIR)/credit_extension.json AMQP_SPEC_JSON_FILES_0_8=$(AMQP_CODEGEN_DIR)/amqp-rabbitmq-0.8.json ERL_CALL=erl_call -sname $(RABBITMQ_NODENAME) -e -- cgit v1.2.1 From 966811099565d21bd0d3f57c0aaf44fba28aef5d Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 29 Apr 2013 16:54:58 +0100 Subject: If there is no password, refuse access rather than blowing up. --- src/rabbit_auth_backend_internal.erl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/rabbit_auth_backend_internal.erl b/src/rabbit_auth_backend_internal.erl index 44231f7b..3db8e2c3 100644 --- a/src/rabbit_auth_backend_internal.erl +++ b/src/rabbit_auth_backend_internal.erl @@ -203,7 +203,9 @@ hash_password(Cleartext) -> <>. check_password(Cleartext, <>) -> - Hash =:= salted_md5(Salt, Cleartext). + Hash =:= salted_md5(Salt, Cleartext); +check_password(_Cleartext, _Any) -> + false. make_salt() -> {A1,A2,A3} = now(), -- cgit v1.2.1 From a103c073e56a87b0b2cc86eb1f1cc0ce68c9b91f Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 29 Apr 2013 17:13:04 +0100 Subject: Comment --- src/rabbit_mirror_queue_mode.erl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/rabbit_mirror_queue_mode.erl b/src/rabbit_mirror_queue_mode.erl index de1c35e2..da4c9b36 100644 --- a/src/rabbit_mirror_queue_mode.erl +++ b/src/rabbit_mirror_queue_mode.erl @@ -25,7 +25,9 @@ -callback description() -> [proplists:property()]. %% Called whenever we think we might need to change nodes for a -%% mirrored queue. +%% mirrored queue. Note that this is called from a variety of +%% contexts, both inside and outside Mnesia transactions. Ideally it +%% will be pure-functional. %% %% Takes: parameters set in the policy, %% current master, -- cgit v1.2.1 From 93fa9d3913bed842d36a15e453625dbfae3c70c5 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 30 Apr 2013 12:30:40 +0100 Subject: Don't silently fail if we can't build deps.mk. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c63e3dfd..da2a809c 100644 --- a/Makefile +++ b/Makefile @@ -369,7 +369,7 @@ TESTABLEGOALS:=$(MAKECMDGOALS) endif ifneq "$(strip $(patsubst clean%,,$(patsubst %clean,,$(TESTABLEGOALS))))" "" --include $(DEPS_FILE) +include $(DEPS_FILE) endif .PHONY: run-qc -- cgit v1.2.1 From 3429727fd33af852bd1a5450d006e6f3878289f6 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 30 Apr 2013 12:40:32 +0100 Subject: Typo; fix compilation on - [{description, 0}, {suggested_queue_nodes, 5}, {validate_policy, 1}]. + [{description, 0}, {suggested_queue_nodes, 5}, {validate_policy, 1}]; behaviour_info(_Other) -> undefined. -- 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(-) 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 c95ce9e1c33c58e436f7a3d6365fdcf0407db9e7 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 1 May 2013 15:06:30 +0100 Subject: Changelogs for 3.1.0. --- packaging/RPMS/Fedora/rabbitmq-server.spec | 3 +++ packaging/debs/Debian/debian/changelog | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/packaging/RPMS/Fedora/rabbitmq-server.spec b/packaging/RPMS/Fedora/rabbitmq-server.spec index 6e02c7a4..7b57cb2f 100644 --- a/packaging/RPMS/Fedora/rabbitmq-server.spec +++ b/packaging/RPMS/Fedora/rabbitmq-server.spec @@ -123,6 +123,9 @@ done rm -rf %{buildroot} %changelog +* Wed May 1 2013 simon@rabbitmq.com 3.1.0-1 +- New Upstream Release + * Tue Dec 11 2012 simon@rabbitmq.com 3.0.1-1 - New Upstream Release diff --git a/packaging/debs/Debian/debian/changelog b/packaging/debs/Debian/debian/changelog index aed68b96..fe5310c8 100644 --- a/packaging/debs/Debian/debian/changelog +++ b/packaging/debs/Debian/debian/changelog @@ -1,3 +1,9 @@ +rabbitmq-server (3.1.0-1) unstable; urgency=low + + * New Upstream Release + + -- Simon MacMullen Wed, 01 May 2013 11:57:58 +0100 + rabbitmq-server (3.0.1-1) unstable; urgency=low * New Upstream Release -- cgit v1.2.1 -- 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(-) 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 23de2b4e37beee15d70b9095ff781014e4cf03c2 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 7 May 2013 15:20:40 +0100 Subject: Remove assertion; it's not always correct if prefetch has been set before. Instead, only set the volume when we have not set prefetch before. --- src/rabbit_limiter.erl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index d9f1170e..1e32f95a 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -324,12 +324,12 @@ prioritise_call(_Msg, _From, _Len, _State) -> 0. handle_call({new, ChPid}, _From, State = #lim{ch_pid = undefined}) -> {reply, ok, State#lim{ch_pid = ChPid}}; -handle_call({limit_prefetch, PrefetchCount, UnackedCount}, _From, State) -> - %% assertion - true = State#lim.prefetch_count == 0 orelse - State#lim.volume == UnackedCount, +handle_call({limit_prefetch, PrefetchCount, UnackedCount}, _From, + State = #lim{prefetch_count = 0}) -> {reply, ok, maybe_notify(State, State#lim{prefetch_count = PrefetchCount, volume = UnackedCount})}; +handle_call({limit_prefetch, PrefetchCount, _UnackedCount}, _From, State) -> + {reply, ok, maybe_notify(State, State#lim{prefetch_count = PrefetchCount})}; handle_call(unlimit_prefetch, _From, State) -> {reply, ok, maybe_notify(State, State#lim{prefetch_count = 0, -- cgit v1.2.1 From 1bc9c7614102d2cbed90c240f682bbdaa9e8de6d Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 7 May 2013 16:31:26 +0100 Subject: Treat missing / garbled x-match header as "all", like we used to. --- src/rabbit_exchange_type_headers.erl | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/rabbit_exchange_type_headers.erl b/src/rabbit_exchange_type_headers.erl index cf2d3140..44f909eb 100644 --- a/src/rabbit_exchange_type_headers.erl +++ b/src/rabbit_exchange_type_headers.erl @@ -67,8 +67,9 @@ validate_binding(_X, #binding{args = Args}) -> {binding_invalid, "x-match field missing", []}} end. -parse_x_match(<<"all">>) -> all; -parse_x_match(<<"any">>) -> any. +parse_x_match({longstr, <<"all">>}) -> all; +parse_x_match({longstr, <<"any">>}) -> any; +parse_x_match(_) -> all. %% legacy; we didn't validate %% Horrendous matching algorithm. Depends for its merge-like %% (linear-time) behaviour on the lists:keysort @@ -80,8 +81,8 @@ parse_x_match(<<"any">>) -> any. %% !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! %% headers_match(Args, Data) -> - {longstr, MK} = rabbit_misc:table_lookup(Args, <<"x-match">>), - headers_match(Args, Data, true, false, parse_x_match(MK)). + MK = parse_x_match(rabbit_misc:table_lookup(Args, <<"x-match">>)), + headers_match(Args, Data, true, false, MK). headers_match([], _Data, AllMatch, _AnyMatch, all) -> AllMatch; -- cgit v1.2.1 From 8261c2f5e21182a64d09ec4b40ecea170ea75fe3 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 7 May 2013 16:31:40 +0100 Subject: Allow x-match header to be missing. --- src/rabbit_exchange_type_headers.erl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/rabbit_exchange_type_headers.erl b/src/rabbit_exchange_type_headers.erl index 44f909eb..5b7f95fe 100644 --- a/src/rabbit_exchange_type_headers.erl +++ b/src/rabbit_exchange_type_headers.erl @@ -63,9 +63,10 @@ validate_binding(_X, #binding{args = Args}) -> {binding_invalid, "Invalid x-match field type ~p (value ~p); " "expected longstr", [Type, Other]}}; - undefined -> {error, - {binding_invalid, "x-match field missing", []}} + undefined -> ok %% [0] end. +%% [0] spec is vague on whether it can be omitted but in practice it's +%% useful to allow people to do this parse_x_match({longstr, <<"all">>}) -> all; parse_x_match({longstr, <<"any">>}) -> any; -- cgit v1.2.1 From dbaf442742bd673d4ee4ac98cfd7ffe760ba68a2 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 8 May 2013 17:08:57 +0100 Subject: Wrap the update in rabbit_vhost:with/2, so we error meaningfully if the vhost in question does not exist. --- src/rabbit_runtime_parameters.erl | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/rabbit_runtime_parameters.erl b/src/rabbit_runtime_parameters.erl index 05520170..a9427ab4 100644 --- a/src/rabbit_runtime_parameters.erl +++ b/src/rabbit_runtime_parameters.erl @@ -100,16 +100,16 @@ set_any0(VHost, Component, Name, Term) -> E end. -mnesia_update(VHost, Component, Name, Term) -> - rabbit_misc:execute_mnesia_transaction( - fun () -> - Res = case mnesia:read(?TABLE, {VHost, Component, Name}, read) of - [] -> new; - [Params] -> {old, Params#runtime_parameters.value} - end, - ok = mnesia:write(?TABLE, c(VHost, Component, Name, Term), write), - Res - end). +mnesia_update(VHost, Comp, Name, Term) -> + F = fun () -> + Res = case mnesia:read(?TABLE, {VHost, Comp, Name}, read) of + [] -> new; + [Params] -> {old, Params#runtime_parameters.value} + end, + ok = mnesia:write(?TABLE, c(VHost, Comp, Name, Term), write), + Res + end, + rabbit_misc:execute_mnesia_transaction(rabbit_vhost:with(VHost, F)). clear(_, <<"policy">> , _) -> {error_string, "policies may not be cleared using this method"}; -- cgit v1.2.1 From 4e852bcd7f868c4637d886c71a25f5be1a6bbe9d Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 9 May 2013 12:16:46 +0100 Subject: Validate on clear. --- src/rabbit_runtime_parameters.erl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rabbit_runtime_parameters.erl b/src/rabbit_runtime_parameters.erl index a9427ab4..d90b7c34 100644 --- a/src/rabbit_runtime_parameters.erl +++ b/src/rabbit_runtime_parameters.erl @@ -127,10 +127,10 @@ clear_any(VHost, Component, Name) -> end. mnesia_clear(VHost, Component, Name) -> - ok = rabbit_misc:execute_mnesia_transaction( - fun () -> - ok = mnesia:delete(?TABLE, {VHost, Component, Name}, write) - end). + F = fun () -> + ok = mnesia:delete(?TABLE, {VHost, Component, Name}, write) + end, + ok = rabbit_misc:execute_mnesia_transaction(rabbit_vhost:with(VHost, F)). list() -> [p(P) || #runtime_parameters{ key = {_VHost, Comp, _Name}} = P <- -- cgit v1.2.1 From ed423cfac2de5210d6e68085e6e15acf2db32476 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 9 May 2013 12:37:32 +0100 Subject: Validate on list --- src/rabbit_runtime_parameters.erl | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/rabbit_runtime_parameters.erl b/src/rabbit_runtime_parameters.erl index d90b7c34..fd0c1915 100644 --- a/src/rabbit_runtime_parameters.erl +++ b/src/rabbit_runtime_parameters.erl @@ -140,6 +140,7 @@ list(VHost) -> list(VHost, '_'). list_component(Component) -> list('_', Component). list(VHost, Component) -> + assert_vhost(VHost), Match = #runtime_parameters{key = {VHost, Component, '_'}, _ = '_'}, [p(P) || #runtime_parameters{key = {_VHost, Comp, _Name}} = P <- mnesia:dirty_match_object(?TABLE, Match), @@ -148,6 +149,12 @@ list(VHost, Component) -> list_formatted(VHost) -> [pset(value, format(pget(value, P)), P) || P <- list(VHost)]. +assert_vhost('_') -> ok; +assert_vhost(VHost) -> case rabbit_vhost:exists(VHost) of + true -> ok; + false -> throw({error, {no_such_vhost, VHost}}) + end. + lookup(VHost, Component, Name) -> case lookup0(VHost, Component, Name, rabbit_misc:const(not_found)) of not_found -> not_found; -- cgit v1.2.1 From e998a4c8ce397ae36d9e26780d9b7b78faed5d75 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 9 May 2013 12:42:41 +0100 Subject: Refactor. --- src/rabbit_runtime_parameters.erl | 8 +------- src/rabbit_vhost.erl | 10 +++++++++- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/rabbit_runtime_parameters.erl b/src/rabbit_runtime_parameters.erl index fd0c1915..e1c962b4 100644 --- a/src/rabbit_runtime_parameters.erl +++ b/src/rabbit_runtime_parameters.erl @@ -140,7 +140,7 @@ list(VHost) -> list(VHost, '_'). list_component(Component) -> list('_', Component). list(VHost, Component) -> - assert_vhost(VHost), + rabbit_vhost:assert(VHost), Match = #runtime_parameters{key = {VHost, Component, '_'}, _ = '_'}, [p(P) || #runtime_parameters{key = {_VHost, Comp, _Name}} = P <- mnesia:dirty_match_object(?TABLE, Match), @@ -149,12 +149,6 @@ list(VHost, Component) -> list_formatted(VHost) -> [pset(value, format(pget(value, P)), P) || P <- list(VHost)]. -assert_vhost('_') -> ok; -assert_vhost(VHost) -> case rabbit_vhost:exists(VHost) of - true -> ok; - false -> throw({error, {no_such_vhost, VHost}}) - end. - lookup(VHost, Component, Name) -> case lookup0(VHost, Component, Name, rabbit_misc:const(not_found)) of not_found -> not_found; diff --git a/src/rabbit_vhost.erl b/src/rabbit_vhost.erl index 2858cf58..b5d8b5e4 100644 --- a/src/rabbit_vhost.erl +++ b/src/rabbit_vhost.erl @@ -20,7 +20,7 @@ %%---------------------------------------------------------------------------- --export([add/1, delete/1, exists/1, list/0, with/2]). +-export([add/1, delete/1, exists/1, list/0, with/2, assert/1]). -export([info/1, info/2, info_all/0, info_all/1]). -ifdef(use_specs). @@ -30,6 +30,7 @@ -spec(exists/1 :: (rabbit_types:vhost()) -> boolean()). -spec(list/0 :: () -> [rabbit_types:vhost()]). -spec(with/2 :: (rabbit_types:vhost(), rabbit_misc:thunk(A)) -> A). +-spec(assert/1 :: (rabbit_types:vhost()) -> 'ok'). -spec(info/1 :: (rabbit_types:vhost()) -> rabbit_types:infos()). -spec(info/2 :: (rabbit_types:vhost(), rabbit_types:info_keys()) @@ -120,6 +121,13 @@ with(VHostPath, Thunk) -> end end. +%% Like with/2 but outside an Mnesia tx +assert('_') -> ok; +assert(VHostPath) -> case rabbit_vhost:exists(VHostPath) of + true -> ok; + false -> throw({error, {no_such_vhost, VHostPath}}) + end. + %%---------------------------------------------------------------------------- infos(Items, X) -> [{Item, i(Item, X)} || Item <- Items]. -- cgit v1.2.1 From 2b06b4f4425b212a98dde5bb94a20fe343aa68ea Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Thu, 9 May 2013 15:42:22 +0100 Subject: Clear queue expiry timer ref when fired --- src/rabbit_amqqueue_process.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 066392f8..d2f4a178 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1356,7 +1356,7 @@ handle_cast(wake_up, State) -> handle_info(maybe_expire, State) -> case is_unused(State) of true -> stop(State); - false -> noreply(ensure_expiry_timer(State)) + false -> noreply(State#q{expiry_timer_ref = undefined}) end; handle_info(drop_expired, State) -> -- cgit v1.2.1 From ae0dbc9500599cad81fd4e1bf9c51396c6e2b1a2 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 10 May 2013 12:39:54 +0100 Subject: TODO++ --- src/rabbit_channel.erl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 52c6140e..37041d34 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -841,6 +841,8 @@ handle_method(#'basic.qos'{prefetch_count = 0}, _, handle_method(#'basic.qos'{prefetch_count = PrefetchCount}, _, State = #ch{limiter = Limiter, unacked_message_q = UAMQ}) -> + %% TODO queue:len(UAMQ) is not strictly right since that counts + %% unacked messages from basic.get too. Pretty obscure though. Limiter1 = rabbit_limiter:limit_prefetch(Limiter, PrefetchCount, queue:len(UAMQ)), {reply, #'basic.qos_ok'{}, -- cgit v1.2.1 From 4a32421a15a1af8f3af4ce22be43cf0d4b67cf69 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 10 May 2013 12:42:37 +0100 Subject: Move the ignore of '_'. --- src/rabbit_runtime_parameters.erl | 5 ++++- src/rabbit_vhost.erl | 1 - 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/rabbit_runtime_parameters.erl b/src/rabbit_runtime_parameters.erl index e1c962b4..18eff722 100644 --- a/src/rabbit_runtime_parameters.erl +++ b/src/rabbit_runtime_parameters.erl @@ -140,7 +140,10 @@ list(VHost) -> list(VHost, '_'). list_component(Component) -> list('_', Component). list(VHost, Component) -> - rabbit_vhost:assert(VHost), + case VHost of + '_' -> ok; + _ -> rabbit_vhost:assert(VHost) + end, Match = #runtime_parameters{key = {VHost, Component, '_'}, _ = '_'}, [p(P) || #runtime_parameters{key = {_VHost, Comp, _Name}} = P <- mnesia:dirty_match_object(?TABLE, Match), diff --git a/src/rabbit_vhost.erl b/src/rabbit_vhost.erl index b5d8b5e4..3bf5354e 100644 --- a/src/rabbit_vhost.erl +++ b/src/rabbit_vhost.erl @@ -122,7 +122,6 @@ with(VHostPath, Thunk) -> end. %% Like with/2 but outside an Mnesia tx -assert('_') -> ok; assert(VHostPath) -> case rabbit_vhost:exists(VHostPath) of true -> ok; false -> throw({error, {no_such_vhost, VHostPath}}) -- 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(-) 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 49471f5697ade1718eaa2d371219eb8d8e081965 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 14 May 2013 14:17:29 +0100 Subject: Tighten up HA policy validation. --- src/rabbit_mirror_queue_misc.erl | 32 +++++++++++++++++++------------- src/rabbit_tests.erl | 29 +++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 13 deletions(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 8787e966..5607bfa9 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -316,24 +316,30 @@ update_mirrors0(OldQ = #amqqueue{name = QName}, %%---------------------------------------------------------------------------- validate_policy(KeyList) -> - Mode = proplists:get_value(<<"ha-mode">>, KeyList), + Mode = proplists:get_value(<<"ha-mode">>, KeyList, none), Params = proplists:get_value(<<"ha-params">>, KeyList, none), - case Mode of - undefined -> ok; - _ -> case module(Mode) of - {ok, M} -> case M:validate_policy(Params) of - ok -> validate_sync_mode(KeyList); - E -> E - end; - _ -> {error, - "~p is not a valid ha-mode value", [Mode]} - end + SyncMode = proplists:get_value(<<"ha-sync-mode">>, KeyList, none), + case {Mode, Params, SyncMode} of + {none, none, none} -> + ok; + {none, _, _} -> + {error, "ha-mode must be specified to specify ha-params or " + "ha-sync-mode", []}; + _ -> + case module(Mode) of + {ok, M} -> case M:validate_policy(Params) of + ok -> validate_sync_mode(SyncMode); + E -> E + end; + _ -> {error, "~p is not a valid ha-mode value", [Mode]} + end end. -validate_sync_mode(KeyList) -> - case proplists:get_value(<<"ha-sync-mode">>, KeyList, <<"manual">>) of +validate_sync_mode(SyncMode) -> + case SyncMode of <<"automatic">> -> ok; <<"manual">> -> ok; + none -> ok; Mode -> {error, "ha-sync-mode must be \"manual\" " "or \"automatic\", got ~p", [Mode]} end. diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 2e46304f..f32fe740 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_ha_policy_validation(), passed = test_server_status(), passed = test_amqp_connection_refusal(), passed = test_confirms(), @@ -1101,6 +1102,34 @@ test_policy_validation() -> rabbit_runtime_parameters_test:unregister_policy_validator(), passed. +test_ha_policy_validation() -> + Set = fun (JSON) -> control_action(set_policy, ["name", ".*", JSON]) end, + OK = fun (JSON) -> ok = Set(JSON) end, + Fail = fun (JSON) -> {error_string, _} = Set(JSON) end, + + OK ("{\"ha-mode\":\"all\"}"), + Fail("{\"ha-mode\":\"made_up\"}"), + + Fail("{\"ha-mode\":\"nodes\"}"), + Fail("{\"ha-mode\":\"nodes\",\"ha-params\":2}"), + Fail("{\"ha-mode\":\"nodes\",\"ha-params\":[\"a\",2]}"), + OK ("{\"ha-mode\":\"nodes\",\"ha-params\":[\"a\",\"b\"]}"), + Fail("{\"ha-params\":[\"a\",\"b\"]}"), + + Fail("{\"ha-mode\":\"exactly\"}"), + Fail("{\"ha-mode\":\"exactly\",\"ha-params\":[\"a\",\"b\"]}"), + OK ("{\"ha-mode\":\"exactly\",\"ha-params\":2}"), + Fail("{\"ha-params\":2}"), + + OK ("{\"ha-mode\":\"all\",\"ha-sync-mode\":\"manual\"}"), + OK ("{\"ha-mode\":\"all\",\"ha-sync-mode\":\"automatic\"}"), + Fail("{\"ha-mode\":\"all\",\"ha-sync-mode\":\"made_up\"}"), + Fail("{\"ha-sync-mode\":\"manual\"}"), + Fail("{\"ha-sync-mode\":\"automatic\"}"), + + ok = control_action(clear_policy, ["name"]), + passed. + test_server_status() -> %% create a few things so there is some useful information to list {_Writer, Limiter, Ch} = test_channel(), -- 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(-) 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 a0b66cae63bf62e432b6d2e52a9d39f1703f07bf Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 20 May 2013 12:23:11 +0100 Subject: Pass through tx'ed acks and rejects in the correct order. --- src/rabbit_channel.erl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 37041d34..1de14b5c 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -1085,8 +1085,9 @@ handle_method(#'tx.commit'{}, _, #ch{tx = none}) -> handle_method(#'tx.commit'{}, _, State = #ch{tx = {Msgs, Acks}, limiter = Limiter}) -> State1 = rabbit_misc:queue_fold(fun deliver_to_queues/2, State, Msgs), - lists:foreach(fun ({ack, A}) -> ack(A, State1); - ({Requeue, A}) -> reject(Requeue, A, Limiter) + Rev = fun (X) -> lists:reverse(lists:sort(X)) end, + lists:foreach(fun ({ack, A}) -> ack(Rev(A), State1); + ({Requeue, A}) -> reject(Requeue, Rev(A), Limiter) end, lists:reverse(Acks)), {noreply, maybe_complete_tx(State1#ch{tx = committing})}; -- cgit v1.2.1 From 665b622697ffcfbd0c1bffa459cb704f39a943f4 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 20 May 2013 12:53:19 +0100 Subject: Remove obsolete and wrong comment. --- src/rabbit_autoheal.erl | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/rabbit_autoheal.erl b/src/rabbit_autoheal.erl index c00c2dd6..529d46b4 100644 --- a/src/rabbit_autoheal.erl +++ b/src/rabbit_autoheal.erl @@ -39,13 +39,8 @@ %% To coordinate the restarting nodes we pick a special node from the %% winning partition - the "winner". Restarting nodes then stop, tell %% the winner they have done so, and wait for it to tell them it is -%% safe to start again. -%% -%% The winner and the leader are not necessarily the same node! Since -%% the leader may end up restarting, we also make sure that it does -%% not announce its decision (and thus cue other nodes to restart) -%% until it has seen a request from every node that has experienced a -%% partition. +%% safe to start again. The winner and the leader are not necessarily +%% the same node. %% %% Possible states: %% -- cgit v1.2.1 From 875969bb3f80061a582e45bc0a5a1de52d047829 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 20 May 2013 12:57:36 +0100 Subject: Ignore autoheal requests if we are already autohealing. --- src/rabbit_autoheal.erl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/rabbit_autoheal.erl b/src/rabbit_autoheal.erl index 529d46b4..becfbb66 100644 --- a/src/rabbit_autoheal.erl +++ b/src/rabbit_autoheal.erl @@ -102,6 +102,12 @@ handle_msg({request_start, Node}, not_healing end; +handle_msg({request_start, Node}, + State, _Partitions) -> + rabbit_log:info("Autoheal request received from ~p when in state ~p; " + "ignoring ~n", [Node, State]), + State; + handle_msg({become_winner, Losers}, not_healing, _Partitions) -> rabbit_log:info("Autoheal: I am the winner, waiting for ~p to stop~n", -- cgit v1.2.1 From 86a2d6f05e938025e91bccca92489ae95a3772a2 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 20 May 2013 13:13:36 +0100 Subject: space-- --- src/rabbit_autoheal.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_autoheal.erl b/src/rabbit_autoheal.erl index becfbb66..f903677b 100644 --- a/src/rabbit_autoheal.erl +++ b/src/rabbit_autoheal.erl @@ -105,7 +105,7 @@ handle_msg({request_start, Node}, handle_msg({request_start, Node}, State, _Partitions) -> rabbit_log:info("Autoheal request received from ~p when in state ~p; " - "ignoring ~n", [Node, State]), + "ignoring~n", [Node, State]), State; handle_msg({become_winner, Losers}, -- 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(+) 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(-) 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(-) 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 630e9f87df0b55a11f41168280fab55c9c9d531e Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Tue, 21 May 2013 14:21:31 +0100 Subject: update changelogs for 3.1.1 --- packaging/RPMS/Fedora/rabbitmq-server.spec | 3 +++ packaging/debs/Debian/debian/changelog | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/packaging/RPMS/Fedora/rabbitmq-server.spec b/packaging/RPMS/Fedora/rabbitmq-server.spec index 7b57cb2f..607719cf 100644 --- a/packaging/RPMS/Fedora/rabbitmq-server.spec +++ b/packaging/RPMS/Fedora/rabbitmq-server.spec @@ -123,6 +123,9 @@ done rm -rf %{buildroot} %changelog +* Mon May 20 2013 tim@rabbitmq.com 3.1.1-1 +- Test release + * Wed May 1 2013 simon@rabbitmq.com 3.1.0-1 - New Upstream Release diff --git a/packaging/debs/Debian/debian/changelog b/packaging/debs/Debian/debian/changelog index fe5310c8..8dffefb0 100644 --- a/packaging/debs/Debian/debian/changelog +++ b/packaging/debs/Debian/debian/changelog @@ -1,3 +1,9 @@ +rabbitmq-server (3.1.1-1) unstable; urgency=low + + * Test release + + -- Tim Watson Mon, 20 May 2013 16:21:20 +0100 + rabbitmq-server (3.1.0-1) unstable; urgency=low * New Upstream Release -- cgit v1.2.1 From 2086f0e154c54ab5f179130660ef79f144dc9f7f Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 24 May 2013 15:18:57 +0100 Subject: No infinity timeout, we could deadlock with the application controller on shutdown. --- src/rabbit.erl | 2 +- src/rabbit_nodes.erl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 3cfa21ba..2a70f016 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -393,7 +393,7 @@ await_startup() -> status() -> S1 = [{pid, list_to_integer(os:getpid())}, - {running_applications, application:which_applications(infinity)}, + {running_applications, application:which_applications()}, {os, os:type()}, {erlang_version, erlang:system_info(system_version)}, {memory, rabbit_vm:memory()}], diff --git a/src/rabbit_nodes.erl b/src/rabbit_nodes.erl index 5640f12a..b52d36e3 100644 --- a/src/rabbit_nodes.erl +++ b/src/rabbit_nodes.erl @@ -96,7 +96,7 @@ cookie_hash() -> base64:encode_to_string(erlang:md5(atom_to_list(erlang:get_cookie()))). is_running(Node, Application) -> - case rpc:call(Node, application, which_applications, [infinity]) of + case rpc:call(Node, application, which_applications, []) of {badrpc, _} -> false; Apps -> proplists:is_defined(Application, Apps) end. -- cgit v1.2.1 From c27b83342d1d8ada3fc8a23c058a475a3fbb3578 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 24 May 2013 15:35:57 +0100 Subject: Look for the rabbit process, not the rabbit application. --- src/rabbit.erl | 2 +- src/rabbit_node_monitor.erl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 2a70f016..f65a8d68 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -421,7 +421,7 @@ status() -> is_running() -> is_running(node()). -is_running(Node) -> rabbit_nodes:is_running(Node, rabbit). +is_running(Node) -> rabbit_nodes:is_process_running(Node, rabbit). environment() -> lists:keysort(1, [P || P = {K, _} <- application:get_all_env(rabbit), diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index 7d844c72..7fcd1f99 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -465,4 +465,4 @@ alive_nodes(Nodes) -> [N || N <- Nodes, pong =:= net_adm:ping(N)]. alive_rabbit_nodes() -> alive_rabbit_nodes(rabbit_mnesia:cluster_nodes(all)). alive_rabbit_nodes(Nodes) -> - [N || N <- alive_nodes(Nodes), rabbit_nodes:is_process_running(N, rabbit)]. + [N || N <- alive_nodes(Nodes), rabbit:is_running(N)]. -- 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(-) 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 -- cgit v1.2.1 -- cgit v1.2.1 -- cgit v1.2.1 From ee8dfa998133c8b13195e5164a25e001fa24ed5e Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 29 May 2013 08:48:48 +0100 Subject: cope with empty rabbit_serial file --- src/rabbit_guid.erl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/rabbit_guid.erl b/src/rabbit_guid.erl index 6c45deea..bec29e59 100644 --- a/src/rabbit_guid.erl +++ b/src/rabbit_guid.erl @@ -63,6 +63,7 @@ update_disk_serial() -> Filename = filename(), Serial = case rabbit_file:read_term_file(Filename) of {ok, [Num]} -> Num; + {ok, []} -> 0; %% [1] {error, enoent} -> 0; {error, Reason} -> throw({error, {cannot_read_serial_file, Filename, Reason}}) @@ -73,6 +74,10 @@ update_disk_serial() -> throw({error, {cannot_write_serial_file, Filename, Reason1}}) end, Serial. +%% [1] a couple of users have reported startup failures due to the +%% file being empty, presumably as a result of filesystem +%% corruption. While rabbit doesn't cope with that in general, in this +%% specific case we can be more accommodating. %% Generate an un-hashed guid. fresh() -> -- cgit v1.2.1 From 8728a5c3dd93e662f1fb6ecee56e184493243de4 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Wed, 29 May 2013 15:31:37 +0100 Subject: Test to trigger VQ assertion failure (ram msgs =< len) --- src/rabbit_tests.erl | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index f32fe740..21c54f3e 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -2393,6 +2393,7 @@ test_variable_queue() -> fun test_variable_queue_ack_limiting/1, fun test_variable_queue_purge/1, fun test_variable_queue_requeue/1, + fun test_variable_queue_requeue_ram_beta/1, fun test_variable_queue_fold/1]], passed. @@ -2491,6 +2492,20 @@ test_variable_queue_requeue(VQ0) -> {empty, VQ3} = rabbit_variable_queue:fetch(true, VQ2), VQ3. +%% requeue from ram_pending_ack into q3, move to delta and then empty queue +test_variable_queue_requeue_ram_beta(VQ0) -> + Count = rabbit_queue_index:next_segment_boundary(0)*2 + 2, + VQ1 = rabbit_tests:variable_queue_publish(false, Count, VQ0), + {VQ2, AcksR} = variable_queue_fetch(Count, false, false, Count, VQ1), + {Back, Front} = lists:split(Count div 2, AcksR), + {_, VQ3} = rabbit_variable_queue:requeue(erlang:tl(Back), VQ2), + VQ4 = rabbit_variable_queue:set_ram_duration_target(0, VQ3), + {_, VQ5} = rabbit_variable_queue:requeue([erlang:hd(Back)], VQ4), + VQ6 = requeue_one_by_one(Front, VQ5), + {VQ7, AcksAll} = variable_queue_fetch(Count, false, true, Count, VQ6), + {_, VQ8} = rabbit_variable_queue:ack(AcksAll, VQ7), + VQ8. + test_variable_queue_purge(VQ0) -> LenDepth = fun (VQ) -> {rabbit_variable_queue:len(VQ), -- cgit v1.2.1 From ef21df62c9d6cf25d95ea5410c1de66912e27d9c Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Wed, 29 May 2013 15:45:54 +0100 Subject: Dehydrate prospective betas on requeue --- src/rabbit_variable_queue.erl | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index f7c6c729..5b39c2c6 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -1363,11 +1363,8 @@ publish_alpha(MsgStatus, State) -> {MsgStatus, inc_ram_msg_count(State)}. publish_beta(MsgStatus, State) -> - {#msg_status { msg = Msg} = MsgStatus1, - #vqstate { ram_msg_count = RamMsgCount } = State1} = - maybe_write_to_disk(true, false, MsgStatus, State), - {MsgStatus1, State1 #vqstate { - ram_msg_count = RamMsgCount + one_if(Msg =/= undefined) }}. + {MsgStatus1, State1} = maybe_write_to_disk(true, false, MsgStatus, State), + {m(trim_msg_status(MsgStatus1)), State1}. %% Rebuild queue, inserting sequence ids to maintain ordering queue_merge(SeqIds, Q, MsgIds, Limit, PubFun, State) -> -- cgit v1.2.1 From dc26293584e7fa45d86c44858b2c400cfb9d68fb Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 29 May 2013 16:40:14 +0100 Subject: Just in case this times out, let's not explode. --- src/rabbit.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index f65a8d68..37ed5ae0 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -393,7 +393,7 @@ await_startup() -> status() -> S1 = [{pid, list_to_integer(os:getpid())}, - {running_applications, application:which_applications()}, + {running_applications, catch application:which_applications()}, {os, os:type()}, {erlang_version, erlang:system_info(system_version)}, {memory, rabbit_vm:memory()}], -- cgit v1.2.1 From 15702b503242a2750e6da46b5cdee7593fb555b9 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 30 May 2013 16:37:14 +0100 Subject: Don't use andalso here, it breaks LDAP when compiled under R12. --- src/rabbit_access_control.erl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/rabbit_access_control.erl b/src/rabbit_access_control.erl index 16387268..cbe89a17 100644 --- a/src/rabbit_access_control.erl +++ b/src/rabbit_access_control.erl @@ -68,8 +68,11 @@ check_vhost_access(User = #user{ username = Username, auth_backend = Module }, VHostPath) -> check_access( fun() -> - rabbit_vhost:exists(VHostPath) andalso - Module:check_vhost_access(User, VHostPath) + %% TODO this could be an andalso shortcut under >R13A + case rabbit_vhost:exists(VHostPath) of + false -> false; + true -> Module:check_vhost_access(User, VHostPath) + end end, "~s failed checking vhost access to ~s for ~s: ~p~n", [Module, VHostPath, Username], -- cgit v1.2.1 From 41b6c1462dfd55de42926782ce22fccad8438c10 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 30 May 2013 17:27:12 +0100 Subject: Change version dependencies for the RPM and deb files. And for the Windows installer in case any Windows user is crazy enough to be running R12. --- packaging/RPMS/Fedora/rabbitmq-server.spec | 4 ++-- packaging/debs/Debian/debian/control | 4 ++-- packaging/windows-exe/rabbitmq_nsi.in | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packaging/RPMS/Fedora/rabbitmq-server.spec b/packaging/RPMS/Fedora/rabbitmq-server.spec index 607719cf..5f261417 100644 --- a/packaging/RPMS/Fedora/rabbitmq-server.spec +++ b/packaging/RPMS/Fedora/rabbitmq-server.spec @@ -12,8 +12,8 @@ Source3: rabbitmq-server.logrotate Source4: rabbitmq-server.ocf URL: http://www.rabbitmq.com/ BuildArch: noarch -BuildRequires: erlang >= R12B-3, python-simplejson, xmlto, libxslt -Requires: erlang >= R12B-3, logrotate +BuildRequires: erlang >= R13B03, python-simplejson, xmlto, libxslt +Requires: erlang >= R13B03, logrotate BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-%{_arch}-root Summary: The RabbitMQ server Requires(post): %%REQUIRES%% diff --git a/packaging/debs/Debian/debian/control b/packaging/debs/Debian/debian/control index 3a15c4b6..02d23547 100644 --- a/packaging/debs/Debian/debian/control +++ b/packaging/debs/Debian/debian/control @@ -4,12 +4,12 @@ Priority: extra Maintainer: RabbitMQ Team Uploaders: Emile Joubert DM-Upload-Allowed: yes -Build-Depends: cdbs, debhelper (>= 5), erlang-dev, python-simplejson, xmlto, xsltproc, erlang-nox (>= 1:12.b.3), erlang-src (>= 1:12.b.3), unzip, zip +Build-Depends: cdbs, debhelper (>= 5), erlang-dev, python-simplejson, xmlto, xsltproc, erlang-nox (>= 1:13.b.3), erlang-src (>= 1:13.b.3), unzip, zip Standards-Version: 3.9.2 Package: rabbitmq-server Architecture: all -Depends: erlang-nox (>= 1:12.b.3) | esl-erlang, adduser, logrotate, ${misc:Depends} +Depends: erlang-nox (>= 1:13.b.3) | esl-erlang, adduser, logrotate, ${misc:Depends} Description: AMQP server written in Erlang RabbitMQ is an implementation of AMQP, the emerging standard for high performance enterprise messaging. The RabbitMQ server is a robust and diff --git a/packaging/windows-exe/rabbitmq_nsi.in b/packaging/windows-exe/rabbitmq_nsi.in index b351430e..101822f7 100644 --- a/packaging/windows-exe/rabbitmq_nsi.in +++ b/packaging/windows-exe/rabbitmq_nsi.in @@ -213,7 +213,7 @@ Function findErlang abort: Abort ${Else} - ${VersionCompare} $2 "5.6.3" $0 + ${VersionCompare} $2 "5.7.4" $0 ${VersionCompare} $2 "5.8.1" $1 ${If} $0 = 2 -- 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(-) 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(-) 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(-) 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 79b05bef4e1eeed3db0f222fd94fad78d09c4135 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 31 May 2013 13:31:39 +0100 Subject: Attempt to deal with missing segment file. --- src/rabbit_queue_index.erl | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/rabbit_queue_index.erl b/src/rabbit_queue_index.erl index ea70208f..9a2d2a4a 100644 --- a/src/rabbit_queue_index.erl +++ b/src/rabbit_queue_index.erl @@ -1022,7 +1022,18 @@ journal_minus_segment1({no_pub, del, ack}, {?PUB, no_del, no_ack}) -> journal_minus_segment1({no_pub, del, ack}, {?PUB, del, no_ack}) -> {{no_pub, no_del, ack}, 0}; journal_minus_segment1({no_pub, del, ack}, {?PUB, del, ack}) -> - {undefined, -1}. + {undefined, -1}; + +%% Missing segment. If flush_journal/1 is interrupted after deleting +%% the segment but before truncating the journal we can get these +%% cases: a delivery and an acknowledgement in the journal, or just an +%% acknowledgement in the journal, but with no segment. In both cases +%% we have really forgotten the message; so ignore what's in the +%% journal. +journal_minus_segment1({no_pub, no_del, ack}, undefined) -> + {undefined, 0}; +journal_minus_segment1({no_pub, del, ack}, undefined) -> + {undefined, 0}. %%---------------------------------------------------------------------------- %% upgrade -- cgit v1.2.1 From 0b9fe40947ed8a671f0210facad94f25e9544055 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 31 May 2013 14:55:48 +0100 Subject: graceful handling of application:which_applications() timeout --- src/app_utils.erl | 2 +- src/rabbit.erl | 2 +- src/rabbit_misc.erl | 13 ++++++++++++- src/rabbit_nodes.erl | 2 +- src/rabbit_plugins.erl | 2 +- src/rabbit_vm.erl | 2 +- 6 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/app_utils.erl b/src/app_utils.erl index 8da436c0..b102ce75 100644 --- a/src/app_utils.erl +++ b/src/app_utils.erl @@ -93,7 +93,7 @@ app_dependency_order(RootApps, StripUnreachable) -> %% Private API wait_for_application(Application) -> - case lists:keymember(Application, 1, application:which_applications()) of + case lists:keymember(Application, 1, rabbit_misc:which_applications()) of true -> ok; false -> timer:sleep(1000), wait_for_application(Application) diff --git a/src/rabbit.erl b/src/rabbit.erl index 37ed5ae0..450a7f32 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -393,7 +393,7 @@ await_startup() -> status() -> S1 = [{pid, list_to_integer(os:getpid())}, - {running_applications, catch application:which_applications()}, + {running_applications, rabbit_misc:which_applications()}, {os, os:type()}, {erlang_version, erlang:system_info(system_version)}, {memory, rabbit_vm:memory()}], diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index c36fb147..a1e95fd5 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -61,7 +61,7 @@ -export([multi_call/2]). -export([os_cmd/1]). -export([gb_sets_difference/2]). --export([version/0]). +-export([version/0, which_applications/0]). -export([sequence_error/1]). -export([json_encode/1, json_decode/1, json_to_term/1, term_to_json/1]). -export([check_expiry/1]). @@ -232,6 +232,7 @@ -spec(os_cmd/1 :: (string()) -> string()). -spec(gb_sets_difference/2 :: (gb_set(), gb_set()) -> gb_set()). -spec(version/0 :: () -> string()). +-spec(which_applications/0 :: () -> [{atom(), string(), string()}]). -spec(sequence_error/1 :: ([({'error', any()} | any())]) -> {'error', any()} | any()). -spec(json_encode/1 :: (any()) -> {'ok', string()} | {'error', any()}). @@ -985,6 +986,16 @@ version() -> {ok, VSN} = application:get_key(rabbit, vsn), VSN. +%% application:which_applications(infinity) is dangerous, since it can +%% cause deadlocks on shutdown. So we have to use a timeout variant, +%% but w/o creating spurious timeout errors. +which_applications() -> + try + application:which_applications() + catch + exit:{timeout, _} -> [] + end. + sequence_error([T]) -> T; sequence_error([{error, _} = Error | _]) -> Error; sequence_error([_ | Rest]) -> sequence_error(Rest). diff --git a/src/rabbit_nodes.erl b/src/rabbit_nodes.erl index b52d36e3..b85646d2 100644 --- a/src/rabbit_nodes.erl +++ b/src/rabbit_nodes.erl @@ -96,7 +96,7 @@ cookie_hash() -> base64:encode_to_string(erlang:md5(atom_to_list(erlang:get_cookie()))). is_running(Node, Application) -> - case rpc:call(Node, application, which_applications, []) of + case rpc:call(Node, rabbit_misc, which_applications, []) of {badrpc, _} -> false; Apps -> proplists:is_defined(Application, Apps) end. diff --git a/src/rabbit_plugins.erl b/src/rabbit_plugins.erl index 58c906eb..6f6515b0 100644 --- a/src/rabbit_plugins.erl +++ b/src/rabbit_plugins.erl @@ -47,7 +47,7 @@ setup() -> active() -> {ok, ExpandDir} = application:get_env(rabbit, plugins_expand_dir), InstalledPlugins = [ P#plugin.name || P <- list(ExpandDir) ], - [App || {App, _, _} <- application:which_applications(), + [App || {App, _, _} <- rabbit_misc:which_applications(), lists:member(App, InstalledPlugins)]. %% @doc Get the list of plugins which are ready to be enabled. diff --git a/src/rabbit_vm.erl b/src/rabbit_vm.erl index c28b0cd5..e97824b9 100644 --- a/src/rabbit_vm.erl +++ b/src/rabbit_vm.erl @@ -99,7 +99,7 @@ bytes(Words) -> Words * erlang:system_info(wordsize). plugin_sups() -> lists:append([plugin_sup(App) || - {App, _, _} <- application:which_applications(), + {App, _, _} <- rabbit_misc:which_applications(), is_plugin(atom_to_list(App))]). plugin_sup(App) -> -- 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(-) 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(-) 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(-) 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 1a0ab0947a66ea448ae27b964d796c718783f770 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 1 Jun 2013 22:39:33 +0100 Subject: fix types (transplanted from eec14a745dd738a98cfb83e86a2b077d4280e314) --- src/priority_queue.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 195ba43314f1ca208bd31edda4d18d9072d871a0 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Tue, 4 Jun 2013 12:23:14 +0100 Subject: Remove expiration property from dead lettered messages --- src/rabbit_amqqueue_process.erl | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index d2f4a178..f7d14381 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -912,9 +912,13 @@ make_dead_letter_msg(Msg = #basic_message{content = Content, HeadersFun1(rabbit_basic:prepend_table_header(<<"x-death">>, Info, Headers)) end, - Content1 = rabbit_basic:map_headers(HeadersFun2, Content), + Content1 = #content{properties = Props} = + rabbit_basic:map_headers(HeadersFun2, Content), + PropsNoExpiration = Props#'P_basic'{expiration = undefined}, + ContentNoExpiration = Content1#content{properties = PropsNoExpiration}, Msg#basic_message{exchange_name = DLX, id = rabbit_guid:gen(), - routing_keys = DeathRoutingKeys, content = Content1}. + routing_keys = DeathRoutingKeys, + content = ContentNoExpiration}. now_micros() -> timer:now_diff(now(), {0,0,0}). -- cgit v1.2.1 From 7f84407e046c979f798e9c685303a7d03ec9c40e Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Thu, 6 Jun 2013 10:31:09 +0100 Subject: add original expiration to x-death headers on per-message-ttl expiry --- src/rabbit_amqqueue_process.erl | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index f7d14381..ed15b6f5 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -898,6 +898,18 @@ make_dead_letter_msg(Msg = #basic_message{content = Content, end, ReasonBin = list_to_binary(atom_to_list(Reason)), TimeSec = rabbit_misc:now_ms() div 1000, + MaybePerMsgTTL = + case Reason of + expired -> + OrigProps = Content#content.properties, + case rabbit_basic:parse_expiration(OrigProps) of + {ok, Exp} when is_integer(Exp) -> + [{<<"original-expiration">>, longstr, + integer_to_list(Exp)}]; + _ -> [] + end; + _ -> [] + end, HeadersFun2 = fun (Headers) -> %% The first routing key is the one specified in the @@ -908,7 +920,7 @@ make_dead_letter_msg(Msg = #basic_message{content = Content, {<<"queue">>, longstr, QName}, {<<"time">>, timestamp, TimeSec}, {<<"exchange">>, longstr, Exchange#resource.name}, - {<<"routing-keys">>, array, RKs1}], + {<<"routing-keys">>, array, RKs1}] ++ MaybePerMsgTTL, HeadersFun1(rabbit_basic:prepend_table_header(<<"x-death">>, Info, Headers)) end, -- cgit v1.2.1 From cf8b72892eb82f7eac0298c36f8510d9ccdf8ed7 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Thu, 6 Jun 2013 11:38:54 +0100 Subject: do not attempt to parse expiration from missing properties --- src/rabbit_amqqueue_process.erl | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index ed15b6f5..892c2a77 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -901,13 +901,8 @@ make_dead_letter_msg(Msg = #basic_message{content = Content, MaybePerMsgTTL = case Reason of expired -> - OrigProps = Content#content.properties, - case rabbit_basic:parse_expiration(OrigProps) of - {ok, Exp} when is_integer(Exp) -> - [{<<"original-expiration">>, longstr, - integer_to_list(Exp)}]; - _ -> [] - end; + OriginalProps = Content#content.properties, + maybe_create_per_msg_ttl_xdeath_header(OriginalProps); _ -> [] end, HeadersFun2 = @@ -932,6 +927,17 @@ make_dead_letter_msg(Msg = #basic_message{content = Content, routing_keys = DeathRoutingKeys, content = ContentNoExpiration}. +maybe_create_per_msg_ttl_xdeath_header(#'P_basic'{}=Props) -> + case rabbit_basic:parse_expiration(Props) of + {ok, Exp} when is_integer(Exp) -> + [{<<"original-expiration">>, longstr, + integer_to_list(Exp)}]; + _ -> + [] + end; +maybe_create_per_msg_ttl_xdeath_header(_) -> + []. + now_micros() -> timer:now_diff(now(), {0,0,0}). infos(Items, State) -> [{Item, i(Item, State)} || Item <- Items]. -- cgit v1.2.1 From f9094e19b9fe49b4b2fd22da366460627b1585b6 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 6 Jun 2013 12:43:30 +0100 Subject: Cosmetic. --- src/rabbit_amqqueue_process.erl | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 892c2a77..d973e9ea 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -898,13 +898,10 @@ make_dead_letter_msg(Msg = #basic_message{content = Content, end, ReasonBin = list_to_binary(atom_to_list(Reason)), TimeSec = rabbit_misc:now_ms() div 1000, - MaybePerMsgTTL = - case Reason of - expired -> - OriginalProps = Content#content.properties, - maybe_create_per_msg_ttl_xdeath_header(OriginalProps); - _ -> [] - end, + PerMsgTTL = case Reason of + expired -> per_msg_ttl_header(Content#content.properties); + _ -> [] + end, HeadersFun2 = fun (Headers) -> %% The first routing key is the one specified in the @@ -915,27 +912,27 @@ make_dead_letter_msg(Msg = #basic_message{content = Content, {<<"queue">>, longstr, QName}, {<<"time">>, timestamp, TimeSec}, {<<"exchange">>, longstr, Exchange#resource.name}, - {<<"routing-keys">>, array, RKs1}] ++ MaybePerMsgTTL, + {<<"routing-keys">>, array, RKs1}] ++ PerMsgTTL, HeadersFun1(rabbit_basic:prepend_table_header(<<"x-death">>, Info, Headers)) end, Content1 = #content{properties = Props} = rabbit_basic:map_headers(HeadersFun2, Content), - PropsNoExpiration = Props#'P_basic'{expiration = undefined}, - ContentNoExpiration = Content1#content{properties = PropsNoExpiration}, - Msg#basic_message{exchange_name = DLX, id = rabbit_guid:gen(), - routing_keys = DeathRoutingKeys, - content = ContentNoExpiration}. - -maybe_create_per_msg_ttl_xdeath_header(#'P_basic'{}=Props) -> + Content2 = Content1#content{properties = + Props#'P_basic'{expiration = undefined}}, + Msg#basic_message{exchange_name = DLX, + id = rabbit_guid:gen(), + routing_keys = DeathRoutingKeys, + content = Content2}. + +per_msg_ttl_header(#'P_basic'{} = Props) -> case rabbit_basic:parse_expiration(Props) of {ok, Exp} when is_integer(Exp) -> - [{<<"original-expiration">>, longstr, - integer_to_list(Exp)}]; + [{<<"original-expiration">>, longstr, integer_to_list(Exp)}]; _ -> [] end; -maybe_create_per_msg_ttl_xdeath_header(_) -> +per_msg_ttl_header(_) -> []. now_micros() -> timer:now_diff(now(), {0,0,0}). -- cgit v1.2.1 From 3567b5db0906f727a269460ac2ba64128fd2e269 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Thu, 6 Jun 2013 15:07:03 +0100 Subject: allows log the per-msg-ttl if it is present --- src/rabbit_amqqueue_process.erl | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index d973e9ea..d3e88e8b 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -898,10 +898,7 @@ make_dead_letter_msg(Msg = #basic_message{content = Content, end, ReasonBin = list_to_binary(atom_to_list(Reason)), TimeSec = rabbit_misc:now_ms() div 1000, - PerMsgTTL = case Reason of - expired -> per_msg_ttl_header(Content#content.properties); - _ -> [] - end, + PerMsgTTL = per_msg_ttl_header(Content#content.properties), HeadersFun2 = fun (Headers) -> %% The first routing key is the one specified in the -- 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(-) 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 eea4bbf3276853d524a1a93e32b4fb90a45067a8 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Mon, 10 Jun 2013 13:27:39 +0100 Subject: Clear exclusive durable queues on boot --- src/rabbit_amqqueue.erl | 37 +++++++++++++++++++++---------------- src/rabbit_amqqueue_process.erl | 9 ++++++--- 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 8c00c85c..b94f08f6 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -577,14 +577,16 @@ resume(QPid, ChPid) -> delegate:cast(QPid, {resume, ChPid}). flush_all(QPids, ChPid) -> delegate:cast(QPids, {flush, ChPid}). -internal_delete1(QueueName) -> - ok = mnesia:delete({rabbit_queue, QueueName}), - %% this 'guarded' delete prevents unnecessary writes to the mnesia - %% disk log - case mnesia:wread({rabbit_durable_queue, QueueName}) of +%% 'guarded' delete prevents unnecessary writes to the mnesia disk log +guarded_delete(Table, QueueName) -> + case mnesia:wread({Table, QueueName}) of [] -> ok; - [_] -> ok = mnesia:delete({rabbit_durable_queue, QueueName}) - end, + [_] -> ok = mnesia:delete({Table, QueueName}) + end. + +internal_delete1(QueueName) -> + guarded_delete(rabbit_durable_queue, QueueName), + guarded_delete(rabbit_queue, QueueName), %% we want to execute some things, as decided by rabbit_exchange, %% after the transaction. rabbit_binding:remove_for_destination(QueueName). @@ -592,15 +594,18 @@ internal_delete1(QueueName) -> internal_delete(QueueName) -> rabbit_misc:execute_mnesia_tx_with_tail( fun () -> - case mnesia:wread({rabbit_queue, QueueName}) of - [] -> rabbit_misc:const({error, not_found}); - [_] -> Deletions = internal_delete1(QueueName), - T = rabbit_binding:process_deletions(Deletions), - fun() -> - ok = T(), - ok = rabbit_event:notify(queue_deleted, - [{name, QueueName}]) - end + case {mnesia:wread({rabbit_queue, QueueName}), + mnesia:wread({rabbit_durable_queue, QueueName})} of + {[], []} -> + rabbit_misc:const({error, not_found}); + _ -> + Deletions = internal_delete1(QueueName), + T = rabbit_binding:process_deletions(Deletions), + fun() -> + ok = T(), + ok = rabbit_event:notify(queue_deleted, + [{name, QueueName}]) + end end end). diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index d2f4a178..a7a227e1 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1047,17 +1047,20 @@ handle_call({init, Recover}, From, case rabbit_misc:is_process_alive(Owner) of true -> erlang:monitor(process, Owner), declare(Recover, From, State); - false -> #q{backing_queue = BQ, backing_queue_state = undefined, - q = #amqqueue{name = QName} = Q} = State, + false -> #q{backing_queue = undefined, + backing_queue_state = undefined, + q = #amqqueue{name = QName} = Q} = State, gen_server2:reply(From, not_found), case Recover of new -> rabbit_log:warning( "Queue ~p exclusive owner went away~n", [QName]); _ -> ok end, + BQ = backing_queue_module(Q), BQS = bq_init(BQ, Q, Recover), %% Rely on terminate to delete the queue. - {stop, normal, State#q{backing_queue_state = BQS}} + {stop, normal, State#q{backing_queue = BQ, + backing_queue_state = BQS}} end; handle_call(info, _From, State) -> -- cgit v1.2.1 From 87c1aaa45f5d6868c0c0403c05ae21a2ae157bb9 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Mon, 10 Jun 2013 14:42:58 +0100 Subject: Display pretty missing exclusive owner log message --- src/rabbit_amqqueue_process.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index d2f4a178..10813838 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1052,7 +1052,8 @@ handle_call({init, Recover}, From, gen_server2:reply(From, not_found), case Recover of new -> rabbit_log:warning( - "Queue ~p exclusive owner went away~n", [QName]); + "Queue ~p exclusive owner went away~n", + [rabbit_misc:rs(QName)]); _ -> ok end, BQS = bq_init(BQ, Q, Recover), -- cgit v1.2.1 From 1dae0adaa07263f38c65c543bf665c6dd9b39f54 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 10 Jun 2013 16:11:27 +0100 Subject: Simplify (and don't bother parsing to integer only to convert back). --- src/rabbit_amqqueue_process.erl | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index d3e88e8b..3a6759f1 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -922,13 +922,10 @@ make_dead_letter_msg(Msg = #basic_message{content = Content, routing_keys = DeathRoutingKeys, content = Content2}. -per_msg_ttl_header(#'P_basic'{} = Props) -> - case rabbit_basic:parse_expiration(Props) of - {ok, Exp} when is_integer(Exp) -> - [{<<"original-expiration">>, longstr, integer_to_list(Exp)}]; - _ -> - [] - end; +per_msg_ttl_header(#'P_basic'{expiration = undefined}) -> + []; +per_msg_ttl_header(#'P_basic'{expiration = Expiration}) -> + [{<<"original-expiration">>, longstr, Expiration}]; per_msg_ttl_header(_) -> []. -- 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(-) 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 23637ac030e38d705ef00c2687e5e316b89913cc Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Tue, 11 Jun 2013 13:56:54 +0100 Subject: Omit emitting stats for non-existent queues on termination --- src/rabbit_amqqueue_process.erl | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index a7a227e1..45a45dba 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -153,6 +153,8 @@ init_state(Q) -> terminate(shutdown = R, State = #q{backing_queue = BQ}) -> terminate_shutdown(fun (BQS) -> BQ:terminate(R, BQS) end, State); +terminate({shutdown, missing_owner}, State) -> + terminate(missing_owner, State); terminate({shutdown, _} = R, State = #q{backing_queue = BQ}) -> terminate_shutdown(fun (BQS) -> BQ:terminate(R, BQS) end, State); terminate(Reason, State = #q{q = #amqqueue{name = QName}, @@ -161,8 +163,11 @@ terminate(Reason, State = #q{q = #amqqueue{name = QName}, fun (BQS) -> BQS1 = BQ:delete_and_terminate(Reason, BQS), %% don't care if the internal delete doesn't return 'ok'. - rabbit_event:if_enabled(State, #q.stats_timer, - fun() -> emit_stats(State) end), + if Reason =/= missing_owner -> + rabbit_event:if_enabled(State, #q.stats_timer, + fun() -> emit_stats(State) end); + true -> ok + end, rabbit_amqqueue:internal_delete(QName), BQS1 end, State). @@ -1059,8 +1064,8 @@ handle_call({init, Recover}, From, BQ = backing_queue_module(Q), BQS = bq_init(BQ, Q, Recover), %% Rely on terminate to delete the queue. - {stop, normal, State#q{backing_queue = BQ, - backing_queue_state = BQS}} + {stop, {shutdown, missing_owner}, + State#q{backing_queue = BQ, backing_queue_state = BQS}} end; handle_call(info, _From, State) -> -- cgit v1.2.1 From 8cbcb07661c6bd83ec685977df85479bd2d24ee6 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 11 Jun 2013 18:01:33 +0100 Subject: Emit consumer_deleted from the channel when we see a consumer go down, to cover the case where an HA queue master dies and is thus not able to emit the consumer_deleted itself. This means we can emit consumer_deleted twice in some circumstances I think, but that should be fine. --- src/rabbit_channel.erl | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 1de14b5c..22692dcb 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -1198,7 +1198,8 @@ handle_publishing_queue_down(QPid, Reason, State = #ch{unconfirmed = UC}) -> handle_consuming_queue_down(QPid, State = #ch{consumer_mapping = ConsumerMapping, - queue_consumers = QCons}) -> + queue_consumers = QCons, + queue_names = QNames}) -> ConsumerTags = case dict:find(QPid, QCons) of error -> gb_sets:new(); {ok, CTags} -> CTags @@ -1208,6 +1209,11 @@ handle_consuming_queue_down(QPid, ok = send(#'basic.cancel'{consumer_tag = CTag, nowait = true}, State), + rabbit_event:notify( + consumer_deleted, + [{consumer_tag, CTag}, + {channel, self()}, + {queue, dict:fetch(QPid, QNames)}]), dict:erase(CTag, CMap) end, ConsumerMapping, ConsumerTags), State#ch{consumer_mapping = ConsumerMapping1, -- 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(-) 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 16ca31ea7c9ccc8569772e296772fc7d9b453997 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Thu, 13 Jun 2013 13:47:08 +0100 Subject: Refactor --- src/rabbit_amqqueue_process.erl | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 45a45dba..4b0e0b89 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -153,24 +153,26 @@ init_state(Q) -> terminate(shutdown = R, State = #q{backing_queue = BQ}) -> terminate_shutdown(fun (BQS) -> BQ:terminate(R, BQS) end, State); -terminate({shutdown, missing_owner}, State) -> - terminate(missing_owner, State); +terminate({shutdown, missing_owner = R}, State) -> + terminate_shutdown(terminate_delete(false, R, State), State); terminate({shutdown, _} = R, State = #q{backing_queue = BQ}) -> terminate_shutdown(fun (BQS) -> BQ:terminate(R, BQS) end, State); -terminate(Reason, State = #q{q = #amqqueue{name = QName}, - backing_queue = BQ}) -> - terminate_shutdown( - fun (BQS) -> - BQS1 = BQ:delete_and_terminate(Reason, BQS), - %% don't care if the internal delete doesn't return 'ok'. - if Reason =/= missing_owner -> - rabbit_event:if_enabled(State, #q.stats_timer, - fun() -> emit_stats(State) end); - true -> ok - end, - rabbit_amqqueue:internal_delete(QName), - BQS1 - end, State). +terminate(Reason, State) -> + terminate_shutdown(terminate_delete(true, Reason, State), State). + +terminate_delete(EmitStats, Reason, + State = #q{q = #amqqueue{name = QName}, + backing_queue = BQ}) -> + fun (BQS) -> + BQS1 = BQ:delete_and_terminate(Reason, BQS), + %% don't care if the internal delete doesn't return 'ok'. + if EmitStats -> rabbit_event:if_enabled(State, #q.stats_timer, + fun() -> emit_stats(State) end); + true -> ok + end, + rabbit_amqqueue:internal_delete(QName), + BQS1 + end. code_change(_OldVsn, State, _Extra) -> {ok, State}. -- cgit v1.2.1 From 340b36e06ac5697204bedea561305e40b30844ff Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Thu, 13 Jun 2013 15:00:30 +0100 Subject: Comments, gen_server-like termination reason and less unnecessary delete guarding --- src/rabbit_amqqueue.erl | 16 +++++++--------- src/rabbit_amqqueue_process.erl | 5 +++-- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index b94f08f6..e0fbaf77 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -577,16 +577,14 @@ resume(QPid, ChPid) -> delegate:cast(QPid, {resume, ChPid}). flush_all(QPids, ChPid) -> delegate:cast(QPids, {flush, ChPid}). -%% 'guarded' delete prevents unnecessary writes to the mnesia disk log -guarded_delete(Table, QueueName) -> - case mnesia:wread({Table, QueueName}) of - [] -> ok; - [_] -> ok = mnesia:delete({Table, QueueName}) - end. - internal_delete1(QueueName) -> - guarded_delete(rabbit_durable_queue, QueueName), - guarded_delete(rabbit_queue, QueueName), + ok = mnesia:delete({rabbit_queue, QueueName}), + %% this 'guarded' delete prevents unnecessary writes to the mnesia + %% disk log + case mnesia:wread({rabbit_durable_queue, QueueName}) of + [] -> ok; + [_] -> ok = mnesia:delete({rabbit_durable_queue, QueueName}) + end, %% we want to execute some things, as decided by rabbit_exchange, %% after the transaction. rabbit_binding:remove_for_destination(QueueName). diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 4b0e0b89..7cc4a51a 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -153,8 +153,9 @@ init_state(Q) -> terminate(shutdown = R, State = #q{backing_queue = BQ}) -> terminate_shutdown(fun (BQS) -> BQ:terminate(R, BQS) end, State); -terminate({shutdown, missing_owner = R}, State) -> - terminate_shutdown(terminate_delete(false, R, State), State); +terminate({shutdown, missing_owner} = Reason, State) -> + %% if the owner was missing then there will be no queue, so don't emit stats + terminate_shutdown(terminate_delete(false, Reason, State), State); terminate({shutdown, _} = R, State = #q{backing_queue = BQ}) -> terminate_shutdown(fun (BQS) -> BQ:terminate(R, BQS) end, State); terminate(Reason, State) -> -- cgit v1.2.1 From 7429e0b61e735c1e5498b18a97c33dd474607c5c Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Thu, 13 Jun 2013 15:06:44 +0100 Subject: Adjust comment location --- src/rabbit_amqqueue_process.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 7cc4a51a..49cf9764 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -166,11 +166,11 @@ terminate_delete(EmitStats, Reason, backing_queue = BQ}) -> fun (BQS) -> BQS1 = BQ:delete_and_terminate(Reason, BQS), - %% don't care if the internal delete doesn't return 'ok'. if EmitStats -> rabbit_event:if_enabled(State, #q.stats_timer, fun() -> emit_stats(State) end); true -> ok end, + %% don't care if the internal delete doesn't return 'ok'. rabbit_amqqueue:internal_delete(QName), BQS1 end. -- cgit v1.2.1 From 9fd4a06fcb563df24689c3e87b8d38e6242c230a Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Mon, 17 Jun 2013 14:01:51 +0100 Subject: Check value when clients update flying_ets --- src/rabbit_msg_store.erl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/rabbit_msg_store.erl b/src/rabbit_msg_store.erl index c63321b5..35c0239f 100644 --- a/src/rabbit_msg_store.erl +++ b/src/rabbit_msg_store.erl @@ -627,7 +627,10 @@ client_update_flying(Diff, MsgId, #client_msstate { flying_ets = FlyingEts, Key = {MsgId, CRef}, case ets:insert_new(FlyingEts, {Key, Diff}) of true -> ok; - false -> try ets:update_counter(FlyingEts, Key, {2, Diff}) + false -> try ets:update_counter(FlyingEts, Key, {2, Diff}) of + 0 -> ok; + Diff -> ok; + Err -> throw({unexpected_flying_ets_counter, Diff, Err}) catch error:badarg -> %% this is guaranteed to succeed since the %% server only removes and updates flying_ets -- cgit v1.2.1 From 484f2158f0c17c4833951e1c7d693a347a1891eb Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Mon, 17 Jun 2013 16:34:23 +0100 Subject: rabbit_node_monitor should depend on rabbit_alarm --- src/rabbit.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 450a7f32..b3fb98af 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -123,7 +123,7 @@ [{description, "node monitor"}, {mfa, {rabbit_sup, start_restartable_child, [rabbit_node_monitor]}}, - {requires, kernel_ready}, + {requires, rabbit_alarm}, {enables, core_initialized}]}). -rabbit_boot_step({core_initialized, -- cgit v1.2.1 From 5f0614d143470458910ca9c262599e6f24fcf1d3 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 17 Jun 2013 16:38:59 +0100 Subject: Throw the key too. And if the server side still fails try to fail with more information. --- src/rabbit_msg_store.erl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/rabbit_msg_store.erl b/src/rabbit_msg_store.erl index 35c0239f..7c2aa13c 100644 --- a/src/rabbit_msg_store.erl +++ b/src/rabbit_msg_store.erl @@ -630,7 +630,7 @@ client_update_flying(Diff, MsgId, #client_msstate { flying_ets = FlyingEts, false -> try ets:update_counter(FlyingEts, Key, {2, Diff}) of 0 -> ok; Diff -> ok; - Err -> throw({unexpected_flying_ets_counter, Diff, Err}) + Err -> throw({bad_flying_ets_update, Diff, Err, Key}) catch error:badarg -> %% this is guaranteed to succeed since the %% server only removes and updates flying_ets @@ -983,7 +983,8 @@ update_flying(Diff, MsgId, CRef, #msstate { flying_ets = FlyingEts }) -> true = ets:delete_object(FlyingEts, {Key, 0}), process; [{_, 0}] -> true = ets:delete_object(FlyingEts, {Key, 0}), - ignore + ignore; + [{_, Err}] -> throw({bad_flying_ets_record, Diff, Err, Key}) end. write_action({true, not_found}, _MsgId, State) -> -- cgit v1.2.1 From b106047a4df6a5d230097a4ada549be69fade0c7 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 17 Jun 2013 18:04:06 +0100 Subject: cosmetic: state the not-immediatly-obvious --- src/rabbit_msg_store.erl | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/rabbit_msg_store.erl b/src/rabbit_msg_store.erl index 7c2aa13c..b783aa18 100644 --- a/src/rabbit_msg_store.erl +++ b/src/rabbit_msg_store.erl @@ -978,7 +978,7 @@ update_flying(Diff, MsgId, CRef, #msstate { flying_ets = FlyingEts }) -> NDiff = -Diff, case ets:lookup(FlyingEts, Key) of [] -> ignore; - [{_, Diff}] -> ignore; + [{_, Diff}] -> ignore; %% [1] [{_, NDiff}] -> ets:update_counter(FlyingEts, Key, {2, Diff}), true = ets:delete_object(FlyingEts, {Key, 0}), process; @@ -986,6 +986,13 @@ update_flying(Diff, MsgId, CRef, #msstate { flying_ets = FlyingEts }) -> ignore; [{_, Err}] -> throw({bad_flying_ets_record, Diff, Err, Key}) end. +%% [1] We can get here, for example, in the following scenario: There +%% is a write followed by a remove in flight. The counter will be 0, +%% so on processing the write the server attempts to delete the +%% entry. If at that point the client injects another write it will +%% either insert a new entry, containing +1, or increment the existing +%% entry to +1, thus preventing its removal. Either way therefore when +%% the server processes the read, the counter will be +1. write_action({true, not_found}, _MsgId, State) -> {ignore, undefined, State}; -- 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. --- ebin/rabbit_app.in | 1 + src/rabbit_memory_monitor.erl | 21 ++++----------------- 2 files changed, 5 insertions(+), 17 deletions(-) diff --git a/ebin/rabbit_app.in b/ebin/rabbit_app.in index 339fa69e..b28214af 100644 --- a/ebin/rabbit_app.in +++ b/ebin/rabbit_app.in @@ -19,6 +19,7 @@ {ssl_listeners, []}, {ssl_options, []}, {vm_memory_high_watermark, 0.4}, + {vm_memory_paging_watermark, 0.2}, {disk_free_limit, 1000000000}, %% 1GB {msg_store_index_module, rabbit_msg_store_ets_index}, {backing_queue_module, rabbit_variable_queue}, 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. --- ebin/rabbit_app.in | 2 +- src/rabbit_memory_monitor.erl | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ebin/rabbit_app.in b/ebin/rabbit_app.in index b28214af..46ed24f9 100644 --- a/ebin/rabbit_app.in +++ b/ebin/rabbit_app.in @@ -19,7 +19,7 @@ {ssl_listeners, []}, {ssl_options, []}, {vm_memory_high_watermark, 0.4}, - {vm_memory_paging_watermark, 0.2}, + {vm_memory_high_watermark_paging_ratio, 0.5}, {disk_free_limit, 1000000000}, %% 1GB {msg_store_index_module, rabbit_msg_store_ets_index}, {backing_queue_module, rabbit_variable_queue}, 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(-) 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(-) 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(-) 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(-) 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 92edfb501c0bd34f58e6ce9a8a4f7c9d13ddacd7 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 20 Jun 2013 14:48:31 +0100 Subject: Do not "recover" queues if there is already a rabbit_queue record for them. If we are restarting quickly it could be the case that we have a slave alive that has not failed over yet. Don't usurp it. --- src/rabbit_amqqueue.erl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index e0fbaf77..84b6f35a 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -219,8 +219,12 @@ find_durable_queues() -> %% TODO: use dirty ops instead rabbit_misc:execute_mnesia_transaction( fun () -> - qlc:e(qlc:q([Q || Q = #amqqueue{pid = Pid} + qlc:e(qlc:q([Q || Q = #amqqueue{name = Name, + pid = Pid} <- mnesia:table(rabbit_durable_queue), + #amqqueue{name = Name2} + <- mnesia:table(rabbit_queue), + Name =:= Name2, node(Pid) == Node])) end). -- cgit v1.2.1 From b72670200638d5848aad710bcf3396eeee9c7fc0 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 20 Jun 2013 15:25:25 +0100 Subject: Umm, be less than 100% wrong. --- src/rabbit_amqqueue.erl | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 84b6f35a..74fd70ae 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -222,9 +222,7 @@ find_durable_queues() -> qlc:e(qlc:q([Q || Q = #amqqueue{name = Name, pid = Pid} <- mnesia:table(rabbit_durable_queue), - #amqqueue{name = Name2} - <- mnesia:table(rabbit_queue), - Name =:= Name2, + mnesia:read(rabbit_queue, Name, read) =:= [], node(Pid) == Node])) end). -- 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(-) 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 08f8f95412c45cf98a7a823e3c1eb30c504c4b68 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 20 Jun 2013 16:50:55 +0100 Subject: Start asn1 too. --- src/rabbit_networking.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_networking.erl b/src/rabbit_networking.erl index 4b6c7538..702df040 100644 --- a/src/rabbit_networking.erl +++ b/src/rabbit_networking.erl @@ -145,7 +145,7 @@ start() -> rabbit_sup:start_supervisor_child( {rabbit_connection_sup,start_link,[]}]). ensure_ssl() -> - ok = app_utils:start_applications([crypto, public_key, ssl]), + ok = app_utils:start_applications([asn1, crypto, public_key, ssl]), {ok, SslOptsConfig} = application:get_env(rabbit, ssl_options), % unknown_ca errors are silently ignored prior to R14B unless we -- 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(-) 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(-) 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 0c3f3e71f9d41361f8afcd903c54db18a5d8385e Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 21 Jun 2013 11:26:15 +0100 Subject: Don't persist gm_pids; by definition when all we have is the rabbit_durable_queue record the cluster has gone down completely so there can't be any pids. Fixes gm_pids leak. Since we're here, fix a similar but less severe leak in sync_slave_pids. (Less severe because rabbit_mirror_queue_misc:store_updated_slaves/1 has the effect of cleaning it up periodically. But it's still wrong.) --- src/rabbit_amqqueue.erl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index e0fbaf77..ef5dd103 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -282,7 +282,10 @@ update(Name, Fun) -> end. store_queue(Q = #amqqueue{durable = true}) -> - ok = mnesia:write(rabbit_durable_queue, Q#amqqueue{slave_pids = []}, write), + ok = mnesia:write(rabbit_durable_queue, + Q#amqqueue{slave_pids = [], + sync_slave_pids = [], + gm_pids = []}, write), ok = mnesia:write(rabbit_queue, Q, write), ok; store_queue(Q = #amqqueue{durable = false}) -> -- cgit v1.2.1 From 85953f05729171ae2a6a3285ca0bb63e353d6654 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 21 Jun 2013 11:28:32 +0100 Subject: Add a TODO for if we are ever unbusy enough to look at removing this code. --- src/rabbit_mirror_queue_misc.erl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 5607bfa9..cb91b650 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -217,6 +217,8 @@ report_deaths(MirrorPid, IsMaster, QueueName, DeadPids) -> store_updated_slaves(Q = #amqqueue{slave_pids = SPids, sync_slave_pids = SSPids}) -> + %% TODO now that we clear sync_slave_pids in rabbit_durable_queue, + %% do we still need this? SSPids1 = [SSPid || SSPid <- SSPids, lists:member(SSPid, SPids)], Q1 = Q#amqqueue{sync_slave_pids = SSPids1}, ok = rabbit_amqqueue:store_queue(Q1), -- cgit v1.2.1 From 0371265eb754b0e7b364d1a5d9e3c7624f59f91f Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 21 Jun 2013 15:48:50 +0100 Subject: Make it clearer what I meant. --- src/rabbit_mirror_queue_misc.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index cb91b650..8a663824 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -218,7 +218,7 @@ report_deaths(MirrorPid, IsMaster, QueueName, DeadPids) -> store_updated_slaves(Q = #amqqueue{slave_pids = SPids, sync_slave_pids = SSPids}) -> %% TODO now that we clear sync_slave_pids in rabbit_durable_queue, - %% do we still need this? + %% do we still need this filtering? SSPids1 = [SSPid || SSPid <- SSPids, lists:member(SSPid, SPids)], Q1 = Q#amqqueue{sync_slave_pids = SSPids1}, ok = rabbit_amqqueue:store_queue(Q1), -- cgit v1.2.1 From dfade0b0a301eaa921d5a1896b4d409d10e9c25f Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 22 Jun 2013 01:12:20 +0100 Subject: prevent invalid state transition from 'dormant' to 'active' w/o registration This was very hard to trigger, but nevertheless possible. --- src/rabbit_limiter.erl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index 1e32f95a..6a09573c 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -255,7 +255,9 @@ safe_call(Pid, Msg, ExitValue) -> fun () -> ExitValue end, fun () -> gen_server2:call(Pid, Msg, infinity) end). -resume(L) -> L#qstate{state = active}. +resume(L = #qstate{state = suspended}) -> + L#qstate{state = active}; +resume(L) -> L. deactivate(L = #qstate{state = dormant}) -> L; deactivate(L) -> -- cgit v1.2.1 From 0fe5ab725681a29beaa3f2183aca8bcb8bfb8bfc Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Mon, 24 Jun 2013 13:14:46 +0100 Subject: Updated changelogs for 3.1.2 --- packaging/RPMS/Fedora/rabbitmq-server.spec | 3 +++ packaging/debs/Debian/debian/changelog | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/packaging/RPMS/Fedora/rabbitmq-server.spec b/packaging/RPMS/Fedora/rabbitmq-server.spec index 607719cf..2e6040ca 100644 --- a/packaging/RPMS/Fedora/rabbitmq-server.spec +++ b/packaging/RPMS/Fedora/rabbitmq-server.spec @@ -123,6 +123,9 @@ done rm -rf %{buildroot} %changelog +* Mon Jun 24 2013 tim@rabbitmq.com 3.1.2-1 +- New Upstream Release + * Mon May 20 2013 tim@rabbitmq.com 3.1.1-1 - Test release diff --git a/packaging/debs/Debian/debian/changelog b/packaging/debs/Debian/debian/changelog index 8dffefb0..404ae616 100644 --- a/packaging/debs/Debian/debian/changelog +++ b/packaging/debs/Debian/debian/changelog @@ -1,3 +1,9 @@ +rabbitmq-server (3.1.2-1) unstable; urgency=low + + * New Upstream Release + + -- Tim Watson Mon, 24 Jun 2013 11:16:41 +0100 + rabbitmq-server (3.1.1-1) unstable; urgency=low * Test release -- cgit v1.2.1 -- cgit v1.2.1 From 9d8c38f818676f0bb0615e3a9fce4ad8f4cab126 Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Mon, 24 Jun 2013 17:36:38 +0400 Subject: Ignore *.orig files --- .hgignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgignore b/.hgignore index 912b4a56..cd017298 100644 --- a/.hgignore +++ b/.hgignore @@ -3,6 +3,7 @@ syntax: glob *~ *.swp *.patch +*.orig erl_crash.dump deps.mk -- 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(-) 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(-) 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 dfe2579cfe2dfb454cfa03536dea77e92c7c2e4f Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 25 Jun 2013 16:35:24 +0100 Subject: Delegate monitoring, with a fairly glaring hole. --- src/delegate.erl | 66 +++++++++++++++++++++++++++--------- src/dmon.erl | 70 +++++++++++++++++++++++++++++++++++++++ src/rabbit_mirror_queue_slave.erl | 14 ++++---- 3 files changed, 128 insertions(+), 22 deletions(-) create mode 100644 src/dmon.erl diff --git a/src/delegate.erl b/src/delegate.erl index e833b819..a205f2f1 100644 --- a/src/delegate.erl +++ b/src/delegate.erl @@ -18,11 +18,14 @@ -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/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}). + %%---------------------------------------------------------------------------- -ifdef(use_specs). @@ -35,6 +38,9 @@ [{pid(), term()}]}). -spec(invoke_no_result/2 :: (pid() | [pid()], fun ((pid()) -> any())) -> 'ok'). +-spec(monitor/1 :: (pid()) -> reference()). +-spec(demonitor/2 :: (pid(), reference()) -> 'true'). + -spec(call/2 :: ( pid(), any()) -> any(); ([pid()], any()) -> {[{pid(), any()}], [{pid(), term()}]}). @@ -78,7 +84,7 @@ invoke(Pids, Fun) when is_list(Pids) -> case orddict:fetch_keys(Grouped) of [] -> {[], []}; RemoteNodes -> gen_server2:multi_call( - RemoteNodes, delegate(RemoteNodes), + RemoteNodes, delegate(self(), RemoteNodes), {invoke, Fun, Grouped}, infinity) end, BadPids = [{Pid, {exit, {nodedown, BadNode}, []}} || @@ -106,12 +112,23 @@ invoke_no_result(Pids, Fun) 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, Fun, Grouped}) + RemoteNodes -> gen_server2:abcast( + RemoteNodes, delegate(self(), RemoteNodes), + {invoke, Fun, Grouped}) end, safe_invoke(LocalPids, Fun), %% must not die ok. +monitor(Pid) -> + Node = node(Pid), + Name = delegate(Pid, [Node]), + gen_server2:call({Name, Node}, {monitor, self(), Pid}, infinity). + +demonitor(Pid, Ref) -> + Node = node(Pid), + Name = delegate(Pid, [Node]), + gen_server2:call({Name, Node}, {demonitor, Ref}, infinity). + call(PidOrPids, Msg) -> invoke(PidOrPids, fun (P) -> gen_server2:call(P, Msg, infinity) end). @@ -134,10 +151,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; @@ -156,21 +173,40 @@ safe_invoke(Pid, Fun) when is_pid(Pid) -> %%---------------------------------------------------------------------------- init([]) -> - {ok, node(), hibernate, + {ok, #state{node = node(), monitors = dict:new()}, hibernate, {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}. -handle_call({invoke, Fun, Grouped}, _From, Node) -> - {reply, safe_invoke(orddict:fetch(Node, Grouped), Fun), Node, hibernate}. +handle_call({invoke, Fun, Grouped}, _From, State = #state{node = Node}) -> + {reply, safe_invoke(orddict:fetch(Node, Grouped), Fun), State, hibernate}; -handle_cast({invoke, Fun, Grouped}, Node) -> +handle_call({monitor, WantsMonitor, ToMonitor}, _From, + State = #state{monitors = Monitors}) -> + Ref = erlang:monitor(process, ToMonitor), + State1 = State#state{monitors = dict:store(Ref, WantsMonitor, Monitors)}, + {reply, Ref, State1, hibernate}; + +handle_call({demonitor, Ref}, _From, State = #state{monitors = Monitors}) -> + %% We need to ensure we don't then respond to a 'DOWN' that's + %% currently in our mailbox - if we did then our client might then + %% get a 'DOWN' after demonitor() returns. + State1 = State#state{monitors = dict:erase(Ref, Monitors)}, + {reply, erlang:demonitor(Ref, [flush]), State1, hibernate}. + +handle_cast({invoke, Fun, Grouped}, State = #state{node = Node}) -> safe_invoke(orddict:fetch(Node, Grouped), Fun), - {noreply, Node, hibernate}. + {noreply, State, hibernate}. + +handle_info({'DOWN', Ref, process, Object, Info}, + State = #state{monitors = Monitors}) -> + WantsMonitor = dict:fetch(Ref, Monitors), + WantsMonitor ! {'DOWN', Ref, process, Object, Info}, + {noreply, State#state{monitors = dict:erase(Ref, Monitors)}, 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/dmon.erl b/src/dmon.erl new file mode 100644 index 00000000..dfb420c3 --- /dev/null +++ b/src/dmon.erl @@ -0,0 +1,70 @@ +%% 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 VMware, Inc. +%% Copyright (c) 2011-2013 VMware, Inc. All rights reserved. +%% + +-module(dmon). + +-export([new/0, monitor/2, monitor_all/2, demonitor/2, is_monitored/2, erase/2, + monitored/1, is_empty/1]). + +-compile({no_auto_import, [monitor/2]}). + +-ifdef(use_specs). + +%%---------------------------------------------------------------------------- + +-export_type([?MODULE/0]). + +-opaque(?MODULE() :: dict()). + +-type(item() :: pid() | {atom(), node()}). + +-spec(new/0 :: () -> ?MODULE()). +-spec(monitor/2 :: (item(), ?MODULE()) -> ?MODULE()). +-spec(monitor_all/2 :: ([item()], ?MODULE()) -> ?MODULE()). +-spec(demonitor/2 :: (item(), ?MODULE()) -> ?MODULE()). +-spec(is_monitored/2 :: (item(), ?MODULE()) -> boolean()). +-spec(erase/2 :: (item(), ?MODULE()) -> ?MODULE()). +-spec(monitored/1 :: (?MODULE()) -> [item()]). +-spec(is_empty/1 :: (?MODULE()) -> boolean()). + +-endif. + +new() -> dict:new(). + +monitor(Item, M) -> + case dict:is_key(Item, M) of + true -> M; + false -> dict:store(Item, delegate:monitor(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). + +demonitor(Item, M) -> + case dict:find(Item, M) of + {ok, MRef} -> delegate:demonitor(Item, MRef), + dict:erase(Item, M); + error -> M + end. + +is_monitored(Item, M) -> dict:is_key(Item, M). + +erase(Item, M) -> dict:erase(Item, M). + +monitored(M) -> dict:fetch_keys(M). + +is_empty(M) -> dict:size(M) == 0. diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 964b0eb4..ca9418c6 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -120,7 +120,7 @@ init(Q = #amqqueue { name = QName }) -> msg_id_ack = dict:new(), msg_id_status = dict:new(), - known_senders = pmon:new(), + known_senders = dmon:new(), depth_delta = undefined }, @@ -488,7 +488,7 @@ promote_me(From, #state { q = Q = #amqqueue { name = QName }, %% Everything that we're monitoring, we need to ensure our new %% coordinator is monitoring. - MPids = pmon:monitored(KS), + MPids = dmon:monitored(KS), ok = rabbit_mirror_queue_coordinator:ensure_monitoring(CPid, MPids), %% We find all the messages that we've received from channels but @@ -602,14 +602,14 @@ ensure_rate_timer(State) -> 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) }. + State #state { known_senders = dmon:monitor(ChPid, KS) }. local_sender_death(ChPid, State = #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 %% essay). - ok = case pmon:is_monitored(ChPid, KS) of + ok = case dmon:is_monitored(ChPid, KS) of false -> ok; true -> confirm_sender_death(ChPid) end, @@ -628,7 +628,7 @@ confirm_sender_death(Pid) -> %% See comment in local_sender_death/2; we might have %% received a sender_death in the meanwhile so check %% again. - ok = case pmon:is_monitored(Pid, KS) of + ok = case dmon:is_monitored(Pid, KS) of false -> ok; true -> gm:broadcast(GM, {ensure_monitoring, [Pid]}), confirm_sender_death(Pid) @@ -776,7 +776,7 @@ process_instruction({sender_death, ChPid}, %% 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 + {ok, case dmon:is_monitored(ChPid, KS) of false -> State; true -> MS1 = case dict:find(ChPid, SQ) of error -> @@ -788,7 +788,7 @@ process_instruction({sender_death, ChPid}, credit_flow:peer_down(ChPid), State #state { sender_queues = dict:erase(ChPid, SQ), msg_id_status = MS1, - known_senders = pmon:demonitor(ChPid, KS) } + known_senders = dmon:demonitor(ChPid, KS) } end}; process_instruction({depth, Depth}, State = #state { backing_queue = BQ, -- cgit v1.2.1 From cf7d449b3df465a77921af7a50e52ee4e9d1c77c Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 25 Jun 2013 17:43:57 +0100 Subject: A bit more faff, to deal with genuinely dying nodes. --- src/dmon.erl | 43 ++++++++++++++++++++++++++++----------- src/rabbit_mirror_queue_slave.erl | 15 ++++++++++---- 2 files changed, 42 insertions(+), 16 deletions(-) diff --git a/src/dmon.erl b/src/dmon.erl index dfb420c3..3f89c83a 100644 --- a/src/dmon.erl +++ b/src/dmon.erl @@ -16,8 +16,8 @@ -module(dmon). --export([new/0, monitor/2, monitor_all/2, demonitor/2, is_monitored/2, erase/2, - monitored/1, is_empty/1]). +-export([new/0, monitor/2, monitor_all/2, demonitor/2, is_monitored/2, + monitored/1, monitored/2, is_empty/1]). -compile({no_auto_import, [monitor/2]}). @@ -36,8 +36,8 @@ -spec(monitor_all/2 :: ([item()], ?MODULE()) -> ?MODULE()). -spec(demonitor/2 :: (item(), ?MODULE()) -> ?MODULE()). -spec(is_monitored/2 :: (item(), ?MODULE()) -> boolean()). --spec(erase/2 :: (item(), ?MODULE()) -> ?MODULE()). -spec(monitored/1 :: (?MODULE()) -> [item()]). +-spec(monitored/2 :: (node(), ?MODULE()) -> [item()]). -spec(is_empty/1 :: (?MODULE()) -> boolean()). -endif. @@ -45,9 +45,14 @@ new() -> dict:new(). monitor(Item, M) -> - case dict:is_key(Item, M) of + N = case dict:find(node(Item), M) of + {ok, N0} -> N0; + error -> dict:new() + end, + case dict:is_key(Item, N) of true -> M; - false -> dict:store(Item, delegate:monitor(Item), M) + false -> N2 = dict:store(Item, delegate:monitor(Item), N), + dict:store(node(Item), N2, M) end. monitor_all([], M) -> M; %% optimisation @@ -55,16 +60,30 @@ monitor_all([Item], M) -> monitor(Item, M); %% optimisation monitor_all(Items, M) -> lists:foldl(fun monitor/2, M, Items). demonitor(Item, M) -> - case dict:find(Item, M) of - {ok, MRef} -> delegate:demonitor(Item, MRef), - dict:erase(Item, M); - error -> M + Node = node(Item), + case dict:find(Node, M) of + {ok, N} -> case dict:find(Item, N) of + {ok, MRef} -> delegate:demonitor(Item, MRef), + N2 = dict:erase(Item, N), + case dict:size(N2) of + 0 -> erlang:monitor_node(Node, false), + dict:erase(Node, M); + _ -> dict:store(Node, N2, M) + end; + error -> M + end; + error -> M end. -is_monitored(Item, M) -> dict:is_key(Item, M). +is_monitored(Item, M) -> dict:is_key(node(Item), M) andalso + dict:is_key(Item, dict:fetch(node(Item), M)). -erase(Item, M) -> dict:erase(Item, M). +monitored(M) -> lists:flatten([dict:fetch_keys(dict:fetch(Node, M)) || + Node <- dict:fetch_keys(M)]). -monitored(M) -> dict:fetch_keys(M). +monitored(Node, M) -> case dict:find(Node, M) of + {ok, N} -> dict:fetch_keys(N); + error -> [] + end. is_empty(M) -> dict:size(M) == 0. diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index ca9418c6..b4c01440 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -273,7 +273,12 @@ 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({node_down, Node}, State) -> + local_sender_node_death(Node, State), + noreply(State); handle_info({'EXIT', _Pid, Reason}, State) -> {stop, Reason, State}; @@ -604,7 +609,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 = dmon: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 @@ -612,8 +617,10 @@ local_sender_death(ChPid, State = #state { known_senders = KS }) -> ok = case dmon:is_monitored(ChPid, KS) of false -> ok; true -> confirm_sender_death(ChPid) - end, - State. + end. + +local_sender_node_death(Node, State = #state { known_senders = KS }) -> + [local_sender_death(ChPid, State) || ChPid <- dmon:monitored(Node, KS)]. confirm_sender_death(Pid) -> %% We have to deal with the possibility that we'll be promoted to -- cgit v1.2.1 From a222f45cb5c56ed51a2f40fa00f99cc5b3b49e90 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Wed, 26 Jun 2013 12:34:16 +0100 Subject: Discard previously-discarded messages correctly --- src/rabbit_amqqueue_process.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index c790a12d..3e3de10b 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -551,7 +551,7 @@ attempt_delivery(Delivery = #delivery{sender = SenderPid, message = Message}, {published, BQS1} -> {true, State#q{backing_queue_state = BQS1}}; {discarded, BQS1} -> - {false, State#q{backing_queue_state = BQS1}} + {true, discard(Delivery, State#q{backing_queue_state = BQS1})} end. deliver_or_enqueue(Delivery = #delivery{message = Message, sender = SenderPid}, -- cgit v1.2.1 From 532b7e702b5be35c5507685337db86f5f9554a9c Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Wed, 26 Jun 2013 12:39:28 +0100 Subject: update package changelogs for 3.1.3 --- packaging/RPMS/Fedora/rabbitmq-server.spec | 3 +++ packaging/debs/Debian/debian/changelog | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/packaging/RPMS/Fedora/rabbitmq-server.spec b/packaging/RPMS/Fedora/rabbitmq-server.spec index 2e6040ca..028b4ec2 100644 --- a/packaging/RPMS/Fedora/rabbitmq-server.spec +++ b/packaging/RPMS/Fedora/rabbitmq-server.spec @@ -123,6 +123,9 @@ done rm -rf %{buildroot} %changelog +* Tue Jun 25 2013 tim@rabbitmq.com 3.1.3-1 +- New Upstream Release + * Mon Jun 24 2013 tim@rabbitmq.com 3.1.2-1 - New Upstream Release diff --git a/packaging/debs/Debian/debian/changelog b/packaging/debs/Debian/debian/changelog index 404ae616..fda29e7d 100644 --- a/packaging/debs/Debian/debian/changelog +++ b/packaging/debs/Debian/debian/changelog @@ -1,3 +1,9 @@ +rabbitmq-server (3.1.3-1) unstable; urgency=low + + * New Upstream Release + + -- Tim Watson Tue, 25 Jun 2013 15:01:12 +0100 + rabbitmq-server (3.1.2-1) unstable; urgency=low * New Upstream Release -- cgit v1.2.1 -- cgit v1.2.1 From 09400b8b088aebe3265fcafc5314e77c3d416625 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 26 Jun 2013 12:59:33 +0100 Subject: Do the monitoring on the correct node(!) and remove all that nonsense about node-wide monitoring. --- src/delegate.erl | 8 ++++++-- src/dmon.erl | 43 +++++++++++---------------------------- src/rabbit_mirror_queue_slave.erl | 7 ------- 3 files changed, 18 insertions(+), 40 deletions(-) diff --git a/src/delegate.erl b/src/delegate.erl index a205f2f1..475b087f 100644 --- a/src/delegate.erl +++ b/src/delegate.erl @@ -119,15 +119,19 @@ invoke_no_result(Pids, Fun) when is_list(Pids) -> safe_invoke(LocalPids, Fun), %% must not die ok. +monitor(Pid) when node(Pid) =:= node() -> + erlang:monitor(process, Pid); monitor(Pid) -> Node = node(Pid), Name = delegate(Pid, [Node]), - gen_server2:call({Name, Node}, {monitor, self(), Pid}, infinity). + gen_server2:call(Name, {monitor, self(), Pid}, infinity). +demonitor(Pid, Ref) when node(Pid) =:= node() -> + erlang:demonitor(Ref, [flush]); demonitor(Pid, Ref) -> Node = node(Pid), Name = delegate(Pid, [Node]), - gen_server2:call({Name, Node}, {demonitor, Ref}, infinity). + gen_server2:call(Name, {demonitor, Ref}, infinity). call(PidOrPids, Msg) -> invoke(PidOrPids, fun (P) -> gen_server2:call(P, Msg, infinity) end). diff --git a/src/dmon.erl b/src/dmon.erl index 3f89c83a..dfb420c3 100644 --- a/src/dmon.erl +++ b/src/dmon.erl @@ -16,8 +16,8 @@ -module(dmon). --export([new/0, monitor/2, monitor_all/2, demonitor/2, is_monitored/2, - monitored/1, monitored/2, is_empty/1]). +-export([new/0, monitor/2, monitor_all/2, demonitor/2, is_monitored/2, erase/2, + monitored/1, is_empty/1]). -compile({no_auto_import, [monitor/2]}). @@ -36,8 +36,8 @@ -spec(monitor_all/2 :: ([item()], ?MODULE()) -> ?MODULE()). -spec(demonitor/2 :: (item(), ?MODULE()) -> ?MODULE()). -spec(is_monitored/2 :: (item(), ?MODULE()) -> boolean()). +-spec(erase/2 :: (item(), ?MODULE()) -> ?MODULE()). -spec(monitored/1 :: (?MODULE()) -> [item()]). --spec(monitored/2 :: (node(), ?MODULE()) -> [item()]). -spec(is_empty/1 :: (?MODULE()) -> boolean()). -endif. @@ -45,14 +45,9 @@ new() -> dict:new(). monitor(Item, M) -> - N = case dict:find(node(Item), M) of - {ok, N0} -> N0; - error -> dict:new() - end, - case dict:is_key(Item, N) of + case dict:is_key(Item, M) of true -> M; - false -> N2 = dict:store(Item, delegate:monitor(Item), N), - dict:store(node(Item), N2, M) + false -> dict:store(Item, delegate:monitor(Item), M) end. monitor_all([], M) -> M; %% optimisation @@ -60,30 +55,16 @@ monitor_all([Item], M) -> monitor(Item, M); %% optimisation monitor_all(Items, M) -> lists:foldl(fun monitor/2, M, Items). demonitor(Item, M) -> - Node = node(Item), - case dict:find(Node, M) of - {ok, N} -> case dict:find(Item, N) of - {ok, MRef} -> delegate:demonitor(Item, MRef), - N2 = dict:erase(Item, N), - case dict:size(N2) of - 0 -> erlang:monitor_node(Node, false), - dict:erase(Node, M); - _ -> dict:store(Node, N2, M) - end; - error -> M - end; - error -> M + case dict:find(Item, M) of + {ok, MRef} -> delegate:demonitor(Item, MRef), + dict:erase(Item, M); + error -> M end. -is_monitored(Item, M) -> dict:is_key(node(Item), M) andalso - dict:is_key(Item, dict:fetch(node(Item), M)). +is_monitored(Item, M) -> dict:is_key(Item, M). -monitored(M) -> lists:flatten([dict:fetch_keys(dict:fetch(Node, M)) || - Node <- dict:fetch_keys(M)]). +erase(Item, M) -> dict:erase(Item, M). -monitored(Node, M) -> case dict:find(Node, M) of - {ok, N} -> dict:fetch_keys(N); - error -> [] - end. +monitored(M) -> dict:fetch_keys(M). is_empty(M) -> dict:size(M) == 0. diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index b4c01440..ac3e0df0 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -276,10 +276,6 @@ handle_info({'DOWN', _MonitorRef, process, ChPid, _Reason}, State) -> local_sender_death(ChPid, State), noreply(State); -handle_info({node_down, Node}, State) -> - local_sender_node_death(Node, State), - noreply(State); - handle_info({'EXIT', _Pid, Reason}, State) -> {stop, Reason, State}; @@ -619,9 +615,6 @@ local_sender_death(ChPid, #state { known_senders = KS }) -> true -> confirm_sender_death(ChPid) end. -local_sender_node_death(Node, State = #state { known_senders = KS }) -> - [local_sender_death(ChPid, State) || ChPid <- dmon:monitored(Node, KS)]. - confirm_sender_death(Pid) -> %% We have to deal with the possibility that we'll be promoted to %% master before this thing gets run. Consequently we set the -- cgit v1.2.1 From de49b4f0f9d28f91b05e6f179cd7e63506936854 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 27 Jun 2013 12:40:07 +0100 Subject: Remove unneeded assertion. --- src/rabbit_variable_queue.erl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 5b39c2c6..73ab64bf 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -546,8 +546,7 @@ publish_delivered(Msg = #basic_message { is_persistent = IsPersistent, id = MsgId }, MsgProps = #message_properties { needs_confirming = NeedsConfirming }, - _ChPid, State = #vqstate { len = 0, - next_seq_id = SeqId, + _ChPid, State = #vqstate { next_seq_id = SeqId, out_counter = OutCount, in_counter = InCount, persistent_count = PCount, -- cgit v1.2.1 From 0178750cc6018eaa1d8929016e8c5ff988eab541 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 27 Jun 2013 12:49:10 +0100 Subject: Recreate that assertion up one level in the call stack, since the callers may still care about it even though VQ itself does not. --- src/rabbit_amqqueue_process.erl | 1 + src/rabbit_mirror_queue_slave.erl | 1 + 2 files changed, 2 insertions(+) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index c790a12d..f71e28e1 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -540,6 +540,7 @@ attempt_delivery(Delivery = #delivery{sender = SenderPid, message = Message}, {false, BQS1} -> deliver_msgs_to_consumers( fun (true, State1 = #q{backing_queue_state = BQS2}) -> + 0 = BQ:len(BQS2), {AckTag, BQS3} = BQ:publish_delivered( Message, Props, SenderPid, BQS2), {{Message, Delivered, AckTag}, diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 964b0eb4..a2ea2c42 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -724,6 +724,7 @@ process_instruction({publish_delivered, ChPid, MsgProps, Msg = #basic_message { id = MsgId }}, State) -> State1 = #state { backing_queue = BQ, backing_queue_state = BQS } = publish_or_discard(published, ChPid, MsgId, State), + 0 = BQ:len(BQS), {AckTag, BQS1} = BQ:publish_delivered(Msg, MsgProps, ChPid, BQS), {ok, maybe_store_ack(true, MsgId, AckTag, State1 #state { backing_queue_state = BQS1 })}; -- cgit v1.2.1 From 133bffae47f6490fe8d4c551765ea18d4e0abbcf Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 27 Jun 2013 13:03:34 +0100 Subject: BQ:is_empty/1. --- src/rabbit_amqqueue_process.erl | 2 +- src/rabbit_mirror_queue_slave.erl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index f71e28e1..4abac2cf 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -540,7 +540,7 @@ attempt_delivery(Delivery = #delivery{sender = SenderPid, message = Message}, {false, BQS1} -> deliver_msgs_to_consumers( fun (true, State1 = #q{backing_queue_state = BQS2}) -> - 0 = BQ:len(BQS2), + true = BQ:is_empty(BQS2), {AckTag, BQS3} = BQ:publish_delivered( Message, Props, SenderPid, BQS2), {{Message, Delivered, AckTag}, diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index a2ea2c42..294e1ebb 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -724,7 +724,7 @@ process_instruction({publish_delivered, ChPid, MsgProps, Msg = #basic_message { id = MsgId }}, State) -> State1 = #state { backing_queue = BQ, backing_queue_state = BQS } = publish_or_discard(published, ChPid, MsgId, State), - 0 = BQ:len(BQS), + true = BQ:is_empty(BQS), {AckTag, BQS1} = BQ:publish_delivered(Msg, MsgProps, ChPid, BQS), {ok, maybe_store_ack(true, MsgId, AckTag, State1 #state { backing_queue_state = BQS1 })}; -- cgit v1.2.1 From 23344f846db43fcedd526f3ce0127fbf4847396e Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Thu, 27 Jun 2013 14:02:17 +0100 Subject: Stem leak when master deals with discarded messages --- src/rabbit_mirror_queue_master.erl | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index bcd4861a..acf77df6 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -229,17 +229,15 @@ discard(MsgId, ChPid, State = #state { gm = GM, %% already been published or published-and-confirmed. To do that %% would require non FIFO access. Hence we should not find %% 'published' or 'confirmed' in this dict:find. - case dict:find(MsgId, SS) of - error -> - ok = gm:broadcast(GM, {discard, ChPid, MsgId}), - BQS1 = BQ:discard(MsgId, ChPid, BQS), - ensure_monitoring( - ChPid, State #state { - backing_queue_state = BQS1, - seen_status = dict:erase(MsgId, SS) }); - {ok, discarded} -> - State - end. + State1 = case dict:find(MsgId, SS) of + error -> + ok = gm:broadcast(GM, {discard, ChPid, MsgId}), + State #state { backing_queue_state = + BQ:discard(MsgId, ChPid, BQS) }; + {ok, discarded} -> + State #state { seen_status = dict:erase(MsgId, SS) } + end, + ensure_monitoring(ChPid, State1). dropwhile(Pred, State = #state{backing_queue = BQ, backing_queue_state = BQS }) -> -- cgit v1.2.1 From dcafe81a206c85dc83846647fa2878a26ccbaf4c Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Thu, 27 Jun 2013 17:48:40 +0100 Subject: Optimisation for handling discarded deliveries --- src/rabbit_amqqueue_process.erl | 11 +++++++++-- src/rabbit_mirror_queue_master.erl | 22 +++++++--------------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 3e3de10b..0d8d6e29 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -533,7 +533,9 @@ run_message_queue(State) -> is_empty(State), State), State1. -attempt_delivery(Delivery = #delivery{sender = SenderPid, message = Message}, +attempt_delivery(Delivery = #delivery{sender = SenderPid, + msg_seq_no = MsgSeqNo, + message = Message}, Props, Delivered, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> case BQ:is_duplicate(Message, BQS) of @@ -551,7 +553,12 @@ attempt_delivery(Delivery = #delivery{sender = SenderPid, message = Message}, {published, BQS1} -> {true, State#q{backing_queue_state = BQS1}}; {discarded, BQS1} -> - {true, discard(Delivery, State#q{backing_queue_state = BQS1})} + State1 = State#q{backing_queue_state = BQS1}, + {true, case MsgSeqNo of + undefined -> State; + _ -> #basic_message{id = MsgId} = Message, + confirm_messages([MsgId], State) + end} end. deliver_or_enqueue(Delivery = #delivery{message = Message, sender = SenderPid}, diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index acf77df6..8c061e52 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -225,18 +225,10 @@ discard(MsgId, ChPid, State = #state { gm = GM, backing_queue = BQ, backing_queue_state = BQS, seen_status = SS }) -> - %% It's a massive error if we get told to discard something that's - %% already been published or published-and-confirmed. To do that - %% would require non FIFO access. Hence we should not find - %% 'published' or 'confirmed' in this dict:find. - State1 = case dict:find(MsgId, SS) of - error -> - ok = gm:broadcast(GM, {discard, ChPid, MsgId}), - State #state { backing_queue_state = - BQ:discard(MsgId, ChPid, BQS) }; - {ok, discarded} -> - State #state { seen_status = dict:erase(MsgId, SS) } - end, + false = dict:is_key(MsgId, SS), %% ASSERTION + ok = gm:broadcast(GM, {discard, ChPid, MsgId}), + State1 = State #state { backing_queue_state = + BQ:discard(MsgId, ChPid, BQS) }, ensure_monitoring(ChPid, State1). dropwhile(Pred, State = #state{backing_queue = BQ, @@ -404,9 +396,9 @@ is_duplicate(Message = #basic_message { id = MsgId }, {published, State #state { seen_status = dict:erase(MsgId, SS), confirmed = [MsgId | Confirmed] }}; {ok, discarded} -> - %% Don't erase from SS here because discard/2 is about to - %% be called and we need to be able to detect this case - {discarded, State} + %% Message was discarded while we were a slave. Erase + %% and let amqqueue_process confirm if necessary. + {discarded, State #state { seen_status = dict:erase(MsgId, SS) }} end. %% --------------------------------------------------------------------------- -- cgit v1.2.1 From befa23f9c56f8b857e13a63bad9ce3f75ed288fd Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Thu, 27 Jun 2013 17:51:38 +0100 Subject: Ignore missing modules when performing HiPE compilation --- src/rabbit.erl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index b3fb98af..31568ef5 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -281,7 +281,8 @@ warn_if_hipe_compilation_failed(false) -> %% long time, so make an exception to our no-stdout policy and display %% progress via stdout. hipe_compile() -> - Count = length(?HIPE_WORTHY), + HipeWorthy = [HW || HW <- ?HIPE_WORTHY, code:which(HW) =/= non_existing], + Count = length(HipeWorthy), io:format("~nHiPE compiling: |~s|~n |", [string:copies("-", Count)]), T1 = erlang:now(), @@ -290,7 +291,7 @@ hipe_compile() -> io:format("#") end || M <- Ms] end) || - Ms <- split(?HIPE_WORTHY, ?HIPE_PROCESSES)], + Ms <- split(HipeWorthy, ?HIPE_PROCESSES)], [receive {'DOWN', MRef, process, _, normal} -> ok; {'DOWN', MRef, process, _, Reason} -> exit(Reason) -- cgit v1.2.1 From 457efdfc6b708e57d01f40f0328a8b5b7ed4bf66 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Fri, 28 Jun 2013 11:38:58 +0100 Subject: Move hipe modules list from macro to environment variable --- ebin/rabbit_app.in | 27 ++++++++++++++++++++++++--- src/rabbit.erl | 23 ++++------------------- 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/ebin/rabbit_app.in b/ebin/rabbit_app.in index 339fa69e..a88c1a61 100644 --- a/ebin/rabbit_app.in +++ b/ebin/rabbit_app.in @@ -14,8 +14,7 @@ %% we also depend on crypto, public_key and ssl but they shouldn't be %% in here as we don't actually want to start it {mod, {rabbit, []}}, - {env, [{hipe_compile, false}, - {tcp_listeners, [5672]}, + {env, [{tcp_listeners, [5672]}, {ssl_listeners, []}, {ssl_options, []}, {vm_memory_high_watermark, 0.4}, @@ -51,5 +50,27 @@ {backlog, 128}, {nodelay, true}, {linger, {true, 0}}, - {exit_on_close, false}]} + {exit_on_close, false}]}, + {hipe_compile, false}, + %% see bug 24513 for how this list was created + {hipe_modules, [rabbit_reader, rabbit_channel, gen_server2, + rabbit_exchange, rabbit_command_assembler, + rabbit_framing_amqp_0_9_1, rabbit_basic, + rabbit_event, lists, queue, priority_queue, + rabbit_router, rabbit_trace, + rabbit_misc, rabbit_binary_parser, + rabbit_exchange_type_direct, rabbit_guid, + rabbit_net, rabbit_amqqueue_process, + rabbit_variable_queue, + rabbit_binary_generator, rabbit_writer, + delegate, gb_sets, lqueue, sets, orddict, + rabbit_amqqueue, rabbit_limiter, gb_trees, + rabbit_queue_index, gen, dict, ordsets, + file_handle_cache, rabbit_msg_store, array, + rabbit_msg_store_ets_index, rabbit_msg_file, + rabbit_exchange_type_fanout, + rabbit_exchange_type_topic, mnesia, + mnesia_lib, rpc, mnesia_tm, qlc, + sofs, proplists, credit_flow, pmon, + ssl_connection, ssl_record, gen_fsm, ssl]} ]}]}. diff --git a/src/rabbit.erl b/src/rabbit.erl index 31568ef5..dddf6f47 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -192,22 +192,6 @@ -define(APPS, [os_mon, mnesia, rabbit]). -%% see bug 24513 for how this list was created --define(HIPE_WORTHY, - [rabbit_reader, rabbit_channel, gen_server2, - rabbit_exchange, rabbit_command_assembler, rabbit_framing_amqp_0_9_1, - rabbit_basic, rabbit_event, lists, queue, priority_queue, - rabbit_router, rabbit_trace, rabbit_misc, rabbit_binary_parser, - rabbit_exchange_type_direct, rabbit_guid, rabbit_net, - rabbit_amqqueue_process, rabbit_variable_queue, - rabbit_binary_generator, rabbit_writer, delegate, gb_sets, lqueue, - sets, orddict, rabbit_amqqueue, rabbit_limiter, gb_trees, - rabbit_queue_index, gen, dict, ordsets, file_handle_cache, - rabbit_msg_store, array, rabbit_msg_store_ets_index, rabbit_msg_file, - rabbit_exchange_type_fanout, rabbit_exchange_type_topic, mnesia, - mnesia_lib, rpc, mnesia_tm, qlc, sofs, proplists, credit_flow, pmon, - ssl_connection, ssl_record, gen_fsm, ssl]). - %% HiPE compilation uses multiple cores anyway, but some bits are %% IO-bound so we can go faster if we parallelise a bit more. In %% practice 2 processes seems just as fast as any other number > 1, @@ -281,8 +265,9 @@ warn_if_hipe_compilation_failed(false) -> %% long time, so make an exception to our no-stdout policy and display %% progress via stdout. hipe_compile() -> - HipeWorthy = [HW || HW <- ?HIPE_WORTHY, code:which(HW) =/= non_existing], - Count = length(HipeWorthy), + {ok, HipeModulesAll} = application:get_env(rabbit, hipe_modules), + HipeModules = [HM || HM <- HipeModulesAll, code:which(HM) =/= non_existing], + Count = length(HipeModules), io:format("~nHiPE compiling: |~s|~n |", [string:copies("-", Count)]), T1 = erlang:now(), @@ -291,7 +276,7 @@ hipe_compile() -> io:format("#") end || M <- Ms] end) || - Ms <- split(HipeWorthy, ?HIPE_PROCESSES)], + Ms <- split(HipeModules, ?HIPE_PROCESSES)], [receive {'DOWN', MRef, process, _, normal} -> ok; {'DOWN', MRef, process, _, Reason} -> exit(Reason) -- cgit v1.2.1 From 585f4bd75d82b65ffc5ce962fa51654ab8f4d044 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 1 Jul 2013 10:49:14 +0100 Subject: s/VMware/GoPivotal/g --- LICENSE-MPL-RabbitMQ | 4 ++-- check_xref | 4 ++-- codegen.py | 8 ++++---- include/gm_specs.hrl | 4 ++-- include/rabbit.hrl | 6 +++--- include/rabbit_msg_store.hrl | 4 ++-- packaging/common/LICENSE.tail | 8 ++++---- packaging/common/rabbitmq-script-wrapper | 4 ++-- packaging/common/rabbitmq-server.ocf | 4 ++-- packaging/debs/Debian/Makefile | 2 +- packaging/standalone/src/rabbit_release.erl | 4 ++-- packaging/windows-exe/rabbitmq_nsi.in | 12 ++++++------ scripts/rabbitmq-defaults | 4 ++-- scripts/rabbitmq-env | 4 ++-- scripts/rabbitmq-plugins | 4 ++-- scripts/rabbitmq-plugins.bat | 4 ++-- scripts/rabbitmq-server | 4 ++-- scripts/rabbitmq-server.bat | 4 ++-- scripts/rabbitmq-service.bat | 4 ++-- scripts/rabbitmqctl | 4 ++-- scripts/rabbitmqctl.bat | 4 ++-- src/app_utils.erl | 4 ++-- src/background_gc.erl | 4 ++-- src/credit_flow.erl | 4 ++-- src/delegate.erl | 4 ++-- src/delegate_sup.erl | 4 ++-- src/dtree.erl | 4 ++-- src/file_handle_cache.erl | 4 ++-- src/gatherer.erl | 4 ++-- src/gen_server2.erl | 2 +- src/gm.erl | 4 ++-- src/gm_soak_test.erl | 4 ++-- src/gm_speed_test.erl | 4 ++-- src/gm_tests.erl | 4 ++-- src/lqueue.erl | 4 ++-- src/mirrored_supervisor.erl | 4 ++-- src/mirrored_supervisor_tests.erl | 4 ++-- src/mnesia_sync.erl | 4 ++-- src/pg_local.erl | 2 +- src/pmon.erl | 4 ++-- src/priority_queue.erl | 4 ++-- src/rabbit.erl | 4 ++-- src/rabbit_access_control.erl | 4 ++-- src/rabbit_alarm.erl | 4 ++-- src/rabbit_amqqueue.erl | 4 ++-- src/rabbit_amqqueue_process.erl | 4 ++-- src/rabbit_amqqueue_sup.erl | 4 ++-- src/rabbit_auth_backend.erl | 4 ++-- src/rabbit_auth_backend_internal.erl | 4 ++-- src/rabbit_auth_mechanism.erl | 4 ++-- src/rabbit_auth_mechanism_amqplain.erl | 4 ++-- src/rabbit_auth_mechanism_cr_demo.erl | 4 ++-- src/rabbit_auth_mechanism_plain.erl | 4 ++-- src/rabbit_autoheal.erl | 4 ++-- src/rabbit_backing_queue.erl | 4 ++-- src/rabbit_backing_queue_qc.erl | 4 ++-- src/rabbit_basic.erl | 4 ++-- src/rabbit_binary_generator.erl | 4 ++-- src/rabbit_binary_parser.erl | 4 ++-- src/rabbit_binding.erl | 4 ++-- src/rabbit_channel.erl | 4 ++-- src/rabbit_channel_sup.erl | 4 ++-- src/rabbit_channel_sup_sup.erl | 4 ++-- src/rabbit_client_sup.erl | 4 ++-- src/rabbit_command_assembler.erl | 4 ++-- src/rabbit_connection_sup.erl | 4 ++-- src/rabbit_control_main.erl | 4 ++-- src/rabbit_direct.erl | 4 ++-- src/rabbit_disk_monitor.erl | 4 ++-- src/rabbit_error_logger.erl | 4 ++-- src/rabbit_error_logger_file_h.erl | 4 ++-- src/rabbit_event.erl | 4 ++-- src/rabbit_exchange.erl | 4 ++-- src/rabbit_exchange_decorator.erl | 4 ++-- src/rabbit_exchange_type.erl | 4 ++-- src/rabbit_exchange_type_direct.erl | 4 ++-- src/rabbit_exchange_type_fanout.erl | 4 ++-- src/rabbit_exchange_type_headers.erl | 4 ++-- src/rabbit_exchange_type_invalid.erl | 4 ++-- src/rabbit_exchange_type_topic.erl | 4 ++-- src/rabbit_file.erl | 4 ++-- src/rabbit_framing.erl | 4 ++-- src/rabbit_guid.erl | 4 ++-- src/rabbit_heartbeat.erl | 4 ++-- src/rabbit_limiter.erl | 4 ++-- src/rabbit_log.erl | 4 ++-- src/rabbit_memory_monitor.erl | 4 ++-- src/rabbit_mirror_queue_coordinator.erl | 4 ++-- src/rabbit_mirror_queue_master.erl | 4 ++-- src/rabbit_mirror_queue_misc.erl | 4 ++-- src/rabbit_mirror_queue_mode.erl | 4 ++-- src/rabbit_mirror_queue_mode_all.erl | 4 ++-- src/rabbit_mirror_queue_mode_exactly.erl | 4 ++-- src/rabbit_mirror_queue_mode_nodes.erl | 4 ++-- src/rabbit_mirror_queue_slave.erl | 4 ++-- src/rabbit_mirror_queue_slave_sup.erl | 4 ++-- src/rabbit_mirror_queue_sync.erl | 4 ++-- src/rabbit_misc.erl | 4 ++-- src/rabbit_mnesia.erl | 4 ++-- src/rabbit_msg_file.erl | 4 ++-- src/rabbit_msg_store.erl | 4 ++-- src/rabbit_msg_store_ets_index.erl | 4 ++-- src/rabbit_msg_store_gc.erl | 4 ++-- src/rabbit_msg_store_index.erl | 4 ++-- src/rabbit_net.erl | 4 ++-- src/rabbit_networking.erl | 4 ++-- src/rabbit_node_monitor.erl | 4 ++-- src/rabbit_nodes.erl | 4 ++-- src/rabbit_parameter_validation.erl | 4 ++-- src/rabbit_plugins.erl | 4 ++-- src/rabbit_plugins_main.erl | 4 ++-- src/rabbit_policy.erl | 4 ++-- src/rabbit_policy_validator.erl | 4 ++-- src/rabbit_prelaunch.erl | 4 ++-- src/rabbit_queue_collector.erl | 4 ++-- src/rabbit_queue_index.erl | 4 ++-- src/rabbit_reader.erl | 4 ++-- src/rabbit_registry.erl | 4 ++-- src/rabbit_restartable_sup.erl | 4 ++-- src/rabbit_router.erl | 4 ++-- src/rabbit_runtime_parameter.erl | 4 ++-- src/rabbit_runtime_parameters.erl | 4 ++-- src/rabbit_runtime_parameters_test.erl | 4 ++-- src/rabbit_sasl_report_file_h.erl | 4 ++-- src/rabbit_ssl.erl | 4 ++-- src/rabbit_sup.erl | 4 ++-- src/rabbit_table.erl | 4 ++-- src/rabbit_tests.erl | 4 ++-- src/rabbit_tests_event_receiver.erl | 4 ++-- src/rabbit_trace.erl | 4 ++-- src/rabbit_types.erl | 4 ++-- src/rabbit_upgrade.erl | 4 ++-- src/rabbit_upgrade_functions.erl | 4 ++-- src/rabbit_variable_queue.erl | 4 ++-- src/rabbit_version.erl | 4 ++-- src/rabbit_vhost.erl | 4 ++-- src/rabbit_vm.erl | 4 ++-- src/rabbit_writer.erl | 4 ++-- src/supervisor2.erl | 2 +- src/supervisor2_tests.erl | 4 ++-- src/tcp_acceptor.erl | 4 ++-- src/tcp_acceptor_sup.erl | 4 ++-- src/tcp_listener.erl | 4 ++-- src/tcp_listener_sup.erl | 4 ++-- src/test_sup.erl | 4 ++-- src/vm_memory_monitor.erl | 4 ++-- src/worker_pool.erl | 4 ++-- src/worker_pool_sup.erl | 4 ++-- src/worker_pool_worker.erl | 4 ++-- 149 files changed, 303 insertions(+), 303 deletions(-) diff --git a/LICENSE-MPL-RabbitMQ b/LICENSE-MPL-RabbitMQ index 4cdf783b..549d0f1c 100644 --- a/LICENSE-MPL-RabbitMQ +++ b/LICENSE-MPL-RabbitMQ @@ -446,8 +446,8 @@ EXHIBIT A -Mozilla Public License. The Original Code is RabbitMQ. - The Initial Developer of the Original Code is VMware, Inc. - Copyright (c) 2007-2013 VMware, Inc. All rights reserved.'' + The Initial Developer of the Original Code is GoPivotal, Inc. + Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved.'' [NOTE: The text of this Exhibit A may differ slightly from the text of the notices in the Source Code files of the Original Code. You should diff --git a/check_xref b/check_xref index df019311..24307fdb 100755 --- a/check_xref +++ b/check_xref @@ -14,8 +14,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2010-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2010-2013 GoPivotal, Inc. All rights reserved. %% main(["-h"]) -> diff --git a/codegen.py b/codegen.py index bf6b70d5..842549cf 100644 --- a/codegen.py +++ b/codegen.py @@ -10,8 +10,8 @@ ## ## The Original Code is RabbitMQ. ## -## The Initial Developer of the Original Code is VMware, Inc. -## Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +## The Initial Developer of the Original Code is GoPivotal, Inc. +## Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. ## from __future__ import nested_scopes @@ -105,8 +105,8 @@ def printFileHeader(): %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %%""" def genErl(spec): diff --git a/include/gm_specs.hrl b/include/gm_specs.hrl index b3dd6615..81555c46 100644 --- a/include/gm_specs.hrl +++ b/include/gm_specs.hrl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -ifdef(use_specs). diff --git a/include/rabbit.hrl b/include/rabbit.hrl index 4282755d..6df44bea 100644 --- a/include/rabbit.hrl +++ b/include/rabbit.hrl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -record(user, {username, @@ -86,7 +86,7 @@ %%---------------------------------------------------------------------------- --define(COPYRIGHT_MESSAGE, "Copyright (C) 2007-2013 VMware, Inc."). +-define(COPYRIGHT_MESSAGE, "Copyright (C) 2007-2013 GoPivotal, Inc."). -define(INFORMATION_MESSAGE, "Licensed under the MPL. See http://www.rabbitmq.com/"). -define(ERTS_MINIMUM, "5.6.3"). diff --git a/include/rabbit_msg_store.hrl b/include/rabbit_msg_store.hrl index 8665dc55..da4fd839 100644 --- a/include/rabbit_msg_store.hrl +++ b/include/rabbit_msg_store.hrl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -include("rabbit.hrl"). diff --git a/packaging/common/LICENSE.tail b/packaging/common/LICENSE.tail index 431ddeb0..2dbaca0a 100644 --- a/packaging/common/LICENSE.tail +++ b/packaging/common/LICENSE.tail @@ -55,8 +55,8 @@ The BSD 2-Clause license is as follows: The rest of this package is licensed under the Mozilla Public License 1.1 Authors and Copyright are as described below: - The Initial Developer of the Original Code is VMware, Inc. - Copyright (c) 2007-2013 VMware, Inc. All rights reserved. + The Initial Developer of the Original Code is GoPivotal, Inc. + Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. MOZILLA PUBLIC LICENSE @@ -507,8 +507,8 @@ EXHIBIT A -Mozilla Public License. The Original Code is RabbitMQ. - The Initial Developer of the Original Code is VMware, Inc. - Copyright (c) 2007-2013 VMware, Inc. All rights reserved.'' + The Initial Developer of the Original Code is GoPivotal, Inc. + Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved.'' [NOTE: The text of this Exhibit A may differ slightly from the text of the notices in the Source Code files of the Original Code. You should diff --git a/packaging/common/rabbitmq-script-wrapper b/packaging/common/rabbitmq-script-wrapper index b9c6ffbf..7e5f7749 100644 --- a/packaging/common/rabbitmq-script-wrapper +++ b/packaging/common/rabbitmq-script-wrapper @@ -11,8 +11,8 @@ ## ## The Original Code is RabbitMQ. ## -## The Initial Developer of the Original Code is VMware, Inc. -## Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +## The Initial Developer of the Original Code is GoPivotal, Inc. +## Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. ## # Escape spaces and quotes, because shell is revolting. diff --git a/packaging/common/rabbitmq-server.ocf b/packaging/common/rabbitmq-server.ocf index ba9579b6..6b3abf3e 100755 --- a/packaging/common/rabbitmq-server.ocf +++ b/packaging/common/rabbitmq-server.ocf @@ -11,8 +11,8 @@ ## ## The Original Code is RabbitMQ. ## -## The Initial Developer of the Original Code is VMware, Inc. -## Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +## The Initial Developer of the Original Code is GoPivotal, Inc. +## Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. ## ## diff --git a/packaging/debs/Debian/Makefile b/packaging/debs/Debian/Makefile index c197915d..6d844364 100644 --- a/packaging/debs/Debian/Makefile +++ b/packaging/debs/Debian/Makefile @@ -28,7 +28,7 @@ package: clean chmod a+x $(UNPACKED_DIR)/debian/rules echo "This package was debianized by Tony Garnock-Jones on\nWed, 3 Jan 2007 15:43:44 +0000.\n\nIt was downloaded from http://www.rabbitmq.com/\n\n" > $(UNPACKED_DIR)/debian/copyright cat $(UNPACKED_DIR)/LICENSE >> $(UNPACKED_DIR)/debian/copyright - echo "\n\nThe Debian packaging is (C) 2007-2013, VMware, Inc. and is licensed\nunder the MPL 1.1, see above.\n" >> $(UNPACKED_DIR)/debian/copyright + echo "\n\nThe Debian packaging is (C) 2007-2013, GoPivotal, Inc. and is licensed\nunder the MPL 1.1, see above.\n" >> $(UNPACKED_DIR)/debian/copyright UNOFFICIAL_RELEASE=$(UNOFFICIAL_RELEASE) VERSION=$(VERSION) ./check-changelog.sh rabbitmq-server $(UNPACKED_DIR) cd $(UNPACKED_DIR); GNUPGHOME=$(GNUPG_PATH)/.gnupg dpkg-buildpackage -rfakeroot $(SIGNING) rm -rf $(UNPACKED_DIR) diff --git a/packaging/standalone/src/rabbit_release.erl b/packaging/standalone/src/rabbit_release.erl index 26f36d68..dd68ee7e 100644 --- a/packaging/standalone/src/rabbit_release.erl +++ b/packaging/standalone/src/rabbit_release.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2012 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2012 GoPivotal, Inc. All rights reserved. %% -module(rabbit_release). diff --git a/packaging/windows-exe/rabbitmq_nsi.in b/packaging/windows-exe/rabbitmq_nsi.in index b351430e..7ba4d17d 100644 --- a/packaging/windows-exe/rabbitmq_nsi.in +++ b/packaging/windows-exe/rabbitmq_nsi.in @@ -24,7 +24,7 @@ InstallDir "$PROGRAMFILES\RabbitMQ Server" ; Registry key to check for directory (so if you install again, it will ; overwrite the old one automatically) -InstallDirRegKey HKLM "Software\VMware, Inc.\RabbitMQ Server" "Install_Dir" +InstallDirRegKey HKLM "Software\GoPivotal, Inc.\RabbitMQ Server" "Install_Dir" ; Request application privileges for Windows Vista RequestExecutionLevel admin @@ -35,9 +35,9 @@ VIProductVersion "%%VERSION%%.0" VIAddVersionKey /LANG=${LANG_ENGLISH} "ProductVersion" "%%VERSION%%" VIAddVersionKey /LANG=${LANG_ENGLISH} "ProductName" "RabbitMQ Server" ;VIAddVersionKey /LANG=${LANG_ENGLISH} "Comments" "" -VIAddVersionKey /LANG=${LANG_ENGLISH} "CompanyName" "VMware, Inc" +VIAddVersionKey /LANG=${LANG_ENGLISH} "CompanyName" "GoPivotal, Inc" ;VIAddVersionKey /LANG=${LANG_ENGLISH} "LegalTrademarks" "" ; TODO ? -VIAddVersionKey /LANG=${LANG_ENGLISH} "LegalCopyright" "Copyright (c) 2007-2013 VMware, Inc. All rights reserved." +VIAddVersionKey /LANG=${LANG_ENGLISH} "LegalCopyright" "Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved." VIAddVersionKey /LANG=${LANG_ENGLISH} "FileDescription" "RabbitMQ Server" VIAddVersionKey /LANG=${LANG_ENGLISH} "FileVersion" "%%VERSION%%" @@ -77,13 +77,13 @@ Section "RabbitMQ Server (required)" Rabbit File "rabbitmq.ico" ; Write the installation path into the registry - WriteRegStr HKLM "SOFTWARE\VMware, Inc.\RabbitMQ Server" "Install_Dir" "$INSTDIR" + WriteRegStr HKLM "SOFTWARE\GoPivotal, Inc.\RabbitMQ Server" "Install_Dir" "$INSTDIR" ; Write the uninstall keys for Windows WriteRegStr HKLM ${uninstall} "DisplayName" "RabbitMQ Server" WriteRegStr HKLM ${uninstall} "UninstallString" "$INSTDIR\uninstall.exe" WriteRegStr HKLM ${uninstall} "DisplayIcon" "$INSTDIR\uninstall.exe,0" - WriteRegStr HKLM ${uninstall} "Publisher" "VMware, Inc." + WriteRegStr HKLM ${uninstall} "Publisher" "GoPivotal, Inc." WriteRegStr HKLM ${uninstall} "DisplayVersion" "%%VERSION%%" WriteRegDWORD HKLM ${uninstall} "NoModify" 1 WriteRegDWORD HKLM ${uninstall} "NoRepair" 1 @@ -151,7 +151,7 @@ Section "Uninstall" ; Remove registry keys DeleteRegKey HKLM ${uninstall} - DeleteRegKey HKLM "SOFTWARE\VMware, Inc.\RabbitMQ Server" + DeleteRegKey HKLM "SOFTWARE\GoPivotal, Inc.\RabbitMQ Server" ; TODO these will fail if the service is not installed - do we care? ExpandEnvStrings $0 %COMSPEC% diff --git a/scripts/rabbitmq-defaults b/scripts/rabbitmq-defaults index 83c5639d..f4b131cd 100644 --- a/scripts/rabbitmq-defaults +++ b/scripts/rabbitmq-defaults @@ -11,8 +11,8 @@ ## ## The Original Code is RabbitMQ. ## -## The Initial Developer of the Original Code is VMware, Inc. -## Copyright (c) 2012-2013 VMware, Inc. All rights reserved. +## The Initial Developer of the Original Code is GoPivotal, Inc. +## Copyright (c) 2012-2013 GoPivotal, Inc. All rights reserved. ## ### next line potentially updated in package install steps diff --git a/scripts/rabbitmq-env b/scripts/rabbitmq-env index 3721f6c7..c76e7e4b 100755 --- a/scripts/rabbitmq-env +++ b/scripts/rabbitmq-env @@ -11,8 +11,8 @@ ## ## The Original Code is RabbitMQ. ## -## The Initial Developer of the Original Code is VMware, Inc. -## Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +## The Initial Developer of the Original Code is GoPivotal, Inc. +## Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. ## # Determine where this script is really located (if this script is diff --git a/scripts/rabbitmq-plugins b/scripts/rabbitmq-plugins index c043c90a..90eb5a5d 100755 --- a/scripts/rabbitmq-plugins +++ b/scripts/rabbitmq-plugins @@ -11,8 +11,8 @@ ## ## The Original Code is RabbitMQ. ## -## The Initial Developer of the Original Code is VMware, Inc. -## Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +## The Initial Developer of the Original Code is GoPivotal, Inc. +## Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. ## # Get default settings with user overrides for (RABBITMQ_) diff --git a/scripts/rabbitmq-plugins.bat b/scripts/rabbitmq-plugins.bat index 4b4dbe47..0d1f128e 100755 --- a/scripts/rabbitmq-plugins.bat +++ b/scripts/rabbitmq-plugins.bat @@ -11,8 +11,8 @@ REM limitations under the License. REM REM The Original Code is RabbitMQ. REM -REM The Initial Developer of the Original Code is VMware, Inc. -REM Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +REM The Initial Developer of the Original Code is GoPivotal, Inc. +REM Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. REM setlocal diff --git a/scripts/rabbitmq-server b/scripts/rabbitmq-server index 161ec2e6..b430eec3 100755 --- a/scripts/rabbitmq-server +++ b/scripts/rabbitmq-server @@ -11,8 +11,8 @@ ## ## The Original Code is RabbitMQ. ## -## The Initial Developer of the Original Code is VMware, Inc. -## Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +## The Initial Developer of the Original Code is GoPivotal, Inc. +## Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. ## # Get default settings with user overrides for (RABBITMQ_) diff --git a/scripts/rabbitmq-server.bat b/scripts/rabbitmq-server.bat index 9fa304e6..b00821ed 100755 --- a/scripts/rabbitmq-server.bat +++ b/scripts/rabbitmq-server.bat @@ -11,8 +11,8 @@ REM limitations under the License. REM REM The Original Code is RabbitMQ. REM -REM The Initial Developer of the Original Code is VMware, Inc. -REM Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +REM The Initial Developer of the Original Code is GoPivotal, Inc. +REM Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. REM setlocal diff --git a/scripts/rabbitmq-service.bat b/scripts/rabbitmq-service.bat index 9c30e74e..d36b130c 100755 --- a/scripts/rabbitmq-service.bat +++ b/scripts/rabbitmq-service.bat @@ -11,8 +11,8 @@ REM limitations under the License. REM REM The Original Code is RabbitMQ. REM -REM The Initial Developer of the Original Code is VMware, Inc. -REM Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +REM The Initial Developer of the Original Code is GoPivotal, Inc. +REM Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. REM setlocal diff --git a/scripts/rabbitmqctl b/scripts/rabbitmqctl index 0368db3f..d0f22ce6 100755 --- a/scripts/rabbitmqctl +++ b/scripts/rabbitmqctl @@ -11,8 +11,8 @@ ## ## The Original Code is RabbitMQ. ## -## The Initial Developer of the Original Code is VMware, Inc. -## Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +## The Initial Developer of the Original Code is GoPivotal, Inc. +## Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. ## # Get default settings with user overrides for (RABBITMQ_) diff --git a/scripts/rabbitmqctl.bat b/scripts/rabbitmqctl.bat index a6d85552..d7cbbb10 100755 --- a/scripts/rabbitmqctl.bat +++ b/scripts/rabbitmqctl.bat @@ -11,8 +11,8 @@ REM limitations under the License. REM REM The Original Code is RabbitMQ. REM -REM The Initial Developer of the Original Code is VMware, Inc. -REM Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +REM The Initial Developer of the Original Code is GoPivotal, Inc. +REM Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. REM setlocal diff --git a/src/app_utils.erl b/src/app_utils.erl index b102ce75..5ae2d295 100644 --- a/src/app_utils.erl +++ b/src/app_utils.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(app_utils). diff --git a/src/background_gc.erl b/src/background_gc.erl index d684d6ea..fbd7ce23 100644 --- a/src/background_gc.erl +++ b/src/background_gc.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(background_gc). diff --git a/src/credit_flow.erl b/src/credit_flow.erl index 106179fd..d48d649e 100644 --- a/src/credit_flow.erl +++ b/src/credit_flow.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(credit_flow). diff --git a/src/delegate.erl b/src/delegate.erl index e833b819..4e1dcd2e 100644 --- a/src/delegate.erl +++ b/src/delegate.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(delegate). diff --git a/src/delegate_sup.erl b/src/delegate_sup.erl index 30400b3e..e31d6d38 100644 --- a/src/delegate_sup.erl +++ b/src/delegate_sup.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(delegate_sup). diff --git a/src/dtree.erl b/src/dtree.erl index 45eea506..5ff36bd9 100644 --- a/src/dtree.erl +++ b/src/dtree.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% %% A dual-index tree. diff --git a/src/file_handle_cache.erl b/src/file_handle_cache.erl index 406add8a..bac7c2c1 100644 --- a/src/file_handle_cache.erl +++ b/src/file_handle_cache.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(file_handle_cache). diff --git a/src/gatherer.erl b/src/gatherer.erl index 0c257a84..c13298ca 100644 --- a/src/gatherer.erl +++ b/src/gatherer.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(gatherer). diff --git a/src/gen_server2.erl b/src/gen_server2.erl index 507d1cda..6690d181 100644 --- a/src/gen_server2.erl +++ b/src/gen_server2.erl @@ -82,7 +82,7 @@ %% can be invoked on the state, get back the result. The state is not %% modified. -%% All modifications are (C) 2009-2013 VMware, Inc. +%% All modifications are (C) 2009-2013 GoPivotal, Inc. %% ``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 diff --git a/src/gm.erl b/src/gm.erl index 3f0909e8..a6735ef8 100644 --- a/src/gm.erl +++ b/src/gm.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(gm). diff --git a/src/gm_soak_test.erl b/src/gm_soak_test.erl index 1034ee2f..b379d218 100644 --- a/src/gm_soak_test.erl +++ b/src/gm_soak_test.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(gm_soak_test). diff --git a/src/gm_speed_test.erl b/src/gm_speed_test.erl index 3fe3b182..768cc462 100644 --- a/src/gm_speed_test.erl +++ b/src/gm_speed_test.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(gm_speed_test). diff --git a/src/gm_tests.erl b/src/gm_tests.erl index efb87a4c..233702ad 100644 --- a/src/gm_tests.erl +++ b/src/gm_tests.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(gm_tests). diff --git a/src/lqueue.erl b/src/lqueue.erl index e2ab2380..4ff7cc0b 100644 --- a/src/lqueue.erl +++ b/src/lqueue.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2011-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2011-2013 GoPivotal, Inc. All rights reserved. %% -module(lqueue). diff --git a/src/mirrored_supervisor.erl b/src/mirrored_supervisor.erl index 33d09f7f..3b16c53a 100644 --- a/src/mirrored_supervisor.erl +++ b/src/mirrored_supervisor.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2011-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2011-2013 GoPivotal, Inc. All rights reserved. %% -module(mirrored_supervisor). diff --git a/src/mirrored_supervisor_tests.erl b/src/mirrored_supervisor_tests.erl index ea6b82c8..780ef11d 100644 --- a/src/mirrored_supervisor_tests.erl +++ b/src/mirrored_supervisor_tests.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2011-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2011-2013 GoPivotal, Inc. All rights reserved. %% -module(mirrored_supervisor_tests). diff --git a/src/mnesia_sync.erl b/src/mnesia_sync.erl index 41a349be..78c566e1 100644 --- a/src/mnesia_sync.erl +++ b/src/mnesia_sync.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(mnesia_sync). diff --git a/src/pg_local.erl b/src/pg_local.erl index 7377fbf0..f535b136 100644 --- a/src/pg_local.erl +++ b/src/pg_local.erl @@ -13,7 +13,7 @@ %% versions of Erlang/OTP. The remaining type specs have been %% removed. -%% All modifications are (C) 2010-2013 VMware, Inc. +%% All modifications are (C) 2010-2013 GoPivotal, Inc. %% %CopyrightBegin% %% diff --git a/src/pmon.erl b/src/pmon.erl index ed32b8b2..b9db66fb 100644 --- a/src/pmon.erl +++ b/src/pmon.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2011-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2011-2013 GoPivotal, Inc. All rights reserved. %% -module(pmon). diff --git a/src/priority_queue.erl b/src/priority_queue.erl index 0dc19819..3d9e7c6a 100644 --- a/src/priority_queue.erl +++ b/src/priority_queue.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% %% Priority queues have essentially the same interface as ordinary diff --git a/src/rabbit.erl b/src/rabbit.erl index b3fb98af..46e3d0e4 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit). diff --git a/src/rabbit_access_control.erl b/src/rabbit_access_control.erl index cbe89a17..90be4f80 100644 --- a/src/rabbit_access_control.erl +++ b/src/rabbit_access_control.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_access_control). diff --git a/src/rabbit_alarm.erl b/src/rabbit_alarm.erl index 6d24d130..6607c4f6 100644 --- a/src/rabbit_alarm.erl +++ b/src/rabbit_alarm.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_alarm). diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 767abeb0..a6181a95 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_amqqueue). diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index f08f8292..ebbe4bab 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_amqqueue_process). diff --git a/src/rabbit_amqqueue_sup.erl b/src/rabbit_amqqueue_sup.erl index d7257a69..0515e82e 100644 --- a/src/rabbit_amqqueue_sup.erl +++ b/src/rabbit_amqqueue_sup.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_amqqueue_sup). diff --git a/src/rabbit_auth_backend.erl b/src/rabbit_auth_backend.erl index 72f81707..4ffc8c3a 100644 --- a/src/rabbit_auth_backend.erl +++ b/src/rabbit_auth_backend.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_auth_backend). diff --git a/src/rabbit_auth_backend_internal.erl b/src/rabbit_auth_backend_internal.erl index 483666b4..61919d05 100644 --- a/src/rabbit_auth_backend_internal.erl +++ b/src/rabbit_auth_backend_internal.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_auth_backend_internal). diff --git a/src/rabbit_auth_mechanism.erl b/src/rabbit_auth_mechanism.erl index 99e4468e..21528b11 100644 --- a/src/rabbit_auth_mechanism.erl +++ b/src/rabbit_auth_mechanism.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_auth_mechanism). diff --git a/src/rabbit_auth_mechanism_amqplain.erl b/src/rabbit_auth_mechanism_amqplain.erl index 847a38f5..8e896b45 100644 --- a/src/rabbit_auth_mechanism_amqplain.erl +++ b/src/rabbit_auth_mechanism_amqplain.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_auth_mechanism_amqplain). diff --git a/src/rabbit_auth_mechanism_cr_demo.erl b/src/rabbit_auth_mechanism_cr_demo.erl index 4b08e4be..8699a9fa 100644 --- a/src/rabbit_auth_mechanism_cr_demo.erl +++ b/src/rabbit_auth_mechanism_cr_demo.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_auth_mechanism_cr_demo). diff --git a/src/rabbit_auth_mechanism_plain.erl b/src/rabbit_auth_mechanism_plain.erl index a35a133a..a7e8fb36 100644 --- a/src/rabbit_auth_mechanism_plain.erl +++ b/src/rabbit_auth_mechanism_plain.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_auth_mechanism_plain). diff --git a/src/rabbit_autoheal.erl b/src/rabbit_autoheal.erl index f903677b..5739c7f3 100644 --- a/src/rabbit_autoheal.erl +++ b/src/rabbit_autoheal.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_autoheal). diff --git a/src/rabbit_backing_queue.erl b/src/rabbit_backing_queue.erl index 2f247448..f05e46e9 100644 --- a/src/rabbit_backing_queue.erl +++ b/src/rabbit_backing_queue.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_backing_queue). diff --git a/src/rabbit_backing_queue_qc.erl b/src/rabbit_backing_queue_qc.erl index 052db3a5..e2bc3247 100644 --- a/src/rabbit_backing_queue_qc.erl +++ b/src/rabbit_backing_queue_qc.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2011-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2011-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_backing_queue_qc). diff --git a/src/rabbit_basic.erl b/src/rabbit_basic.erl index c42289c7..2e825536 100644 --- a/src/rabbit_basic.erl +++ b/src/rabbit_basic.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_basic). diff --git a/src/rabbit_binary_generator.erl b/src/rabbit_binary_generator.erl index 05040485..ae5bbf51 100644 --- a/src/rabbit_binary_generator.erl +++ b/src/rabbit_binary_generator.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_binary_generator). diff --git a/src/rabbit_binary_parser.erl b/src/rabbit_binary_parser.erl index 9407dd2e..dc6d090f 100644 --- a/src/rabbit_binary_parser.erl +++ b/src/rabbit_binary_parser.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_binary_parser). diff --git a/src/rabbit_binding.erl b/src/rabbit_binding.erl index cb86e5ae..91f42e9c 100644 --- a/src/rabbit_binding.erl +++ b/src/rabbit_binding.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_binding). diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 22692dcb..a8e9432c 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_channel). diff --git a/src/rabbit_channel_sup.erl b/src/rabbit_channel_sup.erl index a0c7624b..df2e80ca 100644 --- a/src/rabbit_channel_sup.erl +++ b/src/rabbit_channel_sup.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_channel_sup). diff --git a/src/rabbit_channel_sup_sup.erl b/src/rabbit_channel_sup_sup.erl index 16fd08be..1d9ba48b 100644 --- a/src/rabbit_channel_sup_sup.erl +++ b/src/rabbit_channel_sup_sup.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_channel_sup_sup). diff --git a/src/rabbit_client_sup.erl b/src/rabbit_client_sup.erl index 7cc11fef..d6536e16 100644 --- a/src/rabbit_client_sup.erl +++ b/src/rabbit_client_sup.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_client_sup). diff --git a/src/rabbit_command_assembler.erl b/src/rabbit_command_assembler.erl index a88bec3d..4095ccf1 100644 --- a/src/rabbit_command_assembler.erl +++ b/src/rabbit_command_assembler.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_command_assembler). diff --git a/src/rabbit_connection_sup.erl b/src/rabbit_connection_sup.erl index 31bc51b8..94891629 100644 --- a/src/rabbit_connection_sup.erl +++ b/src/rabbit_connection_sup.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_connection_sup). diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index f5e70365..abc216c0 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_control_main). diff --git a/src/rabbit_direct.erl b/src/rabbit_direct.erl index 53144f3f..9002514f 100644 --- a/src/rabbit_direct.erl +++ b/src/rabbit_direct.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_direct). diff --git a/src/rabbit_disk_monitor.erl b/src/rabbit_disk_monitor.erl index 3bb163a1..29665747 100644 --- a/src/rabbit_disk_monitor.erl +++ b/src/rabbit_disk_monitor.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_disk_monitor). diff --git a/src/rabbit_error_logger.erl b/src/rabbit_error_logger.erl index 1360c82a..184dcf17 100644 --- a/src/rabbit_error_logger.erl +++ b/src/rabbit_error_logger.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_error_logger). diff --git a/src/rabbit_error_logger_file_h.erl b/src/rabbit_error_logger_file_h.erl index eb6247e0..d59641b0 100644 --- a/src/rabbit_error_logger_file_h.erl +++ b/src/rabbit_error_logger_file_h.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_error_logger_file_h). diff --git a/src/rabbit_event.erl b/src/rabbit_event.erl index a91a9916..4d3ddc79 100644 --- a/src/rabbit_event.erl +++ b/src/rabbit_event.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_event). diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index b4bdd348..49952a4d 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_exchange). diff --git a/src/rabbit_exchange_decorator.erl b/src/rabbit_exchange_decorator.erl index 3abaa48c..505998b9 100644 --- a/src/rabbit_exchange_decorator.erl +++ b/src/rabbit_exchange_decorator.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_exchange_decorator). diff --git a/src/rabbit_exchange_type.erl b/src/rabbit_exchange_type.erl index ebc59501..ce7a436b 100644 --- a/src/rabbit_exchange_type.erl +++ b/src/rabbit_exchange_type.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_exchange_type). diff --git a/src/rabbit_exchange_type_direct.erl b/src/rabbit_exchange_type_direct.erl index 10a79c55..52704ab6 100644 --- a/src/rabbit_exchange_type_direct.erl +++ b/src/rabbit_exchange_type_direct.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_exchange_type_direct). diff --git a/src/rabbit_exchange_type_fanout.erl b/src/rabbit_exchange_type_fanout.erl index 3ebd8548..068472bb 100644 --- a/src/rabbit_exchange_type_fanout.erl +++ b/src/rabbit_exchange_type_fanout.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_exchange_type_fanout). diff --git a/src/rabbit_exchange_type_headers.erl b/src/rabbit_exchange_type_headers.erl index 5b7f95fe..baec9c29 100644 --- a/src/rabbit_exchange_type_headers.erl +++ b/src/rabbit_exchange_type_headers.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_exchange_type_headers). diff --git a/src/rabbit_exchange_type_invalid.erl b/src/rabbit_exchange_type_invalid.erl index 07a8004a..84bb2182 100644 --- a/src/rabbit_exchange_type_invalid.erl +++ b/src/rabbit_exchange_type_invalid.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_exchange_type_invalid). diff --git a/src/rabbit_exchange_type_topic.erl b/src/rabbit_exchange_type_topic.erl index ce76ccb0..8ba29deb 100644 --- a/src/rabbit_exchange_type_topic.erl +++ b/src/rabbit_exchange_type_topic.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_exchange_type_topic). diff --git a/src/rabbit_file.erl b/src/rabbit_file.erl index 3ceb4989..0f429ae9 100644 --- a/src/rabbit_file.erl +++ b/src/rabbit_file.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2011-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2011-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_file). diff --git a/src/rabbit_framing.erl b/src/rabbit_framing.erl index 93305483..51aaa999 100644 --- a/src/rabbit_framing.erl +++ b/src/rabbit_framing.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% %% TODO auto-generate diff --git a/src/rabbit_guid.erl b/src/rabbit_guid.erl index bec29e59..70d1f0c1 100644 --- a/src/rabbit_guid.erl +++ b/src/rabbit_guid.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_guid). diff --git a/src/rabbit_heartbeat.erl b/src/rabbit_heartbeat.erl index e878f3bb..df9baed9 100644 --- a/src/rabbit_heartbeat.erl +++ b/src/rabbit_heartbeat.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_heartbeat). diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index 6a09573c..12a13c00 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% %% The purpose of the limiter is to stem the flow of messages from diff --git a/src/rabbit_log.erl b/src/rabbit_log.erl index 74cdeb23..2e3a1bbb 100644 --- a/src/rabbit_log.erl +++ b/src/rabbit_log.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_log). diff --git a/src/rabbit_memory_monitor.erl b/src/rabbit_memory_monitor.erl index 117ff95a..495f6fdd 100644 --- a/src/rabbit_memory_monitor.erl +++ b/src/rabbit_memory_monitor.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% diff --git a/src/rabbit_mirror_queue_coordinator.erl b/src/rabbit_mirror_queue_coordinator.erl index 625e2f07..c9918fed 100644 --- a/src/rabbit_mirror_queue_coordinator.erl +++ b/src/rabbit_mirror_queue_coordinator.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2010-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2010-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_mirror_queue_coordinator). diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index bcd4861a..6791389e 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2010-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2010-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_mirror_queue_master). diff --git a/src/rabbit_mirror_queue_misc.erl b/src/rabbit_mirror_queue_misc.erl index 8a663824..335b7c81 100644 --- a/src/rabbit_mirror_queue_misc.erl +++ b/src/rabbit_mirror_queue_misc.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2010-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2010-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_mirror_queue_misc). diff --git a/src/rabbit_mirror_queue_mode.erl b/src/rabbit_mirror_queue_mode.erl index cd072e25..9e2015d9 100644 --- a/src/rabbit_mirror_queue_mode.erl +++ b/src/rabbit_mirror_queue_mode.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2010-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2010-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_mirror_queue_mode). diff --git a/src/rabbit_mirror_queue_mode_all.erl b/src/rabbit_mirror_queue_mode_all.erl index 84d75b9a..3b5163a3 100644 --- a/src/rabbit_mirror_queue_mode_all.erl +++ b/src/rabbit_mirror_queue_mode_all.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2010-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2010-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_mirror_queue_mode_all). diff --git a/src/rabbit_mirror_queue_mode_exactly.erl b/src/rabbit_mirror_queue_mode_exactly.erl index 2a42c383..2841f87e 100644 --- a/src/rabbit_mirror_queue_mode_exactly.erl +++ b/src/rabbit_mirror_queue_mode_exactly.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2010-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2010-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_mirror_queue_mode_exactly). diff --git a/src/rabbit_mirror_queue_mode_nodes.erl b/src/rabbit_mirror_queue_mode_nodes.erl index aa62ad33..779b439d 100644 --- a/src/rabbit_mirror_queue_mode_nodes.erl +++ b/src/rabbit_mirror_queue_mode_nodes.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2010-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2010-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_mirror_queue_mode_nodes). diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 294e1ebb..38e0da3f 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2010-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2010-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_mirror_queue_slave). diff --git a/src/rabbit_mirror_queue_slave_sup.erl b/src/rabbit_mirror_queue_slave_sup.erl index be3924f0..43dbb4e9 100644 --- a/src/rabbit_mirror_queue_slave_sup.erl +++ b/src/rabbit_mirror_queue_slave_sup.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2010-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2010-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_mirror_queue_slave_sup). diff --git a/src/rabbit_mirror_queue_sync.erl b/src/rabbit_mirror_queue_sync.erl index b8cfe4a9..61e90105 100644 --- a/src/rabbit_mirror_queue_sync.erl +++ b/src/rabbit_mirror_queue_sync.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2010-2012 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2010-2012 GoPivotal, Inc. All rights reserved. %% -module(rabbit_mirror_queue_sync). diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index a1e95fd5..3df13876 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_misc). diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 8cd976fa..ea9bc7d7 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_mnesia). diff --git a/src/rabbit_msg_file.erl b/src/rabbit_msg_file.erl index 81111061..a37106d6 100644 --- a/src/rabbit_msg_file.erl +++ b/src/rabbit_msg_file.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_msg_file). diff --git a/src/rabbit_msg_store.erl b/src/rabbit_msg_store.erl index b783aa18..9a4439a7 100644 --- a/src/rabbit_msg_store.erl +++ b/src/rabbit_msg_store.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_msg_store). diff --git a/src/rabbit_msg_store_ets_index.erl b/src/rabbit_msg_store_ets_index.erl index bbc7db68..c17ff2cb 100644 --- a/src/rabbit_msg_store_ets_index.erl +++ b/src/rabbit_msg_store_ets_index.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_msg_store_ets_index). diff --git a/src/rabbit_msg_store_gc.erl b/src/rabbit_msg_store_gc.erl index 0dd7a7cc..1edd7d51 100644 --- a/src/rabbit_msg_store_gc.erl +++ b/src/rabbit_msg_store_gc.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_msg_store_gc). diff --git a/src/rabbit_msg_store_index.erl b/src/rabbit_msg_store_index.erl index f0096446..bb5f11b0 100644 --- a/src/rabbit_msg_store_index.erl +++ b/src/rabbit_msg_store_index.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_msg_store_index). diff --git a/src/rabbit_net.erl b/src/rabbit_net.erl index b7f4d019..e8c96818 100644 --- a/src/rabbit_net.erl +++ b/src/rabbit_net.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_net). diff --git a/src/rabbit_networking.erl b/src/rabbit_networking.erl index 702df040..6ed6239c 100644 --- a/src/rabbit_networking.erl +++ b/src/rabbit_networking.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_networking). diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index 7fcd1f99..c1de914f 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_node_monitor). diff --git a/src/rabbit_nodes.erl b/src/rabbit_nodes.erl index b85646d2..b54fdd2e 100644 --- a/src/rabbit_nodes.erl +++ b/src/rabbit_nodes.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_nodes). diff --git a/src/rabbit_parameter_validation.erl b/src/rabbit_parameter_validation.erl index a4bd5042..0a878432 100644 --- a/src/rabbit_parameter_validation.erl +++ b/src/rabbit_parameter_validation.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_parameter_validation). diff --git a/src/rabbit_plugins.erl b/src/rabbit_plugins.erl index 6f6515b0..168ced3c 100644 --- a/src/rabbit_plugins.erl +++ b/src/rabbit_plugins.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2011-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2011-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_plugins). diff --git a/src/rabbit_plugins_main.erl b/src/rabbit_plugins_main.erl index 308b80cd..6355f935 100644 --- a/src/rabbit_plugins_main.erl +++ b/src/rabbit_plugins_main.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2011-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2011-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_plugins_main). diff --git a/src/rabbit_policy.erl b/src/rabbit_policy.erl index 0990c662..91ca88dd 100644 --- a/src/rabbit_policy.erl +++ b/src/rabbit_policy.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_policy). diff --git a/src/rabbit_policy_validator.erl b/src/rabbit_policy_validator.erl index f0bc1a30..661db73d 100644 --- a/src/rabbit_policy_validator.erl +++ b/src/rabbit_policy_validator.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_policy_validator). diff --git a/src/rabbit_prelaunch.erl b/src/rabbit_prelaunch.erl index 3ce516d0..be407a02 100644 --- a/src/rabbit_prelaunch.erl +++ b/src/rabbit_prelaunch.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_prelaunch). diff --git a/src/rabbit_queue_collector.erl b/src/rabbit_queue_collector.erl index 521cd78b..6406f7e9 100644 --- a/src/rabbit_queue_collector.erl +++ b/src/rabbit_queue_collector.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_queue_collector). diff --git a/src/rabbit_queue_index.erl b/src/rabbit_queue_index.erl index 9a2d2a4a..0908fb73 100644 --- a/src/rabbit_queue_index.erl +++ b/src/rabbit_queue_index.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_queue_index). diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 61fac0e2..5e633f23 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_reader). diff --git a/src/rabbit_registry.erl b/src/rabbit_registry.erl index 6aae8de6..f933e4e9 100644 --- a/src/rabbit_registry.erl +++ b/src/rabbit_registry.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_registry). diff --git a/src/rabbit_restartable_sup.erl b/src/rabbit_restartable_sup.erl index 4c4ab2cf..65a2ca0a 100644 --- a/src/rabbit_restartable_sup.erl +++ b/src/rabbit_restartable_sup.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_restartable_sup). diff --git a/src/rabbit_router.erl b/src/rabbit_router.erl index 2eaef9a7..00343570 100644 --- a/src/rabbit_router.erl +++ b/src/rabbit_router.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_router). diff --git a/src/rabbit_runtime_parameter.erl b/src/rabbit_runtime_parameter.erl index 6b62c974..ee48165b 100644 --- a/src/rabbit_runtime_parameter.erl +++ b/src/rabbit_runtime_parameter.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_runtime_parameter). diff --git a/src/rabbit_runtime_parameters.erl b/src/rabbit_runtime_parameters.erl index 18eff722..c13c333e 100644 --- a/src/rabbit_runtime_parameters.erl +++ b/src/rabbit_runtime_parameters.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_runtime_parameters). diff --git a/src/rabbit_runtime_parameters_test.erl b/src/rabbit_runtime_parameters_test.erl index 05c1dbc1..05c85881 100644 --- a/src/rabbit_runtime_parameters_test.erl +++ b/src/rabbit_runtime_parameters_test.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_runtime_parameters_test). diff --git a/src/rabbit_sasl_report_file_h.erl b/src/rabbit_sasl_report_file_h.erl index 566db9a9..39a10ac3 100644 --- a/src/rabbit_sasl_report_file_h.erl +++ b/src/rabbit_sasl_report_file_h.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_sasl_report_file_h). diff --git a/src/rabbit_ssl.erl b/src/rabbit_ssl.erl index 96277b68..109bff30 100644 --- a/src/rabbit_ssl.erl +++ b/src/rabbit_ssl.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_ssl). diff --git a/src/rabbit_sup.erl b/src/rabbit_sup.erl index 6a6b2feb..c1deb14b 100644 --- a/src/rabbit_sup.erl +++ b/src/rabbit_sup.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_sup). diff --git a/src/rabbit_table.erl b/src/rabbit_table.erl index d1c0bb1e..a29c57d5 100644 --- a/src/rabbit_table.erl +++ b/src/rabbit_table.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_table). diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 21c54f3e..30cf9114 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_tests). diff --git a/src/rabbit_tests_event_receiver.erl b/src/rabbit_tests_event_receiver.erl index c52394c7..7b756cbc 100644 --- a/src/rabbit_tests_event_receiver.erl +++ b/src/rabbit_tests_event_receiver.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_tests_event_receiver). diff --git a/src/rabbit_trace.erl b/src/rabbit_trace.erl index 432055d4..d0dcaa71 100644 --- a/src/rabbit_trace.erl +++ b/src/rabbit_trace.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_trace). diff --git a/src/rabbit_types.erl b/src/rabbit_types.erl index c6007061..a36613db 100644 --- a/src/rabbit_types.erl +++ b/src/rabbit_types.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_types). diff --git a/src/rabbit_upgrade.erl b/src/rabbit_upgrade.erl index fde0dbe1..1047b823 100644 --- a/src/rabbit_upgrade.erl +++ b/src/rabbit_upgrade.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_upgrade). diff --git a/src/rabbit_upgrade_functions.erl b/src/rabbit_upgrade_functions.erl index b7b1635b..1613838c 100644 --- a/src/rabbit_upgrade_functions.erl +++ b/src/rabbit_upgrade_functions.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_upgrade_functions). diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 73ab64bf..ac2b9f52 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_variable_queue). diff --git a/src/rabbit_version.erl b/src/rabbit_version.erl index f81a4021..c629180e 100644 --- a/src/rabbit_version.erl +++ b/src/rabbit_version.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_version). diff --git a/src/rabbit_vhost.erl b/src/rabbit_vhost.erl index 3bf5354e..fcf77bf9 100644 --- a/src/rabbit_vhost.erl +++ b/src/rabbit_vhost.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_vhost). diff --git a/src/rabbit_vm.erl b/src/rabbit_vm.erl index e97824b9..597f9094 100644 --- a/src/rabbit_vm.erl +++ b/src/rabbit_vm.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_vm). diff --git a/src/rabbit_writer.erl b/src/rabbit_writer.erl index 2d15e6a2..c0b1f8e4 100644 --- a/src/rabbit_writer.erl +++ b/src/rabbit_writer.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_writer). diff --git a/src/supervisor2.erl b/src/supervisor2.erl index c98b528d..23bfe7f1 100644 --- a/src/supervisor2.erl +++ b/src/supervisor2.erl @@ -51,7 +51,7 @@ %% 5) normal, and {shutdown, _} exit reasons are all treated the same %% (i.e. are regarded as normal exits) %% -%% All modifications are (C) 2010-2013 VMware, Inc. +%% All modifications are (C) 2010-2013 GoPivotal, Inc. %% %% %CopyrightBegin% %% diff --git a/src/supervisor2_tests.erl b/src/supervisor2_tests.erl index f19a53e6..a841b1f0 100644 --- a/src/supervisor2_tests.erl +++ b/src/supervisor2_tests.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2011-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2011-2013 GoPivotal, Inc. All rights reserved. %% -module(supervisor2_tests). diff --git a/src/tcp_acceptor.erl b/src/tcp_acceptor.erl index 2725be31..267ce4f1 100644 --- a/src/tcp_acceptor.erl +++ b/src/tcp_acceptor.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(tcp_acceptor). diff --git a/src/tcp_acceptor_sup.erl b/src/tcp_acceptor_sup.erl index 61c747c9..3619875f 100644 --- a/src/tcp_acceptor_sup.erl +++ b/src/tcp_acceptor_sup.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(tcp_acceptor_sup). diff --git a/src/tcp_listener.erl b/src/tcp_listener.erl index 90e84f94..4b4a31b5 100644 --- a/src/tcp_listener.erl +++ b/src/tcp_listener.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(tcp_listener). diff --git a/src/tcp_listener_sup.erl b/src/tcp_listener_sup.erl index 7f850dbc..2a65cc17 100644 --- a/src/tcp_listener_sup.erl +++ b/src/tcp_listener_sup.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(tcp_listener_sup). diff --git a/src/test_sup.erl b/src/test_sup.erl index 3342adb5..51ff7b4e 100644 --- a/src/test_sup.erl +++ b/src/test_sup.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(test_sup). diff --git a/src/vm_memory_monitor.erl b/src/vm_memory_monitor.erl index f70156b6..d60b7fec 100644 --- a/src/vm_memory_monitor.erl +++ b/src/vm_memory_monitor.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% %% In practice Erlang shouldn't be allowed to grow to more than a half diff --git a/src/worker_pool.erl b/src/worker_pool.erl index 3bdeb377..488db5ec 100644 --- a/src/worker_pool.erl +++ b/src/worker_pool.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(worker_pool). diff --git a/src/worker_pool_sup.erl b/src/worker_pool_sup.erl index b9835f1e..24bc375c 100644 --- a/src/worker_pool_sup.erl +++ b/src/worker_pool_sup.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(worker_pool_sup). diff --git a/src/worker_pool_worker.erl b/src/worker_pool_worker.erl index 22b223d2..a976503f 100644 --- a/src/worker_pool_worker.erl +++ b/src/worker_pool_worker.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. %% -module(worker_pool_worker). -- cgit v1.2.1 From 738a63d53c2cec98fd01c00cc1eb5b2390bd1bff Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Mon, 1 Jul 2013 12:52:00 +0100 Subject: Add hipe modules --- ebin/rabbit_app.in | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ebin/rabbit_app.in b/ebin/rabbit_app.in index a88c1a61..c765c7e4 100644 --- a/ebin/rabbit_app.in +++ b/ebin/rabbit_app.in @@ -65,12 +65,14 @@ rabbit_binary_generator, rabbit_writer, delegate, gb_sets, lqueue, sets, orddict, rabbit_amqqueue, rabbit_limiter, gb_trees, - rabbit_queue_index, gen, dict, ordsets, - file_handle_cache, rabbit_msg_store, array, + rabbit_queue_index, rabbit_exchange_decorator, + gen, dict, ordsets, file_handle_cache, + rabbit_msg_store, array, rabbit_msg_store_ets_index, rabbit_msg_file, rabbit_exchange_type_fanout, rabbit_exchange_type_topic, mnesia, mnesia_lib, rpc, mnesia_tm, qlc, sofs, proplists, credit_flow, pmon, - ssl_connection, ssl_record, gen_fsm, ssl]} + ssl_connection, tls_connection, + ssl_record, tls_record, gen_fsm, ssl]} ]}]}. -- cgit v1.2.1 From 9204212084604adfeda94bacfa27350f095e125d Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Mon, 1 Jul 2013 15:01:52 +0100 Subject: Correction and contraction --- src/rabbit_amqqueue_process.erl | 4 ++-- src/rabbit_mirror_queue_master.erl | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 0d8d6e29..c17f8460 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -555,9 +555,9 @@ attempt_delivery(Delivery = #delivery{sender = SenderPid, {discarded, BQS1} -> State1 = State#q{backing_queue_state = BQS1}, {true, case MsgSeqNo of - undefined -> State; + undefined -> State1; _ -> #basic_message{id = MsgId} = Message, - confirm_messages([MsgId], State) + confirm_messages([MsgId], State1) end} end. diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index 8c061e52..f1798f5d 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -227,9 +227,8 @@ discard(MsgId, ChPid, State = #state { gm = GM, seen_status = SS }) -> false = dict:is_key(MsgId, SS), %% ASSERTION ok = gm:broadcast(GM, {discard, ChPid, MsgId}), - State1 = State #state { backing_queue_state = - BQ:discard(MsgId, ChPid, BQS) }, - ensure_monitoring(ChPid, State1). + ensure_monitoring(ChPid, State #state { backing_queue_state = + BQ:discard(MsgId, ChPid, BQS) }. dropwhile(Pred, State = #state{backing_queue = BQ, backing_queue_state = BQS }) -> -- cgit v1.2.1 From cdda1e9f134282ff3fcc59f3226ab389b995e810 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 1 Jul 2013 15:07:32 +0100 Subject: Cosmetic --- ebin/rabbit_app.in | 40 +++++++++++++++++----------------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/ebin/rabbit_app.in b/ebin/rabbit_app.in index c765c7e4..635869a2 100644 --- a/ebin/rabbit_app.in +++ b/ebin/rabbit_app.in @@ -53,26 +53,20 @@ {exit_on_close, false}]}, {hipe_compile, false}, %% see bug 24513 for how this list was created - {hipe_modules, [rabbit_reader, rabbit_channel, gen_server2, - rabbit_exchange, rabbit_command_assembler, - rabbit_framing_amqp_0_9_1, rabbit_basic, - rabbit_event, lists, queue, priority_queue, - rabbit_router, rabbit_trace, - rabbit_misc, rabbit_binary_parser, - rabbit_exchange_type_direct, rabbit_guid, - rabbit_net, rabbit_amqqueue_process, - rabbit_variable_queue, - rabbit_binary_generator, rabbit_writer, - delegate, gb_sets, lqueue, sets, orddict, - rabbit_amqqueue, rabbit_limiter, gb_trees, - rabbit_queue_index, rabbit_exchange_decorator, - gen, dict, ordsets, file_handle_cache, - rabbit_msg_store, array, - rabbit_msg_store_ets_index, rabbit_msg_file, - rabbit_exchange_type_fanout, - rabbit_exchange_type_topic, mnesia, - mnesia_lib, rpc, mnesia_tm, qlc, - sofs, proplists, credit_flow, pmon, - ssl_connection, tls_connection, - ssl_record, tls_record, gen_fsm, ssl]} - ]}]}. + {hipe_modules, + [rabbit_reader, rabbit_channel, gen_server2, rabbit_exchange, + rabbit_command_assembler, rabbit_framing_amqp_0_9_1, rabbit_basic, + rabbit_event, lists, queue, priority_queue, rabbit_router, + rabbit_trace, rabbit_misc, rabbit_binary_parser, + rabbit_exchange_type_direct, rabbit_guid, rabbit_net, + rabbit_amqqueue_process, rabbit_variable_queue, + rabbit_binary_generator, rabbit_writer, delegate, gb_sets, lqueue, + sets, orddict, rabbit_amqqueue, rabbit_limiter, gb_trees, + rabbit_queue_index, rabbit_exchange_decorator, gen, dict, ordsets, + file_handle_cache, rabbit_msg_store, array, + rabbit_msg_store_ets_index, rabbit_msg_file, + rabbit_exchange_type_fanout, rabbit_exchange_type_topic, mnesia, + mnesia_lib, rpc, mnesia_tm, qlc, sofs, proplists, credit_flow, + pmon, ssl_connection, tls_connection, ssl_record, tls_record, + gen_fsm, ssl]} + ]}]}. -- cgit v1.2.1 From 21d64bbe05b9a377b5de79570ebf79a3da3c4089 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Mon, 1 Jul 2013 16:32:17 +0100 Subject: Refactor --- src/rabbit_amqqueue_process.erl | 13 +++---------- src/rabbit_mirror_queue_master.erl | 9 +++++---- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index c17f8460..ae883852 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -533,9 +533,7 @@ run_message_queue(State) -> is_empty(State), State), State1. -attempt_delivery(Delivery = #delivery{sender = SenderPid, - msg_seq_no = MsgSeqNo, - message = Message}, +attempt_delivery(Delivery = #delivery{sender = SenderPid, message = Message}, Props, Delivered, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> case BQ:is_duplicate(Message, BQS) of @@ -551,14 +549,9 @@ attempt_delivery(Delivery = #delivery{sender = SenderPid, true, discard(Delivery, State1)} end, false, State#q{backing_queue_state = BQS1}); {published, BQS1} -> - {true, State#q{backing_queue_state = BQS1}}; + {true, State#q{backing_queue_state = BQS1}}; {discarded, BQS1} -> - State1 = State#q{backing_queue_state = BQS1}, - {true, case MsgSeqNo of - undefined -> State1; - _ -> #basic_message{id = MsgId} = Message, - confirm_messages([MsgId], State1) - end} + {true, State#q{backing_queue_state = BQS1}} end. deliver_or_enqueue(Delivery = #delivery{message = Message, sender = SenderPid}, diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index f1798f5d..3b49a6b8 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -228,7 +228,7 @@ discard(MsgId, ChPid, State = #state { gm = GM, false = dict:is_key(MsgId, SS), %% ASSERTION ok = gm:broadcast(GM, {discard, ChPid, MsgId}), ensure_monitoring(ChPid, State #state { backing_queue_state = - BQ:discard(MsgId, ChPid, BQS) }. + BQ:discard(MsgId, ChPid, BQS) }). dropwhile(Pred, State = #state{backing_queue = BQ, backing_queue_state = BQS }) -> @@ -395,9 +395,10 @@ is_duplicate(Message = #basic_message { id = MsgId }, {published, State #state { seen_status = dict:erase(MsgId, SS), confirmed = [MsgId | Confirmed] }}; {ok, discarded} -> - %% Message was discarded while we were a slave. Erase - %% and let amqqueue_process confirm if necessary. - {discarded, State #state { seen_status = dict:erase(MsgId, SS) }} + %% Message was discarded while we were a slave. + %% Erase and confirm. + {discarded, State #state { seen_status = dict:erase(MsgId, SS), + confirmed = [MsgId | Confirmed] }} end. %% --------------------------------------------------------------------------- -- cgit v1.2.1 From 56aeaa2f84c48c93e1c53fc5a9ffd6121e9044cd Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Tue, 2 Jul 2013 10:49:05 +0100 Subject: Further refactoring --- src/rabbit_amqqueue_process.erl | 4 +--- src/rabbit_backing_queue.erl | 12 ++++-------- src/rabbit_mirror_queue_master.erl | 18 +++++++++--------- 3 files changed, 14 insertions(+), 20 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index ae883852..11079523 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -548,9 +548,7 @@ attempt_delivery(Delivery = #delivery{sender = SenderPid, message = Message}, {{Message, Delivered, undefined}, true, discard(Delivery, State1)} end, false, State#q{backing_queue_state = BQS1}); - {published, BQS1} -> - {true, State#q{backing_queue_state = BQS1}}; - {discarded, BQS1} -> + {true, BQS1} -> {true, State#q{backing_queue_state = BQS1}} end. diff --git a/src/rabbit_backing_queue.erl b/src/rabbit_backing_queue.erl index 2f247448..bf26cb5a 100644 --- a/src/rabbit_backing_queue.erl +++ b/src/rabbit_backing_queue.erl @@ -90,10 +90,7 @@ -> {ack(), state()}. %% Called to inform the BQ about messages which have reached the -%% queue, but are not going to be further passed to BQ for some -%% reason. Note that this may be invoked for messages for which -%% BQ:is_duplicate/2 has already returned {'published' | 'discarded', -%% BQS}. +%% queue, but are not going to be further passed to BQ. -callback discard(rabbit_types:msg_id(), pid(), state()) -> state(). %% Return ids of messages which have been confirmed since the last @@ -216,11 +213,10 @@ -callback invoke(atom(), fun ((atom(), A) -> A), state()) -> state(). %% Called prior to a publish or publish_delivered call. Allows the BQ -%% to signal that it's already seen this message (and in what capacity -%% - i.e. was it published previously or discarded previously) and -%% thus the message should be dropped. +%% to signal that it's already seen this message, (e.g. it was published +%% or discarded previously) and thus the message should be dropped. -callback is_duplicate(rabbit_types:basic_message(), state()) - -> {'false'|'published'|'discarded', state()}. + -> {boolean(), state()}. -else. diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index 3b49a6b8..572cd0ca 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -382,8 +382,9 @@ is_duplicate(Message = #basic_message { id = MsgId }, %% immediately after calling is_duplicate). The msg is %% invalid. We will not see this again, nor will we be %% further involved in confirming this message, so erase. - {published, State #state { seen_status = dict:erase(MsgId, SS) }}; - {ok, confirmed} -> + {true, State #state { seen_status = dict:erase(MsgId, SS) }}; + {ok, Disposition} + when Disposition =:= confirmed %% It got published when we were a slave via gm, and %% confirmed some time after that (maybe even after %% promotion), but before we received the publish from the @@ -392,13 +393,12 @@ is_duplicate(Message = #basic_message { id = MsgId }, %% need to confirm now. As above, amqqueue_process will %% have the entry for the msg_id_to_channel mapping added %% immediately after calling is_duplicate/2. - {published, State #state { seen_status = dict:erase(MsgId, SS), - confirmed = [MsgId | Confirmed] }}; - {ok, discarded} -> - %% Message was discarded while we were a slave. - %% Erase and confirm. - {discarded, State #state { seen_status = dict:erase(MsgId, SS), - confirmed = [MsgId | Confirmed] }} + orelse Disposition =:= discarded -> + %% Message was discarded while we were a slave. Confirm now. + %% As above, amqqueue_process will have the entry for the + %% msg_id_to_channel mapping. + {true, State #state { seen_status = dict:erase(MsgId, SS), + confirmed = [MsgId | Confirmed] }} end. %% --------------------------------------------------------------------------- -- cgit v1.2.1 From e4c7f6bceec82208e65ac86366e9a67817b90f75 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 2 Jul 2013 14:20:29 +0100 Subject: Make the delegate monitoring API a drop in replacement for the built in one, and thus parameterise pmon and remove dmon. --- src/delegate.erl | 48 ++++++++++++++------------- src/dmon.erl | 70 --------------------------------------- src/pmon.erl | 53 +++++++++++++++++++---------- src/rabbit_mirror_queue_slave.erl | 14 ++++---- 4 files changed, 68 insertions(+), 117 deletions(-) delete mode 100644 src/dmon.erl diff --git a/src/delegate.erl b/src/delegate.erl index 475b087f..03086a59 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/1, demonitor/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]). @@ -30,6 +30,10 @@ -ifdef(use_specs). +-export_type([monitor_ref/0]). + +-type(monitor_ref() :: reference() | {atom(), reference()}). + -spec(start_link/1 :: (non_neg_integer()) -> {'ok', pid()} | ignore | {'error', any()}). -spec(invoke/2 :: @@ -38,8 +42,9 @@ [{pid(), term()}]}). -spec(invoke_no_result/2 :: (pid() | [pid()], fun ((pid()) -> any())) -> 'ok'). --spec(monitor/1 :: (pid()) -> reference()). --spec(demonitor/2 :: (pid(), reference()) -> 'true'). +-spec(monitor/2 :: (any(), pid()) -> monitor_ref()). +-spec(demonitor/1 :: (monitor_ref()) -> 'true'). +-spec(demonitor/2 :: (monitor_ref(), [any()]) -> 'true'). -spec(call/2 :: ( pid(), any()) -> any(); @@ -119,19 +124,18 @@ invoke_no_result(Pids, Fun) when is_list(Pids) -> safe_invoke(LocalPids, Fun), %% must not die ok. -monitor(Pid) when node(Pid) =:= node() -> - erlang:monitor(process, Pid); -monitor(Pid) -> - Node = node(Pid), - Name = delegate(Pid, [Node]), - gen_server2:call(Name, {monitor, self(), Pid}, infinity). +monitor(Type, Pid) when node(Pid) =:= node() -> + erlang:monitor(Type, Pid); +monitor(Type, Pid) -> + Name = delegate(Pid, [node(Pid)]), + {Name, gen_server2:call(Name, {monitor, Type, self(), Pid}, infinity)}. -demonitor(Pid, Ref) when node(Pid) =:= node() -> - erlang:demonitor(Ref, [flush]); -demonitor(Pid, Ref) -> - Node = node(Pid), - Name = delegate(Pid, [Node]), - gen_server2:call(Name, {demonitor, Ref}, infinity). +demonitor(Ref) -> ?MODULE:demonitor(Ref, []). + +demonitor(Ref, Options) when is_reference(Ref) -> + erlang:demonitor(Ref, Options); +demonitor({Name, Ref}, Options) -> + gen_server2:call(Name, {demonitor, Ref, Options}, infinity). call(PidOrPids, Msg) -> invoke(PidOrPids, fun (P) -> gen_server2:call(P, Msg, infinity) end). @@ -183,18 +187,16 @@ init([]) -> handle_call({invoke, Fun, Grouped}, _From, State = #state{node = Node}) -> {reply, safe_invoke(orddict:fetch(Node, Grouped), Fun), State, hibernate}; -handle_call({monitor, WantsMonitor, ToMonitor}, _From, +handle_call({monitor, Type, WantsMonitor, ToMonitor}, _From, State = #state{monitors = Monitors}) -> - Ref = erlang:monitor(process, ToMonitor), + Ref = erlang:monitor(Type, ToMonitor), State1 = State#state{monitors = dict:store(Ref, WantsMonitor, Monitors)}, {reply, Ref, State1, hibernate}; -handle_call({demonitor, Ref}, _From, State = #state{monitors = Monitors}) -> - %% We need to ensure we don't then respond to a 'DOWN' that's - %% currently in our mailbox - if we did then our client might then - %% get a 'DOWN' after demonitor() returns. +handle_call({demonitor, Ref, Options}, _From, + State = #state{monitors = Monitors}) -> State1 = State#state{monitors = dict:erase(Ref, Monitors)}, - {reply, erlang:demonitor(Ref, [flush]), State1, hibernate}. + {reply, erlang:demonitor(Ref, Options), State1, hibernate}. handle_cast({invoke, Fun, Grouped}, State = #state{node = Node}) -> safe_invoke(orddict:fetch(Node, Grouped), Fun), diff --git a/src/dmon.erl b/src/dmon.erl deleted file mode 100644 index dfb420c3..00000000 --- a/src/dmon.erl +++ /dev/null @@ -1,70 +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 VMware, Inc. -%% Copyright (c) 2011-2013 VMware, Inc. All rights reserved. -%% - --module(dmon). - --export([new/0, monitor/2, monitor_all/2, demonitor/2, is_monitored/2, erase/2, - monitored/1, is_empty/1]). - --compile({no_auto_import, [monitor/2]}). - --ifdef(use_specs). - -%%---------------------------------------------------------------------------- - --export_type([?MODULE/0]). - --opaque(?MODULE() :: dict()). - --type(item() :: pid() | {atom(), node()}). - --spec(new/0 :: () -> ?MODULE()). --spec(monitor/2 :: (item(), ?MODULE()) -> ?MODULE()). --spec(monitor_all/2 :: ([item()], ?MODULE()) -> ?MODULE()). --spec(demonitor/2 :: (item(), ?MODULE()) -> ?MODULE()). --spec(is_monitored/2 :: (item(), ?MODULE()) -> boolean()). --spec(erase/2 :: (item(), ?MODULE()) -> ?MODULE()). --spec(monitored/1 :: (?MODULE()) -> [item()]). --spec(is_empty/1 :: (?MODULE()) -> boolean()). - --endif. - -new() -> dict:new(). - -monitor(Item, M) -> - case dict:is_key(Item, M) of - true -> M; - false -> dict:store(Item, delegate:monitor(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). - -demonitor(Item, M) -> - case dict:find(Item, M) of - {ok, MRef} -> delegate:demonitor(Item, MRef), - dict:erase(Item, M); - error -> M - end. - -is_monitored(Item, M) -> dict:is_key(Item, M). - -erase(Item, M) -> dict:erase(Item, M). - -monitored(M) -> dict:fetch_keys(M). - -is_empty(M) -> dict:size(M) == 0. diff --git a/src/pmon.erl b/src/pmon.erl index ed32b8b2..136f6b90 100644 --- a/src/pmon.erl +++ b/src/pmon.erl @@ -16,22 +16,31 @@ -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, new/3, 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, monitor, demonitor1, demonitor2}). + -ifdef(use_specs). %%---------------------------------------------------------------------------- -export_type([?MODULE/0]). --opaque(?MODULE() :: dict()). +-opaque(?MODULE() :: #state{dict :: dict(), + monitor :: fun((atom(), any()) -> any()), + demonitor1 :: fun((any()) -> 'true'), + demonitor2 :: fun((any(), [any()]) -> 'true')}). -type(item() :: pid() | {atom(), node()}). -spec(new/0 :: () -> ?MODULE()). +-spec(new/1 :: ('erlang' | 'delegate') -> ?MODULE()). +-spec(new/3 :: (fun((atom(), any()) -> any()), + fun((any()) -> 'true'), + fun((any(), [any()]) -> 'true')) -> ?MODULE()). -spec(monitor/2 :: (item(), ?MODULE()) -> ?MODULE()). -spec(monitor_all/2 :: ([item()], ?MODULE()) -> ?MODULE()). -spec(demonitor/2 :: (item(), ?MODULE()) -> ?MODULE()). @@ -42,29 +51,39 @@ -endif. -new() -> dict:new(). +new() -> new(erlang). + +new(erlang) -> new(fun erlang:monitor/2, + fun erlang:demonitor/1, fun erlang:demonitor/2); +new(delegate) -> new(fun delegate:monitor/2, + fun delegate:demonitor/1, fun delegate:demonitor/2). + +new(Monitor, Demonitor1, Demonitor2) -> #state{dict = dict:new(), + monitor = Monitor, + demonitor1 = Demonitor1, + demonitor2 = Demonitor2}. -monitor(Item, M) -> +monitor(Item, S = #state{dict = M, monitor = Monitor}) -> 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, 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, demonitor1 = Demonitor1}) -> case dict:find(Item, M) of - {ok, MRef} -> erlang:demonitor(MRef), - dict:erase(Item, M); + {ok, MRef} -> Demonitor1(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_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index ac3e0df0..88b0f005 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -120,7 +120,7 @@ init(Q = #amqqueue { name = QName }) -> msg_id_ack = dict:new(), msg_id_status = dict:new(), - known_senders = dmon:new(), + known_senders = pmon:new(delegate), depth_delta = undefined }, @@ -489,7 +489,7 @@ promote_me(From, #state { q = Q = #amqqueue { name = QName }, %% Everything that we're monitoring, we need to ensure our new %% coordinator is monitoring. - MPids = dmon:monitored(KS), + MPids = pmon:monitored(KS), ok = rabbit_mirror_queue_coordinator:ensure_monitoring(CPid, MPids), %% We find all the messages that we've received from channels but @@ -603,14 +603,14 @@ ensure_rate_timer(State) -> 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 = dmon:monitor(ChPid, KS) }. + State #state { known_senders = pmon:monitor(ChPid, 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 %% essay). - ok = case dmon:is_monitored(ChPid, KS) of + ok = case pmon:is_monitored(ChPid, KS) of false -> ok; true -> confirm_sender_death(ChPid) end. @@ -628,7 +628,7 @@ confirm_sender_death(Pid) -> %% See comment in local_sender_death/2; we might have %% received a sender_death in the meanwhile so check %% again. - ok = case dmon:is_monitored(Pid, KS) of + ok = case pmon:is_monitored(Pid, KS) of false -> ok; true -> gm:broadcast(GM, {ensure_monitoring, [Pid]}), confirm_sender_death(Pid) @@ -776,7 +776,7 @@ process_instruction({sender_death, ChPid}, %% 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 dmon:is_monitored(ChPid, KS) of + {ok, case pmon:is_monitored(ChPid, KS) of false -> State; true -> MS1 = case dict:find(ChPid, SQ) of error -> @@ -788,7 +788,7 @@ process_instruction({sender_death, ChPid}, credit_flow:peer_down(ChPid), State #state { sender_queues = dict:erase(ChPid, SQ), msg_id_status = MS1, - known_senders = dmon:demonitor(ChPid, KS) } + known_senders = pmon:demonitor(ChPid, KS) } end}; process_instruction({depth, Depth}, State = #state { backing_queue = BQ, -- cgit v1.2.1 From c70ac2c72afad9035bd7290208a5da6cb0595fb7 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 2 Jul 2013 15:01:10 +0100 Subject: Guard aganist the case where a delegate-executed function does some monitoring. --- src/delegate.erl | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/delegate.erl b/src/delegate.erl index 03086a59..460a4899 100644 --- a/src/delegate.erl +++ b/src/delegate.erl @@ -204,9 +204,13 @@ handle_cast({invoke, Fun, Grouped}, State = #state{node = Node}) -> handle_info({'DOWN', Ref, process, Object, Info}, State = #state{monitors = Monitors}) -> - WantsMonitor = dict:fetch(Ref, Monitors), - WantsMonitor ! {'DOWN', Ref, process, Object, Info}, - {noreply, State#state{monitors = dict:erase(Ref, Monitors)}, hibernate}; + {noreply, case dict:find(Ref, Monitors) of + {ok, WantsMonitor} -> + WantsMonitor ! {'DOWN', Ref, process, Object, Info}, + State#state{monitors = dict:erase(Ref, Monitors)}; + error -> + State + end, hibernate}; handle_info(_Info, State) -> {noreply, State, hibernate}. -- cgit v1.2.1 From ece61e122771dddcad1e0892c55e7d5c3239a1a1 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 3 Jul 2013 11:17:18 +0100 Subject: Stick in yet another supervisor to get shutdown happening in the right order. --- src/rabbit_connection_sup.erl | 11 ++++++++++- src/rabbit_intermediate_sup.erl | 39 +++++++++++++++++++++++++++++++++++++++ src/rabbit_reader.erl | 22 +++++++++++----------- 3 files changed, 60 insertions(+), 12 deletions(-) create mode 100644 src/rabbit_intermediate_sup.erl diff --git a/src/rabbit_connection_sup.erl b/src/rabbit_connection_sup.erl index 31bc51b8..fedfe97a 100644 --- a/src/rabbit_connection_sup.erl +++ b/src/rabbit_connection_sup.erl @@ -42,11 +42,20 @@ start_link() -> 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 close + %% before the reader. 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} = + supervisor2:start_child( + SupPid, + {channel_sup3, {rabbit_intermediate_sup, start_link, []}, + intrinsic, infinity, supervisor, [rabbit_intermediate_sup]}), {ok, ReaderPid} = supervisor2:start_child( SupPid, {reader, {rabbit_reader, start_link, - [SupPid, Collector, + [ChannelSup3Pid, Collector, rabbit_heartbeat:start_heartbeat_fun(SupPid)]}, intrinsic, ?MAX_WAIT, worker, [rabbit_reader]}), {ok, SupPid, ReaderPid}. diff --git a/src/rabbit_intermediate_sup.erl b/src/rabbit_intermediate_sup.erl new file mode 100644 index 00000000..1919d9d6 --- /dev/null +++ b/src/rabbit_intermediate_sup.erl @@ -0,0 +1,39 @@ +%% 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 VMware, Inc. +%% Copyright (c) 2007-2013 VMware, 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 61fac0e2..3cf88d06 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -37,7 +37,7 @@ -record(v1, {parent, sock, connection, callback, recv_len, pending_recv, connection_state, queue_collector, heartbeater, stats_timer, - conn_sup_pid, channel_sup_sup_pid, start_heartbeat_fun, + ch_sup3_pid, channel_sup_sup_pid, start_heartbeat_fun, buf, buf_len, throttle}). -record(connection, {name, host, peer_host, port, peer_port, @@ -103,19 +103,19 @@ %%-------------------------------------------------------------------------- -start_link(ChannelSupSupPid, Collector, StartHeartbeatFun) -> - {ok, proc_lib:spawn_link(?MODULE, init, [self(), ChannelSupSupPid, +start_link(ChannelSup3Pid, Collector, StartHeartbeatFun) -> + {ok, proc_lib:spawn_link(?MODULE, init, [self(), ChannelSup3Pid, Collector, StartHeartbeatFun])}. shutdown(Pid, Explanation) -> gen_server:call(Pid, {shutdown, Explanation}, infinity). -init(Parent, ConnSupPid, Collector, StartHeartbeatFun) -> +init(Parent, ChSup3Pid, Collector, StartHeartbeatFun) -> Deb = sys:debug_options([]), receive {go, Sock, SockTransform} -> start_connection( - Parent, ConnSupPid, Collector, StartHeartbeatFun, Deb, Sock, + Parent, ChSup3Pid, Collector, StartHeartbeatFun, Deb, Sock, SockTransform) end. @@ -201,7 +201,7 @@ socket_op(Sock, Fun) -> exit(normal) end. -start_connection(Parent, ConnSupPid, Collector, StartHeartbeatFun, Deb, +start_connection(Parent, ChSup3Pid, Collector, StartHeartbeatFun, Deb, Sock, SockTransform) -> process_flag(trap_exit, true), Name = case rabbit_net:connection_string(Sock, inbound) of @@ -240,7 +240,7 @@ start_connection(Parent, ConnSupPid, Collector, StartHeartbeatFun, Deb, connection_state = pre_init, queue_collector = Collector, heartbeater = none, - conn_sup_pid = ConnSupPid, + ch_sup3_pid = ChSup3Pid, channel_sup_sup_pid = none, start_heartbeat_fun = StartHeartbeatFun, buf = [], @@ -837,7 +837,7 @@ handle_method0(#'connection.open'{virtual_host = VHostPath}, connection = Connection = #connection{ user = User, protocol = Protocol}, - conn_sup_pid = ConnSupPid, + ch_sup3_pid = ChSup3Pid, sock = Sock, throttle = Throttle}) -> ok = rabbit_access_control:check_vhost_access(User, VHostPath), @@ -847,7 +847,7 @@ handle_method0(#'connection.open'{virtual_host = VHostPath}, Throttle1 = Throttle#throttle{conserve_resources = Conserve}, {ok, ChannelSupSupPid} = supervisor2:start_child( - ConnSupPid, + ChSup3Pid, {channel_sup_sup, {rabbit_channel_sup_sup, start_link, []}, intrinsic, infinity, supervisor, [rabbit_channel_sup_sup]}), State1 = control_throttle( @@ -1048,9 +1048,9 @@ pack_for_1_0(#v1{parent = Parent, recv_len = RecvLen, pending_recv = PendingRecv, queue_collector = QueueCollector, - conn_sup_pid = ConnSupPid, + ch_sup3_pid = ChSup3Pid, start_heartbeat_fun = SHF, buf = Buf, buf_len = BufLen}) -> - {Parent, Sock, RecvLen, PendingRecv, QueueCollector, ConnSupPid, SHF, + {Parent, Sock, RecvLen, PendingRecv, QueueCollector, ChSup3Pid, SHF, Buf, BufLen}. -- cgit v1.2.1 From cf66dc1de13a35b485276090ac8eb62b2dc47849 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 3 Jul 2013 11:48:02 +0100 Subject: Remove the constraint that forget_cluster_node --offline requires the second-to-last node to be up. --- src/rabbit_mnesia.erl | 36 +++++++++++------------------------- 1 file changed, 11 insertions(+), 25 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 8cd976fa..c05f931c 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -294,27 +294,17 @@ remove_node_offline_node(Node) -> %% this operation from disc nodes. case {mnesia:system_info(running_db_nodes) -- [Node], node_type()} of {[], disc} -> - %% Note that while we check if the nodes was the last to go down, - %% apart from the node we're removing from, this is still unsafe. - %% Consider the situation in which A and B are clustered. A goes - %% down, and records B as the running node. Then B gets clustered - %% with C, C goes down and B goes down. In this case, C is the - %% second-to-last, but we don't know that and we'll remove B from A - %% anyway, even if that will lead to bad things. - case cluster_nodes(running) -- [node(), Node] of - [] -> start_mnesia(), - try - %% What we want to do here is replace the last node to - %% go down with the current node. The way we do this - %% is by force loading the table, and making sure that - %% they are loaded. - rabbit_table:force_load(), - rabbit_table:wait_for_replicated(), - forget_cluster_node(Node, false) - after - stop_mnesia() - end; - _ -> e(not_last_node_to_go_down) + start_mnesia(), + try + %% What we want to do here is replace the last node to + %% go down with the current node. The way we do this + %% is by force loading the table, and making sure that + %% they are loaded. + rabbit_table:force_load(), + rabbit_table:wait_for_replicated(), + forget_cluster_node(Node, false) + after + stop_mnesia() end; {_, _} -> e(removing_node_from_offline_node) @@ -879,10 +869,6 @@ error_description(offline_node_no_offline_flag) -> "You are trying to remove a node from an offline node. That is dangerous, " "but can be done with the --offline flag. Please consult the manual " "for rabbitmqctl for more information."; -error_description(not_last_node_to_go_down) -> - "The node you are trying to remove from was not the last to go down " - "(excluding the node you are removing). Please use the the last node " - "to go down to remove nodes when the cluster is offline."; error_description(removing_node_from_offline_node) -> "To remove a node remotely from an offline node, the node you are removing " "from must be a disc node and all the other nodes must be offline."; -- cgit v1.2.1 From 60ea4c8197f68a69e5b471ad528b6572e5d9b9d3 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 3 Jul 2013 12:04:07 +0100 Subject: Warning. --- docs/rabbitmqctl.1.xml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/rabbitmqctl.1.xml b/docs/rabbitmqctl.1.xml index 0f3c0faf..1d641144 100644 --- a/docs/rabbitmqctl.1.xml +++ b/docs/rabbitmqctl.1.xml @@ -405,6 +405,13 @@ must be offline, while the node we are removing from must be online, except when using the --offline flag. + + When using the --offline flag the node you + connect to will become the canonical source for cluster metadata + (e.g. which queues exist), even if it was not before. Therefore + you should use this command on the latest node to shut down if + at all possible. + For example: rabbitmqctl -n hare@mcnulty forget_cluster_node rabbit@stringer -- cgit v1.2.1 From eea4bdb393736e69fada2ffc56e98aaf342a035f Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 3 Jul 2013 13:22:56 +0100 Subject: Make join_cluster idempotent. --- src/rabbit_mnesia.erl | 42 ++++++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 8cd976fa..75a257f0 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -56,7 +56,8 @@ %% Main interface -spec(init/0 :: () -> 'ok'). --spec(join_cluster/2 :: (node(), node_type()) -> 'ok'). +-spec(join_cluster/2 :: (node(), node_type()) + -> 'ok' | {'ok', 'already_member'}). -spec(reset/0 :: () -> 'ok'). -spec(force_reset/0 :: () -> 'ok'). -spec(update_cluster_nodes/1 :: (node()) -> 'ok'). @@ -164,23 +165,24 @@ join_cluster(DiscoveryNode, NodeType) -> {error, _} = E -> throw(E) end, case me_in_nodes(ClusterNodes) of - true -> e(already_clustered); - false -> ok - end, - - %% reset the node. this simplifies things and it will be needed in - %% this case - we're joining a new cluster with new nodes which - %% are not in synch with the current node. I also lifts the burden - %% of reseting the node from the user. - reset_gracefully(), - - %% Join the cluster - rabbit_misc:local_info_msg("Clustering with ~p as ~p node~n", - [ClusterNodes, NodeType]), - ok = init_db_with_mnesia(ClusterNodes, NodeType, true, true), - rabbit_node_monitor:notify_joined_cluster(), - - ok. + false -> + %% reset the node. this simplifies things and it will be needed in + %% this case - we're joining a new cluster with new nodes which + %% are not in synch with the current node. I also lifts the burden + %% of reseting the node from the user. + reset_gracefully(), + + %% Join the cluster + rabbit_misc:local_info_msg("Clustering with ~p as ~p node~n", + [ClusterNodes, NodeType]), + ok = init_db_with_mnesia(ClusterNodes, NodeType, true, true), + rabbit_node_monitor:notify_joined_cluster(), + ok; + true -> + rabbit_misc:local_info_msg("Already member of cluster: ~p~n", + [ClusterNodes]), + {ok, already_member} + end. %% return node to its virgin state, where it is not member of any %% cluster, has no cluster configuration, no local database, and no @@ -853,10 +855,6 @@ error_description(clustering_only_disc_node) -> error_description(resetting_only_disc_node) -> "You cannot reset a node when it is the only disc node in a cluster. " "Please convert another node of the cluster to a disc node first."; -error_description(already_clustered) -> - "You are already clustered with the nodes you have selected. If the " - "node you are trying to cluster with is not present in the current " - "node status, use 'update_cluster_nodes'."; error_description(not_clustered) -> "Non-clustered nodes can only be disc nodes."; error_description(cannot_connect_to_cluster) -> -- cgit v1.2.1 From 610ef3c25452403214c8aa0b36d02ec85374a12f Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 3 Jul 2013 14:26:12 +0100 Subject: Ensure that after we have done forget_cluster_node --offline we will boot the next time. --- src/rabbit_mnesia.erl | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index c05f931c..d8db9229 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -302,7 +302,8 @@ remove_node_offline_node(Node) -> %% they are loaded. rabbit_table:force_load(), rabbit_table:wait_for_replicated(), - forget_cluster_node(Node, false) + forget_cluster_node(Node, false), + force_load_next_boot() after stop_mnesia() end; @@ -429,11 +430,13 @@ init_db(ClusterNodes, NodeType, CheckOtherNodes) -> ok = create_schema(); {[], true, disc} -> %% First disc node up + maybe_force_load(), ok; {[AnotherNode | _], _, _} -> %% Subsequent node in cluster, catch up ensure_version_ok( rpc:call(AnotherNode, rabbit_version, recorded, [])), + maybe_force_load(), ok = rabbit_table:wait_for_replicated(), ok = rabbit_table:create_local_copy(NodeType) end, @@ -513,6 +516,19 @@ copy_db(Destination) -> ok = ensure_mnesia_not_running(), rabbit_file:recursive_copy(dir(), Destination). +force_load_filename() -> + filename:join(rabbit_mnesia:dir(), "force_load"). + +force_load_next_boot() -> + rabbit_file:write_file(force_load_filename(), <<"">>). + +maybe_force_load() -> + case rabbit_file:is_file(force_load_filename()) of + true -> rabbit_table:force_load(), + rabbit_file:delete(force_load_filename()); + false -> ok + end. + %% This does not guarantee us much, but it avoids some situations that %% will definitely end up badly check_cluster_consistency() -> -- cgit v1.2.1 From ac666e08c5405aa0b4e27a7edaaaf05d60e1e55e Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 3 Jul 2013 17:37:53 +0100 Subject: Revert changes to the registry key "HKLM\Software\VMware, Inc.\RabbitMQ Server", which we want to keep constant. --- packaging/windows-exe/rabbitmq_nsi.in | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packaging/windows-exe/rabbitmq_nsi.in b/packaging/windows-exe/rabbitmq_nsi.in index 7ba4d17d..2ab8eee7 100644 --- a/packaging/windows-exe/rabbitmq_nsi.in +++ b/packaging/windows-exe/rabbitmq_nsi.in @@ -24,7 +24,7 @@ InstallDir "$PROGRAMFILES\RabbitMQ Server" ; Registry key to check for directory (so if you install again, it will ; overwrite the old one automatically) -InstallDirRegKey HKLM "Software\GoPivotal, Inc.\RabbitMQ Server" "Install_Dir" +InstallDirRegKey HKLM "Software\VMware, Inc.\RabbitMQ Server" "Install_Dir" ; Request application privileges for Windows Vista RequestExecutionLevel admin @@ -77,7 +77,7 @@ Section "RabbitMQ Server (required)" Rabbit File "rabbitmq.ico" ; Write the installation path into the registry - WriteRegStr HKLM "SOFTWARE\GoPivotal, Inc.\RabbitMQ Server" "Install_Dir" "$INSTDIR" + WriteRegStr HKLM "SOFTWARE\VMware, Inc.\RabbitMQ Server" "Install_Dir" "$INSTDIR" ; Write the uninstall keys for Windows WriteRegStr HKLM ${uninstall} "DisplayName" "RabbitMQ Server" @@ -151,7 +151,7 @@ Section "Uninstall" ; Remove registry keys DeleteRegKey HKLM ${uninstall} - DeleteRegKey HKLM "SOFTWARE\GoPivotal, Inc.\RabbitMQ Server" + DeleteRegKey HKLM "SOFTWARE\VMware, Inc.\RabbitMQ Server" ; TODO these will fail if the service is not installed - do we care? ExpandEnvStrings $0 %COMSPEC% -- cgit v1.2.1 From 3de186a95a8c81cd83b9b4c7e5a7aa5cfbc12f5f Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 4 Jul 2013 14:56:48 +0100 Subject: Get amqqueu_process to montor via delegate too, tighten delegate specs, simplify pmon parameterisation. --- src/delegate.erl | 4 ++-- src/pmon.erl | 33 +++++++++++---------------------- src/rabbit_amqqueue_process.erl | 2 +- 3 files changed, 14 insertions(+), 25 deletions(-) diff --git a/src/delegate.erl b/src/delegate.erl index 460a4899..dad2dd3c 100644 --- a/src/delegate.erl +++ b/src/delegate.erl @@ -42,9 +42,9 @@ [{pid(), term()}]}). -spec(invoke_no_result/2 :: (pid() | [pid()], fun ((pid()) -> any())) -> 'ok'). --spec(monitor/2 :: (any(), pid()) -> monitor_ref()). +-spec(monitor/2 :: ('process', pid()) -> monitor_ref()). -spec(demonitor/1 :: (monitor_ref()) -> 'true'). --spec(demonitor/2 :: (monitor_ref(), [any()]) -> 'true'). +-spec(demonitor/2 :: (monitor_ref(), ['flush']) -> 'true'). -spec(call/2 :: ( pid(), any()) -> any(); diff --git a/src/pmon.erl b/src/pmon.erl index 136f6b90..1e31eb60 100644 --- a/src/pmon.erl +++ b/src/pmon.erl @@ -16,12 +16,12 @@ -module(pmon). --export([new/0, new/1, new/3, monitor/2, monitor_all/2, demonitor/2, +-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, monitor, demonitor1, demonitor2}). +-record(state, {dict, module}). -ifdef(use_specs). @@ -29,18 +29,13 @@ -export_type([?MODULE/0]). --opaque(?MODULE() :: #state{dict :: dict(), - monitor :: fun((atom(), any()) -> any()), - demonitor1 :: fun((any()) -> 'true'), - demonitor2 :: fun((any(), [any()]) -> 'true')}). +-opaque(?MODULE() :: #state{dict :: dict(), + module :: atom()}). -type(item() :: pid() | {atom(), node()}). -spec(new/0 :: () -> ?MODULE()). -spec(new/1 :: ('erlang' | 'delegate') -> ?MODULE()). --spec(new/3 :: (fun((atom(), any()) -> any()), - fun((any()) -> 'true'), - fun((any(), [any()]) -> 'true')) -> ?MODULE()). -spec(monitor/2 :: (item(), ?MODULE()) -> ?MODULE()). -spec(monitor_all/2 :: ([item()], ?MODULE()) -> ?MODULE()). -spec(demonitor/2 :: (item(), ?MODULE()) -> ?MODULE()). @@ -53,29 +48,23 @@ new() -> new(erlang). -new(erlang) -> new(fun erlang:monitor/2, - fun erlang:demonitor/1, fun erlang:demonitor/2); -new(delegate) -> new(fun delegate:monitor/2, - fun delegate:demonitor/1, fun delegate:demonitor/2). +new(Module) -> #state{dict = dict:new(), + module = Module}. -new(Monitor, Demonitor1, Demonitor2) -> #state{dict = dict:new(), - monitor = Monitor, - demonitor1 = Demonitor1, - demonitor2 = Demonitor2}. - -monitor(Item, S = #state{dict = M, monitor = Monitor}) -> +monitor(Item, S = #state{dict = M, module = Module}) -> case dict:is_key(Item, M) of true -> S; - false -> S#state{dict = dict:store(Item, Monitor(process, Item), M)} + false -> S#state{dict = dict:store( + Item, Module:monitor(process, Item), M)} end. 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, S = #state{dict = M, demonitor1 = Demonitor1}) -> +demonitor(Item, S = #state{dict = M, module = Module}) -> case dict:find(Item, M) of - {ok, MRef} -> Demonitor1(MRef), + {ok, MRef} -> Module:demonitor(MRef), S#state{dict = dict:erase(Item, M)}; error -> M end. diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index c790a12d..c5045609 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). -- cgit v1.2.1 From c72036e9cfb18b92ff2df372d117c0c32a5a7bf6 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 4 Jul 2013 17:03:21 +0100 Subject: More sensible API for partitions, do not return errors. --- src/rabbit_autoheal.erl | 3 +-- src/rabbit_mnesia.erl | 3 +-- src/rabbit_node_monitor.erl | 11 ++++++++--- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/rabbit_autoheal.erl b/src/rabbit_autoheal.erl index f903677b..6cbfcce3 100644 --- a/src/rabbit_autoheal.erl +++ b/src/rabbit_autoheal.erl @@ -181,8 +181,7 @@ partition_value(Partition) -> all_partitions(PartitionedWith) -> Nodes = rabbit_mnesia:cluster_nodes(all), Partitions = [{node(), PartitionedWith} | - [rpc:call(Node, rabbit_node_monitor, partitions, []) - || Node <- Nodes -- [node()]]], + rabbit_node_monitor:partitions(Nodes -- [node()])], all_partitions(Partitions, [Nodes]). all_partitions([], Partitions) -> diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 75a257f0..7e37c858 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -341,8 +341,7 @@ status() -> end. mnesia_partitions(Nodes) -> - {Replies, _BadNodes} = rpc:multicall( - Nodes, rabbit_node_monitor, partitions, []), + Replies = rabbit_node_monitor:partitions(Nodes), [Reply || Reply = {_, R} <- Replies, R =/= []]. is_running() -> mnesia:system_info(is_running) =:= yes. diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index 7fcd1f99..1cafb50f 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -24,7 +24,7 @@ write_cluster_status/1, read_cluster_status/0, update_cluster_status/0, reset_cluster_status/0]). -export([notify_node_up/0, notify_joined_cluster/0, notify_left_cluster/1]). --export([partitions/0, subscribe/1]). +-export([partitions/0, partitions/1, subscribe/1]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, @@ -57,7 +57,8 @@ -spec(notify_joined_cluster/0 :: () -> 'ok'). -spec(notify_left_cluster/1 :: (node()) -> 'ok'). --spec(partitions/0 :: () -> {node(), [node()]}). +-spec(partitions/0 :: () -> [node()]). +-spec(partitions/1 :: ([node()]) -> [{node(), [node()]}]). -spec(subscribe/1 :: (pid()) -> 'ok'). -spec(all_rabbit_nodes_up/0 :: () -> boolean()). @@ -187,6 +188,10 @@ notify_left_cluster(Node) -> partitions() -> gen_server:call(?SERVER, partitions, infinity). +partitions(Nodes) -> + {Replies, _} = gen_server:multi_call(Nodes, ?SERVER, partitions, infinity), + Replies. + subscribe(Pid) -> gen_server:cast(?SERVER, {subscribe, Pid}). @@ -208,7 +213,7 @@ init([]) -> autoheal = rabbit_autoheal:init()}}. handle_call(partitions, _From, State = #state{partitions = Partitions}) -> - {reply, {node(), Partitions}, State}; + {reply, Partitions, State}; handle_call(_Request, _From, State) -> {noreply, State}. -- 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(-) 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 1b2c564d4a086277ee5811eae7d09bab09525eee Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Tue, 9 Jul 2013 12:02:25 +0100 Subject: ensure that srcdist inserts the correct version into the bundled makefile --- Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile b/Makefile index 9a6ad7a6..79d62b51 100644 --- a/Makefile +++ b/Makefile @@ -262,6 +262,8 @@ srcdist: distclean cp -r $(AMQP_CODEGEN_DIR)/* $(TARGET_SRC_DIR)/codegen/ cp codegen.py Makefile generate_app generate_deps calculate-relative $(TARGET_SRC_DIR) + sed -i -e "s:^VERSION?=0.0.0:VERSION=${VERSION}:" $(TARGET_SRC_DIR)/Makefile + cp -r scripts $(TARGET_SRC_DIR) cp -r $(DOCS_DIR) $(TARGET_SRC_DIR) chmod 0755 $(TARGET_SRC_DIR)/scripts/* -- cgit v1.2.1 From ff96a34159cf49f4a51e622861a4df1b4b0236c4 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Wed, 17 Jul 2013 10:25:58 +0100 Subject: Add version.mk, avoid sed when building srcdist, allow VERSION to be provided --- Makefile | 5 +++-- version.mk | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 version.mk diff --git a/Makefile b/Makefile index 79d62b51..56d4b3c0 100644 --- a/Makefile +++ b/Makefile @@ -56,7 +56,8 @@ endif #other args: +native +"{hipe,[o3,verbose]}" -Ddebug=true +debug_info +no_strict_record_tests ERLC_OPTS=-I $(INCLUDE_DIR) -o $(EBIN_DIR) -Wall -v +debug_info $(call boolean_macro,$(USE_SPECS),use_specs) $(call boolean_macro,$(USE_PROPER_QC),use_proper_qc) -VERSION?=0.0.0 +include version.mk + PLUGINS_SRC_DIR?=$(shell [ -d "plugins-src" ] && echo "plugins-src" || echo ) PLUGINS_DIR=plugins TARBALL_NAME=rabbitmq-server-$(VERSION) @@ -262,7 +263,7 @@ srcdist: distclean cp -r $(AMQP_CODEGEN_DIR)/* $(TARGET_SRC_DIR)/codegen/ cp codegen.py Makefile generate_app generate_deps calculate-relative $(TARGET_SRC_DIR) - sed -i -e "s:^VERSION?=0.0.0:VERSION=${VERSION}:" $(TARGET_SRC_DIR)/Makefile + echo "VERSION?=${VERSION}" > $(TARGET_SRC_DIR)/version.mk cp -r scripts $(TARGET_SRC_DIR) cp -r $(DOCS_DIR) $(TARGET_SRC_DIR) diff --git a/version.mk b/version.mk new file mode 100644 index 00000000..5683af4a --- /dev/null +++ b/version.mk @@ -0,0 +1 @@ +VERSION?=0.0.0 -- cgit v1.2.1 From 5c123c21456a364871e1ef52b626a780ad9f1150 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Fri, 19 Jul 2013 13:23:10 +0100 Subject: Slaves detect stale master pids on startup --- src/gm.erl | 25 ++++++++++++++++++++++++- src/rabbit_mirror_queue_slave.erl | 7 ++++--- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/gm.erl b/src/gm.erl index 3f0909e8..76e769d9 100644 --- a/src/gm.erl +++ b/src/gm.erl @@ -81,6 +81,12 @@ %% Provide the Pid. Returns a proplist with various facts, including %% the group name and the current group members. %% +%% validate_members/2 +%% Check whether a given member list agrees with the chosen member's +%% view. Any differences will be communicated via the members_changed +%% callback. If there are no differences then there will be no reply. +%% Note that members will not necessarily share the same view. +%% %% forget_group/1 %% Provide the group name. Removes its mnesia record. Makes no attempt %% to ensure the group is empty. @@ -377,7 +383,7 @@ -behaviour(gen_server2). -export([create_tables/0, start_link/4, leave/1, broadcast/2, - confirmed_broadcast/2, info/1, forget_group/1]). + confirmed_broadcast/2, info/1, validate_members/2, forget_group/1]). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3, prioritise_info/3]). @@ -438,6 +444,7 @@ -spec(broadcast/2 :: (pid(), any()) -> 'ok'). -spec(confirmed_broadcast/2 :: (pid(), any()) -> 'ok'). -spec(info/1 :: (pid()) -> rabbit_types:infos()). +-spec(validate_members/2 :: (pid(), [pid()]) -> 'ok'). -spec(forget_group/1 :: (group_name()) -> 'ok'). %% The joined, members_changed and handle_msg callbacks can all return @@ -524,6 +531,9 @@ confirmed_broadcast(Server, Msg) -> info(Server) -> gen_server2:call(Server, info, infinity). +validate_members(Server, Members) -> + gen_server2:cast(Server, {validate_members, Members}). + forget_group(GroupName) -> {atomic, ok} = mnesia:sync_transaction( fun () -> @@ -659,6 +669,19 @@ handle_cast(join, State = #state { self = Self, handle_callback_result( {Module:joined(Args, get_pids(all_known_members(View))), State1}); +handle_cast({validate_members, OldMembers}, + State = #state { view = View, + module = Module, + callback_args = Args }) -> + NewMembers = get_pids(all_known_members(View)), + Births = NewMembers -- OldMembers, + Deaths = OldMembers -- NewMembers, + case {Births, Deaths} of + {[], []} -> noreply(State); + _ -> Result = Module:members_changed(Args, Births, Deaths), + handle_callback_result({Result, State}) + end; + handle_cast(leave, State) -> {stop, normal, State}. diff --git a/src/rabbit_mirror_queue_slave.erl b/src/rabbit_mirror_queue_slave.erl index 294e1ebb..5e83e8a4 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -100,7 +100,7 @@ init(Q = #amqqueue { name = QName }) -> Node = node(), case rabbit_misc:execute_mnesia_transaction( fun() -> init_it(Self, GM, Node, QName) end) of - {new, QPid} -> + {new, QPid, GMPids} -> erlang:monitor(process, QPid), ok = file_handle_cache:register_callback( rabbit_amqqueue, set_maximum_since_use, [Self]), @@ -127,6 +127,7 @@ init(Q = #amqqueue { name = QName }) -> rabbit_event:notify(queue_slave_created, infos(?CREATION_EVENT_KEYS, State)), ok = gm:broadcast(GM, request_depth), + ok = gm:validate_members(GM, [GM | [G || {G, _} <- GMPids]]), {ok, State, hibernate, {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}; @@ -144,7 +145,7 @@ init_it(Self, GM, Node, QName) -> mnesia:read({rabbit_queue, QName}), case [Pid || Pid <- [QPid | SPids], node(Pid) =:= Node] of [] -> add_slave(Q, Self, GM), - {new, QPid}; + {new, QPid, GMPids}; [QPid] -> case rabbit_misc:is_process_alive(QPid) of true -> duplicate_live_master; false -> {stale, QPid} @@ -156,7 +157,7 @@ init_it(Self, GM, Node, QName) -> gm_pids = [T || T = {_, S} <- GMPids, S =/= SPid] }, add_slave(Q1, Self, GM), - {new, QPid} + {new, QPid, GMPids} end end. -- cgit v1.2.1 From f314d73b87ec497bf91c95abfdb545b189853361 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Fri, 19 Jul 2013 13:40:45 +0100 Subject: Don't roll over to a lower GM view version than we have ourselves --- src/gm.erl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/gm.erl b/src/gm.erl index 3f0909e8..28ebfd47 100644 --- a/src/gm.erl +++ b/src/gm.erl @@ -1053,7 +1053,7 @@ prune_or_create_group(Self, GroupName, TxnFun) -> fun () -> GroupNew = #gm_group { name = GroupName, members = [Self], - version = ?VERSION_START }, + version = get_version(Self) }, case mnesia:read({?GROUP_TABLE, GroupName}) of [] -> mnesia:write(GroupNew), @@ -1294,6 +1294,8 @@ remove_erased_members(MembersState, View) -> MembersState1) end, blank_member_state(), all_known_members(View)). +get_version({Version, _Pid}) -> Version. + get_pid({_Version, Pid}) -> Pid. get_pids(Ids) -> [Pid || {_Version, Pid} <- Ids]. -- cgit v1.2.1 From b59ec38defb1457893c5f2d59c69b3e50a51ed63 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Fri, 19 Jul 2013 13:50:17 +0100 Subject: Checking gm_pids is a better way of filtering out HA queues --- src/rabbit_amqqueue.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 767abeb0..d62e6476 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -650,7 +650,7 @@ on_node_down(Node) -> fun () -> QsDels = qlc:e(qlc:q([{QName, delete_queue(QName)} || #amqqueue{name = QName, pid = Pid, - slave_pids = []} + gm_pids = []} <- mnesia:table(rabbit_queue), node(Pid) == Node andalso not rabbit_misc:is_process_alive(Pid)])), -- cgit v1.2.1 From 19aee057841b02b1e874794cb85be776ea854780 Mon Sep 17 00:00:00 2001 From: Alvaro Videla Date: Mon, 22 Jul 2013 21:19:34 +0200 Subject: introduces ssl_apps as a env parameter --- ebin/rabbit_app.in | 5 ++++- packaging/standalone/src/rabbit_release.erl | 4 +++- src/rabbit_networking.erl | 3 ++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/ebin/rabbit_app.in b/ebin/rabbit_app.in index 635869a2..5822078b 100644 --- a/ebin/rabbit_app.in +++ b/ebin/rabbit_app.in @@ -68,5 +68,8 @@ rabbit_exchange_type_fanout, rabbit_exchange_type_topic, mnesia, mnesia_lib, rpc, mnesia_tm, qlc, sofs, proplists, credit_flow, pmon, ssl_connection, tls_connection, ssl_record, tls_record, - gen_fsm, ssl]} + gen_fsm, ssl]}, + %% see bug 25668 about why we need these + {ssl_apps, + [asn1, crypto, public_key, ssl]} ]}]}. diff --git a/packaging/standalone/src/rabbit_release.erl b/packaging/standalone/src/rabbit_release.erl index 26f36d68..9473cbda 100644 --- a/packaging/standalone/src/rabbit_release.erl +++ b/packaging/standalone/src/rabbit_release.erl @@ -54,7 +54,9 @@ start() -> end, %% we need a list of ERTS apps we need to ship with rabbit - BaseApps = AllApps -- PluginAppNames, + {ok, SslAppsConfig} = application:get_env(rabbit, ssl_apps), + + BaseApps = SslAppsConfig ++ AllApps -- PluginAppNames, AppVersions = [determine_version(App) || App <- BaseApps], RabbitVersion = proplists:get_value(rabbit, AppVersions), diff --git a/src/rabbit_networking.erl b/src/rabbit_networking.erl index 702df040..ebbedab8 100644 --- a/src/rabbit_networking.erl +++ b/src/rabbit_networking.erl @@ -145,7 +145,8 @@ start() -> rabbit_sup:start_supervisor_child( {rabbit_connection_sup,start_link,[]}]). ensure_ssl() -> - ok = app_utils:start_applications([asn1, crypto, public_key, ssl]), + {ok, SslAppsConfig} = application:get_env(rabbit, ssl_apps), + 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 -- 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(-) 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 c433f342d1a61b5a188bb4de449127a8ddb5433f Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 23 Jul 2013 14:33:17 +0100 Subject: Make monitoring via delegates async. This has the downside that you can't monitor the same pid more than once from the same process, but that is enforced by pmon anyway which is the only client of this code. The upside is that cross-cluster basic.get doesn't deadlock... --- src/delegate.erl | 51 +++++++++++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/src/delegate.erl b/src/delegate.erl index dad2dd3c..30ee33b6 100644 --- a/src/delegate.erl +++ b/src/delegate.erl @@ -24,7 +24,7 @@ -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). --record(state, {node, monitors}). +-record(state, {node, monitors, name}). %%---------------------------------------------------------------------------- @@ -32,7 +32,7 @@ -export_type([monitor_ref/0]). --type(monitor_ref() :: reference() | {atom(), reference()}). +-type(monitor_ref() :: reference() | {atom(), pid()}). -spec(start_link/1 :: (non_neg_integer()) -> {'ok', pid()} | ignore | {'error', any()}). @@ -61,7 +61,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, Fun) when is_pid(Pid) andalso node(Pid) =:= node() -> Fun(Pid); @@ -128,14 +129,15 @@ monitor(Type, Pid) when node(Pid) =:= node() -> erlang:monitor(Type, Pid); monitor(Type, Pid) -> Name = delegate(Pid, [node(Pid)]), - {Name, gen_server2:call(Name, {monitor, Type, self(), Pid}, infinity)}. + 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, Ref}, Options) -> - gen_server2:call(Name, {demonitor, Ref, Options}, infinity). +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). @@ -180,34 +182,39 @@ safe_invoke(Pid, Fun) when is_pid(Pid) -> %%---------------------------------------------------------------------------- -init([]) -> - {ok, #state{node = node(), monitors = dict:new()}, 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, Fun, Grouped}, _From, State = #state{node = Node}) -> - {reply, safe_invoke(orddict:fetch(Node, Grouped), Fun), State, hibernate}; + {reply, safe_invoke(orddict:fetch(Node, Grouped), Fun), State, hibernate}. -handle_call({monitor, Type, WantsMonitor, ToMonitor}, _From, +handle_cast({monitor, Type, WantsMonitor, Pid}, State = #state{monitors = Monitors}) -> - Ref = erlang:monitor(Type, ToMonitor), - State1 = State#state{monitors = dict:store(Ref, WantsMonitor, Monitors)}, - {reply, Ref, State1, hibernate}; + Ref = erlang:monitor(Type, Pid), + Monitors1 = dict:store(Pid, {WantsMonitor, Ref}, Monitors), + {noreply, State#state{monitors = Monitors1}, hibernate}; -handle_call({demonitor, Ref, Options}, _From, +handle_cast({demonitor, Pid, Options}, State = #state{monitors = Monitors}) -> - State1 = State#state{monitors = dict:erase(Ref, Monitors)}, - {reply, erlang:demonitor(Ref, Options), State1, hibernate}. + {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, Fun, Grouped}, State = #state{node = Node}) -> safe_invoke(orddict:fetch(Node, Grouped), Fun), {noreply, State, hibernate}. -handle_info({'DOWN', Ref, process, Object, Info}, - State = #state{monitors = Monitors}) -> - {noreply, case dict:find(Ref, Monitors) of - {ok, WantsMonitor} -> - WantsMonitor ! {'DOWN', Ref, process, Object, Info}, - State#state{monitors = dict:erase(Ref, Monitors)}; +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}; -- 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(-) 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(-) 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(-) 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 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(-) 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(-) 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(-) 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 d568194617e033749eeb757dd62c60127037cee0 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Fri, 26 Jul 2013 15:27:06 +0100 Subject: Backout 3b5a3e3d2feb --- src/rabbit_amqqueue.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index d62e6476..767abeb0 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -650,7 +650,7 @@ on_node_down(Node) -> fun () -> QsDels = qlc:e(qlc:q([{QName, delete_queue(QName)} || #amqqueue{name = QName, pid = Pid, - gm_pids = []} + slave_pids = []} <- mnesia:table(rabbit_queue), node(Pid) == Node andalso not rabbit_misc:is_process_alive(Pid)])), -- cgit v1.2.1 From 0f4d07ed4964eac471bb07e78cd5e614d588d263 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 27 Jul 2013 04:20:44 +0100 Subject: placate dialyzer --- src/rabbit_reader.erl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 3cf88d06..0fa812c6 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -756,6 +756,9 @@ refuse_connection(Sock, Exception, {A, B, C, D}) -> ok = inet_op(fun () -> rabbit_net:send(Sock, <<"AMQP",A,B,C,D>>) end), throw(Exception). +-ifdef(use_specs). +-spec(refuse_connection/2 :: (rabbit_net:socket(), any()) -> no_return()). +-endif. refuse_connection(Sock, Exception) -> refuse_connection(Sock, Exception, {0, 0, 9, 1}). -- cgit v1.2.1 From b97d76ee28d152c558217241aae61e4fd11918f5 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 29 Jul 2013 15:43:02 +0100 Subject: cosmetic --- ebin/rabbit_app.in | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ebin/rabbit_app.in b/ebin/rabbit_app.in index 5822078b..a4582e2d 100644 --- a/ebin/rabbit_app.in +++ b/ebin/rabbit_app.in @@ -69,7 +69,5 @@ mnesia_lib, rpc, mnesia_tm, qlc, sofs, proplists, credit_flow, pmon, ssl_connection, tls_connection, ssl_record, tls_record, gen_fsm, ssl]}, - %% see bug 25668 about why we need these - {ssl_apps, - [asn1, crypto, public_key, ssl]} + {ssl_apps, [asn1, crypto, public_key, ssl]} ]}]}. -- cgit v1.2.1 From e2f502dcebfef16beffd6f308d6471d2d47bee4d Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Tue, 30 Jul 2013 13:07:14 +0100 Subject: Work around Clink shell issue This shell fails to suppress its copyright banner if there is no space between "/c" and the rest of the commandline. --- src/rabbit_control_main.erl | 2 +- src/rabbit_disk_monitor.erl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index f5e70365..c195ace5 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -602,7 +602,7 @@ process_up(Pid) -> run_ps(Pid) =:= 0 end}, {win32, fun () -> - Res = os:cmd("tasklist /nh /fi \"pid eq " ++ + Res = os:cmd(" tasklist /nh /fi \"pid eq " ++ Pid ++ "\" 2>&1"), case re:run(Res, "erl\\.exe", [{capture, none}]) of match -> true; diff --git a/src/rabbit_disk_monitor.erl b/src/rabbit_disk_monitor.erl index 3bb163a1..0e2c9ca1 100644 --- a/src/rabbit_disk_monitor.erl +++ b/src/rabbit_disk_monitor.erl @@ -168,7 +168,7 @@ get_disk_free(Dir, {unix, Sun}) get_disk_free(Dir, {unix, _}) -> parse_free_unix(rabbit_misc:os_cmd("/bin/df -kP " ++ Dir)); get_disk_free(Dir, {win32, _}) -> - parse_free_win32(os:cmd("dir /-C /W \"" ++ Dir ++ [$"])); + parse_free_win32(os:cmd(" dir /-C /W \"" ++ Dir ++ [$"])); get_disk_free(_, Platform) -> {unknown, Platform}. -- 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(-) 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(-) 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(-) 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(-) 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(-) 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 2c32f6cdb2c84d1324645423f132214ebf4f1031 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Wed, 31 Jul 2013 12:30:25 +0100 Subject: Add space for clink os cmds separately --- src/rabbit_control_main.erl | 4 ++-- src/rabbit_disk_monitor.erl | 3 ++- src/rabbit_misc.erl | 5 +++++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index c195ace5..4be77f82 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -602,8 +602,8 @@ process_up(Pid) -> run_ps(Pid) =:= 0 end}, {win32, fun () -> - Res = os:cmd(" tasklist /nh /fi \"pid eq " ++ - Pid ++ "\" 2>&1"), + Cmd = "tasklist /nh /fi \"pid eq " ++ Pid ++ "\" ", + Res = os:cmd(rabbit_misc:win32_cmd(Cmd ++ "2>&1")), case re:run(Res, "erl\\.exe", [{capture, none}]) of match -> true; _ -> false diff --git a/src/rabbit_disk_monitor.erl b/src/rabbit_disk_monitor.erl index 0e2c9ca1..227bbcee 100644 --- a/src/rabbit_disk_monitor.erl +++ b/src/rabbit_disk_monitor.erl @@ -168,7 +168,8 @@ get_disk_free(Dir, {unix, Sun}) get_disk_free(Dir, {unix, _}) -> parse_free_unix(rabbit_misc:os_cmd("/bin/df -kP " ++ Dir)); get_disk_free(Dir, {win32, _}) -> - parse_free_win32(os:cmd(" dir /-C /W \"" ++ Dir ++ [$"])); + Cmd = "dir /-C /W \"" ++ Dir ++ [$"], + parse_free_win32(os:cmd(rabbit_misc:win32_cmd(Cmd))); get_disk_free(_, Platform) -> {unknown, Platform}. diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index a1e95fd5..672d3b40 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -60,6 +60,7 @@ -export([append_rpc_all_nodes/4]). -export([multi_call/2]). -export([os_cmd/1]). +-export([win32_cmd/1]). -export([gb_sets_difference/2]). -export([version/0, which_applications/0]). -export([sequence_error/1]). @@ -230,6 +231,7 @@ -spec(multi_call/2 :: ([pid()], any()) -> {[{pid(), any()}], [{pid(), any()}]}). -spec(os_cmd/1 :: (string()) -> string()). +-spec(win32_cmd/1 :: (string()) -> string()). -spec(gb_sets_difference/2 :: (gb_set(), gb_set()) -> gb_set()). -spec(version/0 :: () -> string()). -spec(which_applications/0 :: () -> [{atom(), string(), string()}]). @@ -979,6 +981,9 @@ os_cmd(Command) -> _ -> os:cmd(Command) end. +%% Clink workaround: http://code.google.com/p/clink/issues/detail?id=141 +win32_cmd(Command) -> " " ++ Command. + gb_sets_difference(S1, S2) -> gb_sets:fold(fun gb_sets:delete_any/2, S1, S2). -- cgit v1.2.1 From a52c76366e752a0139531bf20f7df16c36540550 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Wed, 31 Jul 2013 13:09:46 +0100 Subject: Update copyright on added module --- src/rabbit_intermediate_sup.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_intermediate_sup.erl b/src/rabbit_intermediate_sup.erl index 1919d9d6..a9381f20 100644 --- a/src/rabbit_intermediate_sup.erl +++ b/src/rabbit_intermediate_sup.erl @@ -10,8 +10,8 @@ %% %% The Original Code is RabbitMQ. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2013 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2013-2013 GoPivotal, Inc. All rights reserved. %% -module(rabbit_intermediate_sup). -- cgit v1.2.1 From f671dc3cfde4612c57c4d6f5c62b7db3694c243a Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Wed, 31 Jul 2013 16:15:01 +0100 Subject: Check DLX permissions on queue declare --- src/rabbit_channel.erl | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index a8e9432c..3f2f7afa 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -986,6 +986,13 @@ handle_method(#'queue.declare'{queue = QueueNameBin, return_queue_declare_ok(QueueName, NoWait, MessageCount, ConsumerCount, State); {error, not_found} -> + case rabbit_misc:r_arg(VHostPath, exchange, Args, + <<"x-dead-letter-exchange">>) of + undefined -> ok; + DLX -> check_read_permitted(QueueName, State), + check_write_permitted(DLX, State), + ok + end, case rabbit_amqqueue:declare(QueueName, Durable, AutoDelete, Args, Owner) of {new, #amqqueue{pid = QPid}} -> -- cgit v1.2.1 From 5d75c6e30b01b0129cca3c43775a7706565a061d Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 31 Jul 2013 17:06:51 +0100 Subject: Integrate win32_cmd/1 into rabbit_misc:os_cmd/1, so that we have just one function we can always use instead of os:cmd/1. --- src/rabbit_control_main.erl | 2 +- src/rabbit_disk_monitor.erl | 3 +-- src/rabbit_misc.erl | 21 ++++++++++++--------- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index 4be77f82..c57c9f4a 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -603,7 +603,7 @@ process_up(Pid) -> end}, {win32, fun () -> Cmd = "tasklist /nh /fi \"pid eq " ++ Pid ++ "\" ", - Res = os:cmd(rabbit_misc:win32_cmd(Cmd ++ "2>&1")), + Res = rabbit_misc:os_cmd(Cmd ++ "2>&1"), case re:run(Res, "erl\\.exe", [{capture, none}]) of match -> true; _ -> false diff --git a/src/rabbit_disk_monitor.erl b/src/rabbit_disk_monitor.erl index 227bbcee..9b7bafd5 100644 --- a/src/rabbit_disk_monitor.erl +++ b/src/rabbit_disk_monitor.erl @@ -168,8 +168,7 @@ get_disk_free(Dir, {unix, Sun}) get_disk_free(Dir, {unix, _}) -> parse_free_unix(rabbit_misc:os_cmd("/bin/df -kP " ++ Dir)); get_disk_free(Dir, {win32, _}) -> - Cmd = "dir /-C /W \"" ++ Dir ++ [$"], - parse_free_win32(os:cmd(rabbit_misc:win32_cmd(Cmd))); + parse_free_win32(rabbit_misc:os_cmd("dir /-C /W \"" ++ Dir ++ [$"])); get_disk_free(_, Platform) -> {unknown, Platform}. diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 672d3b40..ce7d73c7 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -60,7 +60,6 @@ -export([append_rpc_all_nodes/4]). -export([multi_call/2]). -export([os_cmd/1]). --export([win32_cmd/1]). -export([gb_sets_difference/2]). -export([version/0, which_applications/0]). -export([sequence_error/1]). @@ -231,7 +230,6 @@ -spec(multi_call/2 :: ([pid()], any()) -> {[{pid(), any()}], [{pid(), any()}]}). -spec(os_cmd/1 :: (string()) -> string()). --spec(win32_cmd/1 :: (string()) -> string()). -spec(gb_sets_difference/2 :: (gb_set(), gb_set()) -> gb_set()). -spec(version/0 :: () -> string()). -spec(which_applications/0 :: () -> [{atom(), string(), string()}]). @@ -975,15 +973,20 @@ receive_multi_call([{Mref, Pid} | MonitorPids], Good, Bad) -> end. os_cmd(Command) -> - Exec = hd(string:tokens(Command, " ")), - case os:find_executable(Exec) of - false -> throw({command_not_found, Exec}); - _ -> os:cmd(Command) + case os:type() of + {win32, _} -> + %% Clink workaround; see + %% http://code.google.com/p/clink/issues/detail?id=141 + os:cmd(" " + Command); + _ -> + %% Don't just return "/bin/sh: : not found" if not found + Exec = hd(string:tokens(Command, " ")), + case os:find_executable(Exec) of + false -> throw({command_not_found, Exec}); + _ -> os:cmd(Command) + end end. -%% Clink workaround: http://code.google.com/p/clink/issues/detail?id=141 -win32_cmd(Command) -> " " ++ Command. - gb_sets_difference(S1, S2) -> gb_sets:fold(fun gb_sets:delete_any/2, S1, S2). -- cgit v1.2.1 From 0936a78fdda57cfea97e840b0088a94fed3f2903 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Wed, 31 Jul 2013 17:39:37 +0100 Subject: Check dlx type while checking permissions --- src/rabbit_amqqueue.erl | 1 - src/rabbit_misc.erl | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index a6181a95..23242a54 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -412,7 +412,6 @@ check_declare_arguments(QueueName, Args) -> args() -> [{<<"x-expires">>, fun check_expires_arg/2}, {<<"x-message-ttl">>, fun check_message_ttl_arg/2}, - {<<"x-dead-letter-exchange">>, fun check_string_arg/2}, {<<"x-dead-letter-routing-key">>, fun check_dlxrk_arg/2}, {<<"x-max-length">>, fun check_max_length_arg/2}]. diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 3df13876..be9d2712 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -370,7 +370,9 @@ r_arg(#resource{virtual_host = VHostPath}, Kind, Table, Key) -> r_arg(VHostPath, Kind, Table, Key) -> case table_lookup(Table, Key) of {longstr, NameBin} -> r(VHostPath, Kind, NameBin); - undefined -> undefined + undefined -> undefined; + Other -> protocol_error(precondition_failed, + "invalid arg: ~p", [Other]) end. rs(#resource{virtual_host = VHostPath, kind = Kind, name = Name}) -> -- cgit v1.2.1 From 56cf607740f4db887b8b97058b4f85b08a89a239 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 1 Aug 2013 04:55:26 +0100 Subject: add helper process for lifecycle hooks --- src/supervised_lifecycle.erl | 68 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 src/supervised_lifecycle.erl diff --git a/src/supervised_lifecycle.erl b/src/supervised_lifecycle.erl new file mode 100644 index 00000000..8b306f6f --- /dev/null +++ b/src/supervised_lifecycle.erl @@ -0,0 +1,68 @@ +%% 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. +%% + +%% Invoke callbacks on startup and termination. +%% +%% Simply hook this process into a supervision hierarchy, to have the +%% callbacks invoked at a precise point during the establishment and +%% teardown of that hierarchy, respectively. +%% +%% Or launch the process independently, and link to it, to have the +%% callbacks invoked on startup and when the linked process +%% terminates, respectively. + +-module(supervised_lifecycle). + +-behavior(gen_server). + +-export([start_link/3]). + +%% gen_server callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, + code_change/3]). + +%%---------------------------------------------------------------------------- + +-ifdef(use_specs). + +-spec(start_link/3 :: (atom(), rabbit_types:mfargs(), rabbit_types:mfargs()) -> + rabbit_types:ok_pid_or_error()). + +-endif. + +%%---------------------------------------------------------------------------- + +start_link(Name, StartMFA, StopMFA) -> + gen_server:start_link({local, Name}, ?MODULE, [StartMFA, StopMFA], []). + +%%---------------------------------------------------------------------------- + +init([{M, F, A}, StopMFA]) -> + process_flag(trap_exit, true), + apply(M, F, A), + {ok, StopMFA}. + +handle_call(_Request, _From, State) -> {noreply, State}. + +handle_cast(_Msg, State) -> {noreply, State}. + +handle_info(_Info, State) -> {noreply, State}. + +terminate(_Reason, {M, F, A}) -> + apply(M, F, A), + ok. + +code_change(_OldVsn, State, _Extra) -> {ok, State}. -- cgit v1.2.1 From 28b7430489dce992c935a04d51b8b87f6a0e3538 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 1 Aug 2013 04:58:32 +0100 Subject: add/remove log handler at precise point in system lifecycle Previously the handler stuck around for too long in the event of failed startup, resulting in spurious errors. --- src/rabbit.erl | 8 ++++++-- src/rabbit_error_logger.erl | 13 ++++++++++--- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index eae3b802..3724c32e 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -161,7 +161,12 @@ -rabbit_boot_step({log_relay, [{description, "error log relay"}, - {mfa, {rabbit_error_logger, boot, []}}, + {mfa, {rabbit_sup, start_child, + [rabbit_error_logger_lifecycle, + supervised_lifecycle, + [rabbit_error_logger_lifecycle, + {rabbit_error_logger, start, []}, + {rabbit_error_logger, stop, []}]]}}, {requires, routing_ready}, {enables, networking}]}). @@ -443,7 +448,6 @@ start(normal, []) -> end. stop(_State) -> - terminated_ok = error_logger:delete_report_handler(rabbit_error_logger), ok = rabbit_alarm:stop(), ok = case rabbit_mnesia:is_clustered() of true -> rabbit_amqqueue:on_node_down(node()); diff --git a/src/rabbit_error_logger.erl b/src/rabbit_error_logger.erl index 184dcf17..17ed8563 100644 --- a/src/rabbit_error_logger.erl +++ b/src/rabbit_error_logger.erl @@ -22,7 +22,7 @@ -behaviour(gen_event). --export([boot/0]). +-export([start/0, stop/0]). -export([init/1, terminate/2, code_change/3, handle_call/2, handle_event/2, handle_info/2]). @@ -31,16 +31,23 @@ -ifdef(use_specs). --spec(boot/0 :: () -> 'ok'). +-spec(start/0 :: () -> 'ok'). +-spec(stop/0 :: () -> 'ok'). -endif. %%---------------------------------------------------------------------------- -boot() -> +start() -> {ok, DefaultVHost} = application:get_env(default_vhost), ok = error_logger:add_report_handler(?MODULE, [DefaultVHost]). +stop() -> + terminated_ok = error_logger:delete_report_handler(rabbit_error_logger), + ok. + +%%---------------------------------------------------------------------------- + init([DefaultVHost]) -> #exchange{} = rabbit_exchange:declare( rabbit_misc:r(DefaultVHost, exchange, ?LOG_EXCH_NAME), -- cgit v1.2.1 From 0896ef6263ff2ed5d75890e1fee0a39daed2269a Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 1 Aug 2013 05:52:37 +0100 Subject: handle errors returned by boot steps --- src/rabbit.erl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/rabbit.erl b/src/rabbit.erl index eae3b802..17426cfc 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -472,6 +472,9 @@ run_boot_step({_StepName, Attributes}) -> MFAs -> [try apply(M,F,A) + of + ok -> ok; + {error, Reason} -> boot_error(Reason, not_available) catch _:Reason -> boot_error(Reason, erlang:get_stacktrace()) end || {M,F,A} <- MFAs], -- cgit v1.2.1 From a6225fd5cd01929846953fd767baa3727a9a1098 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Thu, 1 Aug 2013 11:51:36 +0100 Subject: Better error message for DLX type error --- src/rabbit_channel.erl | 19 +++++++++++++------ src/rabbit_misc.erl | 9 ++++----- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 3f2f7afa..f0904163 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -986,12 +986,19 @@ handle_method(#'queue.declare'{queue = QueueNameBin, return_queue_declare_ok(QueueName, NoWait, MessageCount, ConsumerCount, State); {error, not_found} -> - case rabbit_misc:r_arg(VHostPath, exchange, Args, - <<"x-dead-letter-exchange">>) of - undefined -> ok; - DLX -> check_read_permitted(QueueName, State), - check_write_permitted(DLX, State), - ok + DlxKey = <<"x-dead-letter-exchange">>, + case rabbit_misc:r_arg(VHostPath, exchange, Args, DlxKey) of + undefined -> + ok; + {error, {invalid_type, Type}} -> + rabbit_misc:protocol_error( + precondition_failed, + "invalid type '~s' for arg '~s' in ~s", + [Type, DlxKey, rabbit_misc:rs(QueueName)]); + DLX -> + check_read_permitted(QueueName, State), + check_write_permitted(DLX, State), + ok end, case rabbit_amqqueue:declare(QueueName, Durable, AutoDelete, Args, Owner) of diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index be9d2712..e4016e83 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -142,9 +142,9 @@ when is_subtype(K, atom())). -spec(r_arg/4 :: (rabbit_types:vhost() | rabbit_types:r(atom()), K, - rabbit_framing:amqp_table(), binary()) - -> undefined | rabbit_types:r(K) - when is_subtype(K, atom())). + rabbit_framing:amqp_table(), binary()) -> undefined | + {error, {invalid_type, rabbit_framing:amqp_field_type()}} | + rabbit_types:r(K) when is_subtype(K, atom())). -spec(rs/1 :: (rabbit_types:r(atom())) -> string()). -spec(enable_cover/0 :: () -> ok_or_error()). -spec(start_cover/1 :: ([{string(), string()} | string()]) -> 'ok'). @@ -371,8 +371,7 @@ r_arg(VHostPath, Kind, Table, Key) -> case table_lookup(Table, Key) of {longstr, NameBin} -> r(VHostPath, Kind, NameBin); undefined -> undefined; - Other -> protocol_error(precondition_failed, - "invalid arg: ~p", [Other]) + {Type, _} -> {error, {invalid_type, Type}} end. rs(#resource{virtual_host = VHostPath, kind = Kind, name = Name}) -> -- cgit v1.2.1 From 4b5d31ca5304687ac7b2fb3600efb0fa7260a9e6 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Thu, 1 Aug 2013 12:06:01 +0100 Subject: Transplant of bug25617 to stable --- src/rabbit_file.erl | 60 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 42 insertions(+), 18 deletions(-) diff --git a/src/rabbit_file.erl b/src/rabbit_file.erl index 0f429ae9..4cf314ca 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,29 +138,17 @@ 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); - {error, _} = E -> E + Bin when is_binary(Bin) -> write_file1(Path, Bin, Modes1); + {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) -> @@ -168,6 +158,40 @@ make_binary(List) -> {error, Reason} end. +write_file1(Path, Bin, Modes) -> + try + 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_synced_copy(Path, Modes, Fun) -> + 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 + 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. + %% 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 1d272beb7bfead4c8ad159229c2ba114fde9844f Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Thu, 1 Aug 2013 13:04:47 +0100 Subject: Type checks for alternative exchanges in the same way as DLX --- src/rabbit_channel.erl | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index f0904163..0aeaa96b 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -894,9 +894,14 @@ handle_method(#'exchange.declare'{exchange = ExchangeNameBin, {ok, FoundX} -> FoundX; {error, not_found} -> check_name('exchange', ExchangeNameBin), - case rabbit_misc:r_arg(VHostPath, exchange, Args, - <<"alternate-exchange">>) of + AeKey = <<"alternate-exchange">>, + case rabbit_misc:r_arg(VHostPath, exchange, Args, AeKey) of undefined -> ok; + {error, {invalid_type, Type}} -> + rabbit_misc:protocol_error( + precondition_failed, + "invalid type '~s' for arg '~s' in ~s", + [Type, AeKey, rabbit_misc:rs(ExchangeName)]); AName -> check_read_permitted(ExchangeName, State), check_write_permitted(AName, State), ok -- 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(-) 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(-) 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(-) 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(-) 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 0a3633c7e311e6783454f92bda72a73f086b4f77 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 5 Aug 2013 12:51:15 +0100 Subject: refactor: rpc:call already optimises calls to local nodes plus get rid of an unnecessary 'case'. --- src/rabbit_misc.erl | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 8d8cb07c..84b15aa9 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -900,13 +900,8 @@ ntoab(IP) -> _ -> "[" ++ Str ++ "]" end. -is_process_alive(Pid) when node(Pid) =:= node() -> - erlang:is_process_alive(Pid); is_process_alive(Pid) -> - case rpc:call(node(Pid), erlang, is_process_alive, [Pid]) of - true -> true; - _ -> false - end. + rpc:call(node(Pid), erlang, is_process_alive, [Pid]) =:= true. pget(K, P) -> proplists:get_value(K, P). pget(K, P, D) -> proplists:get_value(K, P, D). -- cgit v1.2.1 From f30667732c4012dfb77b5bf8a32e5c0d4e524183 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 5 Aug 2013 14:56:51 +0100 Subject: remove unused function --- src/rabbit_amqqueue.erl | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 23242a54..a1efaf65 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -415,9 +415,6 @@ args() -> {<<"x-dead-letter-routing-key">>, fun check_dlxrk_arg/2}, {<<"x-max-length">>, fun check_max_length_arg/2}]. -check_string_arg({longstr, _}, _Args) -> ok; -check_string_arg({Type, _}, _Args) -> {error, {unacceptable_type, Type}}. - check_int_arg({Type, _}, _) -> case lists:member(Type, ?INTEGER_ARG_TYPES) of true -> ok; -- cgit v1.2.1 From d081e9ff99b4caf59f128f940a7e76559c59f19f Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 5 Aug 2013 15:02:09 +0100 Subject: simplify --- src/rabbit_channel.erl | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 0aeaa96b..d6c1e8c0 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -898,8 +898,7 @@ handle_method(#'exchange.declare'{exchange = ExchangeNameBin, case rabbit_misc:r_arg(VHostPath, exchange, Args, AeKey) of undefined -> ok; {error, {invalid_type, Type}} -> - rabbit_misc:protocol_error( - precondition_failed, + precondition_failed( "invalid type '~s' for arg '~s' in ~s", [Type, AeKey, rabbit_misc:rs(ExchangeName)]); AName -> check_read_permitted(ExchangeName, State), @@ -996,10 +995,9 @@ handle_method(#'queue.declare'{queue = QueueNameBin, undefined -> ok; {error, {invalid_type, Type}} -> - rabbit_misc:protocol_error( - precondition_failed, - "invalid type '~s' for arg '~s' in ~s", - [Type, DlxKey, rabbit_misc:rs(QueueName)]); + precondition_failed( + "invalid type '~s' for arg '~s' in ~s", + [Type, DlxKey, rabbit_misc:rs(QueueName)]); DLX -> check_read_permitted(QueueName, State), check_write_permitted(DLX, State), -- cgit v1.2.1 From 06624451996a382461fd41a9f6cfadc919129573 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 5 Aug 2013 15:17:22 +0100 Subject: refactor type spec --- src/rabbit_misc.erl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index e4016e83..0c04cb42 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -142,9 +142,11 @@ when is_subtype(K, atom())). -spec(r_arg/4 :: (rabbit_types:vhost() | rabbit_types:r(atom()), K, - rabbit_framing:amqp_table(), binary()) -> undefined | - {error, {invalid_type, rabbit_framing:amqp_field_type()}} | - rabbit_types:r(K) when is_subtype(K, atom())). + rabbit_framing:amqp_table(), binary()) -> + undefined | + rabbit_types:error( + {invalid_type, rabbit_framing:amqp_field_type()}) | + rabbit_types:r(K) when is_subtype(K, atom())). -spec(rs/1 :: (rabbit_types:r(atom())) -> string()). -spec(enable_cover/0 :: () -> ok_or_error()). -spec(start_cover/1 :: ([{string(), string()} | string()]) -> 'ok'). -- 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(-) 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 e8a80d836fbe0641f989baf9a8c7c2bc810ff1b5 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 6 Aug 2013 17:32:49 +0100 Subject: Fix embarassment perpetrated in 97f0e3842c04. --- src/rabbit_misc.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index ce7d73c7..4db25213 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -977,7 +977,7 @@ os_cmd(Command) -> {win32, _} -> %% Clink workaround; see %% http://code.google.com/p/clink/issues/detail?id=141 - os:cmd(" " + Command); + os:cmd(" " ++ Command); _ -> %% Don't just return "/bin/sh: : not found" if not found Exec = hd(string:tokens(Command, " ")), -- cgit v1.2.1 From 5dbe662b47815e960d7830df1ccb7e9ead511e0e Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 7 Aug 2013 11:05:14 +0100 Subject: await_cluster_recovery/0 should return ok. --- src/rabbit_node_monitor.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index 805f1b2b..0e6028c6 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -378,7 +378,8 @@ await_cluster_recovery() -> run_outside_applications(fun () -> rabbit:stop(), wait_for_cluster_recovery(Nodes) - end). + end), + ok. run_outside_applications(Fun) -> spawn(fun () -> -- cgit v1.2.1 -- 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(-) 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(-) 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(-) 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(-) 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(-) 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(-) 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(-) 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(-) 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 02c0c3902142fa0c5d8c2f42d2fe1563d4a03719 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 9 Aug 2013 12:18:58 +0100 Subject: Man page updates --- docs/rabbitmqctl.1.xml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/rabbitmqctl.1.xml b/docs/rabbitmqctl.1.xml index 1d641144..b2361cde 100644 --- a/docs/rabbitmqctl.1.xml +++ b/docs/rabbitmqctl.1.xml @@ -960,7 +960,7 @@ - set_policy -p vhostpath name pattern definition priority + set_policy -p vhostpath --priority priority --apply-to apply-to name pattern definition Sets a policy. @@ -989,7 +989,13 @@ priority - The priority of the policy as an integer, defaulting to 0. Higher numbers indicate greater precedence. + The priority of the policy as an integer. Higher numbers indicate greater precedence. The default is 0. + + + + apply-to + + Which types of object this policy should apply to - "queues", "exchanges" or "all". The default is "all". -- 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(-) 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 5823a6a220788070370022598e85a7011b88d178 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 13 Aug 2013 15:39:02 +0100 Subject: Silence the badarg on two near-simultaneous pauses. --- src/rabbit_node_monitor.erl | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/rabbit_node_monitor.erl b/src/rabbit_node_monitor.erl index 0e6028c6..57dce7cd 100644 --- a/src/rabbit_node_monitor.erl +++ b/src/rabbit_node_monitor.erl @@ -386,10 +386,12 @@ run_outside_applications(Fun) -> %% If our group leader is inside an application we are about %% to stop, application:stop/1 does not return. group_leader(whereis(init), self()), - %% Ensure only one such process at a time, will - %% exit(badarg) (harmlessly) if one is already running - register(rabbit_outside_app_process, self()), - Fun() + %% Ensure only one such process at a time, the + %% exit(badarg) is harmless if one is already running + try register(rabbit_outside_app_process, self()) of + true -> Fun() + catch error:badarg -> ok + end end). wait_for_cluster_recovery(Nodes) -> -- cgit v1.2.1 From e974de39fcaad5c60fbfea6eb70d09abc7fc425c Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 14 Aug 2013 17:12:44 +0100 Subject: Revert dd08c9204760 --- src/delegate.erl | 89 ++++++++------------------------------- src/pmon.erl | 42 ++++++++---------- src/rabbit_amqqueue_process.erl | 2 +- src/rabbit_mirror_queue_slave.erl | 10 ++--- 4 files changed, 41 insertions(+), 102 deletions(-) diff --git a/src/delegate.erl b/src/delegate.erl index 7a06c1e4..4e1dcd2e 100644 --- a/src/delegate.erl +++ b/src/delegate.erl @@ -18,22 +18,15 @@ -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, 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()}). - -spec(start_link/1 :: (non_neg_integer()) -> {'ok', pid()} | ignore | {'error', any()}). -spec(invoke/2 :: @@ -42,10 +35,6 @@ [{pid(), term()}]}). -spec(invoke_no_result/2 :: (pid() | [pid()], fun ((pid()) -> 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()}]}). @@ -61,8 +50,7 @@ %%---------------------------------------------------------------------------- start_link(Num) -> - Name = delegate_name(Num), - gen_server2:start_link({local, Name}, ?MODULE, [Name], []). + gen_server2:start_link({local, delegate_name(Num)}, ?MODULE, [], []). invoke(Pid, Fun) when is_pid(Pid) andalso node(Pid) =:= node() -> Fun(Pid); @@ -90,7 +78,7 @@ invoke(Pids, Fun) when is_list(Pids) -> case orddict:fetch_keys(Grouped) of [] -> {[], []}; RemoteNodes -> gen_server2:multi_call( - RemoteNodes, delegate(self(), RemoteNodes), + RemoteNodes, delegate(RemoteNodes), {invoke, Fun, Grouped}, infinity) end, BadPids = [{Pid, {exit, {nodedown, BadNode}, []}} || @@ -118,27 +106,12 @@ invoke_no_result(Pids, Fun) 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}) + RemoteNodes -> gen_server2:abcast(RemoteNodes, delegate(RemoteNodes), + {invoke, Fun, Grouped}) end, safe_invoke(LocalPids, Fun), %% 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, fun (P) -> gen_server2:call(P, Msg, infinity) end). @@ -161,10 +134,10 @@ group_pids_by_node(Pids) -> delegate_name(Hash) -> list_to_atom("delegate_" ++ integer_to_list(Hash)). -delegate(Pid, RemoteNodes) -> +delegate(RemoteNodes) -> case get(delegate) of undefined -> Name = delegate_name( - erlang:phash2(Pid, + erlang:phash2(self(), delegate_sup:count(RemoteNodes))), put(delegate, Name), Name; @@ -182,48 +155,22 @@ safe_invoke(Pid, Fun) when is_pid(Pid) -> %%---------------------------------------------------------------------------- -init([Name]) -> - {ok, #state{node = node(), monitors = dict:new(), name = Name}, hibernate, +init([]) -> + {ok, node(), 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_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, Fun, Grouped}, State = #state{node = Node}) -> - safe_invoke(orddict:fetch(Node, Grouped), Fun), - {noreply, State, hibernate}. +handle_call({invoke, Fun, Grouped}, _From, Node) -> + {reply, safe_invoke(orddict:fetch(Node, Grouped), Fun), Node, 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_cast({invoke, Fun, Grouped}, Node) -> + safe_invoke(orddict:fetch(Node, Grouped), Fun), + {noreply, Node, hibernate}. -handle_info(_Info, State) -> - {noreply, State, hibernate}. +handle_info(_Info, Node) -> + {noreply, Node, hibernate}. terminate(_Reason, _State) -> ok. -code_change(_OldVsn, State, _Extra) -> - {ok, State}. +code_change(_OldVsn, Node, _Extra) -> + {ok, Node}. diff --git a/src/pmon.erl b/src/pmon.erl index 86308167..b9db66fb 100644 --- a/src/pmon.erl +++ b/src/pmon.erl @@ -16,26 +16,22 @@ -module(pmon). --export([new/0, new/1, monitor/2, monitor_all/2, demonitor/2, - is_monitored/2, erase/2, monitored/1, is_empty/1]). +-export([new/0, 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() :: #state{dict :: dict(), - module :: atom()}). +-opaque(?MODULE() :: dict()). -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()). @@ -46,33 +42,29 @@ -endif. -new() -> new(erlang). - -new(Module) -> #state{dict = dict:new(), - module = Module}. +new() -> dict:new(). -monitor(Item, S = #state{dict = M, module = Module}) -> +monitor(Item, M) -> case dict:is_key(Item, M) of - true -> S; - false -> S#state{dict = dict:store( - Item, Module:monitor(process, Item), M)} + true -> M; + false -> dict:store(Item, erlang:monitor(process, Item), M) end. -monitor_all([], S) -> S; %% optimisation -monitor_all([Item], S) -> monitor(Item, S); %% optimisation -monitor_all(Items, S) -> lists:foldl(fun monitor/2, S, Items). +monitor_all([], M) -> M; %% optimisation +monitor_all([Item], M) -> monitor(Item, M); %% optimisation +monitor_all(Items, M) -> lists:foldl(fun monitor/2, M, Items). -demonitor(Item, S = #state{dict = M, module = Module}) -> +demonitor(Item, M) -> case dict:find(Item, M) of - {ok, MRef} -> Module:demonitor(MRef), - S#state{dict = dict:erase(Item, M)}; + {ok, MRef} -> erlang:demonitor(MRef), + dict:erase(Item, M); error -> M end. -is_monitored(Item, #state{dict = M}) -> dict:is_key(Item, M). +is_monitored(Item, M) -> dict:is_key(Item, M). -erase(Item, S = #state{dict = M}) -> S#state{dict = dict:erase(Item, M)}. +erase(Item, M) -> dict:erase(Item, M). -monitored(#state{dict = M}) -> dict:fetch_keys(M). +monitored(M) -> dict:fetch_keys(M). -is_empty(#state{dict = M}) -> dict:size(M) == 0. +is_empty(M) -> dict:size(M) == 0. diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 6e0eb9bf..e61cba02 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(delegate), + senders = pmon:new(), 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 1996fd0a..54ee7129 100644 --- a/src/rabbit_mirror_queue_slave.erl +++ b/src/rabbit_mirror_queue_slave.erl @@ -120,7 +120,7 @@ init(Q = #amqqueue { name = QName }) -> msg_id_ack = dict:new(), msg_id_status = dict:new(), - known_senders = pmon:new(delegate), + known_senders = pmon:new(), depth_delta = undefined }, @@ -274,8 +274,7 @@ handle_info({'DOWN', _MonitorRef, process, MPid, _Reason}, noreply(State); handle_info({'DOWN', _MonitorRef, process, ChPid, _Reason}, State) -> - local_sender_death(ChPid, State), - noreply(State); + noreply(local_sender_death(ChPid, State)); handle_info({'EXIT', _Pid, Reason}, State) -> {stop, Reason, State}; @@ -606,7 +605,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 { known_senders = KS }) -> +local_sender_death(ChPid, State = #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 @@ -614,7 +613,8 @@ local_sender_death(ChPid, #state { known_senders = KS }) -> ok = case pmon:is_monitored(ChPid, KS) of false -> ok; true -> confirm_sender_death(ChPid) - end. + end, + State. confirm_sender_death(Pid) -> %% We have to deal with the possibility that we'll be promoted to -- 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(-) 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(-) 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(+) 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 -- cgit v1.2.1 From 3402700d7f8ade8555754cbc3c944e538bf33e76 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 15 Aug 2013 14:39:16 +0100 Subject: Changelog for 3.1.5 --- packaging/RPMS/Fedora/rabbitmq-server.spec | 3 +++ packaging/debs/Debian/debian/changelog | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/packaging/RPMS/Fedora/rabbitmq-server.spec b/packaging/RPMS/Fedora/rabbitmq-server.spec index 028b4ec2..f2195d84 100644 --- a/packaging/RPMS/Fedora/rabbitmq-server.spec +++ b/packaging/RPMS/Fedora/rabbitmq-server.spec @@ -123,6 +123,9 @@ done rm -rf %{buildroot} %changelog +* Thu Aug 15 2013 simon@rabbitmq.com 3.1.5-1 +- New Upstream Release + * Tue Jun 25 2013 tim@rabbitmq.com 3.1.3-1 - New Upstream Release diff --git a/packaging/debs/Debian/debian/changelog b/packaging/debs/Debian/debian/changelog index fda29e7d..3212514e 100644 --- a/packaging/debs/Debian/debian/changelog +++ b/packaging/debs/Debian/debian/changelog @@ -1,3 +1,9 @@ +rabbitmq-server (3.1.5-1) unstable; urgency=low + + * New Upstream Release + + -- Simon MacMullen Thu, 15 Aug 2013 11:03:13 +0100 + rabbitmq-server (3.1.3-1) unstable; urgency=low * New Upstream Release -- 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(-) 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(-) 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(-) 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(-) 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(-) 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(-) 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(-) 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