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 -- 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 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 -- 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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