From d1e0cee435ef648514626187c99929278357b2e2 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Tue, 15 Jun 2010 14:28:38 +0100 Subject: server implements 0.9.1 channel shutdown protocol --- src/rabbit_reader.erl | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 8e7cd39f..282fd12d 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -371,7 +371,7 @@ close_connection(State = #v1{connection = #connection{ State#v1{connection_state = closed}. close_channel(Channel, State) -> - put({channel, Channel}, closing), + put({channel, Channel}, {closing, get({channel, Channel})}), State. handle_channel_exit(Channel, Reason, State) -> @@ -483,13 +483,18 @@ handle_frame(Type, Channel, Payload, State) -> end, ok = rabbit_framing_channel:process(ChPid, AnalyzedFrame), State; - closing -> + {closing, {chpid, ChPid}} -> %% According to the spec, after sending a %% channel.close we must ignore all frames except + %% channel.close and channel.close_ok. In the + %% event of a channel.close, we should send back a %% channel.close_ok. case AnalyzedFrame of {method, 'channel.close_ok', _} -> erase({channel, Channel}); + {method, 'channel.close', _} -> + erase({channel, Channel}), + ok = rabbit_framing_channel:process(ChPid, AnalyzedFrame); _ -> ok end, State; -- cgit v1.2.1 From 37704ba6be973ab6653749bfe21a51dbb7e98981 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Wed, 16 Jun 2010 13:07:52 +0100 Subject: server implements 0.9.1 connection shutdown protocol --- src/rabbit_reader.erl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 8e7cd39f..e50a9a63 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -651,6 +651,12 @@ handle_method0(#'connection.close'{}, State = #v1{connection_state = running}) -> lists:foreach(fun rabbit_framing_channel:shutdown/1, all_channels()), maybe_close(State#v1{connection_state = closing}); +handle_method0(#'connection.close'{}, State = #v1{connection_state = CS}) + when CS =:= closing; CS =:= closed -> + %% We're already closed or closing, so we don't need to cleanup + %% anything. + ok = send_on_channel0(State#v1.sock, #'connection.close_ok'{}), + State; handle_method0(#'connection.close_ok'{}, State = #v1{connection_state = closed}) -> self() ! terminate_connection, -- cgit v1.2.1 From 4626253f57570a8d8fd6adc569b4794d89c6c800 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Wed, 16 Jun 2010 13:28:39 +0100 Subject: simplified Channel.Close code --- src/rabbit_reader.erl | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 282fd12d..bb880dad 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -371,7 +371,7 @@ close_connection(State = #v1{connection = #connection{ State#v1{connection_state = closed}. close_channel(Channel, State) -> - put({channel, Channel}, {closing, get({channel, Channel})}), + put({channel, Channel}, closing), State. handle_channel_exit(Channel, Reason, State) -> @@ -483,7 +483,7 @@ handle_frame(Type, Channel, Payload, State) -> end, ok = rabbit_framing_channel:process(ChPid, AnalyzedFrame), State; - {closing, {chpid, ChPid}} -> + closing -> %% According to the spec, after sending a %% channel.close we must ignore all frames except %% channel.close and channel.close_ok. In the @@ -493,8 +493,11 @@ handle_frame(Type, Channel, Payload, State) -> {method, 'channel.close_ok', _} -> erase({channel, Channel}); {method, 'channel.close', _} -> - erase({channel, Channel}), - ok = rabbit_framing_channel:process(ChPid, AnalyzedFrame); + %% We're already closing this channel, so + %% there's no cleanup to do (notify + %% queues, etc.) + ok = rabbit_writer:send_command(State#v1.sock, + #'channel.close_ok'{}); _ -> ok end, State; -- cgit v1.2.1 From b17931d913a79e6e06c9b97acfdbe0bf322c9c23 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Fri, 18 Jun 2010 17:29:18 +0100 Subject: updated comments --- src/rabbit_reader.erl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index e50a9a63..c6b7f19f 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -105,6 +105,8 @@ %% heartbeat timeout -> *throw* %% closing: %% socket close -> *terminate* +%% receive connection.close -> send connection.close_ok, +%% *closing* %% receive frame -> ignore, *closing* %% handshake_timeout -> ignore, *closing* %% heartbeat timeout -> *throw* @@ -121,6 +123,8 @@ %% start terminate_connection timer, *closed* %% closed: %% socket close -> *terminate* +%% receive connection.close -> send connection.close_ok, +%% *closed* %% receive connection.close_ok -> self() ! terminate_connection, %% *closed* %% receive frame -> ignore, *closed* -- cgit v1.2.1 From 163bb8b9addbfde2e40c7e07eaa7d1d49284746a Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Tue, 10 Aug 2010 15:12:35 +0100 Subject: added puback to framing codecs --- Makefile | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index e060c804..195c4ddf 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,3 @@ - TMPDIR ?= /tmp RABBITMQ_NODENAME ?= rabbit @@ -59,6 +58,7 @@ SIBLING_CODEGEN_DIR=../rabbitmq-codegen/ AMQP_CODEGEN_DIR=$(shell [ -d $(SIBLING_CODEGEN_DIR) ] && echo $(SIBLING_CODEGEN_DIR) || echo codegen) AMQP_SPEC_JSON_FILES_0_9_1=$(AMQP_CODEGEN_DIR)/amqp-rabbitmq-0.9.1.json AMQP_SPEC_JSON_FILES_0_8=$(AMQP_CODEGEN_DIR)/amqp-rabbitmq-0.8.json +RABBIT_SPEC_EXTENSIONS=$(AMQP_CODEGEN_DIR)/puback-extension.json ERL_CALL=erl_call -sname $(RABBITMQ_NODENAME) -e @@ -101,14 +101,14 @@ $(EBIN_DIR)/rabbit.app: $(EBIN_DIR)/rabbit_app.in $(BEAM_TARGETS) generate_app $(EBIN_DIR)/%.beam: erlc $(ERLC_OPTS) -pa $(EBIN_DIR) $< -$(INCLUDE_DIR)/rabbit_framing.hrl: codegen.py $(AMQP_CODEGEN_DIR)/amqp_codegen.py $(AMQP_SPEC_JSON_FILES_0_9_1) $(AMQP_SPEC_JSON_FILES_0_8) - $(PYTHON) codegen.py --ignore-conflicts header $(AMQP_SPEC_JSON_FILES_0_9_1) $(AMQP_SPEC_JSON_FILES_0_8) $@ +$(INCLUDE_DIR)/rabbit_framing.hrl: codegen.py $(AMQP_CODEGEN_DIR)/amqp_codegen.py $(AMQP_SPEC_JSON_FILES_0_9_1) $(AMQP_SPEC_JSON_FILES_0_8) $(RABBIT_SPEC_EXTENSIONS) + $(PYTHON) codegen.py --ignore-conflicts header $(AMQP_SPEC_JSON_FILES_0_9_1) $(AMQP_SPEC_JSON_FILES_0_8) $(RABBIT_SPEC_EXTENSIONS) $@ -$(SOURCE_DIR)/rabbit_framing_amqp_0_9_1.erl: codegen.py $(AMQP_CODEGEN_DIR)/amqp_codegen.py $(AMQP_SPEC_JSON_FILES_0_9_1) - $(PYTHON) codegen.py body $(AMQP_SPEC_JSON_FILES_0_9_1) $@ +$(SOURCE_DIR)/rabbit_framing_amqp_0_9_1.erl: codegen.py $(AMQP_CODEGEN_DIR)/amqp_codegen.py $(AMQP_SPEC_JSON_FILES_0_9_1) $(RABBIT_SPEC_EXTENSIONS) + $(PYTHON) codegen.py body $(AMQP_SPEC_JSON_FILES_0_9_1) $(RABBIT_SPEC_EXTENSIONS) $@ -$(SOURCE_DIR)/rabbit_framing_amqp_0_8.erl: codegen.py $(AMQP_CODEGEN_DIR)/amqp_codegen.py $(AMQP_SPEC_JSON_FILES_0_8) - $(PYTHON) codegen.py body $(AMQP_SPEC_JSON_FILES_0_8) $@ +$(SOURCE_DIR)/rabbit_framing_amqp_0_8.erl: codegen.py $(AMQP_CODEGEN_DIR)/amqp_codegen.py $(AMQP_SPEC_JSON_FILES_0_8) $(RABBIT_SPEC_EXTENSIONS) + $(PYTHON) codegen.py body $(AMQP_SPEC_JSON_FILES_0_8) $(RABBIT_SPEC_EXTENSIONS) $@ dialyze: $(BEAM_TARGETS) $(BASIC_PLT) $(ERL_EBIN) -eval \ -- cgit v1.2.1 From 9e9f2fb71886d6fb19872bd7386a37438e8116e6 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Tue, 10 Aug 2010 17:56:33 +0100 Subject: added pubAck handlers in rabbit_channel A transactional channel cannot be made pubAck. A pubAck channel cannot be made transactional. --- src/rabbit_channel.erl | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 582960e7..e42f49c5 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -47,7 +47,8 @@ transaction_id, tx_participants, next_tag, uncommitted_ack_q, unacked_message_q, username, virtual_host, most_recently_declared_queue, - consumer_mapping, blocking, queue_collector_pid, stats_timer}). + consumer_mapping, blocking, queue_collector_pid, stats_timer, + pubAck_mode}). -define(MAX_PERMISSION_CACHE_SIZE, 12). @@ -170,7 +171,8 @@ init([Channel, ReaderPid, WriterPid, Username, VHost, CollectorPid]) -> consumer_mapping = dict:new(), blocking = dict:new(), queue_collector_pid = CollectorPid, - stats_timer = rabbit_event:init_stats_timer()}, + stats_timer = rabbit_event:init_stats_timer(), + pubAck_mode = none}, rabbit_event:notify( channel_created, [{Item, i(Item, State)} || Item <- ?CREATION_EVENT_KEYS]), @@ -830,12 +832,17 @@ handle_method(#'queue.purge'{queue = QueueNameBin, return_ok(State, NoWait, #'queue.purge_ok'{message_count = PurgedMessageCount}); -handle_method(#'tx.select'{}, _, State = #ch{transaction_id = none}) -> +handle_method(#'tx.select'{}, _, State = #ch{transaction_id = none, + pubAck_mode = none}) -> {reply, #'tx.select_ok'{}, new_tx(State)}; -handle_method(#'tx.select'{}, _, State) -> +handle_method(#'tx.select'{}, _, State = #ch{pubAck_mode = none}) -> {reply, #'tx.select_ok'{}, State}; +handle_method(#'tx.select'{}, _, _State) -> + rabbit_misc:protocol_error( + precondition_failed, "a pubAck channel cannot be made transactional", []); + handle_method(#'tx.commit'{}, _, #ch{transaction_id = none}) -> rabbit_misc:protocol_error( precondition_failed, "channel is not transactional", []); @@ -850,6 +857,26 @@ handle_method(#'tx.rollback'{}, _, #ch{transaction_id = none}) -> handle_method(#'tx.rollback'{}, _, State) -> {reply, #'tx.rollback_ok'{}, internal_rollback(State)}; +handle_method(#'pubAck.select'{many = Many}, _, + State = #ch{transaction_id = none}) -> + rabbit_log:info("got pubAck.select{many = ~p}~n", [Many]), + {noreply, State#ch{ pubAck_mode = case Many of + true -> 'pubAckMany'; + false -> 'pubAck' + end + }}; +handle_method(#'pubAck.select'{}, _, State) -> + rabbit_misc:protocol_error( + precondition_failed, "transactional channel cannot be made pubAck", []); + +handle_method(#'pubAck.deselect'{}, _, State = #ch{ pubAck_mode = 'none' }) -> + rabbit_misc:protocol_error( + precondition_failed, "channel must first be in pubAck, to deselect", []); + +handle_method(#'pubAck.deselect'{}, _, State) -> + rabbit_log:info("got pubAck.deselect{}~n"), + {reply, #'pubAck.deselect_ok'{}, State#ch{ pubAck_mode = 'none'}}; + handle_method(#'channel.flow'{active = true}, _, State = #ch{limiter_pid = LimiterPid}) -> LimiterPid1 = case rabbit_limiter:unblock(LimiterPid) of @@ -858,6 +885,7 @@ handle_method(#'channel.flow'{active = true}, _, end, {reply, #'channel.flow_ok'{active = true}, State#ch{limiter_pid = LimiterPid1}}; + handle_method(#'channel.flow'{active = false}, _, State = #ch{limiter_pid = LimiterPid, consumer_mapping = Consumers}) -> -- cgit v1.2.1 From 1f9da7eca02b0fe649cf2ef1bcb59523a8be2220 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Wed, 11 Aug 2010 13:54:36 +0100 Subject: added counter and single pubacks for transient messages Publishes are counter starting with the first one after pubAck.select(). If many is set to false, transient messages are ack'd as soon as the are received, but after the exchange name is resolved. --- src/rabbit_channel.erl | 51 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index e42f49c5..38c48550 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -49,6 +49,7 @@ username, virtual_host, most_recently_declared_queue, consumer_mapping, blocking, queue_collector_pid, stats_timer, pubAck_mode}). +-record(pubAck, {enabled, count, many}). -define(MAX_PERMISSION_CACHE_SIZE, 12). @@ -172,7 +173,8 @@ init([Channel, ReaderPid, WriterPid, Username, VHost, CollectorPid]) -> blocking = dict:new(), queue_collector_pid = CollectorPid, stats_timer = rabbit_event:init_stats_timer(), - pubAck_mode = none}, + pubAck_mode = #pubAck{ enabled = false, + count = 0 }}, rabbit_event:notify( channel_created, [{Item, i(Item, State)} || Item <- ?CREATION_EVENT_KEYS]), @@ -393,6 +395,16 @@ queue_blocked(QPid, State = #ch{blocking = Blocking}) -> State#ch{blocking = Blocking1} end. +handle_pubAck(PA = #pubAck{ enabled = false }, _) -> + PA; +handle_pubAck(PA = #pubAck{ count = Count, many = false }, WriterPid) -> + rabbit_log:info("handling pubAck in single mode (#~p)~n", [Count]), + ok = rabbit_writer:send_command(WriterPid, #'basic.ack'{ delivery_tag = Count }), + PA#pubAck{ count = Count+1 }; +handle_pubAck(PA = #pubAck{ count = Count }, _WriterPid) -> + rabbit_log:info("handling pubAck in many mode (#~p)~n", [Count]), + PA#pubAck{ count = Count+1 }. + handle_method(#'channel.open'{}, _, State = #ch{state = starting}) -> {reply, #'channel.open_ok'{}, State#ch{state = running}}; @@ -417,10 +429,12 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, immediate = Immediate}, Content, State = #ch{virtual_host = VHostPath, transaction_id = TxnKey, - writer_pid = WriterPid}) -> + writer_pid = WriterPid, + pubAck_mode = PubAck}) -> ExchangeName = rabbit_misc:r(VHostPath, exchange, ExchangeNameBin), check_write_permitted(ExchangeName, State), Exchange = rabbit_exchange:lookup_or_die(ExchangeName), + PubAck1 = handle_pubAck(PubAck, WriterPid), %% We decode the content's properties here because we're almost %% certain to want to look at delivery-mode and priority. DecodedContent = rabbit_binary_parser:ensure_content_decoded(Content), @@ -442,9 +456,10 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, maybe_incr_stats([{ExchangeName, 1} | [{{QPid, ExchangeName}, 1} || QPid <- DeliveredQPids]], publish, State), + State1 = State#ch{ pubAck_mode = PubAck1 }, {noreply, case TxnKey of - none -> State; - _ -> add_tx_participants(DeliveredQPids, State) + none -> State1; + _ -> add_tx_participants(DeliveredQPids, State1) end}; handle_method(#'basic.ack'{delivery_tag = DeliveryTag, @@ -832,11 +847,13 @@ handle_method(#'queue.purge'{queue = QueueNameBin, return_ok(State, NoWait, #'queue.purge_ok'{message_count = PurgedMessageCount}); -handle_method(#'tx.select'{}, _, State = #ch{transaction_id = none, - pubAck_mode = none}) -> +handle_method(#'tx.select'{}, _, + State = #ch{transaction_id = none, + pubAck_mode = #pubAck{enabled = false}}) -> {reply, #'tx.select_ok'{}, new_tx(State)}; -handle_method(#'tx.select'{}, _, State = #ch{pubAck_mode = none}) -> +handle_method(#'tx.select'{}, _, + State = #ch{pubAck_mode = #pubAck{enabled = false}}) -> {reply, #'tx.select_ok'{}, State}; handle_method(#'tx.select'{}, _, _State) -> @@ -858,24 +875,24 @@ handle_method(#'tx.rollback'{}, _, State) -> {reply, #'tx.rollback_ok'{}, internal_rollback(State)}; handle_method(#'pubAck.select'{many = Many}, _, - State = #ch{transaction_id = none}) -> + State = #ch{ transaction_id = none, + pubAck_mode = PA}) -> rabbit_log:info("got pubAck.select{many = ~p}~n", [Many]), - {noreply, State#ch{ pubAck_mode = case Many of - true -> 'pubAckMany'; - false -> 'pubAck' - end - }}; -handle_method(#'pubAck.select'{}, _, State) -> + {noreply, State#ch{ pubAck_mode = PA#'pubAck'{ enabled = true, + many = Many } }}; +handle_method(#'pubAck.select'{}, _, _State) -> rabbit_misc:protocol_error( precondition_failed, "transactional channel cannot be made pubAck", []); -handle_method(#'pubAck.deselect'{}, _, State = #ch{ pubAck_mode = 'none' }) -> +handle_method(#'pubAck.deselect'{}, _, + #ch{ pubAck_mode = #pubAck { enabled = false }}) -> rabbit_misc:protocol_error( precondition_failed, "channel must first be in pubAck, to deselect", []); -handle_method(#'pubAck.deselect'{}, _, State) -> +handle_method(#'pubAck.deselect'{}, _, State = #ch{ pubAck_mode = PA }) -> rabbit_log:info("got pubAck.deselect{}~n"), - {reply, #'pubAck.deselect_ok'{}, State#ch{ pubAck_mode = 'none'}}; + {reply, #'pubAck.deselect_ok'{}, + State#ch{ pubAck_mode = PA#pubAck{ enabled = false }}}; handle_method(#'channel.flow'{active = true}, _, State = #ch{limiter_pid = LimiterPid}) -> -- cgit v1.2.1 From 1061deaadb59343c66026e6c67f89413197f4aa6 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Wed, 11 Aug 2010 14:01:16 +0100 Subject: check that messages are transient before ack'ing blindly --- src/rabbit_channel.erl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 38c48550..d06b7a30 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -395,14 +395,14 @@ queue_blocked(QPid, State = #ch{blocking = Blocking}) -> State#ch{blocking = Blocking1} end. -handle_pubAck(PA = #pubAck{ enabled = false }, _) -> +handle_pubAck(PA = #pubAck{ enabled = false }, _, _) -> PA; -handle_pubAck(PA = #pubAck{ count = Count, many = false }, WriterPid) -> +handle_pubAck(PA = #pubAck{ count = Count, many = false }, false, WriterPid) -> rabbit_log:info("handling pubAck in single mode (#~p)~n", [Count]), ok = rabbit_writer:send_command(WriterPid, #'basic.ack'{ delivery_tag = Count }), PA#pubAck{ count = Count+1 }; -handle_pubAck(PA = #pubAck{ count = Count }, _WriterPid) -> - rabbit_log:info("handling pubAck in many mode (#~p)~n", [Count]), +handle_pubAck(PA = #pubAck{ count = Count }, IsPersistent, _WriterPid) -> + rabbit_log:info("handling pubAck (#~p, persistent = ~p)~n", [Count, IsPersistent]), PA#pubAck{ count = Count+1 }. handle_method(#'channel.open'{}, _, State = #ch{state = starting}) -> @@ -434,11 +434,11 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, ExchangeName = rabbit_misc:r(VHostPath, exchange, ExchangeNameBin), check_write_permitted(ExchangeName, State), Exchange = rabbit_exchange:lookup_or_die(ExchangeName), - PubAck1 = handle_pubAck(PubAck, WriterPid), %% We decode the content's properties here because we're almost %% certain to want to look at delivery-mode and priority. DecodedContent = rabbit_binary_parser:ensure_content_decoded(Content), IsPersistent = is_message_persistent(DecodedContent), + PubAck1 = handle_pubAck(PubAck, IsPersistent, WriterPid), Message = #basic_message{exchange_name = ExchangeName, routing_key = RoutingKey, content = DecodedContent, -- cgit v1.2.1 From 5163bac9370889aadc3233a407341adf6d88a83b Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Wed, 11 Aug 2010 16:06:00 +0100 Subject: rename pubAck -> confirm --- Makefile | 2 +- src/rabbit_channel.erl | 65 +++++++++++++++++++++++--------------------------- 2 files changed, 31 insertions(+), 36 deletions(-) diff --git a/Makefile b/Makefile index 195c4ddf..b39c09f6 100644 --- a/Makefile +++ b/Makefile @@ -58,7 +58,7 @@ SIBLING_CODEGEN_DIR=../rabbitmq-codegen/ AMQP_CODEGEN_DIR=$(shell [ -d $(SIBLING_CODEGEN_DIR) ] && echo $(SIBLING_CODEGEN_DIR) || echo codegen) AMQP_SPEC_JSON_FILES_0_9_1=$(AMQP_CODEGEN_DIR)/amqp-rabbitmq-0.9.1.json AMQP_SPEC_JSON_FILES_0_8=$(AMQP_CODEGEN_DIR)/amqp-rabbitmq-0.8.json -RABBIT_SPEC_EXTENSIONS=$(AMQP_CODEGEN_DIR)/puback-extension.json +RABBIT_SPEC_EXTENSIONS=$(AMQP_CODEGEN_DIR)/confirm-extension.json ERL_CALL=erl_call -sname $(RABBITMQ_NODENAME) -e diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index d06b7a30..35e95a18 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -48,8 +48,8 @@ uncommitted_ack_q, unacked_message_q, username, virtual_host, most_recently_declared_queue, consumer_mapping, blocking, queue_collector_pid, stats_timer, - pubAck_mode}). --record(pubAck, {enabled, count, many}). + confirm}). +-record(confirm, {enabled, count, multiple}). -define(MAX_PERMISSION_CACHE_SIZE, 12). @@ -173,8 +173,8 @@ init([Channel, ReaderPid, WriterPid, Username, VHost, CollectorPid]) -> blocking = dict:new(), queue_collector_pid = CollectorPid, stats_timer = rabbit_event:init_stats_timer(), - pubAck_mode = #pubAck{ enabled = false, - count = 0 }}, + confirm = #confirm{ enabled = false, + count = 0 }}, rabbit_event:notify( channel_created, [{Item, i(Item, State)} || Item <- ?CREATION_EVENT_KEYS]), @@ -395,15 +395,15 @@ queue_blocked(QPid, State = #ch{blocking = Blocking}) -> State#ch{blocking = Blocking1} end. -handle_pubAck(PA = #pubAck{ enabled = false }, _, _) -> - PA; -handle_pubAck(PA = #pubAck{ count = Count, many = false }, false, WriterPid) -> - rabbit_log:info("handling pubAck in single mode (#~p)~n", [Count]), +handle_confirm(C = #confirm{ enabled = false }, _, _) -> + C; +handle_confirm(C = #confirm{ count = Count, multiple = false }, false, WriterPid) -> + rabbit_log:info("handling confirm in single mode (#~p)~n", [Count]), ok = rabbit_writer:send_command(WriterPid, #'basic.ack'{ delivery_tag = Count }), - PA#pubAck{ count = Count+1 }; -handle_pubAck(PA = #pubAck{ count = Count }, IsPersistent, _WriterPid) -> - rabbit_log:info("handling pubAck (#~p, persistent = ~p)~n", [Count, IsPersistent]), - PA#pubAck{ count = Count+1 }. + C#confirm{ count = Count+1 }; +handle_confirm(C = #confirm{ count = Count }, IsPersistent, _WriterPid) -> + rabbit_log:info("handling confirm (#~p, persistent = ~p)~n", [Count, IsPersistent]), + C#confirm{ count = Count+1 }. handle_method(#'channel.open'{}, _, State = #ch{state = starting}) -> {reply, #'channel.open_ok'{}, State#ch{state = running}}; @@ -430,7 +430,7 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, Content, State = #ch{virtual_host = VHostPath, transaction_id = TxnKey, writer_pid = WriterPid, - pubAck_mode = PubAck}) -> + confirm = Confirm}) -> ExchangeName = rabbit_misc:r(VHostPath, exchange, ExchangeNameBin), check_write_permitted(ExchangeName, State), Exchange = rabbit_exchange:lookup_or_die(ExchangeName), @@ -438,7 +438,7 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, %% certain to want to look at delivery-mode and priority. DecodedContent = rabbit_binary_parser:ensure_content_decoded(Content), IsPersistent = is_message_persistent(DecodedContent), - PubAck1 = handle_pubAck(PubAck, IsPersistent, WriterPid), + Confirm1 = handle_confirm(Confirm, IsPersistent, WriterPid), Message = #basic_message{exchange_name = ExchangeName, routing_key = RoutingKey, content = DecodedContent, @@ -456,7 +456,7 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, maybe_incr_stats([{ExchangeName, 1} | [{{QPid, ExchangeName}, 1} || QPid <- DeliveredQPids]], publish, State), - State1 = State#ch{ pubAck_mode = PubAck1 }, + State1 = State#ch{ confirm = Confirm1 }, {noreply, case TxnKey of none -> State1; _ -> add_tx_participants(DeliveredQPids, State1) @@ -849,16 +849,16 @@ handle_method(#'queue.purge'{queue = QueueNameBin, handle_method(#'tx.select'{}, _, State = #ch{transaction_id = none, - pubAck_mode = #pubAck{enabled = false}}) -> + confirm = #confirm{enabled = false}}) -> {reply, #'tx.select_ok'{}, new_tx(State)}; handle_method(#'tx.select'{}, _, - State = #ch{pubAck_mode = #pubAck{enabled = false}}) -> + State = #ch{confirm = #confirm{enabled = false}}) -> {reply, #'tx.select_ok'{}, State}; handle_method(#'tx.select'{}, _, _State) -> rabbit_misc:protocol_error( - precondition_failed, "a pubAck channel cannot be made transactional", []); + precondition_failed, "a confirm channel cannot be made transactional", []); handle_method(#'tx.commit'{}, _, #ch{transaction_id = none}) -> rabbit_misc:protocol_error( @@ -874,25 +874,20 @@ handle_method(#'tx.rollback'{}, _, #ch{transaction_id = none}) -> handle_method(#'tx.rollback'{}, _, State) -> {reply, #'tx.rollback_ok'{}, internal_rollback(State)}; -handle_method(#'pubAck.select'{many = Many}, _, +handle_method(#'confirm.select'{multiple = Multiple, nowait = NoWait}, _, State = #ch{ transaction_id = none, - pubAck_mode = PA}) -> - rabbit_log:info("got pubAck.select{many = ~p}~n", [Many]), - {noreply, State#ch{ pubAck_mode = PA#'pubAck'{ enabled = true, - many = Many } }}; -handle_method(#'pubAck.select'{}, _, _State) -> - rabbit_misc:protocol_error( - precondition_failed, "transactional channel cannot be made pubAck", []); - -handle_method(#'pubAck.deselect'{}, _, - #ch{ pubAck_mode = #pubAck { enabled = false }}) -> + confirm = C}) -> + rabbit_log:info("got confirm.select{multiple = ~p, nowait = ~p}~n", + [Multiple, NoWait]), + State1 = State#ch{confirm = C#confirm{ enabled = true, + multiple = Multiple }}, + case NoWait of + true -> {noreply, State1}; + false -> {reply, #'confirm.select_ok'{}, State1} + end; +handle_method(#'confirm.select'{}, _, _State) -> rabbit_misc:protocol_error( - precondition_failed, "channel must first be in pubAck, to deselect", []); - -handle_method(#'pubAck.deselect'{}, _, State = #ch{ pubAck_mode = PA }) -> - rabbit_log:info("got pubAck.deselect{}~n"), - {reply, #'pubAck.deselect_ok'{}, - State#ch{ pubAck_mode = PA#pubAck{ enabled = false }}}; + precondition_failed, "transactional channel cannot be made confirm", []); handle_method(#'channel.flow'{active = true}, _, State = #ch{limiter_pid = LimiterPid}) -> -- cgit v1.2.1 From 125ebcdcc209b4550256fc1a9d1078d9c79c91be Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Wed, 11 Aug 2010 17:20:49 +0100 Subject: subsequent confirm.selects cannot change the multiple setting --- src/rabbit_channel.erl | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 35e95a18..09d1b385 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -49,7 +49,7 @@ username, virtual_host, most_recently_declared_queue, consumer_mapping, blocking, queue_collector_pid, stats_timer, confirm}). --record(confirm, {enabled, count, multiple}). +-record(confirm, {enabled, count, multiple, tref}). -define(MAX_PERMISSION_CACHE_SIZE, 12). @@ -174,7 +174,9 @@ init([Channel, ReaderPid, WriterPid, Username, VHost, CollectorPid]) -> queue_collector_pid = CollectorPid, stats_timer = rabbit_event:init_stats_timer(), confirm = #confirm{ enabled = false, - count = 0 }}, + count = 0, + multiple = false, + tref = not_started }}, rabbit_event:notify( channel_created, [{Item, i(Item, State)} || Item <- ?CREATION_EVENT_KEYS]), @@ -874,9 +876,11 @@ handle_method(#'tx.rollback'{}, _, #ch{transaction_id = none}) -> handle_method(#'tx.rollback'{}, _, State) -> {reply, #'tx.rollback_ok'{}, internal_rollback(State)}; -handle_method(#'confirm.select'{multiple = Multiple, nowait = NoWait}, _, +handle_method(#'confirm.select'{multiple = Multiple, + nowait = NoWait}, + _, State = #ch{ transaction_id = none, - confirm = C}) -> + confirm = C = #confirm{enabled = false}}) -> rabbit_log:info("got confirm.select{multiple = ~p, nowait = ~p}~n", [Multiple, NoWait]), State1 = State#ch{confirm = C#confirm{ enabled = true, @@ -885,6 +889,22 @@ handle_method(#'confirm.select'{multiple = Multiple, nowait = NoWait}, _, true -> {noreply, State1}; false -> {reply, #'confirm.select_ok'{}, State1} end; + +handle_method(#'confirm.select'{multiple = Multiple, nowait = NoWait}, + _, + State = #ch{confirm = #confirm{enabled = true, + multiple = Multiple}}) -> + rabbit_log:info("got a confirm.select with same options~n"), + case NoWait of + true -> {noreply, State}; + false -> {reply, #'confirm.select_ok'{}, State} + end; + +handle_method(#'confirm.select'{}, + _, + #ch{confirm = #confirm{enabled = true}}) -> + rabbit_misc:protocol_error( + precondition_failed, "cannot change confirm channel multiple setting", []); handle_method(#'confirm.select'{}, _, _State) -> rabbit_misc:protocol_error( precondition_failed, "transactional channel cannot be made confirm", []); -- cgit v1.2.1 From 23a776e0e2c66a103dcc4f60ebfa24ad97fd418e Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Wed, 11 Aug 2010 18:00:22 +0100 Subject: added support for multiple publisher acks for transient messages When in confirm multiple mode, transient messages queue up in a gb_set. Every 5s, these acks are sent out (individually, for now). --- src/rabbit_channel.erl | 38 +++++++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 09d1b385..dbfb11c0 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -49,7 +49,7 @@ username, virtual_host, most_recently_declared_queue, consumer_mapping, blocking, queue_collector_pid, stats_timer, confirm}). --record(confirm, {enabled, count, multiple, tref}). +-record(confirm, {enabled, count, multiple, tref, held_acks}). -define(MAX_PERMISSION_CACHE_SIZE, 12). @@ -70,6 +70,8 @@ -define(INFO_KEYS, ?CREATION_EVENT_KEYS ++ ?STATISTICS_KEYS -- [pid]). +-define(MULTIPLE_ACK_FLUSH_INTERVAL, 5000). + %%---------------------------------------------------------------------------- -ifdef(use_specs). @@ -176,7 +178,8 @@ init([Channel, ReaderPid, WriterPid, Username, VHost, CollectorPid]) -> confirm = #confirm{ enabled = false, count = 0, multiple = false, - tref = not_started }}, + tref = not_started, + held_acks = gb_sets:new()}}, rabbit_event:notify( channel_created, [{Item, i(Item, State)} || Item <- ?CREATION_EVENT_KEYS]), @@ -253,7 +256,20 @@ handle_info({'EXIT', _Pid, Reason}, State) -> {stop, Reason, State}; handle_info({'DOWN', _MRef, process, QPid, _Reason}, State) -> erase_queue_stats(QPid), - {noreply, queue_blocked(QPid, State)}. + {noreply, queue_blocked(QPid, State)}; +handle_info(multiple_ack_flush, + State = #ch{ writer_pid = WriterPid, + confirm = #confirm{held_acks = As} } ) -> + rabbit_log:info("channel got a multiple_ack_flush message~n" + "held acks: ~p~n", [gb_sets:to_list(As)]), + gb_sets:fold(fun(A, L) -> + ok = rabbit_writer:send_command( + WriterPid, + #'basic.ack'{ delivery_tag = A }), + L + end, -1, As), + {noreply, State#ch{confirm = #confirm{ held_acks = gb_sets:new()}}}. + handle_pre_hibernate(State) -> ok = clear_permission_cache(), @@ -400,9 +416,15 @@ queue_blocked(QPid, State = #ch{blocking = Blocking}) -> handle_confirm(C = #confirm{ enabled = false }, _, _) -> C; handle_confirm(C = #confirm{ count = Count, multiple = false }, false, WriterPid) -> - rabbit_log:info("handling confirm in single mode (#~p)~n", [Count]), + rabbit_log:info("handling confirm in single transient mode (#~p)~n", [Count]), ok = rabbit_writer:send_command(WriterPid, #'basic.ack'{ delivery_tag = Count }), C#confirm{ count = Count+1 }; +handle_confirm(C = #confirm{ count = Count, + multiple = true, + held_acks = As }, false, _WriterPid) -> + rabbit_log:info("handling confirm in multiple transient mode (#~p)~n", [Count]), + C#confirm{ count = Count+1, + held_acks = gb_sets:add(Count, As) }; handle_confirm(C = #confirm{ count = Count }, IsPersistent, _WriterPid) -> rabbit_log:info("handling confirm (#~p, persistent = ~p)~n", [Count, IsPersistent]), C#confirm{ count = Count+1 }. @@ -883,8 +905,14 @@ handle_method(#'confirm.select'{multiple = Multiple, confirm = C = #confirm{enabled = false}}) -> rabbit_log:info("got confirm.select{multiple = ~p, nowait = ~p}~n", [Multiple, NoWait]), + TRef = case Multiple of + false -> not_started; + true -> timer:send_interval(?MULTIPLE_ACK_FLUSH_INTERVAL, + multiple_ack_flush) + end, State1 = State#ch{confirm = C#confirm{ enabled = true, - multiple = Multiple }}, + multiple = Multiple, + tref = TRef }}, case NoWait of true -> {noreply, State1}; false -> {reply, #'confirm.select_ok'{}, State1} -- cgit v1.2.1 From cba57a28caec049eccad2f4bfa621fcd0f58dcfe Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Thu, 12 Aug 2010 11:04:13 +0100 Subject: support for multiple confirmations Suppose the channel is in confirm multiple mode, and acks for the following messages are outstanding: > 3 4 5 7 10 13 The server sends one confirm-up-to 5 ack and three more acks for 7, 10 and 13. If the client receives a basic.ack, it should interpret it like this: basic.ack{delivery-tag=N, multiple=false} => the published message with sequence number N has been handled basic.ack{delivery-tag=N, multiple=true} => all the published messages with sequence numbers up to and including N have been handled --- src/rabbit_channel.erl | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index dbfb11c0..b5c5d8a7 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -262,14 +262,35 @@ handle_info(multiple_ack_flush, confirm = #confirm{held_acks = As} } ) -> rabbit_log:info("channel got a multiple_ack_flush message~n" "held acks: ~p~n", [gb_sets:to_list(As)]), - gb_sets:fold(fun(A, L) -> - ok = rabbit_writer:send_command( - WriterPid, - #'basic.ack'{ delivery_tag = A }), - L - end, -1, As), + case gb_sets:is_empty(As) of + true -> ok; + false -> flush_multiple(As, WriterPid) + end, {noreply, State#ch{confirm = #confirm{ held_acks = gb_sets:new()}}}. +flush_multiple(Acks, WriterPid) -> + [First | Rest] = gb_sets:to_list(Acks), + flush_multiple(First, Rest, WriterPid). + +flush_multiple(Prev, [Cur | Rest], WriterPid) -> + ExpNext = Prev+1, + case Cur of + ExpNext -> + flush_multiple(Cur, Rest, WriterPid); + _ -> + flush_multiple(Prev, [], WriterPid), + lists:foreach(fun(A) -> + ok = rabbit_writer:send_command( + WriterPid, + #'basic.ack'{delivery_tag = A}) + end, [Cur | Rest]) + end; +flush_multiple(Prev, [], WriterPid) -> + ok = rabbit_writer:send_command( + WriterPid, + #'basic.ack'{ delivery_tag = Prev, + multiple = true }). + handle_pre_hibernate(State) -> ok = clear_permission_cache(), -- cgit v1.2.1 From 3624d2beb2710aef09433d1b888cb85206fefc38 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Thu, 12 Aug 2010 12:20:50 +0100 Subject: refactor + timer cancelled on channel terminate When the channel terminates (for whatever reason), any outstanding acks are simply lost. The reasoning behind this is that: 1) if the channel closed due to an exception, it should close immediately; 2) if the client wants to close the channel, but receive all of the acks first, it should wait for the acks and only then send channel.close. --- src/rabbit_channel.erl | 58 +++++++++++++++++++++++++++----------------------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index b5c5d8a7..fe2cbbda 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -259,14 +259,14 @@ handle_info({'DOWN', _MRef, process, QPid, _Reason}, State) -> {noreply, queue_blocked(QPid, State)}; handle_info(multiple_ack_flush, State = #ch{ writer_pid = WriterPid, - confirm = #confirm{held_acks = As} } ) -> + confirm = C = #confirm{held_acks = As} } ) -> rabbit_log:info("channel got a multiple_ack_flush message~n" "held acks: ~p~n", [gb_sets:to_list(As)]), case gb_sets:is_empty(As) of true -> ok; false -> flush_multiple(As, WriterPid) end, - {noreply, State#ch{confirm = #confirm{ held_acks = gb_sets:new()}}}. + {noreply, State#ch{confirm = C#confirm{ held_acks = gb_sets:new()}}}. flush_multiple(Acks, WriterPid) -> [First | Rest] = gb_sets:to_list(Acks), @@ -501,10 +501,9 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, maybe_incr_stats([{ExchangeName, 1} | [{{QPid, ExchangeName}, 1} || QPid <- DeliveredQPids]], publish, State), - State1 = State#ch{ confirm = Confirm1 }, {noreply, case TxnKey of - none -> State1; - _ -> add_tx_participants(DeliveredQPids, State1) + none -> State#ch{confirm = Confirm1}; + _ -> add_tx_participants(DeliveredQPids, State) end}; handle_method(#'basic.ack'{delivery_tag = DeliveryTag, @@ -892,19 +891,17 @@ handle_method(#'queue.purge'{queue = QueueNameBin, return_ok(State, NoWait, #'queue.purge_ok'{message_count = PurgedMessageCount}); -handle_method(#'tx.select'{}, _, - State = #ch{transaction_id = none, - confirm = #confirm{enabled = false}}) -> - {reply, #'tx.select_ok'{}, new_tx(State)}; - -handle_method(#'tx.select'{}, _, - State = #ch{confirm = #confirm{enabled = false}}) -> - {reply, #'tx.select_ok'{}, State}; -handle_method(#'tx.select'{}, _, _State) -> +handle_method(#'tx.select'{}, _, #ch{confirm = #confirm{enabled = false}}) -> rabbit_misc:protocol_error( precondition_failed, "a confirm channel cannot be made transactional", []); +handle_method(#'tx.select'{}, _, State = #ch{transaction_id = none}) -> + {reply, #'tx.select_ok'{}, new_tx(State)}; + +handle_method(#'tx.select'{}, _, State) -> + {reply, #'tx.select_ok'{}, State}; + handle_method(#'tx.commit'{}, _, #ch{transaction_id = none}) -> rabbit_misc:protocol_error( precondition_failed, "channel is not transactional", []); @@ -919,18 +916,21 @@ handle_method(#'tx.rollback'{}, _, #ch{transaction_id = none}) -> handle_method(#'tx.rollback'{}, _, State) -> {reply, #'tx.rollback_ok'{}, internal_rollback(State)}; -handle_method(#'confirm.select'{multiple = Multiple, - nowait = NoWait}, +handle_method(#'confirm.select'{}, _, #ch{transaction_id = TxId}) + when TxId =/= none -> + rabbit_misc:protocol_error( + precondition_failed, "transactional channel cannot be made confirm", []); + +handle_method(#'confirm.select'{multiple = Multiple, nowait = NoWait}, _, - State = #ch{ transaction_id = none, - confirm = C = #confirm{enabled = false}}) -> + State = #ch{confirm = C = #confirm{enabled = false}}) -> rabbit_log:info("got confirm.select{multiple = ~p, nowait = ~p}~n", [Multiple, NoWait]), - TRef = case Multiple of - false -> not_started; - true -> timer:send_interval(?MULTIPLE_ACK_FLUSH_INTERVAL, - multiple_ack_flush) - end, + {ok, TRef} = case Multiple of + false -> {ok, not_started}; + true -> timer:send_interval(?MULTIPLE_ACK_FLUSH_INTERVAL, + multiple_ack_flush) + end, State1 = State#ch{confirm = C#confirm{ enabled = true, multiple = Multiple, tref = TRef }}, @@ -954,9 +954,6 @@ handle_method(#'confirm.select'{}, #ch{confirm = #confirm{enabled = true}}) -> rabbit_misc:protocol_error( precondition_failed, "cannot change confirm channel multiple setting", []); -handle_method(#'confirm.select'{}, _, _State) -> - rabbit_misc:protocol_error( - precondition_failed, "transactional channel cannot be made confirm", []); handle_method(#'channel.flow'{active = true}, _, State = #ch{limiter_pid = LimiterPid}) -> @@ -1198,7 +1195,14 @@ internal_deliver(WriterPid, Notify, ConsumerTag, DeliveryTag, false -> rabbit_writer:send_command(WriterPid, M, Content) end. -terminate(#ch{writer_pid = WriterPid, limiter_pid = LimiterPid}) -> +terminate(#ch{writer_pid = WriterPid, limiter_pid = LimiterPid, + confirm = #confirm {tref = TRef}}) -> + case TRef of + not_started -> ok; + _ -> + rabbit_log:info("Canceling multiple ack timer: ~p~n", [TRef]), + {ok, cancel} = timer:cancel(TRef) + end, pg_local:leave(rabbit_channels, self()), rabbit_event:notify(channel_closed, [{pid, self()}]), rabbit_writer:shutdown(WriterPid), -- cgit v1.2.1 From bae7cc718da7162018cb70b5ae61de5eb6c9fdff Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Thu, 12 Aug 2010 14:08:34 +0100 Subject: refactor timer code; looks more like msg_store now --- src/rabbit_channel.erl | 161 ++++++++++++++++++++++++++----------------------- 1 file changed, 86 insertions(+), 75 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index fe2cbbda..bebfc9b7 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -38,7 +38,7 @@ -export([start_link/6, do/2, do/3, shutdown/1]). -export([send_command/2, deliver/4, flushed/2]). -export([list/0, info_keys/0, info/1, info/2, info_all/0, info_all/1]). --export([emit_stats/1, flush/1]). +-export([emit_stats/1, flush/1, flush_multiple_acks/1]). -export([init/1, terminate/2, code_change/3, handle_call/3, handle_cast/2, handle_info/2, handle_pre_hibernate/1]). @@ -152,6 +152,10 @@ emit_stats(Pid) -> flush(Pid) -> gen_server2:call(Pid, flush). +flush_multiple_acks(Pid) -> + gen_server2:cast(Pid, multiple_ack_flush). + + %%--------------------------------------------------------------------------- init([Channel, ReaderPid, WriterPid, Username, VHost, CollectorPid]) -> @@ -175,11 +179,10 @@ init([Channel, ReaderPid, WriterPid, Username, VHost, CollectorPid]) -> blocking = dict:new(), queue_collector_pid = CollectorPid, stats_timer = rabbit_event:init_stats_timer(), - confirm = #confirm{ enabled = false, - count = 0, - multiple = false, - tref = not_started, - held_acks = gb_sets:new()}}, + confirm = #confirm{enabled = false, + count = 0, + multiple = false, + held_acks = gb_sets:new()}}, rabbit_event:notify( channel_created, [{Item, i(Item, State)} || Item <- ?CREATION_EVENT_KEYS]), @@ -246,7 +249,19 @@ handle_cast({deliver, ConsumerTag, AckRequired, Msg}, handle_cast(emit_stats, State) -> internal_emit_stats(State), - {noreply, State}. + {noreply, State}; + +handle_cast(multiple_ack_flush, + State = #ch{writer_pid = WriterPid, + confirm = C = #confirm{held_acks = As}}) -> + rabbit_log:info("channel got a multiple_ack_flush message~n" + "held acks: ~p~n", [gb_sets:to_list(As)]), + case gb_sets:is_empty(As) of + true -> ok; + false -> flush_multiple(As, WriterPid) + end, + {noreply, State#ch{confirm = C#confirm{held_acks = gb_sets:new(), + tref = undefined}}}. handle_info({'EXIT', WriterPid, Reason = {writer, send_failed, _Error}}, State = #ch{writer_pid = WriterPid}) -> @@ -256,41 +271,7 @@ handle_info({'EXIT', _Pid, Reason}, State) -> {stop, Reason, State}; handle_info({'DOWN', _MRef, process, QPid, _Reason}, State) -> erase_queue_stats(QPid), - {noreply, queue_blocked(QPid, State)}; -handle_info(multiple_ack_flush, - State = #ch{ writer_pid = WriterPid, - confirm = C = #confirm{held_acks = As} } ) -> - rabbit_log:info("channel got a multiple_ack_flush message~n" - "held acks: ~p~n", [gb_sets:to_list(As)]), - case gb_sets:is_empty(As) of - true -> ok; - false -> flush_multiple(As, WriterPid) - end, - {noreply, State#ch{confirm = C#confirm{ held_acks = gb_sets:new()}}}. - -flush_multiple(Acks, WriterPid) -> - [First | Rest] = gb_sets:to_list(Acks), - flush_multiple(First, Rest, WriterPid). - -flush_multiple(Prev, [Cur | Rest], WriterPid) -> - ExpNext = Prev+1, - case Cur of - ExpNext -> - flush_multiple(Cur, Rest, WriterPid); - _ -> - flush_multiple(Prev, [], WriterPid), - lists:foreach(fun(A) -> - ok = rabbit_writer:send_command( - WriterPid, - #'basic.ack'{delivery_tag = A}) - end, [Cur | Rest]) - end; -flush_multiple(Prev, [], WriterPid) -> - ok = rabbit_writer:send_command( - WriterPid, - #'basic.ack'{ delivery_tag = Prev, - multiple = true }). - + {noreply, queue_blocked(QPid, State)}. handle_pre_hibernate(State) -> ok = clear_permission_cache(), @@ -434,21 +415,24 @@ queue_blocked(QPid, State = #ch{blocking = Blocking}) -> State#ch{blocking = Blocking1} end. -handle_confirm(C = #confirm{ enabled = false }, _, _) -> - C; -handle_confirm(C = #confirm{ count = Count, multiple = false }, false, WriterPid) -> +handle_confirm(State = #ch{confirm = #confirm{enabled = false}}, _) -> + State; +handle_confirm(State = #ch{writer_pid = WriterPid, + confirm = C = #confirm{ count = Count, multiple = false}}, + false) -> rabbit_log:info("handling confirm in single transient mode (#~p)~n", [Count]), ok = rabbit_writer:send_command(WriterPid, #'basic.ack'{ delivery_tag = Count }), - C#confirm{ count = Count+1 }; -handle_confirm(C = #confirm{ count = Count, - multiple = true, - held_acks = As }, false, _WriterPid) -> + State#ch{confirm = C#confirm{ count = Count+1 }}; +handle_confirm(State = #ch{confirm = C = #confirm{count = Count, + multiple = true, + held_acks = As}}, false) -> rabbit_log:info("handling confirm in multiple transient mode (#~p)~n", [Count]), - C#confirm{ count = Count+1, - held_acks = gb_sets:add(Count, As) }; -handle_confirm(C = #confirm{ count = Count }, IsPersistent, _WriterPid) -> + State1 = start_ack_timer(State), + State1#ch{confirm = C#confirm{count = Count+1, + held_acks = gb_sets:add(Count, As)}}; +handle_confirm(State = #ch{confirm = C = #confirm{count = Count}}, IsPersistent) -> rabbit_log:info("handling confirm (#~p, persistent = ~p)~n", [Count, IsPersistent]), - C#confirm{ count = Count+1 }. + State#ch{confirm = C#confirm{count = Count+1}}. handle_method(#'channel.open'{}, _, State = #ch{state = starting}) -> {reply, #'channel.open_ok'{}, State#ch{state = running}}; @@ -474,8 +458,7 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, immediate = Immediate}, Content, State = #ch{virtual_host = VHostPath, transaction_id = TxnKey, - writer_pid = WriterPid, - confirm = Confirm}) -> + writer_pid = WriterPid}) -> ExchangeName = rabbit_misc:r(VHostPath, exchange, ExchangeNameBin), check_write_permitted(ExchangeName, State), Exchange = rabbit_exchange:lookup_or_die(ExchangeName), @@ -483,7 +466,7 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, %% certain to want to look at delivery-mode and priority. DecodedContent = rabbit_binary_parser:ensure_content_decoded(Content), IsPersistent = is_message_persistent(DecodedContent), - Confirm1 = handle_confirm(Confirm, IsPersistent, WriterPid), + State1 = handle_confirm(State, IsPersistent), Message = #basic_message{exchange_name = ExchangeName, routing_key = RoutingKey, content = DecodedContent, @@ -500,10 +483,10 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, end, maybe_incr_stats([{ExchangeName, 1} | [{{QPid, ExchangeName}, 1} || - QPid <- DeliveredQPids]], publish, State), + QPid <- DeliveredQPids]], publish, State1), {noreply, case TxnKey of - none -> State#ch{confirm = Confirm1}; - _ -> add_tx_participants(DeliveredQPids, State) + none -> State1; + _ -> add_tx_participants(DeliveredQPids, State1) end}; handle_method(#'basic.ack'{delivery_tag = DeliveryTag, @@ -926,14 +909,8 @@ handle_method(#'confirm.select'{multiple = Multiple, nowait = NoWait}, State = #ch{confirm = C = #confirm{enabled = false}}) -> rabbit_log:info("got confirm.select{multiple = ~p, nowait = ~p}~n", [Multiple, NoWait]), - {ok, TRef} = case Multiple of - false -> {ok, not_started}; - true -> timer:send_interval(?MULTIPLE_ACK_FLUSH_INTERVAL, - multiple_ack_flush) - end, - State1 = State#ch{confirm = C#confirm{ enabled = true, - multiple = Multiple, - tref = TRef }}, + State1 = State#ch{confirm = C#confirm{enabled = true, + multiple = Multiple}}, case NoWait of true -> {noreply, State1}; false -> {reply, #'confirm.select_ok'{}, State1} @@ -1195,14 +1172,8 @@ internal_deliver(WriterPid, Notify, ConsumerTag, DeliveryTag, false -> rabbit_writer:send_command(WriterPid, M, Content) end. -terminate(#ch{writer_pid = WriterPid, limiter_pid = LimiterPid, - confirm = #confirm {tref = TRef}}) -> - case TRef of - not_started -> ok; - _ -> - rabbit_log:info("Canceling multiple ack timer: ~p~n", [TRef]), - {ok, cancel} = timer:cancel(TRef) - end, +terminate(State = #ch{writer_pid = WriterPid, limiter_pid = LimiterPid}) -> + stop_ack_timer(State), pg_local:leave(rabbit_channels, self()), rabbit_event:notify(channel_closed, [{pid, self()}]), rabbit_writer:shutdown(WriterPid), @@ -1284,3 +1255,43 @@ erase_queue_stats(QPid) -> erase({queue_stats, QPid}), [erase({queue_exchange_stats, QX}) || {{queue_exchange_stats, QX = {QPid0, _}}, _} <- get(), QPid =:= QPid0]. + +start_ack_timer(State = #ch{confirm = C = #confirm{tref = undefined}}) -> + rabbit_log:info("starting ack timer...~n"), + {ok, TRef} = timer:apply_after(?MULTIPLE_ACK_FLUSH_INTERVAL, + ?MODULE, flush_multiple_acks, [self()]), + State#ch{confirm = C#confirm{tref = TRef}}; +start_ack_timer(State) -> + rabbit_log:info("timer already started.. nop~n"), + State. + +stop_ack_timer(State = #ch{confirm = #confirm{tref = undefined}}) -> + rabbit_log:info("stopping a stopped ack timer.. nop~n"), + State; +stop_ack_timer(State = #ch{confirm = C = #confirm{tref = TRef}}) -> + rabbit_log:info("canceling ack timer: ~p~n", [TRef]), + {ok, cancel} = timer:cancel(TRef), + State#ch{confirm = C#confirm{tref = undefined}}. + +flush_multiple(Acks, WriterPid) -> + [First | Rest] = gb_sets:to_list(Acks), + flush_multiple(First, Rest, WriterPid). + +flush_multiple(Prev, [Cur | Rest], WriterPid) -> + ExpNext = Prev+1, + case Cur of + ExpNext -> + flush_multiple(Cur, Rest, WriterPid); + _ -> + flush_multiple(Prev, [], WriterPid), + lists:foreach(fun(A) -> + ok = rabbit_writer:send_command( + WriterPid, + #'basic.ack'{delivery_tag = A}) + end, [Cur | Rest]) + end; +flush_multiple(Prev, [], WriterPid) -> + ok = rabbit_writer:send_command( + WriterPid, + #'basic.ack'{ delivery_tag = Prev, + multiple = true }). -- cgit v1.2.1 From f4c7b78a9330ee62e40f768b734f6f13f4db3c3c Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Thu, 12 Aug 2010 14:39:03 +0100 Subject: minor clarification --- 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 bebfc9b7..22511b24 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -257,7 +257,7 @@ handle_cast(multiple_ack_flush, rabbit_log:info("channel got a multiple_ack_flush message~n" "held acks: ~p~n", [gb_sets:to_list(As)]), case gb_sets:is_empty(As) of - true -> ok; + true -> ok; % this should never be the case false -> flush_multiple(As, WriterPid) end, {noreply, State#ch{confirm = C#confirm{held_acks = gb_sets:new(), -- cgit v1.2.1 From 8d9a539ddfb5145a08637b1620daa9a1f465ad1c Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Thu, 12 Aug 2010 14:52:46 +0100 Subject: type; all java tests pass now --- 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 22511b24..5c153b85 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -875,7 +875,7 @@ handle_method(#'queue.purge'{queue = QueueNameBin, #'queue.purge_ok'{message_count = PurgedMessageCount}); -handle_method(#'tx.select'{}, _, #ch{confirm = #confirm{enabled = false}}) -> +handle_method(#'tx.select'{}, _, #ch{confirm = #confirm{enabled = true}}) -> rabbit_misc:protocol_error( precondition_failed, "a confirm channel cannot be made transactional", []); -- cgit v1.2.1 From b00ecae35d2cd3bd2562b8d0a93b7c17ba3152ac Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Thu, 12 Aug 2010 15:07:06 +0100 Subject: only have one ack timer running at a time --- 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 5c153b85..ae97066b 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -423,11 +423,10 @@ handle_confirm(State = #ch{writer_pid = WriterPid, rabbit_log:info("handling confirm in single transient mode (#~p)~n", [Count]), ok = rabbit_writer:send_command(WriterPid, #'basic.ack'{ delivery_tag = Count }), State#ch{confirm = C#confirm{ count = Count+1 }}; -handle_confirm(State = #ch{confirm = C = #confirm{count = Count, - multiple = true, - held_acks = As}}, false) -> +handle_confirm(State = #ch{confirm = #confirm{multiple = true}}, false) -> + State1 = #ch{confirm = C = #confirm{count = Count, + held_acks = As}} = start_ack_timer(State), rabbit_log:info("handling confirm in multiple transient mode (#~p)~n", [Count]), - State1 = start_ack_timer(State), State1#ch{confirm = C#confirm{count = Count+1, held_acks = gb_sets:add(Count, As)}}; handle_confirm(State = #ch{confirm = C = #confirm{count = Count}}, IsPersistent) -> -- cgit v1.2.1 From 18157b73f8cf52c0840e1d0869691531c49ed833 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Fri, 13 Aug 2010 10:25:17 +0100 Subject: some instrumentation --- src/rabbit_amqqueue_process.erl | 16 ++++++++++++---- src/rabbit_channel.erl | 12 +++++++++++- src/rabbit_variable_queue.erl | 1 + 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index d52660c5..4bb2109e 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -345,9 +345,12 @@ deliver_msgs_to_consumers(Funs = {PredFun, DeliverFun}, FunAcc, ChPid, ConsumerTag, AckRequired, {QName, self(), AckTag, IsDelivered, Message}), ChAckTags1 = case AckRequired of - true -> sets:add_element( - AckTag, ChAckTags); - false -> ChAckTags + true -> + rabbit_log:info("Delivered message but waiting for ack~n"), + sets:add_element(AckTag, ChAckTags); + false -> + rabbit_log:info("Delivered message and not waiting for ack~n"), + ChAckTags end, NewC = C#cr{unsent_message_count = Count + 1, acktags = ChAckTags1}, @@ -384,6 +387,7 @@ deliver_msgs_to_consumers(Funs = {PredFun, DeliverFun}, FunAcc, {FunAcc, State} end; {empty, _} -> + rabbit_log:info("Message on a queue without consumers~n"), {FunAcc, State} end. @@ -405,7 +409,8 @@ run_message_queue(State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> {_IsEmpty1, State1} = deliver_msgs_to_consumers(Funs, IsEmpty, State), State1. -attempt_delivery(none, _ChPid, Message, State = #q{backing_queue = BQ}) -> +attempt_delivery(none, ChPid, Message, State = #q{backing_queue = BQ}) -> + rabbit_log:info("Attempting delivery of message~n"), PredFun = fun (IsEmpty, _State) -> not IsEmpty end, DeliverFun = fun (AckRequired, false, State1 = #q{backing_queue_state = BQS}) -> @@ -414,6 +419,7 @@ attempt_delivery(none, _ChPid, Message, State = #q{backing_queue = BQ}) -> {{Message, false, AckTag}, true, State1#q{backing_queue_state = BQS1}} end, + ConfirmFun = fun() -> rabbit_channel:confirm(ChPid, -1) end, deliver_msgs_to_consumers({ PredFun, DeliverFun }, false, State); attempt_delivery(Txn, ChPid, Message, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> @@ -421,6 +427,7 @@ attempt_delivery(Txn, ChPid, Message, State = #q{backing_queue = BQ, {true, State#q{backing_queue_state = BQ:tx_publish(Txn, Message, BQS)}}. deliver_or_enqueue(Txn, ChPid, Message, State = #q{backing_queue = BQ}) -> + rabbit_log:info("deliver_or_enqueue called for message~n"), case attempt_delivery(Txn, ChPid, Message, State) of {true, NewState} -> {true, NewState}; @@ -799,6 +806,7 @@ handle_cast({deliver, Txn, Message, ChPid}, State) -> handle_cast({ack, Txn, AckTags, ChPid}, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> + rabbit_log:info("Queue process got an ack~n"), case lookup_ch(ChPid) of not_found -> noreply(State); diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index ae97066b..07d35ce0 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -38,7 +38,7 @@ -export([start_link/6, do/2, do/3, shutdown/1]). -export([send_command/2, deliver/4, flushed/2]). -export([list/0, info_keys/0, info/1, info/2, info_all/0, info_all/1]). --export([emit_stats/1, flush/1, flush_multiple_acks/1]). +-export([emit_stats/1, flush/1, flush_multiple_acks/1, confirm/2]). -export([init/1, terminate/2, code_change/3, handle_call/3, handle_cast/2, handle_info/2, handle_pre_hibernate/1]). @@ -99,6 +99,8 @@ -spec(info_all/0 :: () -> [[rabbit_types:info()]]). -spec(info_all/1 :: ([rabbit_types:info_key()]) -> [[rabbit_types:info()]]). -spec(emit_stats/1 :: (pid()) -> 'ok'). +-spec(flush_multiple_acks/1 :: (pid()) -> 'ok'). +-spec(confirm/2 ::(pid(), integer()) -> 'ok'). -endif. @@ -155,6 +157,9 @@ flush(Pid) -> flush_multiple_acks(Pid) -> gen_server2:cast(Pid, multiple_ack_flush). +confirm(Pid, MessageSequenceNumber) -> + gen_server2:cast(Pid, {confirm, MessageSequenceNumber}). + %%--------------------------------------------------------------------------- @@ -262,6 +267,10 @@ handle_cast(multiple_ack_flush, end, {noreply, State#ch{confirm = C#confirm{held_acks = gb_sets:new(), tref = undefined}}}. +%handle_cast({confirm, MsgSeqNo}, State) -> +% rabbit_log:info("got confirm for #~p~n", [MsgSeqNo]), +% {noreply, SOMETHING MAGIC + handle_info({'EXIT', WriterPid, Reason = {writer, send_failed, _Error}}, State = #ch{writer_pid = WriterPid}) -> @@ -492,6 +501,7 @@ handle_method(#'basic.ack'{delivery_tag = DeliveryTag, multiple = Multiple}, _, State = #ch{transaction_id = TxnKey, unacked_message_q = UAMQ}) -> + rabbit_log:info("channel received a basic.ack~n"), {Acked, Remaining} = collect_acks(UAMQ, DeliveryTag, Multiple), QIncs = ack(TxnKey, Acked), Participants = [QPid || {QPid, _} <- QIncs], diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 0f52eee8..714eb206 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -1004,6 +1004,7 @@ publish(Msg = #basic_message { is_persistent = IsPersistent }, persistent_count = PCount, durable = IsDurable, ram_msg_count = RamMsgCount }) -> + rabbit_log:info("message ~p got to variable_queue:publish~n", [Msg#basic_message.guid]), IsPersistent1 = IsDurable andalso IsPersistent, MsgStatus = (msg_status(IsPersistent1, SeqId, Msg)) #msg_status { is_delivered = IsDelivered, msg_on_disk = MsgOnDisk }, -- cgit v1.2.1 From 3e7fac0c40f9eee9f19be71f4fa271ba59e39143 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Fri, 13 Aug 2010 11:55:46 +0100 Subject: new logic Message sequence numbers are assigned uniquely when the basic.publish is received. If confirm is not enabled, this is undefined. Rabbit_channel gains a new API method: confirm. This schedules an ack for the given sequence number. The idea is that repeated acks are allowed at this point and de-duplication is done by the channel. This way, in order to confirm a message, another module just needs to know 1) the channel and 2) the message sequence number. No de-duplication is done at this point. --- src/rabbit_channel.erl | 47 ++++++++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 07d35ce0..04875b5e 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -266,10 +266,10 @@ handle_cast(multiple_ack_flush, false -> flush_multiple(As, WriterPid) end, {noreply, State#ch{confirm = C#confirm{held_acks = gb_sets:new(), - tref = undefined}}}. -%handle_cast({confirm, MsgSeqNo}, State) -> -% rabbit_log:info("got confirm for #~p~n", [MsgSeqNo]), -% {noreply, SOMETHING MAGIC + tref = undefined}}}; +handle_cast({confirm, MsgSeqNo}, State) -> + rabbit_log:info("got confirm for #~p~n", [MsgSeqNo]), + {noreply, send_or_enqueue_ack(MsgSeqNo, State)}. handle_info({'EXIT', WriterPid, Reason = {writer, send_failed, _Error}}, @@ -424,23 +424,18 @@ queue_blocked(QPid, State = #ch{blocking = Blocking}) -> State#ch{blocking = Blocking1} end. -handle_confirm(State = #ch{confirm = #confirm{enabled = false}}, _) -> +send_or_enqueue_ack(_, State = #ch{confirm = #confirm{enabled = false}}) -> State; -handle_confirm(State = #ch{writer_pid = WriterPid, - confirm = C = #confirm{ count = Count, multiple = false}}, - false) -> - rabbit_log:info("handling confirm in single transient mode (#~p)~n", [Count]), - ok = rabbit_writer:send_command(WriterPid, #'basic.ack'{ delivery_tag = Count }), - State#ch{confirm = C#confirm{ count = Count+1 }}; -handle_confirm(State = #ch{confirm = #confirm{multiple = true}}, false) -> - State1 = #ch{confirm = C = #confirm{count = Count, - held_acks = As}} = start_ack_timer(State), - rabbit_log:info("handling confirm in multiple transient mode (#~p)~n", [Count]), - State1#ch{confirm = C#confirm{count = Count+1, - held_acks = gb_sets:add(Count, As)}}; -handle_confirm(State = #ch{confirm = C = #confirm{count = Count}}, IsPersistent) -> - rabbit_log:info("handling confirm (#~p, persistent = ~p)~n", [Count, IsPersistent]), - State#ch{confirm = C#confirm{count = Count+1}}. +send_or_enqueue_ack(MsgSeqNo, + State = #ch{writer_pid = WriterPid, + confirm = #confirm{multiple = false}}) -> + rabbit_log:info("handling confirm in single mode (#~p)~n", [MsgSeqNo]), + ok = rabbit_writer:send_command(WriterPid, #'basic.ack'{delivery_tag = MsgSeqNo}), + State; +send_or_enqueue_ack(MsgSeqNo, State = #ch{confirm = #confirm{multiple = true}}) -> + rabbit_log:info("handling confirm in multiple mode (#~p)~n", [MsgSeqNo]), + State1 = #ch{confirm = C = #confirm{held_acks = As}} = start_ack_timer(State), + State1#ch{confirm = C#confirm{held_acks = gb_sets:add(MsgSeqNo, As)}}. handle_method(#'channel.open'{}, _, State = #ch{state = starting}) -> {reply, #'channel.open_ok'{}, State#ch{state = running}}; @@ -474,7 +469,17 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, %% certain to want to look at delivery-mode and priority. DecodedContent = rabbit_binary_parser:ensure_content_decoded(Content), IsPersistent = is_message_persistent(DecodedContent), - State1 = handle_confirm(State, IsPersistent), + {_MsgSeqNo, State1} + = case State#ch.confirm#confirm.enabled of + false -> + {undefined, State}; + true -> + Count = State#ch.confirm#confirm.count, + NewState = send_or_enqueue_ack(Count, State), + Confirm = NewState#ch.confirm, + {Count, + NewState#ch{confirm = Confirm#confirm{count = Count+1}}} + end, Message = #basic_message{exchange_name = ExchangeName, routing_key = RoutingKey, content = DecodedContent, -- cgit v1.2.1 From 726b09a1cc493ee352c59d47d0d7383025029ea6 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Fri, 13 Aug 2010 12:21:01 +0100 Subject: only transient messages are immediately ack'd (so far) In addition, a message sequence number is recorded in the #delivery{} sent to the exchange. If the message doesn't need to be ack'd, this sequence number is undefined. --- include/rabbit.hrl | 2 +- src/rabbit_basic.erl | 12 +++++++++--- src/rabbit_channel.erl | 18 ++++++++++++------ 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/include/rabbit.hrl b/include/rabbit.hrl index b9abd788..bf680217 100644 --- a/include/rabbit.hrl +++ b/include/rabbit.hrl @@ -69,7 +69,7 @@ is_persistent}). -record(ssl_socket, {tcp, ssl}). --record(delivery, {mandatory, immediate, txn, sender, message}). +-record(delivery, {mandatory, immediate, txn, sender, message, msg_seq_no}). -record(amqp_error, {name, explanation, method = none}). -record(event, {type, props, timestamp}). diff --git a/src/rabbit_basic.erl b/src/rabbit_basic.erl index d62fc07c..3aa25d63 100644 --- a/src/rabbit_basic.erl +++ b/src/rabbit_basic.erl @@ -33,7 +33,7 @@ -include("rabbit.hrl"). -include("rabbit_framing.hrl"). --export([publish/1, message/4, properties/1, delivery/4]). +-export([publish/1, message/4, properties/1, delivery/4, delivery/5]). -export([publish/4, publish/7]). -export([build_content/2, from_content/1]). -export([is_message_persistent/1]). @@ -51,8 +51,12 @@ -spec(publish/1 :: (rabbit_types:delivery()) -> publish_result()). -spec(delivery/4 :: + (boolean(), boolean(), rabbit_types:maybe(rabbit_type:txn()), + rabbit_types:message()) -> rabiit_types:delivery()). +-spec(delivery/5 :: (boolean(), boolean(), rabbit_types:maybe(rabbit_types:txn()), - rabbit_types:message()) -> rabbit_types:delivery()). + rabbit_types:message(), integer() | undefined) + -> rabbit_types:delivery()). -spec(message/4 :: (rabbit_exchange:name(), rabbit_router:routing_key(), properties_input(), binary()) @@ -93,8 +97,10 @@ publish(Delivery = #delivery{ end. delivery(Mandatory, Immediate, Txn, Message) -> + delivery(Mandatory, Immediate, Txn, Message, undefined). +delivery(Mandatory, Immediate, Txn, Message, MsgSeqNo) -> #delivery{mandatory = Mandatory, immediate = Immediate, txn = Txn, - sender = self(), message = Message}. + sender = self(), message = Message, msg_seq_no = MsgSeqNo}. build_content(Properties, BodyBin) -> %% basic.publish hasn't changed so we can just hard-code amqp_0_9_1 diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 04875b5e..58f03e7a 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -157,8 +157,8 @@ flush(Pid) -> flush_multiple_acks(Pid) -> gen_server2:cast(Pid, multiple_ack_flush). -confirm(Pid, MessageSequenceNumber) -> - gen_server2:cast(Pid, {confirm, MessageSequenceNumber}). +confirm(Pid, MsgSeqNo) -> + gen_server2:cast(Pid, {confirm, MsgSeqNo}). %%--------------------------------------------------------------------------- @@ -469,15 +469,21 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, %% certain to want to look at delivery-mode and priority. DecodedContent = rabbit_binary_parser:ensure_content_decoded(Content), IsPersistent = is_message_persistent(DecodedContent), - {_MsgSeqNo, State1} + {MsgSeqNo, State1} = case State#ch.confirm#confirm.enabled of false -> {undefined, State}; true -> Count = State#ch.confirm#confirm.count, - NewState = send_or_enqueue_ack(Count, State), + {CountOrUndefined, NewState} = + case IsPersistent of + true -> {Count, State}; + false -> {undefined, + send_or_enqueue_ack( + Count, State)} + end, Confirm = NewState#ch.confirm, - {Count, + {CountOrUndefined, NewState#ch{confirm = Confirm#confirm{count = Count+1}}} end, Message = #basic_message{exchange_name = ExchangeName, @@ -488,7 +494,7 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, {RoutingRes, DeliveredQPids} = rabbit_exchange:publish( Exchange, - rabbit_basic:delivery(Mandatory, Immediate, TxnKey, Message)), + rabbit_basic:delivery(Mandatory, Immediate, TxnKey, Message, MsgSeqNo)), case RoutingRes of routed -> ok; unroutable -> ok = basic_return(Message, WriterPid, no_route); -- cgit v1.2.1 From 26927ebf94c220a82e206350b3bb33def48aa8b7 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Fri, 13 Aug 2010 15:44:04 +0100 Subject: partial acks for persistent messages The publisher gets an ack in the following cases: - the broker receives a transient message (after the exchange has been resolved), - the broker sends the persistent message to a consumer (regardless of whether the consumer acks that message, rejects it, etc.). Still to do: - the broker writes the message to disk. --- include/rabbit.hrl | 4 ++-- src/rabbit_amqqueue_process.erl | 36 ++++++++++++++++++++---------------- src/rabbit_basic.erl | 10 ++-------- src/rabbit_channel.erl | 7 ++++--- 4 files changed, 28 insertions(+), 29 deletions(-) diff --git a/include/rabbit.hrl b/include/rabbit.hrl index bf680217..e8255346 100644 --- a/include/rabbit.hrl +++ b/include/rabbit.hrl @@ -66,10 +66,10 @@ -record(listener, {node, protocol, host, port}). -record(basic_message, {exchange_name, routing_key, content, guid, - is_persistent}). + is_persistent, msg_seq_no, origin}). -record(ssl_socket, {tcp, ssl}). --record(delivery, {mandatory, immediate, txn, sender, message, msg_seq_no}). +-record(delivery, {mandatory, immediate, txn, sender, message}). -record(amqp_error, {name, explanation, method = none}). -record(event, {type, props, timestamp}). diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 4bb2109e..38eedad3 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -324,7 +324,7 @@ record_current_channel_tx(ChPid, Txn) -> %% that wasn't happening already) store_ch_record((ch_record(ChPid))#cr{txn = Txn}). -deliver_msgs_to_consumers(Funs = {PredFun, DeliverFun}, FunAcc, +deliver_msgs_to_consumers(Funs = {PredFun, DeliverFun, ConfirmFun}, FunAcc, State = #q{q = #amqqueue{name = QName}, active_consumers = ActiveConsumers, blocked_consumers = BlockedConsumers}) -> @@ -344,13 +344,10 @@ deliver_msgs_to_consumers(Funs = {PredFun, DeliverFun}, FunAcc, rabbit_channel:deliver( ChPid, ConsumerTag, AckRequired, {QName, self(), AckTag, IsDelivered, Message}), + ConfirmFun(Message), ChAckTags1 = case AckRequired of - true -> - rabbit_log:info("Delivered message but waiting for ack~n"), - sets:add_element(AckTag, ChAckTags); - false -> - rabbit_log:info("Delivered message and not waiting for ack~n"), - ChAckTags + true -> sets:add_element(AckTag, ChAckTags); + false -> ChAckTags end, NewC = C#cr{unsent_message_count = Count + 1, acktags = ChAckTags1}, @@ -387,7 +384,6 @@ deliver_msgs_to_consumers(Funs = {PredFun, DeliverFun}, FunAcc, {FunAcc, State} end; {empty, _} -> - rabbit_log:info("Message on a queue without consumers~n"), {FunAcc, State} end. @@ -402,15 +398,23 @@ deliver_from_queue_deliver(AckRequired, false, {{Message, IsDelivered, AckTag}, 0 == Remaining, State #q { backing_queue_state = BQS1 }}. +confirm_function(#basic_message{msg_seq_no = MsgSeqNo, origin = ChPid}) -> + case MsgSeqNo of + undefined -> ok; + _ -> rabbit_channel:confirm(ChPid, MsgSeqNo) + end. + run_message_queue(State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> Funs = {fun deliver_from_queue_pred/2, - fun deliver_from_queue_deliver/3}, + fun deliver_from_queue_deliver/3, + fun confirm_function/1}, IsEmpty = BQ:is_empty(BQS), {_IsEmpty1, State1} = deliver_msgs_to_consumers(Funs, IsEmpty, State), State1. -attempt_delivery(none, ChPid, Message, State = #q{backing_queue = BQ}) -> - rabbit_log:info("Attempting delivery of message~n"), +attempt_delivery(none, ChPid, Message = #basic_message{msg_seq_no = MsgSeqNo}, + State = #q{backing_queue = BQ}) -> + rabbit_log:info("Attempting delivery of message #~p~n", [MsgSeqNo]), PredFun = fun (IsEmpty, _State) -> not IsEmpty end, DeliverFun = fun (AckRequired, false, State1 = #q{backing_queue_state = BQS}) -> @@ -419,10 +423,10 @@ attempt_delivery(none, ChPid, Message, State = #q{backing_queue = BQ}) -> {{Message, false, AckTag}, true, State1#q{backing_queue_state = BQS1}} end, - ConfirmFun = fun() -> rabbit_channel:confirm(ChPid, -1) end, - deliver_msgs_to_consumers({ PredFun, DeliverFun }, false, State); -attempt_delivery(Txn, ChPid, Message, State = #q{backing_queue = BQ, - backing_queue_state = BQS}) -> + ConfirmFun = fun confirm_function/1, + deliver_msgs_to_consumers({ PredFun, DeliverFun, ConfirmFun }, false, State); +attempt_delivery(Txn, ChPid, Message, + State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> record_current_channel_tx(ChPid, Txn), {true, State#q{backing_queue_state = BQ:tx_publish(Txn, Message, BQS)}}. @@ -433,6 +437,7 @@ deliver_or_enqueue(Txn, ChPid, Message, State = #q{backing_queue = BQ}) -> {true, NewState}; {false, NewState} -> %% Txn is none and no unblocked channels with consumers + rabbit_log:info("No consumers on unblocked channels; enqueueing...~n"), BQS = BQ:publish(Message, State #q.backing_queue_state), {false, NewState#q{backing_queue_state = BQS}} end. @@ -806,7 +811,6 @@ handle_cast({deliver, Txn, Message, ChPid}, State) -> handle_cast({ack, Txn, AckTags, ChPid}, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> - rabbit_log:info("Queue process got an ack~n"), case lookup_ch(ChPid) of not_found -> noreply(State); diff --git a/src/rabbit_basic.erl b/src/rabbit_basic.erl index 3aa25d63..2682203a 100644 --- a/src/rabbit_basic.erl +++ b/src/rabbit_basic.erl @@ -33,7 +33,7 @@ -include("rabbit.hrl"). -include("rabbit_framing.hrl"). --export([publish/1, message/4, properties/1, delivery/4, delivery/5]). +-export([publish/1, message/4, properties/1, delivery/4]). -export([publish/4, publish/7]). -export([build_content/2, from_content/1]). -export([is_message_persistent/1]). @@ -53,10 +53,6 @@ -spec(delivery/4 :: (boolean(), boolean(), rabbit_types:maybe(rabbit_type:txn()), rabbit_types:message()) -> rabiit_types:delivery()). --spec(delivery/5 :: - (boolean(), boolean(), rabbit_types:maybe(rabbit_types:txn()), - rabbit_types:message(), integer() | undefined) - -> rabbit_types:delivery()). -spec(message/4 :: (rabbit_exchange:name(), rabbit_router:routing_key(), properties_input(), binary()) @@ -97,10 +93,8 @@ publish(Delivery = #delivery{ end. delivery(Mandatory, Immediate, Txn, Message) -> - delivery(Mandatory, Immediate, Txn, Message, undefined). -delivery(Mandatory, Immediate, Txn, Message, MsgSeqNo) -> #delivery{mandatory = Mandatory, immediate = Immediate, txn = Txn, - sender = self(), message = Message, msg_seq_no = MsgSeqNo}. + sender = self(), message = Message}. build_content(Properties, BodyBin) -> %% basic.publish hasn't changed so we can just hard-code amqp_0_9_1 diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 58f03e7a..fe303261 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -490,11 +490,13 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, routing_key = RoutingKey, content = DecodedContent, guid = rabbit_guid:guid(), - is_persistent = IsPersistent}, + is_persistent = IsPersistent, + msg_seq_no = MsgSeqNo, + origin = self()}, {RoutingRes, DeliveredQPids} = rabbit_exchange:publish( Exchange, - rabbit_basic:delivery(Mandatory, Immediate, TxnKey, Message, MsgSeqNo)), + rabbit_basic:delivery(Mandatory, Immediate, TxnKey, Message)), case RoutingRes of routed -> ok; unroutable -> ok = basic_return(Message, WriterPid, no_route); @@ -512,7 +514,6 @@ handle_method(#'basic.ack'{delivery_tag = DeliveryTag, multiple = Multiple}, _, State = #ch{transaction_id = TxnKey, unacked_message_q = UAMQ}) -> - rabbit_log:info("channel received a basic.ack~n"), {Acked, Remaining} = collect_acks(UAMQ, DeliveryTag, Multiple), QIncs = ack(TxnKey, Acked), Participants = [QPid || {QPid, _} <- QIncs], -- cgit v1.2.1 From 99d723174ffc925b2090b01c316f9be4db9af1df Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Fri, 13 Aug 2010 16:08:19 +0100 Subject: puback is also sent after something basic.gets the message --- src/rabbit_amqqueue_process.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 38eedad3..2f038462 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -688,6 +688,7 @@ handle_call({basic_get, ChPid, NoAck}, _From, case BQ:fetch(AckRequired, BQS) of {empty, BQS1} -> reply(empty, State1#q{backing_queue_state = BQS1}); {{Message, IsDelivered, AckTag, Remaining}, BQS1} -> + confirm_function(Message), case AckRequired of true -> C = #cr{acktags = ChAckTags} = ch_record(ChPid), store_ch_record( -- cgit v1.2.1 From 951de158ab0b0d7af91dd774f8d2150c8da0b966 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Fri, 13 Aug 2010 16:16:44 +0100 Subject: simplified code a bit --- src/rabbit_amqqueue_process.erl | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 2f038462..4fd50380 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -324,7 +324,7 @@ record_current_channel_tx(ChPid, Txn) -> %% that wasn't happening already) store_ch_record((ch_record(ChPid))#cr{txn = Txn}). -deliver_msgs_to_consumers(Funs = {PredFun, DeliverFun, ConfirmFun}, FunAcc, +deliver_msgs_to_consumers(Funs = {PredFun, DeliverFun }, FunAcc, State = #q{q = #amqqueue{name = QName}, active_consumers = ActiveConsumers, blocked_consumers = BlockedConsumers}) -> @@ -344,7 +344,7 @@ deliver_msgs_to_consumers(Funs = {PredFun, DeliverFun, ConfirmFun}, FunAcc, rabbit_channel:deliver( ChPid, ConsumerTag, AckRequired, {QName, self(), AckTag, IsDelivered, Message}), - ConfirmFun(Message), + confirm_message(Message), ChAckTags1 = case AckRequired of true -> sets:add_element(AckTag, ChAckTags); false -> ChAckTags @@ -398,7 +398,7 @@ deliver_from_queue_deliver(AckRequired, false, {{Message, IsDelivered, AckTag}, 0 == Remaining, State #q { backing_queue_state = BQS1 }}. -confirm_function(#basic_message{msg_seq_no = MsgSeqNo, origin = ChPid}) -> +confirm_message(#basic_message{msg_seq_no = MsgSeqNo, origin = ChPid}) -> case MsgSeqNo of undefined -> ok; _ -> rabbit_channel:confirm(ChPid, MsgSeqNo) @@ -406,13 +406,12 @@ confirm_function(#basic_message{msg_seq_no = MsgSeqNo, origin = ChPid}) -> run_message_queue(State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> Funs = {fun deliver_from_queue_pred/2, - fun deliver_from_queue_deliver/3, - fun confirm_function/1}, + fun deliver_from_queue_deliver/3}, IsEmpty = BQ:is_empty(BQS), {_IsEmpty1, State1} = deliver_msgs_to_consumers(Funs, IsEmpty, State), State1. -attempt_delivery(none, ChPid, Message = #basic_message{msg_seq_no = MsgSeqNo}, +attempt_delivery(none, _ChPid, Message = #basic_message{msg_seq_no = MsgSeqNo}, State = #q{backing_queue = BQ}) -> rabbit_log:info("Attempting delivery of message #~p~n", [MsgSeqNo]), PredFun = fun (IsEmpty, _State) -> not IsEmpty end, @@ -423,8 +422,7 @@ attempt_delivery(none, ChPid, Message = #basic_message{msg_seq_no = MsgSeqNo}, {{Message, false, AckTag}, true, State1#q{backing_queue_state = BQS1}} end, - ConfirmFun = fun confirm_function/1, - deliver_msgs_to_consumers({ PredFun, DeliverFun, ConfirmFun }, false, State); + deliver_msgs_to_consumers({ PredFun, DeliverFun }, false, State); attempt_delivery(Txn, ChPid, Message, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> record_current_channel_tx(ChPid, Txn), @@ -688,7 +686,7 @@ handle_call({basic_get, ChPid, NoAck}, _From, case BQ:fetch(AckRequired, BQS) of {empty, BQS1} -> reply(empty, State1#q{backing_queue_state = BQS1}); {{Message, IsDelivered, AckTag, Remaining}, BQS1} -> - confirm_function(Message), + confirm_message(Message), case AckRequired of true -> C = #cr{acktags = ChAckTags} = ch_record(ChPid), store_ch_record( -- cgit v1.2.1 From ca082c57372da1fc05977df657b1956f01f479db Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Fri, 13 Aug 2010 16:36:37 +0100 Subject: pubacks are sent right after basic.returns --- src/rabbit_channel.erl | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index fe303261..85bae8e4 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -497,17 +497,21 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, rabbit_exchange:publish( Exchange, rabbit_basic:delivery(Mandatory, Immediate, TxnKey, Message)), - case RoutingRes of - routed -> ok; - unroutable -> ok = basic_return(Message, WriterPid, no_route); - not_delivered -> ok = basic_return(Message, WriterPid, no_consumers) - end, + State2 = case RoutingRes of + routed -> State1; + unroutable -> + ok = basic_return(Message, WriterPid, no_route), + send_or_enqueue_ack(MsgSeqNo, State1); + not_delivered -> + ok = basic_return(Message, WriterPid, no_consumers), + send_or_enqueue_ack(MsgSeqNo, State1) + end, maybe_incr_stats([{ExchangeName, 1} | [{{QPid, ExchangeName}, 1} || - QPid <- DeliveredQPids]], publish, State1), + QPid <- DeliveredQPids]], publish, State2), {noreply, case TxnKey of - none -> State1; - _ -> add_tx_participants(DeliveredQPids, State1) + none -> State2; + _ -> add_tx_participants(DeliveredQPids, State2) end}; handle_method(#'basic.ack'{delivery_tag = DeliveryTag, -- cgit v1.2.1 From 02eac84cd7d15916f5bc9e23ee39845625cf9db8 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Fri, 13 Aug 2010 17:16:16 +0100 Subject: puback for persistent non-mandatory, non-immediate, unrouted messages --- src/rabbit_router.erl | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/rabbit_router.erl b/src/rabbit_router.erl index ec049a1a..14d93497 100644 --- a/src/rabbit_router.erl +++ b/src/rabbit_router.erl @@ -54,7 +54,8 @@ %%---------------------------------------------------------------------------- deliver(QPids, Delivery = #delivery{mandatory = false, - immediate = false}) -> + immediate = false, + message = Msg}) -> %% optimisation: when Mandatory = false and Immediate = false, %% rabbit_amqqueue:deliver will deliver the message to the queue %% process asynchronously, and return true, which means all the @@ -64,6 +65,11 @@ deliver(QPids, Delivery = #delivery{mandatory = false, %% case below. delegate:invoke_no_result( QPids, fun (Pid) -> rabbit_amqqueue:deliver(Pid, Delivery) end), + case {QPids, Msg#basic_message.msg_seq_no} of + {[], undefined} -> ok; + {[], MsgSeqNo} -> rabbit_channel:confirm(Msg#basic_message.origin, MsgSeqNo); + _ -> ok + end, {routed, QPids}; deliver(QPids, Delivery) -> -- cgit v1.2.1 From a71f11f702fa9f2bcc236d36f53d2641231f07e6 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Fri, 13 Aug 2010 17:42:03 +0100 Subject: refactoring What's done: - PubAck after transient messages - PubAck after basic.returns - PubAck after message delivered to a consumer (disregarding consumer acks) - PubAck after message got - out of order ack'ing - multiple ack'ing Whant's not done: - PubAck de-duplication - PubAck after message hits disk --- src/rabbit_amqqueue_process.erl | 9 +++------ src/rabbit_channel.erl | 9 ++++----- src/rabbit_router.erl | 1 - 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 4fd50380..082de83a 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -344,6 +344,7 @@ deliver_msgs_to_consumers(Funs = {PredFun, DeliverFun }, FunAcc, rabbit_channel:deliver( ChPid, ConsumerTag, AckRequired, {QName, self(), AckTag, IsDelivered, Message}), + % PubAck after message delivered to consumer (disregard consumer acks) confirm_message(Message), ChAckTags1 = case AckRequired of true -> sets:add_element(AckTag, ChAckTags); @@ -399,10 +400,7 @@ deliver_from_queue_deliver(AckRequired, false, State #q { backing_queue_state = BQS1 }}. confirm_message(#basic_message{msg_seq_no = MsgSeqNo, origin = ChPid}) -> - case MsgSeqNo of - undefined -> ok; - _ -> rabbit_channel:confirm(ChPid, MsgSeqNo) - end. + rabbit_channel:confirm(ChPid, MsgSeqNo). run_message_queue(State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> Funs = {fun deliver_from_queue_pred/2, @@ -413,7 +411,6 @@ run_message_queue(State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> attempt_delivery(none, _ChPid, Message = #basic_message{msg_seq_no = MsgSeqNo}, State = #q{backing_queue = BQ}) -> - rabbit_log:info("Attempting delivery of message #~p~n", [MsgSeqNo]), PredFun = fun (IsEmpty, _State) -> not IsEmpty end, DeliverFun = fun (AckRequired, false, State1 = #q{backing_queue_state = BQS}) -> @@ -429,7 +426,6 @@ attempt_delivery(Txn, ChPid, Message, {true, State#q{backing_queue_state = BQ:tx_publish(Txn, Message, BQS)}}. deliver_or_enqueue(Txn, ChPid, Message, State = #q{backing_queue = BQ}) -> - rabbit_log:info("deliver_or_enqueue called for message~n"), case attempt_delivery(Txn, ChPid, Message, State) of {true, NewState} -> {true, NewState}; @@ -686,6 +682,7 @@ handle_call({basic_get, ChPid, NoAck}, _From, case BQ:fetch(AckRequired, BQS) of {empty, BQS1} -> reply(empty, State1#q{backing_queue_state = BQS1}); {{Message, IsDelivered, AckTag, Remaining}, BQS1} -> + % PubAck after message got confirm_message(Message), case AckRequired of true -> C = #cr{acktags = ChAckTags} = ch_record(ChPid), diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 85bae8e4..21d4ff2a 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -268,7 +268,6 @@ handle_cast(multiple_ack_flush, {noreply, State#ch{confirm = C#confirm{held_acks = gb_sets:new(), tref = undefined}}}; handle_cast({confirm, MsgSeqNo}, State) -> - rabbit_log:info("got confirm for #~p~n", [MsgSeqNo]), {noreply, send_or_enqueue_ack(MsgSeqNo, State)}. @@ -424,6 +423,8 @@ queue_blocked(QPid, State = #ch{blocking = Blocking}) -> State#ch{blocking = Blocking1} end. +send_or_enqueue_ack(undefined, State) -> + State; send_or_enqueue_ack(_, State = #ch{confirm = #confirm{enabled = false}}) -> State; send_or_enqueue_ack(MsgSeqNo, @@ -469,6 +470,7 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, %% certain to want to look at delivery-mode and priority. DecodedContent = rabbit_binary_parser:ensure_content_decoded(Content), IsPersistent = is_message_persistent(DecodedContent), + % PubAck transient messages immediately {MsgSeqNo, State1} = case State#ch.confirm#confirm.enabled of false -> @@ -497,6 +499,7 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, rabbit_exchange:publish( Exchange, rabbit_basic:delivery(Mandatory, Immediate, TxnKey, Message)), + % PubAck after basic.returns State2 = case RoutingRes of routed -> State1; unroutable -> @@ -1282,19 +1285,15 @@ erase_queue_stats(QPid) -> {{queue_exchange_stats, QX = {QPid0, _}}, _} <- get(), QPid =:= QPid0]. start_ack_timer(State = #ch{confirm = C = #confirm{tref = undefined}}) -> - rabbit_log:info("starting ack timer...~n"), {ok, TRef} = timer:apply_after(?MULTIPLE_ACK_FLUSH_INTERVAL, ?MODULE, flush_multiple_acks, [self()]), State#ch{confirm = C#confirm{tref = TRef}}; start_ack_timer(State) -> - rabbit_log:info("timer already started.. nop~n"), State. stop_ack_timer(State = #ch{confirm = #confirm{tref = undefined}}) -> - rabbit_log:info("stopping a stopped ack timer.. nop~n"), State; stop_ack_timer(State = #ch{confirm = C = #confirm{tref = TRef}}) -> - rabbit_log:info("canceling ack timer: ~p~n", [TRef]), {ok, cancel} = timer:cancel(TRef), State#ch{confirm = C#confirm{tref = undefined}}. diff --git a/src/rabbit_router.erl b/src/rabbit_router.erl index 14d93497..0f8611d0 100644 --- a/src/rabbit_router.erl +++ b/src/rabbit_router.erl @@ -66,7 +66,6 @@ deliver(QPids, Delivery = #delivery{mandatory = false, delegate:invoke_no_result( QPids, fun (Pid) -> rabbit_amqqueue:deliver(Pid, Delivery) end), case {QPids, Msg#basic_message.msg_seq_no} of - {[], undefined} -> ok; {[], MsgSeqNo} -> rabbit_channel:confirm(Msg#basic_message.origin, MsgSeqNo); _ -> ok end, -- cgit v1.2.1 From 0e1aee4ea7f28aa22f84c040971c306ab3f7196d Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Fri, 13 Aug 2010 18:05:29 +0100 Subject: typo + reformatting --- src/rabbit_amqqueue_process.erl | 7 +++---- src/rabbit_basic.erl | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 082de83a..cdc8cdd2 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -409,8 +409,7 @@ run_message_queue(State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> {_IsEmpty1, State1} = deliver_msgs_to_consumers(Funs, IsEmpty, State), State1. -attempt_delivery(none, _ChPid, Message = #basic_message{msg_seq_no = MsgSeqNo}, - State = #q{backing_queue = BQ}) -> +attempt_delivery(none, _ChPid, Message, State = #q{backing_queue = BQ}) -> PredFun = fun (IsEmpty, _State) -> not IsEmpty end, DeliverFun = fun (AckRequired, false, State1 = #q{backing_queue_state = BQS}) -> @@ -420,8 +419,8 @@ attempt_delivery(none, _ChPid, Message = #basic_message{msg_seq_no = MsgSeqNo}, State1#q{backing_queue_state = BQS1}} end, deliver_msgs_to_consumers({ PredFun, DeliverFun }, false, State); -attempt_delivery(Txn, ChPid, Message, - State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> +attempt_delivery(Txn, ChPid, Message, State = #q{backing_queue = BQ, + backing_queue_state = BQS}) -> record_current_channel_tx(ChPid, Txn), {true, State#q{backing_queue_state = BQ:tx_publish(Txn, Message, BQS)}}. diff --git a/src/rabbit_basic.erl b/src/rabbit_basic.erl index 2682203a..62ef3317 100644 --- a/src/rabbit_basic.erl +++ b/src/rabbit_basic.erl @@ -51,7 +51,7 @@ -spec(publish/1 :: (rabbit_types:delivery()) -> publish_result()). -spec(delivery/4 :: - (boolean(), boolean(), rabbit_types:maybe(rabbit_type:txn()), + (boolean(), boolean(), rabbit_types:maybe(rabbit_types:txn()), rabbit_types:message()) -> rabiit_types:delivery()). -spec(message/4 :: (rabbit_exchange:name(), rabbit_router:routing_key(), -- cgit v1.2.1 From 7da1432a2c29a95979294ab04ee080f77ce9de7b Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Sun, 15 Aug 2010 22:44:38 +0100 Subject: PubAcks are de-dupped in rabbit_channel --- src/rabbit_channel.erl | 93 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 64 insertions(+), 29 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 21d4ff2a..0571e454 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -49,7 +49,7 @@ username, virtual_host, most_recently_declared_queue, consumer_mapping, blocking, queue_collector_pid, stats_timer, confirm}). --record(confirm, {enabled, count, multiple, tref, held_acks}). +-record(confirm, {enabled, count, multiple, tref, held_acks, need_acking}). -define(MAX_PERMISSION_CACHE_SIZE, 12). @@ -187,7 +187,8 @@ init([Channel, ReaderPid, WriterPid, Username, VHost, CollectorPid]) -> confirm = #confirm{enabled = false, count = 0, multiple = false, - held_acks = gb_sets:new()}}, + held_acks = gb_sets:new(), + need_acking = gb_sets:new()}}, rabbit_event:notify( channel_created, [{Item, i(Item, State)} || Item <- ?CREATION_EVENT_KEYS]), @@ -258,12 +259,13 @@ handle_cast(emit_stats, State) -> handle_cast(multiple_ack_flush, State = #ch{writer_pid = WriterPid, - confirm = C = #confirm{held_acks = As}}) -> + confirm = C = #confirm{held_acks = As, + need_acking = NA}}) -> rabbit_log:info("channel got a multiple_ack_flush message~n" "held acks: ~p~n", [gb_sets:to_list(As)]), case gb_sets:is_empty(As) of true -> ok; % this should never be the case - false -> flush_multiple(As, WriterPid) + false -> flush_multiple(As, WriterPid, gb_sets:smallest(NA)) end, {noreply, State#ch{confirm = C#confirm{held_acks = gb_sets:new(), tref = undefined}}}; @@ -428,15 +430,37 @@ send_or_enqueue_ack(undefined, State) -> send_or_enqueue_ack(_, State = #ch{confirm = #confirm{enabled = false}}) -> State; send_or_enqueue_ack(MsgSeqNo, - State = #ch{writer_pid = WriterPid, - confirm = #confirm{multiple = false}}) -> + State = #ch{confirm = #confirm{multiple = false}}) -> rabbit_log:info("handling confirm in single mode (#~p)~n", [MsgSeqNo]), - ok = rabbit_writer:send_command(WriterPid, #'basic.ack'{delivery_tag = MsgSeqNo}), - State; + do_if_not_dup(MsgSeqNo, State, + fun(MSN, S = #ch{writer_pid = WriterPid}) -> + rabbit_log:info("confirm #~p is not a dup!~n", [MSN]), + ok = rabbit_writer:send_command( + WriterPid, #'basic.ack'{delivery_tag = MSN}), + S + end); send_or_enqueue_ack(MsgSeqNo, State = #ch{confirm = #confirm{multiple = true}}) -> rabbit_log:info("handling confirm in multiple mode (#~p)~n", [MsgSeqNo]), - State1 = #ch{confirm = C = #confirm{held_acks = As}} = start_ack_timer(State), - State1#ch{confirm = C#confirm{held_acks = gb_sets:add(MsgSeqNo, As)}}. + do_if_not_dup(MsgSeqNo, State, + fun(MSN, S) -> + rabbit_log:info("confirm #~p is not a dup!~n", [MSN]), + State1 + = #ch{confirm = C = #confirm{held_acks = As}} + = start_ack_timer(S), + State1#ch{confirm = + C#confirm{held_acks = + gb_sets:add(MSN, As)}} + end). + +do_if_not_dup(MsgSeqNo, State = #ch{confirm = #confirm{need_acking = NA}}, Fun) -> + case gb_sets:is_element(MsgSeqNo, NA) of + true -> + State1 = #ch{confirm = C} = Fun(MsgSeqNo, State), + State1#ch{confirm = C#confirm{need_acking = gb_sets:delete(MsgSeqNo, NA)}}; + false -> + State + end. + handle_method(#'channel.open'{}, _, State = #ch{state = starting}) -> {reply, #'channel.open_ok'{}, State#ch{state = running}}; @@ -477,16 +501,22 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, {undefined, State}; true -> Count = State#ch.confirm#confirm.count, + + Confirm = State#ch.confirm, + Confirm1 = Confirm#confirm{ + need_acking = gb_sets:add(Count, + Confirm#confirm.need_acking)}, + {CountOrUndefined, NewState} = case IsPersistent of - true -> {Count, State}; + true -> {Count, State#ch{confirm = Confirm1}}; false -> {undefined, send_or_enqueue_ack( - Count, State)} + Count, State#ch{confirm = Confirm1})} end, - Confirm = NewState#ch.confirm, + Confirm2 = NewState#ch.confirm, {CountOrUndefined, - NewState#ch{confirm = Confirm#confirm{count = Count+1}}} + NewState#ch{confirm = Confirm2#confirm{count = Count+1}}} end, Message = #basic_message{exchange_name = ExchangeName, routing_key = RoutingKey, @@ -1297,25 +1327,30 @@ stop_ack_timer(State = #ch{confirm = C = #confirm{tref = TRef}}) -> {ok, cancel} = timer:cancel(TRef), State#ch{confirm = C#confirm{tref = undefined}}. -flush_multiple(Acks, WriterPid) -> +flush_multiple(Acks, WriterPid, SmallestNotAcked) -> [First | Rest] = gb_sets:to_list(Acks), - flush_multiple(First, Rest, WriterPid). + Remaining = case Rest of + [] -> [First]; + _ -> flush_multiple(First, Rest, WriterPid, SmallestNotAcked) + end, + lists:foreach(fun(A) -> + ok = rabbit_writer:send_command( + WriterPid, + #'basic.ack'{delivery_tag = A}) + end, Remaining). -flush_multiple(Prev, [Cur | Rest], WriterPid) -> +flush_multiple(Prev, [Cur | Rest], WriterPid, SNA) -> ExpNext = Prev+1, - case Cur of - ExpNext -> - flush_multiple(Cur, Rest, WriterPid); + case {SNA >= Cur, Cur} of + {true, ExpNext} -> + flush_multiple(Cur, Rest, WriterPid, SNA); _ -> - flush_multiple(Prev, [], WriterPid), - lists:foreach(fun(A) -> - ok = rabbit_writer:send_command( - WriterPid, - #'basic.ack'{delivery_tag = A}) - end, [Cur | Rest]) + flush_multiple(Prev, [], WriterPid, SNA), + [Cur | Rest] end; -flush_multiple(Prev, [], WriterPid) -> +flush_multiple(Prev, [], WriterPid, _) -> ok = rabbit_writer:send_command( WriterPid, - #'basic.ack'{ delivery_tag = Prev, - multiple = true }). + #'basic.ack'{delivery_tag = Prev, + multiple = true}), + []. -- cgit v1.2.1 From 4dc68799444f557224f058891ab8722cde93738c Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Mon, 16 Aug 2010 11:31:48 +0100 Subject: refactoring --- src/rabbit_channel.erl | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 0571e454..f6e72814 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -434,7 +434,6 @@ send_or_enqueue_ack(MsgSeqNo, rabbit_log:info("handling confirm in single mode (#~p)~n", [MsgSeqNo]), do_if_not_dup(MsgSeqNo, State, fun(MSN, S = #ch{writer_pid = WriterPid}) -> - rabbit_log:info("confirm #~p is not a dup!~n", [MSN]), ok = rabbit_writer:send_command( WriterPid, #'basic.ack'{delivery_tag = MSN}), S @@ -443,7 +442,6 @@ send_or_enqueue_ack(MsgSeqNo, State = #ch{confirm = #confirm{multiple = true}}) rabbit_log:info("handling confirm in multiple mode (#~p)~n", [MsgSeqNo]), do_if_not_dup(MsgSeqNo, State, fun(MSN, S) -> - rabbit_log:info("confirm #~p is not a dup!~n", [MSN]), State1 = #ch{confirm = C = #confirm{held_acks = As}} = start_ack_timer(S), @@ -500,23 +498,24 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, false -> {undefined, State}; true -> - Count = State#ch.confirm#confirm.count, - Confirm = State#ch.confirm, - Confirm1 = Confirm#confirm{ - need_acking = gb_sets:add(Count, - Confirm#confirm.need_acking)}, - - {CountOrUndefined, NewState} = + Count = Confirm#confirm.count, + % Add the current message to the need_acking list + State01 = State#ch{confirm = Confirm#confirm{ + need_acking = gb_sets:add(Count, + Confirm#confirm.need_acking)}}, + % Ack transient messages now + {CountOrUndefined, State02} = case IsPersistent of - true -> {Count, State#ch{confirm = Confirm1}}; + true -> {Count, State01}; false -> {undefined, send_or_enqueue_ack( - Count, State#ch{confirm = Confirm1})} + Count, State01)} end, - Confirm2 = NewState#ch.confirm, + % Increase the PubAck counter + Confirm02 = State02#ch.confirm, {CountOrUndefined, - NewState#ch{confirm = Confirm2#confirm{count = Count+1}}} + State02#ch{confirm = Confirm02#confirm{count = Count+1}}} end, Message = #basic_message{exchange_name = ExchangeName, routing_key = RoutingKey, -- cgit v1.2.1 From 07aa5bd1e04efd9553455e972a6577367965d9a4 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Tue, 17 Aug 2010 09:44:49 +0100 Subject: first stab at keeping track of when messages are synced to disk --- src/rabbit_amqqueue_process.erl | 1 - src/rabbit_msg_store.erl | 93 +++++++++++++++++++++++++++++++---------- src/rabbit_variable_queue.erl | 23 +++++++++- 3 files changed, 92 insertions(+), 25 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index cdc8cdd2..a25ad48b 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -430,7 +430,6 @@ deliver_or_enqueue(Txn, ChPid, Message, State = #q{backing_queue = BQ}) -> {true, NewState}; {false, NewState} -> %% Txn is none and no unblocked channels with consumers - rabbit_log:info("No consumers on unblocked channels; enqueueing...~n"), BQS = BQ:publish(Message, State #q.backing_queue_state), {false, NewState#q{backing_queue_state = BQS}} end. diff --git a/src/rabbit_msg_store.erl b/src/rabbit_msg_store.erl index 63100571..09eb7cfd 100644 --- a/src/rabbit_msg_store.erl +++ b/src/rabbit_msg_store.erl @@ -35,7 +35,8 @@ -export([start_link/4, write/4, read/3, contains/2, remove/2, release/2, sync/3, client_init/2, client_terminate/1, - client_delete_and_terminate/3, successfully_recovered_state/1]). + client_delete_and_terminate/3, successfully_recovered_state/1, + register_callback/2]). -export([sync/1, gc_done/4, set_maximum_since_use/2, gc/3]). %% internal @@ -82,7 +83,9 @@ cur_file_cache_ets, %% tid of current file cache table client_refs, %% set of references of all registered clients successfully_recovered, %% boolean: did we recover state? - file_size_limit %% how big are our files allowed to get? + file_size_limit, %% how big are our files allowed to get? + pid_to_fun, %% pid to callback function mapping + pid_to_guids %% pid to synced messages mapping }). -record(client_msstate, @@ -318,7 +321,7 @@ start_link(Server, Dir, ClientRefs, StartupFunState) -> write(Server, Guid, Msg, CState = #client_msstate { cur_file_cache_ets = CurFileCacheEts }) -> ok = update_msg_cache(CurFileCacheEts, Guid, Msg), - {gen_server2:cast(Server, {write, Guid, Msg}), CState}. + {gen_server2:cast(Server, {write, self(), Guid, Msg}), CState}. read(Server, Guid, CState = #client_msstate { dedup_cache_ets = DedupCacheEts, @@ -385,6 +388,9 @@ client_delete_and_terminate(CState, Server, Ref) -> successfully_recovered_state(Server) -> gen_server2:call(Server, successfully_recovered_state, infinity). +register_callback(Server, Fun) -> + gen_server2:call(Server, {register_callback, Fun}, infinity). + %%---------------------------------------------------------------------------- %% Client-side-only helpers %%---------------------------------------------------------------------------- @@ -539,7 +545,9 @@ init([Server, BaseDir, ClientRefs, {MsgRefDeltaGen, MsgRefDeltaGenInit}]) -> cur_file_cache_ets = CurFileCacheEts, client_refs = ClientRefs1, successfully_recovered = AllCleanShutdown, - file_size_limit = FileSizeLimit + file_size_limit = FileSizeLimit, + pid_to_fun = dict:new(), + pid_to_guids = dict:new() }, ok = case AllCleanShutdown of @@ -601,21 +609,32 @@ handle_call(successfully_recovered_state, _From, State) -> handle_call({delete_client, CRef}, _From, State = #msstate { client_refs = ClientRefs }) -> reply(ok, - State #msstate { client_refs = sets:del_element(CRef, ClientRefs) }). + State #msstate { client_refs = sets:del_element(CRef, ClientRefs) }); + +handle_call({register_callback, Fun}, {Pid, _}, + State = #msstate { pid_to_fun = PTF }) -> + rabbit_log:info("Registering callback ~p~n", [{Pid, Fun}]), + erlang:monitor(process, Pid), + reply(ok, + State #msstate { pid_to_fun = dict:store(Pid, Fun, PTF) }). -handle_cast({write, Guid, Msg}, + +handle_cast({write, Pid, Guid, Msg}, State = #msstate { current_file_handle = CurHdl, current_file = CurFile, sum_valid_data = SumValid, sum_file_size = SumFileSize, file_summary_ets = FileSummaryEts, - cur_file_cache_ets = CurFileCacheEts }) -> + cur_file_cache_ets = CurFileCacheEts, + pid_to_fun = PTF, + pid_to_guids = PTG}) -> true = 0 =< ets:update_counter(CurFileCacheEts, Guid, {3, -1}), case index_lookup(Guid, State) of not_found -> %% New message, lots to do {ok, CurOffset} = file_handle_cache:current_virtual_offset(CurHdl), {ok, TotalSize} = rabbit_msg_file:append(CurHdl, Guid, Msg), + rabbit_log:info("message ~p written to disk~n", [Guid]), ok = index_insert(#msg_location { guid = Guid, ref_count = 1, file = CurFile, offset = CurOffset, total_size = TotalSize }, @@ -641,7 +660,11 @@ handle_cast({write, Guid, Msg}, maybe_roll_to_new_file( NextOffset, State #msstate { sum_valid_data = SumValid + TotalSize, - sum_file_size = SumFileSize + TotalSize })); + sum_file_size = SumFileSize + TotalSize, + pid_to_guids = case dict:find(Pid, PTF) of + {ok, _} -> dict:append(Pid, Guid, PTG); + error -> PTG + end})); #msg_location { ref_count = RefCount } -> %% We already know about it, just update counter. Only %% update field otherwise bad interaction with concurrent GC @@ -720,6 +743,14 @@ handle_cast({set_maximum_since_use, Age}, State) -> handle_info(timeout, State) -> noreply(internal_sync(State)); +handle_info({'DOWN', _MRef, process, Pid, _Reason}, + State = #msstate { pid_to_fun = PTF, + pid_to_guids = PTG }) -> + % A queue with a callback has died, so remove it from dicts. + rabbit_log:info("Queue ~p has gone down~n", [Pid]), + {noreply, State #msstate { pid_to_fun = dict:erase(Pid, PTF), + pid_to_guids = dict:erase(Pid, PTG) }}; + handle_info({'EXIT', _Pid, Reason}, State) -> {stop, Reason, State}. @@ -767,14 +798,19 @@ reply(Reply, State) -> {State1, Timeout} = next_state(State), {reply, Reply, State1, Timeout}. -next_state(State = #msstate { on_sync = [], sync_timer_ref = undefined }) -> - {State, hibernate}; -next_state(State = #msstate { sync_timer_ref = undefined }) -> - {start_sync_timer(State), 0}; -next_state(State = #msstate { on_sync = [] }) -> - {stop_sync_timer(State), hibernate}; -next_state(State) -> - {State, 0}. +next_state(State = #msstate { sync_timer_ref = undefined, + on_sync = OS, + pid_to_guids = PTG }) -> + case {OS, dict:size(PTG)} of + {[], 0} -> {State, hibernate}; + _ -> {start_sync_timer(State), 0} + end; +next_state(State = #msstate { on_sync = OS, + pid_to_guids = PTG }) -> + case {OS, dict:size(PTG)} of + {[], 0} -> {stop_sync_timer(State), hibernate}; + _ -> {State, 0} + end. start_sync_timer(State = #msstate { sync_timer_ref = undefined }) -> {ok, TRef} = timer:apply_after(?SYNC_INTERVAL, ?MODULE, sync, [self()]), @@ -787,14 +823,25 @@ stop_sync_timer(State = #msstate { sync_timer_ref = TRef }) -> State #msstate { sync_timer_ref = undefined }. internal_sync(State = #msstate { current_file_handle = CurHdl, - on_sync = Syncs }) -> + on_sync = Syncs, + pid_to_fun = PTF, + pid_to_guids = PTG }) -> + rabbit_log:info("msg_store syncing~ncallbacks: ~p~nguids: ~p~n", [dict:to_list(PTF), dict:to_list(PTG)]), State1 = stop_sync_timer(State), - case Syncs of - [] -> State1; - _ -> ok = file_handle_cache:sync(CurHdl), - lists:foreach(fun (K) -> K() end, lists:reverse(Syncs)), - State1 #msstate { on_sync = [] } - end. + State2 = case Syncs of + [] -> State1; + _ -> ok = file_handle_cache:sync(CurHdl), + lists:foreach(fun (K) -> K() end, lists:reverse(Syncs)), + State1 #msstate { on_sync = [] } + end, + dict:map(fun(Pid, Guids) -> + case dict:find(Pid, PTF) of + {ok, Fun} -> Fun(Guids); + error -> ok %% shouldn't happen + end + end, PTG), + State2 #msstate { pid_to_guids = dict:new() }. + read_message(Guid, From, State = #msstate { dedup_cache_ets = DedupCacheEts }) -> diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 714eb206..ca459665 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -398,6 +398,7 @@ init(QueueName, IsDurable, Recover) -> true -> rabbit_msg_store:client_init(?PERSISTENT_MSG_STORE, PRef); false -> undefined end, + register_puback_callback(?PERSISTENT_MSG_STORE), TransientClient = rabbit_msg_store:client_init(?TRANSIENT_MSG_STORE, TRef), State = #vqstate { q1 = queue:new(), @@ -1004,7 +1005,6 @@ publish(Msg = #basic_message { is_persistent = IsPersistent }, persistent_count = PCount, durable = IsDurable, ram_msg_count = RamMsgCount }) -> - rabbit_log:info("message ~p got to variable_queue:publish~n", [Msg#basic_message.guid]), IsPersistent1 = IsDurable andalso IsPersistent, MsgStatus = (msg_status(IsPersistent1, SeqId, Msg)) #msg_status { is_delivered = IsDelivered, msg_on_disk = MsgOnDisk }, @@ -1068,6 +1068,27 @@ maybe_write_to_disk(ForceMsg, ForceIndex, MsgStatus, {MsgStatus2, State #vqstate { index_state = IndexState1, msg_store_clients = MSCState1 }}. +%%---------------------------------------------------------------------------- +%% Internal gubbins for pubacks +%%---------------------------------------------------------------------------- + +register_puback_callback(MessageStore) -> + rabbit_msg_store:register_callback( + MessageStore, + fun (Guids) -> + spawn(fun () -> ok = rabbit_misc:with_exit_handler( + fun () -> + rabbit_log:info("something bad happened while sending pubacks back to channel"), + ok + end, + fun () -> + lists:foreach(fun(G) -> + rabbit_log:info("send puback back to channel for ~p~n", [G]) end, Guids), + ok + end) + end) + end). + %%---------------------------------------------------------------------------- %% Internal gubbins for acks %%---------------------------------------------------------------------------- -- cgit v1.2.1 From f3812071cad62f351c9a2f9bf6b15c3d31ee7985 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Tue, 17 Aug 2010 10:45:33 +0100 Subject: move msg_seq_no from #basic_message{} to #delivery{} --- include/rabbit.hrl | 4 +-- src/rabbit_amqqueue.erl | 15 ++++++----- src/rabbit_amqqueue_process.erl | 55 +++++++++++++++++++++++++++-------------- src/rabbit_basic.erl | 11 +++++++-- src/rabbit_channel.erl | 6 ++--- src/rabbit_router.erl | 9 ++++--- 6 files changed, 64 insertions(+), 36 deletions(-) diff --git a/include/rabbit.hrl b/include/rabbit.hrl index e8255346..161b9b0b 100644 --- a/include/rabbit.hrl +++ b/include/rabbit.hrl @@ -66,10 +66,10 @@ -record(listener, {node, protocol, host, port}). -record(basic_message, {exchange_name, routing_key, content, guid, - is_persistent, msg_seq_no, origin}). + is_persistent}). -record(ssl_socket, {tcp, ssl}). --record(delivery, {mandatory, immediate, txn, sender, message}). +-record(delivery, {mandatory, immediate, txn, sender, message, origin, msg_seq_no}). -record(amqp_error, {name, explanation, method = none}). -record(event, {type, props, timestamp}). diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 2453280e..c25fd9b4 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -363,15 +363,18 @@ delete(#amqqueue{ pid = QPid }, IfUnused, IfEmpty) -> purge(#amqqueue{ pid = QPid }) -> delegate_call(QPid, purge, infinity). deliver(QPid, #delivery{immediate = true, - txn = Txn, sender = ChPid, message = Message}) -> - gen_server2:call(QPid, {deliver_immediately, Txn, Message, ChPid}, + txn = Txn, sender = ChPid, message = Message, + msg_seq_no = MsgSeqNo}) -> + gen_server2:call(QPid, {deliver_immediately, Txn, Message, MsgSeqNo, ChPid}, infinity); deliver(QPid, #delivery{mandatory = true, - txn = Txn, sender = ChPid, message = Message}) -> - gen_server2:call(QPid, {deliver, Txn, Message, ChPid}, infinity), + txn = Txn, sender = ChPid, message = Message, + msg_seq_no = MsgSeqNo}) -> + gen_server2:call(QPid, {deliver, Txn, Message, MsgSeqNo, ChPid}, infinity), true; -deliver(QPid, #delivery{txn = Txn, sender = ChPid, message = Message}) -> - gen_server2:cast(QPid, {deliver, Txn, Message, ChPid}), +deliver(QPid, #delivery{txn = Txn, sender = ChPid, message = Message, + msg_seq_no = MsgSeqNo}) -> + gen_server2:cast(QPid, {deliver, Txn, Message, MsgSeqNo, ChPid}), true. requeue(QPid, MsgIds, ChPid) -> diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index a25ad48b..0dbd7f17 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -60,7 +60,8 @@ sync_timer_ref, rate_timer_ref, expiry_timer_ref, - stats_timer + stats_timer, + guid_to_channel }). -record(consumer, {tag, ack_required}). @@ -122,7 +123,8 @@ init(Q) -> sync_timer_ref = undefined, rate_timer_ref = undefined, expiry_timer_ref = undefined, - stats_timer = rabbit_event:init_stats_timer()}, hibernate, + stats_timer = rabbit_event:init_stats_timer(), + guid_to_channel = dict:new()}, hibernate, {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}. terminate(shutdown, State = #q{backing_queue = BQ}) -> @@ -345,7 +347,7 @@ deliver_msgs_to_consumers(Funs = {PredFun, DeliverFun }, FunAcc, ChPid, ConsumerTag, AckRequired, {QName, self(), AckTag, IsDelivered, Message}), % PubAck after message delivered to consumer (disregard consumer acks) - confirm_message(Message), + State2 = confirm_message(Message#basic_message.guid, State1), ChAckTags1 = case AckRequired of true -> sets:add_element(AckTag, ChAckTags); false -> ChAckTags @@ -365,10 +367,10 @@ deliver_msgs_to_consumers(Funs = {PredFun, DeliverFun }, FunAcc, {ActiveConsumers1, queue:in(QEntry, BlockedConsumers1)} end, - State2 = State1#q{ + State3 = State2#q{ active_consumers = NewActiveConsumers, blocked_consumers = NewBlockedConsumers}, - deliver_msgs_to_consumers(Funs, FunAcc1, State2); + deliver_msgs_to_consumers(Funs, FunAcc1, State3); %% if IsMsgReady then we've hit the limiter false when IsMsgReady -> store_ch_record(C#cr{is_limit_active = true}), @@ -399,8 +401,21 @@ deliver_from_queue_deliver(AckRequired, false, {{Message, IsDelivered, AckTag}, 0 == Remaining, State #q { backing_queue_state = BQS1 }}. -confirm_message(#basic_message{msg_seq_no = MsgSeqNo, origin = ChPid}) -> - rabbit_channel:confirm(ChPid, MsgSeqNo). +confirm_message(Guid, State = #q{guid_to_channel = GTC}) -> + case dict:find(Guid, GTC) of + {ok, {ChPid, MsgSeqNo}} -> + rabbit_channel:confirm(ChPid, MsgSeqNo), + State#q{guid_to_channel = dict:erase(Guid, GTC)}; + _ -> + State + end. + +maybe_record_confirm_message(undefined, _, _, State) -> + State; +maybe_record_confirm_message(MsgSeqNo, Message, ChPid, State) -> + State#q{guid_to_channel = + dict:store(Message#basic_message.guid, {ChPid, MsgSeqNo}, + State#q.guid_to_channel)}. run_message_queue(State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> Funs = {fun deliver_from_queue_pred/2, @@ -635,7 +650,7 @@ handle_call(consumers, _From, [{ChPid, ConsumerTag, AckRequired} | Acc] end, [], queue:join(ActiveConsumers, BlockedConsumers)), State); -handle_call({deliver_immediately, Txn, Message, ChPid}, _From, State) -> +handle_call({deliver_immediately, Txn, Message, MsgSeqNo, ChPid}, _From, State) -> %% Synchronous, "immediate" delivery mode %% %% FIXME: Is this correct semantics? @@ -649,13 +664,16 @@ handle_call({deliver_immediately, Txn, Message, ChPid}, _From, State) -> %% just all ready-to-consume queues get the message, with unready %% queues discarding the message? %% - {Delivered, NewState} = attempt_delivery(Txn, ChPid, Message, State), - reply(Delivered, NewState); + State1 = maybe_record_confirm_message(MsgSeqNo, Message, ChPid, State), + {Delivered, State2} = attempt_delivery(Txn, ChPid, Message, State1 +), + reply(Delivered, State2); -handle_call({deliver, Txn, Message, ChPid}, _From, State) -> +handle_call({deliver, Txn, Message, MsgSeqNo, ChPid}, _From, State) -> %% Synchronous, "mandatory" delivery mode - {Delivered, NewState} = deliver_or_enqueue(Txn, ChPid, Message, State), - reply(Delivered, NewState); + State1 = maybe_record_confirm_message(MsgSeqNo, Message, ChPid, State), + {Delivered, State2} = deliver_or_enqueue(Txn, ChPid, Message, State1), + reply(Delivered, State2); handle_call({commit, Txn, ChPid}, From, State) -> NewState = commit_transaction(Txn, From, ChPid, State), @@ -681,7 +699,7 @@ handle_call({basic_get, ChPid, NoAck}, _From, {empty, BQS1} -> reply(empty, State1#q{backing_queue_state = BQS1}); {{Message, IsDelivered, AckTag, Remaining}, BQS1} -> % PubAck after message got - confirm_message(Message), + State2 = confirm_message(Message#basic_message.guid, State1), case AckRequired of true -> C = #cr{acktags = ChAckTags} = ch_record(ChPid), store_ch_record( @@ -689,7 +707,7 @@ handle_call({basic_get, ChPid, NoAck}, _From, false -> ok end, Msg = {QName, self(), AckTag, IsDelivered, Message}, - reply({ok, Remaining, Msg}, State1#q{backing_queue_state = BQS1}) + reply({ok, Remaining, Msg}, State2#q{backing_queue_state = BQS1}) end; handle_call({basic_consume, NoAck, ChPid, LimiterPid, @@ -798,10 +816,11 @@ handle_call({requeue, AckTags, ChPid}, From, State) -> handle_call({maybe_run_queue_via_backing_queue, Fun}, _From, State) -> reply(ok, maybe_run_queue_via_backing_queue(Fun, State)). -handle_cast({deliver, Txn, Message, ChPid}, State) -> +handle_cast({deliver, Txn, Message, MsgSeqNo, ChPid}, State) -> %% Asynchronous, non-"mandatory", non-"immediate" deliver mode. - {_Delivered, NewState} = deliver_or_enqueue(Txn, ChPid, Message, State), - noreply(NewState); + State1 = maybe_record_confirm_message(MsgSeqNo, Message, ChPid, State), + {_Delivered, State2} = deliver_or_enqueue(Txn, ChPid, Message, State1), + noreply(State2); handle_cast({ack, Txn, AckTags, ChPid}, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> diff --git a/src/rabbit_basic.erl b/src/rabbit_basic.erl index 62ef3317..b7cfa09a 100644 --- a/src/rabbit_basic.erl +++ b/src/rabbit_basic.erl @@ -33,7 +33,7 @@ -include("rabbit.hrl"). -include("rabbit_framing.hrl"). --export([publish/1, message/4, properties/1, delivery/4]). +-export([publish/1, message/4, properties/1, delivery/4, delivery/5]). -export([publish/4, publish/7]). -export([build_content/2, from_content/1]). -export([is_message_persistent/1]). @@ -53,6 +53,10 @@ -spec(delivery/4 :: (boolean(), boolean(), rabbit_types:maybe(rabbit_types:txn()), rabbit_types:message()) -> rabiit_types:delivery()). +-spec(delivery/5 :: + (boolean(), boolean(), rabbit_types:maybe(rabbit_types:txn()), + rabbit_types:message(), undefined | integer()) + -> rabiit_types:delivery()). -spec(message/4 :: (rabbit_exchange:name(), rabbit_router:routing_key(), properties_input(), binary()) @@ -93,8 +97,11 @@ publish(Delivery = #delivery{ end. delivery(Mandatory, Immediate, Txn, Message) -> + delivery(Mandatory, Immediate, Txn, Message, undefined). + +delivery(Mandatory, Immediate, Txn, Message, MsgSeqNo) -> #delivery{mandatory = Mandatory, immediate = Immediate, txn = Txn, - sender = self(), message = Message}. + sender = self(), message = Message, msg_seq_no = MsgSeqNo}. build_content(Properties, BodyBin) -> %% basic.publish hasn't changed so we can just hard-code amqp_0_9_1 diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index f6e72814..954fb888 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -521,13 +521,11 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, routing_key = RoutingKey, content = DecodedContent, guid = rabbit_guid:guid(), - is_persistent = IsPersistent, - msg_seq_no = MsgSeqNo, - origin = self()}, + is_persistent = IsPersistent}, {RoutingRes, DeliveredQPids} = rabbit_exchange:publish( Exchange, - rabbit_basic:delivery(Mandatory, Immediate, TxnKey, Message)), + rabbit_basic:delivery(Mandatory, Immediate, TxnKey, Message, MsgSeqNo)), % PubAck after basic.returns State2 = case RoutingRes of routed -> State1; diff --git a/src/rabbit_router.erl b/src/rabbit_router.erl index 0f8611d0..a927ec64 100644 --- a/src/rabbit_router.erl +++ b/src/rabbit_router.erl @@ -55,7 +55,8 @@ deliver(QPids, Delivery = #delivery{mandatory = false, immediate = false, - message = Msg}) -> + message = Msg, + msg_seq_no = MsgSeqNo}) -> %% optimisation: when Mandatory = false and Immediate = false, %% rabbit_amqqueue:deliver will deliver the message to the queue %% process asynchronously, and return true, which means all the @@ -65,9 +66,9 @@ deliver(QPids, Delivery = #delivery{mandatory = false, %% case below. delegate:invoke_no_result( QPids, fun (Pid) -> rabbit_amqqueue:deliver(Pid, Delivery) end), - case {QPids, Msg#basic_message.msg_seq_no} of - {[], MsgSeqNo} -> rabbit_channel:confirm(Msg#basic_message.origin, MsgSeqNo); - _ -> ok + case {QPids, MsgSeqNo} of + {[], _} -> rabbit_channel:confirm(self(), MsgSeqNo); + _ -> ok end, {routed, QPids}; -- cgit v1.2.1 From 3022b1a8751617ad853710a0a96c90c21643a4b8 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Tue, 17 Aug 2010 12:53:18 +0100 Subject: persistent messages are ack'd only after the consumer acks it --- src/rabbit_amqqueue_process.erl | 39 ++++++++++++++++++++++++--------------- src/rabbit_variable_queue.erl | 1 + 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 0dbd7f17..f3fce61a 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -346,12 +346,13 @@ deliver_msgs_to_consumers(Funs = {PredFun, DeliverFun }, FunAcc, rabbit_channel:deliver( ChPid, ConsumerTag, AckRequired, {QName, self(), AckTag, IsDelivered, Message}), - % PubAck after message delivered to consumer (disregard consumer acks) - State2 = confirm_message(Message#basic_message.guid, State1), - ChAckTags1 = case AckRequired of - true -> sets:add_element(AckTag, ChAckTags); - false -> ChAckTags - end, + {State2, ChAckTags1} = + case AckRequired of + true -> {State1, sets:add_element(AckTag, ChAckTags)}; + false -> + {confirm_message_internal(Message#basic_message.guid, + State1), ChAckTags} + end, NewC = C#cr{unsent_message_count = Count + 1, acktags = ChAckTags1}, store_ch_record(NewC), @@ -401,7 +402,7 @@ deliver_from_queue_deliver(AckRequired, false, {{Message, IsDelivered, AckTag}, 0 == Remaining, State #q { backing_queue_state = BQS1 }}. -confirm_message(Guid, State = #q{guid_to_channel = GTC}) -> +confirm_message_internal(Guid, State = #q{guid_to_channel = GTC}) -> case dict:find(Guid, GTC) of {ok, {ChPid, MsgSeqNo}} -> rabbit_channel:confirm(ChPid, MsgSeqNo), @@ -698,14 +699,16 @@ handle_call({basic_get, ChPid, NoAck}, _From, case BQ:fetch(AckRequired, BQS) of {empty, BQS1} -> reply(empty, State1#q{backing_queue_state = BQS1}); {{Message, IsDelivered, AckTag, Remaining}, BQS1} -> - % PubAck after message got - State2 = confirm_message(Message#basic_message.guid, State1), - case AckRequired of - true -> C = #cr{acktags = ChAckTags} = ch_record(ChPid), - store_ch_record( - C#cr{acktags = sets:add_element(AckTag, ChAckTags)}); - false -> ok - end, + State2 = case AckRequired of + true -> + C = #cr{acktags = ChAckTags} = ch_record(ChPid), + store_ch_record( + C#cr{acktags = sets:add_element(AckTag, ChAckTags)}), + State1; + false -> + confirm_message_internal(Message#basic_message.guid, + State1) + end, Msg = {QName, self(), AckTag, IsDelivered, Message}, reply({ok, Remaining, Msg}, State2#q{backing_queue_state = BQS1}) end; @@ -816,6 +819,7 @@ handle_call({requeue, AckTags, ChPid}, From, State) -> handle_call({maybe_run_queue_via_backing_queue, Fun}, _From, State) -> reply(ok, maybe_run_queue_via_backing_queue(Fun, State)). + handle_cast({deliver, Txn, Message, MsgSeqNo, ChPid}, State) -> %% Asynchronous, non-"mandatory", non-"immediate" deliver mode. State1 = maybe_record_confirm_message(MsgSeqNo, Message, ChPid, State), @@ -838,6 +842,11 @@ handle_cast({ack, Txn, AckTags, ChPid}, noreply(State#q{backing_queue_state = BQS1}) end; +handle_cast({confirm_messages, Guids}, State) -> + noreply(lists:foldl(fun (Guid, State0) -> + confirm_message_internal(Guid, State0) + end, State, Guids)); + handle_cast({reject, AckTags, Requeue, ChPid}, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> case lookup_ch(ChPid) of diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index ca459665..e4020b60 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -1137,6 +1137,7 @@ ack(MsgStoreFun, Fun, AckTags, State) -> end, {{[], orddict:new()}, State}, AckTags), IndexState1 = rabbit_queue_index:ack(SeqIds, IndexState), ok = orddict:fold(fun (MsgStore, Guids, ok) -> + gen_server2:cast(self(), {confirm_messages, Guids}), MsgStoreFun(MsgStore, Guids) end, ok, GuidsByStore), PCount1 = PCount - case orddict:find(?PERSISTENT_MSG_STORE, GuidsByStore) of -- cgit v1.2.1 From 140cb51025d248e9585738322c2ae0052b88002d Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Tue, 17 Aug 2010 15:51:14 +0100 Subject: queue_index keeps track of which messages have been published but not written to disk There are 2 ways queue_index writes something to disk: - the journal is synced, and - the journal is flushed (i.e. scattered across many files and those files closed). Since queue_index is the same process as variable_queue, we just keep a list of guids that have been published and we just call an on_sync handler whenever these are written to disk. --- src/rabbit_queue_index.erl | 51 +++++++++++++++++++++++++++---------------- src/rabbit_variable_queue.erl | 3 +++ 2 files changed, 35 insertions(+), 19 deletions(-) diff --git a/src/rabbit_queue_index.erl b/src/rabbit_queue_index.erl index d6b8bb28..a82da57a 100644 --- a/src/rabbit_queue_index.erl +++ b/src/rabbit_queue_index.erl @@ -31,7 +31,7 @@ -module(rabbit_queue_index). --export([init/4, terminate/2, delete_and_terminate/1, publish/4, +-export([init/5, terminate/2, delete_and_terminate/1, publish/4, deliver/2, ack/2, sync/2, flush/1, read/3, next_segment_boundary/1, bounds/1, recover/1]). @@ -164,7 +164,7 @@ %%---------------------------------------------------------------------------- -record(qistate, { dir, segments, journal_handle, dirty_count, - max_journal_entries }). + max_journal_entries, on_sync, unsynced_guids }). -record(segment, { num, path, journal_entries, unacked }). @@ -187,15 +187,18 @@ segments :: 'undefined' | seg_dict(), journal_handle :: hdl(), dirty_count :: integer(), - max_journal_entries :: non_neg_integer() + max_journal_entries :: non_neg_integer(), + on_sync :: fun (([rabbit_guid:guid()]) -> ok), + unsynced_guids :: [rabbit_guid:guid()] }). -type(startup_fun_state() :: - {(fun ((A) -> 'finished' | {rabbit_guid:guid(), non_neg_integer(), A})), - A}). + {fun ((A) -> 'finished' | {rabbit_guid:guid(), non_neg_integer(), A}), + A}). --spec(init/4 :: (rabbit_amqqueue:name(), boolean(), boolean(), - fun ((rabbit_guid:guid()) -> boolean())) -> - {'undefined' | non_neg_integer(), [any()], qistate()}). +-spec(init/5 :: (rabbit_amqqueue:name(), boolean(), boolean(), + fun ((rabbit_guid:guid()) -> boolean()), + fun (([seq_id()]) -> ok)) + -> {'undefined' | non_neg_integer(), [any()], qistate()}). -spec(terminate/2 :: ([any()], qistate()) -> qistate()). -spec(delete_and_terminate/1 :: (qistate()) -> qistate()). -spec(publish/4 :: (rabbit_guid:guid(), seq_id(), boolean(), qistate()) -> @@ -220,20 +223,23 @@ %% public API %%---------------------------------------------------------------------------- -init(Name, Recover, MsgStoreRecovered, ContainsCheckFun) -> - State = #qistate { dir = Dir } = blank_state(Name, not Recover), +init(Name, Recover, MsgStoreRecovered, ContainsCheckFun, OnSyncFun) -> + State + = #qistate { dir = Dir } + = blank_state(Name, not Recover), + State1 = State #qistate { on_sync = OnSyncFun, unsynced_guids = [] }, Terms = case read_shutdown_terms(Dir) of {error, _} -> []; {ok, Terms1} -> Terms1 end, CleanShutdown = detect_clean_shutdown(Dir), - {Count, State1} = + {Count, State2} = case CleanShutdown andalso MsgStoreRecovered of true -> RecoveredCounts = proplists:get_value(segments, Terms, []), - init_clean(RecoveredCounts, State); - false -> init_dirty(CleanShutdown, ContainsCheckFun, State) + init_clean(RecoveredCounts, State1); + false -> init_dirty(CleanShutdown, ContainsCheckFun, State1) end, - {Count, Terms, State1}. + {Count, Terms, State2}. terminate(Terms, State) -> {SegmentCounts, State1 = #qistate { dir = Dir }} = terminate(State), @@ -253,7 +259,9 @@ publish(Guid, SeqId, IsPersistent, State) when is_binary(Guid) -> true -> ?PUB_PERSIST_JPREFIX; false -> ?PUB_TRANS_JPREFIX end):?JPREFIX_BITS, SeqId:?SEQ_BITS>>, Guid]), - maybe_flush_journal(add_to_journal(SeqId, {Guid, IsPersistent}, State1)). + State2 = State#qistate { unsynced_guids = + [Guid | State#qistate.unsynced_guids] }, + maybe_flush_journal(add_to_journal(SeqId, {Guid, IsPersistent}, State2)). deliver(SeqIds, State) -> deliver_or_ack(del, SeqIds, State). @@ -265,7 +273,8 @@ sync([], State) -> State; sync(_SeqIds, State = #qistate { journal_handle = undefined }) -> State; -sync(_SeqIds, State = #qistate { journal_handle = JournalHdl }) -> +sync(SeqIds, State = #qistate { journal_handle = JournalHdl, + on_sync = OnSyncFun }) -> %% The SeqIds here contains the SeqId of every publish and ack in %% the transaction. Ideally we should go through these seqids and %% only sync the journal if the pubs or acks appear in the @@ -275,7 +284,8 @@ sync(_SeqIds, State = #qistate { journal_handle = JournalHdl }) -> %% seqids not being in the journal, provided the transaction isn't %% emptied (handled above anyway). ok = file_handle_cache:sync(JournalHdl), - State. + OnSyncFun(State#qistate.unsynced_guids), + State#qistate { unsynced_guids = [] }. flush(State = #qistate { dirty_count = 0 }) -> State; flush(State) -> flush_journal(State). @@ -557,7 +567,9 @@ maybe_flush_journal(State = #qistate { dirty_count = DCount, maybe_flush_journal(State) -> State. -flush_journal(State = #qistate { segments = Segments }) -> +flush_journal(State = #qistate { segments = Segments, + on_sync = OnSyncFun, + unsynced_guids = UGs }) -> Segments1 = segment_fold( fun (#segment { unacked = 0, path = Path }, SegmentsN) -> @@ -572,7 +584,8 @@ flush_journal(State = #qistate { segments = Segments }) -> {JournalHdl, State1} = get_journal_handle(State #qistate { segments = Segments1 }), ok = file_handle_cache:clear(JournalHdl), - State1 #qistate { dirty_count = 0 }. + OnSyncFun(UGs), + State1 #qistate { dirty_count = 0, unsynced_guids = [] }. append_journal_to_segment(#segment { journal_entries = JEntries, path = Path } = Segment) -> diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index e4020b60..2b0919a1 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -375,6 +375,9 @@ init(QueueName, IsDurable, Recover) -> rabbit_msg_store:successfully_recovered_state(?PERSISTENT_MSG_STORE), fun (Guid) -> rabbit_msg_store:contains(?PERSISTENT_MSG_STORE, Guid) + end, + fun (Guids) -> + rabbit_log:info("message indices ~p commited to disk~n", [Guids]) end), {LowSeqId, NextSeqId, IndexState1} = rabbit_queue_index:bounds(IndexState), -- cgit v1.2.1 From 85f35e28589079c2d55a6dc255f301dec4fd35b4 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Tue, 17 Aug 2010 17:05:44 +0100 Subject: publisher acks for persistent messages Acknowledgements are sent to the producer in the following cases (assuming the channel in confirm mode): - a transient message has been received (i.e. it is ack'd immediately), - a basic return has been sent (the ack is sent immediately afterwards), - a persistent message has been consumed [by a consumer] or got [via basic.get] and ack'd by the client (or the retrieval was no-ack), - a persistent message has been written to disk (both the message and its index). At least in theory, every message published on a confirm channel will at some point be ack'd. A message is only ack'd once and the reason it was ack'd is hidden. --- src/rabbit_amqqueue_process.erl | 54 +++++++++++++++++++++++++++++++++++------ src/rabbit_msg_store.erl | 2 -- src/rabbit_variable_queue.erl | 22 ++++++----------- 3 files changed, 53 insertions(+), 25 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index f3fce61a..8bed40be 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -61,7 +61,9 @@ rate_timer_ref, expiry_timer_ref, stats_timer, - guid_to_channel + guid_to_channel, + msgs_on_disk, + msg_indices_on_disk }). -record(consumer, {tag, ack_required}). @@ -124,7 +126,9 @@ init(Q) -> rate_timer_ref = undefined, expiry_timer_ref = undefined, stats_timer = rabbit_event:init_stats_timer(), - guid_to_channel = dict:new()}, hibernate, + guid_to_channel = dict:new(), + msgs_on_disk = gb_sets:new(), + msg_indices_on_disk = gb_sets:new()}, hibernate, {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}. terminate(shutdown, State = #q{backing_queue = BQ}) -> @@ -402,21 +406,26 @@ deliver_from_queue_deliver(AckRequired, false, {{Message, IsDelivered, AckTag}, 0 == Remaining, State #q { backing_queue_state = BQS1 }}. -confirm_message_internal(Guid, State = #q{guid_to_channel = GTC}) -> +confirm_message_internal(Guid, State = #q { guid_to_channel = GTC, + msgs_on_disk = MOD, + msg_indices_on_disk = MIOD }) -> case dict:find(Guid, GTC) of {ok, {ChPid, MsgSeqNo}} -> rabbit_channel:confirm(ChPid, MsgSeqNo), - State#q{guid_to_channel = dict:erase(Guid, GTC)}; + State #q { guid_to_channel = dict:erase(Guid, GTC), + msgs_on_disk = gb_sets:delete_any(Guid, MOD), + msg_indices_on_disk = gb_sets:delete_any(Guid, MIOD) }; _ -> State end. maybe_record_confirm_message(undefined, _, _, State) -> State; -maybe_record_confirm_message(MsgSeqNo, Message, ChPid, State) -> - State#q{guid_to_channel = - dict:store(Message#basic_message.guid, {ChPid, MsgSeqNo}, - State#q.guid_to_channel)}. +maybe_record_confirm_message(MsgSeqNo, + #basic_message { guid = Guid }, + ChPid, State) -> + State #q { guid_to_channel = + dict:store(Guid, {ChPid, MsgSeqNo}, State#q.guid_to_channel) }. run_message_queue(State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> Funs = {fun deliver_from_queue_pred/2, @@ -847,6 +856,35 @@ handle_cast({confirm_messages, Guids}, State) -> confirm_message_internal(Guid, State0) end, State, Guids)); +handle_cast({msgs_written_to_disk, Guids}, + State = #q{msgs_on_disk = MOD, + msg_indices_on_disk = MIOD}) -> + GuidSet = gb_sets:from_list(Guids), + ToConfirmMsgs = gb_sets:intersection(GuidSet, MIOD), + gb_sets:fold(fun (Guid, State0) -> + confirm_message_internal(Guid, State0) + end, State, ToConfirmMsgs), + noreply(State#q{msgs_on_disk = + gb_sets:difference(gb_sets:union(MOD, GuidSet), + ToConfirmMsgs), + msg_indices_on_disk = + gb_sets:difference(MIOD, ToConfirmMsgs)}); + +handle_cast({msg_indices_written_to_disk, Guids}, + State = #q{msgs_on_disk = MOD, + msg_indices_on_disk = MIOD}) -> + rabbit_log:info("Message indices written to disk: ~p~n", [Guids]), + GuidSet = gb_sets:from_list(Guids), + ToConfirmMsgs = gb_sets:intersection(GuidSet, MOD), + gb_sets:fold(fun (Guid, State0) -> + confirm_message_internal(Guid, State0) + end, State, ToConfirmMsgs), + noreply(State#q{msgs_on_disk = + gb_sets:difference(MOD, ToConfirmMsgs), + msg_indices_on_disk = + gb_sets:difference(gb_sets:union(MIOD, GuidSet), + ToConfirmMsgs)}); + handle_cast({reject, AckTags, Requeue, ChPid}, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> case lookup_ch(ChPid) of diff --git a/src/rabbit_msg_store.erl b/src/rabbit_msg_store.erl index 09eb7cfd..9e917fe5 100644 --- a/src/rabbit_msg_store.erl +++ b/src/rabbit_msg_store.erl @@ -634,7 +634,6 @@ handle_cast({write, Pid, Guid, Msg}, %% New message, lots to do {ok, CurOffset} = file_handle_cache:current_virtual_offset(CurHdl), {ok, TotalSize} = rabbit_msg_file:append(CurHdl, Guid, Msg), - rabbit_log:info("message ~p written to disk~n", [Guid]), ok = index_insert(#msg_location { guid = Guid, ref_count = 1, file = CurFile, offset = CurOffset, total_size = TotalSize }, @@ -826,7 +825,6 @@ internal_sync(State = #msstate { current_file_handle = CurHdl, on_sync = Syncs, pid_to_fun = PTF, pid_to_guids = PTG }) -> - rabbit_log:info("msg_store syncing~ncallbacks: ~p~nguids: ~p~n", [dict:to_list(PTF), dict:to_list(PTG)]), State1 = stop_sync_timer(State), State2 = case Syncs of [] -> State1; diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 2b0919a1..454193c2 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -369,6 +369,7 @@ stop_msg_store() -> ok = rabbit_sup:stop_child(?TRANSIENT_MSG_STORE). init(QueueName, IsDurable, Recover) -> + Self = self(), {DeltaCount, Terms, IndexState} = rabbit_queue_index:init( QueueName, Recover, @@ -377,7 +378,7 @@ init(QueueName, IsDurable, Recover) -> rabbit_msg_store:contains(?PERSISTENT_MSG_STORE, Guid) end, fun (Guids) -> - rabbit_log:info("message indices ~p commited to disk~n", [Guids]) + gen_server2:cast(Self, {msg_indices_written_to_disk, Guids}) end), {LowSeqId, NextSeqId, IndexState1} = rabbit_queue_index:bounds(IndexState), @@ -401,7 +402,7 @@ init(QueueName, IsDurable, Recover) -> true -> rabbit_msg_store:client_init(?PERSISTENT_MSG_STORE, PRef); false -> undefined end, - register_puback_callback(?PERSISTENT_MSG_STORE), + register_confirm_callback(?PERSISTENT_MSG_STORE), TransientClient = rabbit_msg_store:client_init(?TRANSIENT_MSG_STORE, TRef), State = #vqstate { q1 = queue:new(), @@ -1072,24 +1073,15 @@ maybe_write_to_disk(ForceMsg, ForceIndex, MsgStatus, msg_store_clients = MSCState1 }}. %%---------------------------------------------------------------------------- -%% Internal gubbins for pubacks +%% Internal gubbins for confirms %%---------------------------------------------------------------------------- -register_puback_callback(MessageStore) -> +register_confirm_callback(MessageStore) -> + Self = self(), rabbit_msg_store:register_callback( MessageStore, fun (Guids) -> - spawn(fun () -> ok = rabbit_misc:with_exit_handler( - fun () -> - rabbit_log:info("something bad happened while sending pubacks back to channel"), - ok - end, - fun () -> - lists:foreach(fun(G) -> - rabbit_log:info("send puback back to channel for ~p~n", [G]) end, Guids), - ok - end) - end) + gen_server2:cast(Self, {msgs_written_to_disk, Guids}) end). %%---------------------------------------------------------------------------- -- cgit v1.2.1 From f9a55870431b7665a3e4ab7c41d14a899993e795 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Tue, 17 Aug 2010 17:33:13 +0100 Subject: refactoring --- src/rabbit_channel.erl | 117 +++++++++++++++++++++++-------------------------- 1 file changed, 56 insertions(+), 61 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 954fb888..e32ed1c4 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -48,8 +48,8 @@ uncommitted_ack_q, unacked_message_q, username, virtual_host, most_recently_declared_queue, consumer_mapping, blocking, queue_collector_pid, stats_timer, - confirm}). --record(confirm, {enabled, count, multiple, tref, held_acks, need_acking}). + confirm_enabled, published_count, confirm_multiple, confirm_tref, + held_confirms, need_confirming}). -define(MAX_PERMISSION_CACHE_SIZE, 12). @@ -167,28 +167,28 @@ init([Channel, ReaderPid, WriterPid, Username, VHost, CollectorPid]) -> process_flag(trap_exit, true), link(WriterPid), ok = pg_local:join(rabbit_channels, self()), - State = #ch{state = starting, - channel = Channel, - reader_pid = ReaderPid, - writer_pid = WriterPid, - limiter_pid = undefined, - transaction_id = none, - tx_participants = sets:new(), - next_tag = 1, - uncommitted_ack_q = queue:new(), - unacked_message_q = queue:new(), - username = Username, - virtual_host = VHost, - most_recently_declared_queue = <<>>, - consumer_mapping = dict:new(), - blocking = dict:new(), - queue_collector_pid = CollectorPid, - stats_timer = rabbit_event:init_stats_timer(), - confirm = #confirm{enabled = false, - count = 0, - multiple = false, - held_acks = gb_sets:new(), - need_acking = gb_sets:new()}}, + State = #ch{ state = starting, + channel = Channel, + reader_pid = ReaderPid, + writer_pid = WriterPid, + limiter_pid = undefined, + transaction_id = none, + tx_participants = sets:new(), + next_tag = 1, + uncommitted_ack_q = queue:new(), + unacked_message_q = queue:new(), + username = Username, + virtual_host = VHost, + most_recently_declared_queue = <<>>, + consumer_mapping = dict:new(), + blocking = dict:new(), + queue_collector_pid = CollectorPid, + stats_timer = rabbit_event:init_stats_timer(), + confirm_enabled = false, + published_count = 0, + confirm_multiple = false, + held_confirms = gb_sets:new(), + need_confirming = gb_sets:new() }, rabbit_event:notify( channel_created, [{Item, i(Item, State)} || Item <- ?CREATION_EVENT_KEYS]), @@ -258,17 +258,17 @@ handle_cast(emit_stats, State) -> {noreply, State}; handle_cast(multiple_ack_flush, - State = #ch{writer_pid = WriterPid, - confirm = C = #confirm{held_acks = As, - need_acking = NA}}) -> + State = #ch{writer_pid = WriterPid, + held_confirms = As, + need_confirming = NA}) -> rabbit_log:info("channel got a multiple_ack_flush message~n" "held acks: ~p~n", [gb_sets:to_list(As)]), case gb_sets:is_empty(As) of true -> ok; % this should never be the case false -> flush_multiple(As, WriterPid, gb_sets:smallest(NA)) end, - {noreply, State#ch{confirm = C#confirm{held_acks = gb_sets:new(), - tref = undefined}}}; + {noreply, State #ch { held_confirms = gb_sets:new(), + confirm_tref = undefined }}; handle_cast({confirm, MsgSeqNo}, State) -> {noreply, send_or_enqueue_ack(MsgSeqNo, State)}. @@ -427,10 +427,10 @@ queue_blocked(QPid, State = #ch{blocking = Blocking}) -> send_or_enqueue_ack(undefined, State) -> State; -send_or_enqueue_ack(_, State = #ch{confirm = #confirm{enabled = false}}) -> +send_or_enqueue_ack(_, State = #ch{confirm_enabled = false}) -> State; send_or_enqueue_ack(MsgSeqNo, - State = #ch{confirm = #confirm{multiple = false}}) -> + State = #ch{confirm_multiple = false}) -> rabbit_log:info("handling confirm in single mode (#~p)~n", [MsgSeqNo]), do_if_not_dup(MsgSeqNo, State, fun(MSN, S = #ch{writer_pid = WriterPid}) -> @@ -438,23 +438,20 @@ send_or_enqueue_ack(MsgSeqNo, WriterPid, #'basic.ack'{delivery_tag = MSN}), S end); -send_or_enqueue_ack(MsgSeqNo, State = #ch{confirm = #confirm{multiple = true}}) -> +send_or_enqueue_ack(MsgSeqNo, State = #ch{confirm_multiple = true}) -> rabbit_log:info("handling confirm in multiple mode (#~p)~n", [MsgSeqNo]), do_if_not_dup(MsgSeqNo, State, fun(MSN, S) -> - State1 - = #ch{confirm = C = #confirm{held_acks = As}} - = start_ack_timer(S), - State1#ch{confirm = - C#confirm{held_acks = - gb_sets:add(MSN, As)}} + State1 = start_ack_timer(S), + State1 #ch { held_confirms = + gb_sets:add(MSN, State1#ch.held_confirms) } end). -do_if_not_dup(MsgSeqNo, State = #ch{confirm = #confirm{need_acking = NA}}, Fun) -> +do_if_not_dup(MsgSeqNo, State = #ch{need_confirming = NA}, Fun) -> case gb_sets:is_element(MsgSeqNo, NA) of true -> - State1 = #ch{confirm = C} = Fun(MsgSeqNo, State), - State1#ch{confirm = C#confirm{need_acking = gb_sets:delete(MsgSeqNo, NA)}}; + State1 = Fun(MsgSeqNo, State), + State1 #ch { need_confirming = gb_sets:delete(MsgSeqNo, NA) }; false -> State end. @@ -494,16 +491,15 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, IsPersistent = is_message_persistent(DecodedContent), % PubAck transient messages immediately {MsgSeqNo, State1} - = case State#ch.confirm#confirm.enabled of + = case State#ch.confirm_enabled of false -> {undefined, State}; true -> - Confirm = State#ch.confirm, - Count = Confirm#confirm.count, + Count = State#ch.published_count, % Add the current message to the need_acking list - State01 = State#ch{confirm = Confirm#confirm{ - need_acking = gb_sets:add(Count, - Confirm#confirm.need_acking)}}, + State01 = State #ch { + need_confirming = gb_sets:add(Count, + State#ch.need_confirming) }, % Ack transient messages now {CountOrUndefined, State02} = case IsPersistent of @@ -513,9 +509,8 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, Count, State01)} end, % Increase the PubAck counter - Confirm02 = State02#ch.confirm, {CountOrUndefined, - State02#ch{confirm = Confirm02#confirm{count = Count+1}}} + State02 #ch { published_count = Count + 1 }} end, Message = #basic_message{exchange_name = ExchangeName, routing_key = RoutingKey, @@ -930,7 +925,7 @@ handle_method(#'queue.purge'{queue = QueueNameBin, #'queue.purge_ok'{message_count = PurgedMessageCount}); -handle_method(#'tx.select'{}, _, #ch{confirm = #confirm{enabled = true}}) -> +handle_method(#'tx.select'{}, _, #ch{confirm_enabled = true}) -> rabbit_misc:protocol_error( precondition_failed, "a confirm channel cannot be made transactional", []); @@ -961,11 +956,11 @@ handle_method(#'confirm.select'{}, _, #ch{transaction_id = TxId}) handle_method(#'confirm.select'{multiple = Multiple, nowait = NoWait}, _, - State = #ch{confirm = C = #confirm{enabled = false}}) -> + State = #ch{confirm_enabled = false}) -> rabbit_log:info("got confirm.select{multiple = ~p, nowait = ~p}~n", [Multiple, NoWait]), - State1 = State#ch{confirm = C#confirm{enabled = true, - multiple = Multiple}}, + State1 = State #ch { confirm_enabled = true, + confirm_multiple = Multiple }, case NoWait of true -> {noreply, State1}; false -> {reply, #'confirm.select_ok'{}, State1} @@ -973,8 +968,8 @@ handle_method(#'confirm.select'{multiple = Multiple, nowait = NoWait}, handle_method(#'confirm.select'{multiple = Multiple, nowait = NoWait}, _, - State = #ch{confirm = #confirm{enabled = true, - multiple = Multiple}}) -> + State = #ch{confirm_enabled = true, + confirm_multiple = Multiple}) -> rabbit_log:info("got a confirm.select with same options~n"), case NoWait of true -> {noreply, State}; @@ -983,7 +978,7 @@ handle_method(#'confirm.select'{multiple = Multiple, nowait = NoWait}, handle_method(#'confirm.select'{}, _, - #ch{confirm = #confirm{enabled = true}}) -> + #ch{confirm_enabled = true}) -> rabbit_misc:protocol_error( precondition_failed, "cannot change confirm channel multiple setting", []); @@ -1311,18 +1306,18 @@ erase_queue_stats(QPid) -> [erase({queue_exchange_stats, QX}) || {{queue_exchange_stats, QX = {QPid0, _}}, _} <- get(), QPid =:= QPid0]. -start_ack_timer(State = #ch{confirm = C = #confirm{tref = undefined}}) -> +start_ack_timer(State = #ch{confirm_tref = undefined}) -> {ok, TRef} = timer:apply_after(?MULTIPLE_ACK_FLUSH_INTERVAL, ?MODULE, flush_multiple_acks, [self()]), - State#ch{confirm = C#confirm{tref = TRef}}; + State #ch { confirm_tref = TRef }; start_ack_timer(State) -> State. -stop_ack_timer(State = #ch{confirm = #confirm{tref = undefined}}) -> +stop_ack_timer(State = #ch{confirm_tref = undefined}) -> State; -stop_ack_timer(State = #ch{confirm = C = #confirm{tref = TRef}}) -> +stop_ack_timer(State = #ch{confirm_tref = TRef}) -> {ok, cancel} = timer:cancel(TRef), - State#ch{confirm = C#confirm{tref = undefined}}. + State #ch { confirm_tref = undefined }. flush_multiple(Acks, WriterPid, SmallestNotAcked) -> [First | Rest] = gb_sets:to_list(Acks), -- cgit v1.2.1 From 463044df5eefb1537b752518cb55080866cb10ff Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Wed, 18 Aug 2010 12:12:49 +0100 Subject: confirm transient messages only after basic.return --- src/rabbit_channel.erl | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index e32ed1c4..17d84533 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -489,28 +489,18 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, %% certain to want to look at delivery-mode and priority. DecodedContent = rabbit_binary_parser:ensure_content_decoded(Content), IsPersistent = is_message_persistent(DecodedContent), - % PubAck transient messages immediately + %% PubAck transient messages immediately {MsgSeqNo, State1} = case State#ch.confirm_enabled of false -> {undefined, State}; true -> Count = State#ch.published_count, - % Add the current message to the need_acking list - State01 = State #ch { - need_confirming = gb_sets:add(Count, - State#ch.need_confirming) }, - % Ack transient messages now - {CountOrUndefined, State02} = - case IsPersistent of - true -> {Count, State01}; - false -> {undefined, - send_or_enqueue_ack( - Count, State01)} - end, - % Increase the PubAck counter - {CountOrUndefined, - State02 #ch { published_count = Count + 1 }} + {Count, + State #ch { published_count = Count + 1, + need_confirming = + gb_sets:add(Count, + State#ch.need_confirming) }} end, Message = #basic_message{exchange_name = ExchangeName, routing_key = RoutingKey, @@ -520,10 +510,20 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, {RoutingRes, DeliveredQPids} = rabbit_exchange:publish( Exchange, - rabbit_basic:delivery(Mandatory, Immediate, TxnKey, Message, MsgSeqNo)), - % PubAck after basic.returns + rabbit_basic:delivery(Mandatory, Immediate, TxnKey, Message, + case IsPersistent of + true -> MsgSeqNo; + false -> undefined + end)), State2 = case RoutingRes of - routed -> State1; + %% Confirm transient messages now + routed -> + case IsPersistent of + true -> State1; + false -> send_or_enqueue_ack( + MsgSeqNo, State1) + end; + %% Confirm after basic.returns unroutable -> ok = basic_return(Message, WriterPid, no_route), send_or_enqueue_ack(MsgSeqNo, State1); -- cgit v1.2.1 From 39d4084a0c80624840b3b1dcf1ac2734001e9883 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Wed, 18 Aug 2010 15:03:56 +0100 Subject: deleting a queue causes all of its messages to be puback'd --- src/rabbit_amqqueue_process.erl | 1 - src/rabbit_channel.erl | 61 +++++++++++++++++++++++++++++++++-------- src/rabbit_msg_store.erl | 1 - src/rabbit_router.erl | 24 ++++++++++++---- 4 files changed, 69 insertions(+), 18 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 8bed40be..8979062b 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -873,7 +873,6 @@ handle_cast({msgs_written_to_disk, Guids}, handle_cast({msg_indices_written_to_disk, Guids}, State = #q{msgs_on_disk = MOD, msg_indices_on_disk = MIOD}) -> - rabbit_log:info("Message indices written to disk: ~p~n", [Guids]), GuidSet = gb_sets:from_list(Guids), ToConfirmMsgs = gb_sets:intersection(GuidSet, MOD), gb_sets:fold(fun (Guid, State0) -> diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 17d84533..76b962fb 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -49,7 +49,7 @@ username, virtual_host, most_recently_declared_queue, consumer_mapping, blocking, queue_collector_pid, stats_timer, confirm_enabled, published_count, confirm_multiple, confirm_tref, - held_confirms, need_confirming}). + held_confirms, need_confirming, qpid_to_msgs}). -define(MAX_PERMISSION_CACHE_SIZE, 12). @@ -188,7 +188,8 @@ init([Channel, ReaderPid, WriterPid, Username, VHost, CollectorPid]) -> published_count = 0, confirm_multiple = false, held_confirms = gb_sets:new(), - need_confirming = gb_sets:new() }, + need_confirming = gb_sets:new(), + qpid_to_msgs = dict:new() }, rabbit_event:notify( channel_created, [{Item, i(Item, State)} || Item <- ?CREATION_EVENT_KEYS]), @@ -269,8 +270,14 @@ handle_cast(multiple_ack_flush, end, {noreply, State #ch { held_confirms = gb_sets:new(), confirm_tref = undefined }}; + handle_cast({confirm, MsgSeqNo}, State) -> - {noreply, send_or_enqueue_ack(MsgSeqNo, State)}. + {noreply, send_or_enqueue_ack(MsgSeqNo, State)}; + +handle_cast({msg_sent_to_queues, MsgSeqNo, QPids}, State) -> + {noreply, lists:foldl(fun (QPid, State0) -> + msg_sent_to_queues(MsgSeqNo, QPid, State0) + end, State, QPids)}. handle_info({'EXIT', WriterPid, Reason = {writer, send_failed, _Error}}, @@ -279,9 +286,18 @@ handle_info({'EXIT', WriterPid, Reason = {writer, send_failed, _Error}}, {stop, normal, State}; handle_info({'EXIT', _Pid, Reason}, State) -> {stop, Reason, State}; -handle_info({'DOWN', _MRef, process, QPid, _Reason}, State) -> - erase_queue_stats(QPid), - {noreply, queue_blocked(QPid, State)}. +handle_info({'DOWN', _MRef, process, QPid, Reason}, + State = #ch{qpid_to_msgs = QTM}) -> + case dict:find(QPid, QTM) of + {ok, Msgs} -> + S = gb_sets:fold(fun (MsgSeqNo, State0) -> + send_or_enqueue_ack(MsgSeqNo, State0) + end, State, Msgs), + {noreply, S #ch {qpid_to_msgs = dict:erase(QPid, QTM)}}; + error -> + erase_queue_stats(QPid), + {noreply, queue_blocked(QPid, State)} + end. handle_pre_hibernate(State) -> ok = clear_permission_cache(), @@ -433,20 +449,42 @@ send_or_enqueue_ack(MsgSeqNo, State = #ch{confirm_multiple = false}) -> rabbit_log:info("handling confirm in single mode (#~p)~n", [MsgSeqNo]), do_if_not_dup(MsgSeqNo, State, - fun(MSN, S = #ch{writer_pid = WriterPid}) -> + fun(MSN, S = #ch{writer_pid = WriterPid, + qpid_to_msgs = QTM}) -> ok = rabbit_writer:send_command( WriterPid, #'basic.ack'{delivery_tag = MSN}), - S + S #ch { qpid_to_msgs = + dict:map(fun (_, Msgs) -> + gb_sets:delete_any(MsgSeqNo, Msgs) + end, QTM) } end); send_or_enqueue_ack(MsgSeqNo, State = #ch{confirm_multiple = true}) -> rabbit_log:info("handling confirm in multiple mode (#~p)~n", [MsgSeqNo]), do_if_not_dup(MsgSeqNo, State, - fun(MSN, S) -> + fun(MSN, S = #ch{qpid_to_msgs = QTM}) -> State1 = start_ack_timer(S), State1 #ch { held_confirms = - gb_sets:add(MSN, State1#ch.held_confirms) } + gb_sets:add(MSN, State1#ch.held_confirms), + qpid_to_msgs = + dict:map(fun (_, Msgs) -> + gb_sets:delete_any(MsgSeqNo, + Msgs) + end, QTM) } end). +msg_sent_to_queues(MsgSeqNo, QPid, State = #ch{qpid_to_msgs = QTM}) -> + case dict:find(QPid, QTM) of + {ok, Msgs} -> + State #ch {qpid_to_msgs = dict:store(QPid, + gb_sets:add(MsgSeqNo, Msgs), + QTM) }; + error -> + erlang:monitor(process, QPid), + State #ch { qpid_to_msgs = dict:store(QPid, + gb_sets:add(MsgSeqNo, gb_sets:new()), + QTM) } + end. + do_if_not_dup(MsgSeqNo, State = #ch{need_confirming = NA}, Fun) -> case gb_sets:is_element(MsgSeqNo, NA) of true -> @@ -489,7 +527,6 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, %% certain to want to look at delivery-mode and priority. DecodedContent = rabbit_binary_parser:ensure_content_decoded(Content), IsPersistent = is_message_persistent(DecodedContent), - %% PubAck transient messages immediately {MsgSeqNo, State1} = case State#ch.confirm_enabled of false -> @@ -1256,9 +1293,11 @@ maybe_incr_stats(QXIncs, Measure, #ch{stats_timer = StatsTimer}) -> end. incr_stats({QPid, _} = QX, Inc, Measure) -> + io:format("incr_stats for ~p~n", [QPid]), maybe_monitor(QPid), update_measures(queue_exchange_stats, QX, Inc, Measure); incr_stats(QPid, Inc, Measure) when is_pid(QPid) -> + io:format("incr_stats for ~p~n", [QPid]), maybe_monitor(QPid), update_measures(queue_stats, QPid, Inc, Measure); incr_stats(X, Inc, Measure) -> diff --git a/src/rabbit_msg_store.erl b/src/rabbit_msg_store.erl index 9e917fe5..8fe02939 100644 --- a/src/rabbit_msg_store.erl +++ b/src/rabbit_msg_store.erl @@ -746,7 +746,6 @@ handle_info({'DOWN', _MRef, process, Pid, _Reason}, State = #msstate { pid_to_fun = PTF, pid_to_guids = PTG }) -> % A queue with a callback has died, so remove it from dicts. - rabbit_log:info("Queue ~p has gone down~n", [Pid]), {noreply, State #msstate { pid_to_fun = dict:erase(Pid, PTF), pid_to_guids = dict:erase(Pid, PTG) }}; diff --git a/src/rabbit_router.erl b/src/rabbit_router.erl index a927ec64..179c45bb 100644 --- a/src/rabbit_router.erl +++ b/src/rabbit_router.erl @@ -55,7 +55,6 @@ deliver(QPids, Delivery = #delivery{mandatory = false, immediate = false, - message = Msg, msg_seq_no = MsgSeqNo}) -> %% optimisation: when Mandatory = false and Immediate = false, %% rabbit_amqqueue:deliver will deliver the message to the queue @@ -67,12 +66,16 @@ deliver(QPids, Delivery = #delivery{mandatory = false, delegate:invoke_no_result( QPids, fun (Pid) -> rabbit_amqqueue:deliver(Pid, Delivery) end), case {QPids, MsgSeqNo} of - {[], _} -> rabbit_channel:confirm(self(), MsgSeqNo); + {[], _} -> + %% No queues will get the message. This is fine, so we + %% just confirm it. + rabbit_channel:confirm(self(), MsgSeqNo); _ -> ok end, + maybe_inform_channel(MsgSeqNo, QPids), {routed, QPids}; -deliver(QPids, Delivery) -> +deliver(QPids, Delivery = #delivery{msg_seq_no = MsgSeqNo}) -> {Success, _} = delegate:invoke(QPids, fun (Pid) -> @@ -80,8 +83,14 @@ deliver(QPids, Delivery) -> end), {Routed, Handled} = lists:foldl(fun fold_deliveries/2, {false, []}, Success), - check_delivery(Delivery#delivery.mandatory, Delivery#delivery.immediate, - {Routed, Handled}). + case check_delivery(Delivery#delivery.mandatory, Delivery#delivery.immediate, + {Routed, Handled}) of + {routed, Qs} -> + maybe_inform_channel(MsgSeqNo, Qs), + {routed, Qs}; + O -> + O + end. %% TODO: Maybe this should be handled by a cursor instead. %% TODO: This causes a full scan for each entry with the same exchange @@ -119,3 +128,8 @@ fold_deliveries({_, false},{_, Handled}) -> {true, Handled}. check_delivery(true, _ , {false, []}) -> {unroutable, []}; check_delivery(_ , true, {_ , []}) -> {not_delivered, []}; check_delivery(_ , _ , {_ , Qs}) -> {routed, Qs}. + +maybe_inform_channel(undefine, _) -> + ok; +maybe_inform_channel(MsgSeqNo, QPids) -> + gen_server2:cast(self(), {msg_sent_to_queues, MsgSeqNo, QPids}). -- cgit v1.2.1 From 927123ab191313b0ad2d8b28d29ba50ec0f11c9f Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Wed, 18 Aug 2010 16:43:33 +0100 Subject: fixed a small empty-set bug --- src/rabbit_channel.erl | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 76b962fb..f305194a 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -262,11 +262,12 @@ handle_cast(multiple_ack_flush, State = #ch{writer_pid = WriterPid, held_confirms = As, need_confirming = NA}) -> - rabbit_log:info("channel got a multiple_ack_flush message~n" - "held acks: ~p~n", [gb_sets:to_list(As)]), case gb_sets:is_empty(As) of true -> ok; % this should never be the case - false -> flush_multiple(As, WriterPid, gb_sets:smallest(NA)) + false -> flush_multiple(As, WriterPid, case gb_sets:is_empty(NA) of + false -> gb_sets:smallest(NA); + true -> gb_sets:largest(As)+1 + end) end, {noreply, State #ch { held_confirms = gb_sets:new(), confirm_tref = undefined }}; @@ -447,7 +448,6 @@ send_or_enqueue_ack(_, State = #ch{confirm_enabled = false}) -> State; send_or_enqueue_ack(MsgSeqNo, State = #ch{confirm_multiple = false}) -> - rabbit_log:info("handling confirm in single mode (#~p)~n", [MsgSeqNo]), do_if_not_dup(MsgSeqNo, State, fun(MSN, S = #ch{writer_pid = WriterPid, qpid_to_msgs = QTM}) -> @@ -459,7 +459,6 @@ send_or_enqueue_ack(MsgSeqNo, end, QTM) } end); send_or_enqueue_ack(MsgSeqNo, State = #ch{confirm_multiple = true}) -> - rabbit_log:info("handling confirm in multiple mode (#~p)~n", [MsgSeqNo]), do_if_not_dup(MsgSeqNo, State, fun(MSN, S = #ch{qpid_to_msgs = QTM}) -> State1 = start_ack_timer(S), -- cgit v1.2.1 From ce2d00203a0d3e32b368b5f3030232785f581c4c Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Thu, 19 Aug 2010 14:04:23 +0100 Subject: don't do any unnecessary work when publishing in confirm mode --- src/rabbit_router.erl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/rabbit_router.erl b/src/rabbit_router.erl index 179c45bb..3d270916 100644 --- a/src/rabbit_router.erl +++ b/src/rabbit_router.erl @@ -66,11 +66,12 @@ deliver(QPids, Delivery = #delivery{mandatory = false, delegate:invoke_no_result( QPids, fun (Pid) -> rabbit_amqqueue:deliver(Pid, Delivery) end), case {QPids, MsgSeqNo} of - {[], _} -> + {_, undefined} -> ok; + {[], _} -> %% No queues will get the message. This is fine, so we %% just confirm it. rabbit_channel:confirm(self(), MsgSeqNo); - _ -> ok + _ -> ok end, maybe_inform_channel(MsgSeqNo, QPids), {routed, QPids}; @@ -129,7 +130,7 @@ check_delivery(true, _ , {false, []}) -> {unroutable, []}; check_delivery(_ , true, {_ , []}) -> {not_delivered, []}; check_delivery(_ , _ , {_ , Qs}) -> {routed, Qs}. -maybe_inform_channel(undefine, _) -> +maybe_inform_channel(undefined, _) -> ok; maybe_inform_channel(MsgSeqNo, QPids) -> gen_server2:cast(self(), {msg_sent_to_queues, MsgSeqNo, QPids}). -- cgit v1.2.1 From 3d9e6f81ef3851f91d6d52d5b787a737e0eba814 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Thu, 19 Aug 2010 15:35:39 +0100 Subject: refactoring + don't do useless work --- src/rabbit_amqqueue_process.erl | 11 +++++++---- src/rabbit_msg_store.erl | 9 ++++----- src/rabbit_variable_queue.erl | 21 ++++++++------------- 3 files changed, 19 insertions(+), 22 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 8979062b..9521158a 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -330,7 +330,7 @@ record_current_channel_tx(ChPid, Txn) -> %% that wasn't happening already) store_ch_record((ch_record(ChPid))#cr{txn = Txn}). -deliver_msgs_to_consumers(Funs = {PredFun, DeliverFun }, FunAcc, +deliver_msgs_to_consumers(Funs = {PredFun, DeliverFun}, FunAcc, State = #q{q = #amqqueue{name = QName}, active_consumers = ActiveConsumers, blocked_consumers = BlockedConsumers}) -> @@ -353,9 +353,9 @@ deliver_msgs_to_consumers(Funs = {PredFun, DeliverFun }, FunAcc, {State2, ChAckTags1} = case AckRequired of true -> {State1, sets:add_element(AckTag, ChAckTags)}; - false -> - {confirm_message_internal(Message#basic_message.guid, - State1), ChAckTags} + false -> {confirm_message_internal( + Message#basic_message.guid, + State1), ChAckTags} end, NewC = C#cr{unsent_message_count = Count + 1, acktags = ChAckTags1}, @@ -410,6 +410,8 @@ confirm_message_internal(Guid, State = #q { guid_to_channel = GTC, msgs_on_disk = MOD, msg_indices_on_disk = MIOD }) -> case dict:find(Guid, GTC) of + {ok, {_, undefined}} -> + State; {ok, {ChPid, MsgSeqNo}} -> rabbit_channel:confirm(ChPid, MsgSeqNo), State #q { guid_to_channel = dict:erase(Guid, GTC), @@ -851,6 +853,7 @@ handle_cast({ack, Txn, AckTags, ChPid}, noreply(State#q{backing_queue_state = BQS1}) end; +%% Called when variable queue gets ack from a consumer. handle_cast({confirm_messages, Guids}, State) -> noreply(lists:foldl(fun (Guid, State0) -> confirm_message_internal(Guid, State0) diff --git a/src/rabbit_msg_store.erl b/src/rabbit_msg_store.erl index 8fe02939..445aff48 100644 --- a/src/rabbit_msg_store.erl +++ b/src/rabbit_msg_store.erl @@ -36,7 +36,7 @@ -export([start_link/4, write/4, read/3, contains/2, remove/2, release/2, sync/3, client_init/2, client_terminate/1, client_delete_and_terminate/3, successfully_recovered_state/1, - register_callback/2]). + register_sync_callback/2]). -export([sync/1, gc_done/4, set_maximum_since_use/2, gc/3]). %% internal @@ -388,8 +388,8 @@ client_delete_and_terminate(CState, Server, Ref) -> successfully_recovered_state(Server) -> gen_server2:call(Server, successfully_recovered_state, infinity). -register_callback(Server, Fun) -> - gen_server2:call(Server, {register_callback, Fun}, infinity). +register_sync_callback(Server, Fun) -> + gen_server2:call(Server, {register_sync_callback, Fun}, infinity). %%---------------------------------------------------------------------------- %% Client-side-only helpers @@ -611,9 +611,8 @@ handle_call({delete_client, CRef}, _From, reply(ok, State #msstate { client_refs = sets:del_element(CRef, ClientRefs) }); -handle_call({register_callback, Fun}, {Pid, _}, +handle_call({register_sync_callback, Fun}, {Pid, _}, State = #msstate { pid_to_fun = PTF }) -> - rabbit_log:info("Registering callback ~p~n", [{Pid, Fun}]), erlang:monitor(process, Pid), reply(ok, State #msstate { pid_to_fun = dict:store(Pid, Fun, PTF) }). diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 454193c2..71638345 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -402,7 +402,14 @@ init(QueueName, IsDurable, Recover) -> true -> rabbit_msg_store:client_init(?PERSISTENT_MSG_STORE, PRef); false -> undefined end, - register_confirm_callback(?PERSISTENT_MSG_STORE), + + Self = self(), + rabbit_msg_store:register_sync_callback( + ?PERSISTENT_MSG_STORE, + fun (Guids) -> + gen_server2:cast(Self, {msgs_written_to_disk, Guids}) + end), + TransientClient = rabbit_msg_store:client_init(?TRANSIENT_MSG_STORE, TRef), State = #vqstate { q1 = queue:new(), @@ -1072,18 +1079,6 @@ maybe_write_to_disk(ForceMsg, ForceIndex, MsgStatus, {MsgStatus2, State #vqstate { index_state = IndexState1, msg_store_clients = MSCState1 }}. -%%---------------------------------------------------------------------------- -%% Internal gubbins for confirms -%%---------------------------------------------------------------------------- - -register_confirm_callback(MessageStore) -> - Self = self(), - rabbit_msg_store:register_callback( - MessageStore, - fun (Guids) -> - gen_server2:cast(Self, {msgs_written_to_disk, Guids}) - end). - %%---------------------------------------------------------------------------- %% Internal gubbins for acks %%---------------------------------------------------------------------------- -- cgit v1.2.1 From dfcf5f5707cab504b988aaffaefdce8cfa4202a6 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Thu, 19 Aug 2010 17:20:09 +0100 Subject: fixed an ever-expanding dictionary The problem with the current design is that the lowest layer that knows about confirmations is amqqueue_process. This means that amqqueue_process, variable_queue, msg_store and queue_index need to do all the bookkeeping associated with confirmations even if they're not enabled. Mind you, this doesn't slow things down noticeably. --- src/rabbit_amqqueue_process.erl | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 9521158a..0abd2108 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -410,16 +410,13 @@ confirm_message_internal(Guid, State = #q { guid_to_channel = GTC, msgs_on_disk = MOD, msg_indices_on_disk = MIOD }) -> case dict:find(Guid, GTC) of - {ok, {_, undefined}} -> - State; - {ok, {ChPid, MsgSeqNo}} -> - rabbit_channel:confirm(ChPid, MsgSeqNo), - State #q { guid_to_channel = dict:erase(Guid, GTC), - msgs_on_disk = gb_sets:delete_any(Guid, MOD), - msg_indices_on_disk = gb_sets:delete_any(Guid, MIOD) }; - _ -> - State - end. + {ok, {ChPid, undefined}} -> ok; + {ok, {ChPid, MsgSeqNo}} -> rabbit_channel:confirm(ChPid, MsgSeqNo); + _ -> ok + end, + State #q { guid_to_channel = dict:erase(Guid, GTC), + msgs_on_disk = gb_sets:delete_any(Guid, MOD), + msg_indices_on_disk = gb_sets:delete_any(Guid, MIOD) }. maybe_record_confirm_message(undefined, _, _, State) -> State; -- cgit v1.2.1 From 57f5c9e120c9a4ab02b9e98ab04d2dc1a571e204 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Fri, 20 Aug 2010 10:12:19 +0100 Subject: stopped a race --- src/rabbit_amqqueue_process.erl | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 0abd2108..15939802 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -410,7 +410,7 @@ confirm_message_internal(Guid, State = #q { guid_to_channel = GTC, msgs_on_disk = MOD, msg_indices_on_disk = MIOD }) -> case dict:find(Guid, GTC) of - {ok, {ChPid, undefined}} -> ok; + {ok, {_ , undefined}} -> ok; {ok, {ChPid, MsgSeqNo}} -> rabbit_channel:confirm(ChPid, MsgSeqNo); _ -> ok end, @@ -857,9 +857,13 @@ handle_cast({confirm_messages, Guids}, State) -> end, State, Guids)); handle_cast({msgs_written_to_disk, Guids}, - State = #q{msgs_on_disk = MOD, + State = #q{guid_to_channel = GTC, + msgs_on_disk = MOD, msg_indices_on_disk = MIOD}) -> - GuidSet = gb_sets:from_list(Guids), + GuidSet = gb_sets:from_list( + lists:filter(fun(Guid) -> + dict:is_key(Guid, GTC) + end, Guids)), ToConfirmMsgs = gb_sets:intersection(GuidSet, MIOD), gb_sets:fold(fun (Guid, State0) -> confirm_message_internal(Guid, State0) @@ -871,9 +875,13 @@ handle_cast({msgs_written_to_disk, Guids}, gb_sets:difference(MIOD, ToConfirmMsgs)}); handle_cast({msg_indices_written_to_disk, Guids}, - State = #q{msgs_on_disk = MOD, + State = #q{guid_to_channel = GTC, + msgs_on_disk = MOD, msg_indices_on_disk = MIOD}) -> - GuidSet = gb_sets:from_list(Guids), + GuidSet = gb_sets:from_list( + lists:filter(fun(Guid) -> + dict:is_key(Guid, GTC) + end, Guids)), ToConfirmMsgs = gb_sets:intersection(GuidSet, MOD), gb_sets:fold(fun (Guid, State0) -> confirm_message_internal(Guid, State0) -- cgit v1.2.1 From 75b0d91d33da67918cb4a681812b8e91abb01358 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Wed, 25 Aug 2010 21:11:28 +0100 Subject: solve a warning --- 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 f305194a..ae49b301 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -287,7 +287,7 @@ handle_info({'EXIT', WriterPid, Reason = {writer, send_failed, _Error}}, {stop, normal, State}; handle_info({'EXIT', _Pid, Reason}, State) -> {stop, Reason, State}; -handle_info({'DOWN', _MRef, process, QPid, Reason}, +handle_info({'DOWN', _MRef, process, QPid, _Reason}, State = #ch{qpid_to_msgs = QTM}) -> case dict:find(QPid, QTM) of {ok, Msgs} -> -- cgit v1.2.1 From e9a20ee2f315b05921ea1021b8142b659b061e00 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Tue, 31 Aug 2010 10:35:46 +0100 Subject: fixed message leak caused by confirms I've added variable_queue:init/4. It calls variable_queue:init/3 and the fourth parameter is a tuple of 3 functions: the function to call when the indices of some guids hit the disk, the function to call when some guids hit the disk and the function to call when some guids have been ack'd by the consumer. Rabbit_test supplies nops rather than the usual casts, so the superfluous messages aren't generated. --- src/rabbit_tests.erl | 14 ++++++++++---- src/rabbit_variable_queue.erl | 38 +++++++++++++++++++++++++------------- 2 files changed, 35 insertions(+), 17 deletions(-) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index d783c43e..fcc3d92c 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -1801,7 +1801,8 @@ assert_props(List, PropVals) -> with_fresh_variable_queue(Fun) -> ok = empty_test_queue(), - VQ = rabbit_variable_queue:init(test_queue(), true, false), + VQ = rabbit_variable_queue:init(test_queue(), true, false, + {fun nop/1, fun nop/1, fun nop/1}), S0 = rabbit_variable_queue:status(VQ), assert_props(S0, [{q1, 0}, {q2, 0}, {delta, {delta, undefined, 0, undefined}}, @@ -1913,7 +1914,8 @@ test_variable_queue_all_the_bits_not_covered_elsewhere1(VQ0) -> {VQ5, _AckTags1} = variable_queue_fetch(Count, false, false, Count, VQ4), _VQ6 = rabbit_variable_queue:terminate(VQ5), - VQ7 = rabbit_variable_queue:init(test_queue(), true, true), + VQ7 = rabbit_variable_queue:init(test_queue(), true, true, + {fun nop/1, fun nop/1, fun nop/1}), {{_Msg1, true, _AckTag1, Count1}, VQ8} = rabbit_variable_queue:fetch(true, VQ7), VQ9 = variable_queue_publish(false, 1, VQ8), @@ -1929,7 +1931,8 @@ test_variable_queue_all_the_bits_not_covered_elsewhere2(VQ0) -> VQ4 = rabbit_variable_queue:requeue(AckTags, VQ3), VQ5 = rabbit_variable_queue:idle_timeout(VQ4), _VQ6 = rabbit_variable_queue:terminate(VQ5), - VQ7 = rabbit_variable_queue:init(test_queue(), true, true), + VQ7 = rabbit_variable_queue:init(test_queue(), true, true, + {fun nop/1, fun nop/1, fun nop/1}), {empty, VQ8} = rabbit_variable_queue:fetch(false, VQ7), VQ8. @@ -1959,10 +1962,13 @@ test_queue_recover() -> {ok, CountMinusOne, {QName, QPid1, _AckTag, true, _Msg}} = rabbit_amqqueue:basic_get(Q1, self(), false), exit(QPid1, shutdown), - VQ1 = rabbit_variable_queue:init(QName, true, true), + VQ1 = rabbit_variable_queue:init(QName, true, true, + {fun nop/1, fun nop/1, fun nop/1}), {{_Msg1, true, _AckTag1, CountMinusOne}, VQ2} = rabbit_variable_queue:fetch(true, VQ1), _VQ3 = rabbit_variable_queue:delete_and_terminate(VQ2), rabbit_amqqueue:internal_delete(QName) end), passed. + +nop(_) -> ok. diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 71638345..0b0cec46 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -31,7 +31,7 @@ -module(rabbit_variable_queue). --export([init/3, terminate/1, delete_and_terminate/1, +-export([init/3, init/4, terminate/1, delete_and_terminate/1, purge/1, publish/2, publish_delivered/3, fetch/2, ack/2, tx_publish/3, tx_ack/3, tx_rollback/2, tx_commit/3, requeue/2, len/1, is_empty/1, @@ -236,7 +236,8 @@ ram_index_count, out_counter, in_counter, - rates + rates, + confirm_functions }). -record(rates, { egress, ingress, avg_egress, avg_ingress, timestamp }). @@ -322,7 +323,8 @@ ram_index_count :: non_neg_integer(), out_counter :: non_neg_integer(), in_counter :: non_neg_integer(), - rates :: rates() }). + rates :: rates(), + confirm_functions :: {any(), any(), any()} }). -include("rabbit_backing_queue_spec.hrl"). @@ -370,6 +372,19 @@ stop_msg_store() -> init(QueueName, IsDurable, Recover) -> Self = self(), + init(QueueName, IsDurable, Recover, + { fun(Guids) -> %% index-on-disk fun + gen_server2:cast(Self, + {msg_indices_written_to_disk, Guids}) + end, + fun (Guids) -> %% msg-on-disk fun + gen_server2:cast(Self, {msgs_written_to_disk, Guids}) + end, + fun (Guids) -> %% ack-received fun + gen_server2:cast(Self, {confirm_messages, Guids}) + end }). + +init(QueueName, IsDurable, Recover, {IndicesOnDisk, MsgsOnDisk, _} = CF) -> {DeltaCount, Terms, IndexState} = rabbit_queue_index:init( QueueName, Recover, @@ -377,9 +392,7 @@ init(QueueName, IsDurable, Recover) -> fun (Guid) -> rabbit_msg_store:contains(?PERSISTENT_MSG_STORE, Guid) end, - fun (Guids) -> - gen_server2:cast(Self, {msg_indices_written_to_disk, Guids}) - end), + IndicesOnDisk), {LowSeqId, NextSeqId, IndexState1} = rabbit_queue_index:bounds(IndexState), {PRef, TRef, Terms1} = @@ -403,12 +416,9 @@ init(QueueName, IsDurable, Recover) -> false -> undefined end, - Self = self(), rabbit_msg_store:register_sync_callback( ?PERSISTENT_MSG_STORE, - fun (Guids) -> - gen_server2:cast(Self, {msgs_written_to_disk, Guids}) - end), + MsgsOnDisk), TransientClient = rabbit_msg_store:client_init(?TRANSIENT_MSG_STORE, TRef), State = #vqstate { @@ -440,7 +450,8 @@ init(QueueName, IsDurable, Recover) -> ingress = {Now, DeltaCount1}, avg_egress = 0.0, avg_ingress = 0.0, - timestamp = Now } }, + timestamp = Now }, + confirm_functions = CF}, a(maybe_deltas_to_betas(State)). terminate(State) -> @@ -1115,7 +1126,8 @@ remove_pending_ack(KeepPersistent, ack(_MsgStoreFun, _Fun, [], State) -> State; -ack(MsgStoreFun, Fun, AckTags, State) -> +ack(MsgStoreFun, Fun, AckTags, + State = #vqstate { confirm_functions = {_, _, AcksReceived} }) -> {{SeqIds, GuidsByStore}, State1 = #vqstate { index_state = IndexState, persistent_count = PCount }} = lists:foldl( @@ -1127,7 +1139,7 @@ ack(MsgStoreFun, Fun, AckTags, State) -> end, {{[], orddict:new()}, State}, AckTags), IndexState1 = rabbit_queue_index:ack(SeqIds, IndexState), ok = orddict:fold(fun (MsgStore, Guids, ok) -> - gen_server2:cast(self(), {confirm_messages, Guids}), + AcksReceived(Guids), MsgStoreFun(MsgStore, Guids) end, ok, GuidsByStore), PCount1 = PCount - case orddict:find(?PERSISTENT_MSG_STORE, GuidsByStore) of -- cgit v1.2.1 From 86aaf376cd5717b924a9bb4cf9b0a13444d8146e Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Tue, 31 Aug 2010 11:56:22 +0100 Subject: pending acks are sent out when the channel becomes idle or every 1s --- src/rabbit_channel.erl | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 0964700c..bebb59a6 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -70,7 +70,7 @@ -define(INFO_KEYS, ?CREATION_EVENT_KEYS ++ ?STATISTICS_KEYS -- [pid]). --define(MULTIPLE_ACK_FLUSH_INTERVAL, 5000). +-define(MULTIPLE_ACK_FLUSH_INTERVAL, 1000). %%---------------------------------------------------------------------------- @@ -266,13 +266,7 @@ handle_cast(multiple_ack_flush, State = #ch{writer_pid = WriterPid, held_confirms = As, need_confirming = NA}) -> - case gb_sets:is_empty(As) of - true -> ok; % this should never be the case - false -> flush_multiple(As, WriterPid, case gb_sets:is_empty(NA) of - false -> gb_sets:smallest(NA); - true -> gb_sets:largest(As)+1 - end) - end, + handle_multiple_flush(WriterPid, As, NA), {noreply, State #ch { held_confirms = gb_sets:new(), confirm_tref = undefined }}; @@ -299,9 +293,14 @@ handle_info({'DOWN', _MRef, process, QPid, _Reason}, erase_queue_stats(QPid), {noreply, queue_blocked(QPid, State1)}. -handle_pre_hibernate(State) -> +handle_pre_hibernate(State = #ch { writer_pid = WriterPid, + held_confirms = As, + need_confirming = NA }) -> ok = clear_permission_cache(), - {hibernate, stop_stats_timer(State)}. + handle_multiple_flush(WriterPid, As, NA), + {hibernate, stop_stats_timer( + State #ch { held_confirms = gb_sets:new(), + confirm_tref = undefined })}. terminate(_Reason, State = #ch{state = terminating}) -> terminate(State); @@ -1354,6 +1353,16 @@ stop_ack_timer(State = #ch{confirm_tref = TRef}) -> {ok, cancel} = timer:cancel(TRef), State #ch { confirm_tref = undefined }. +handle_multiple_flush(WriterPid, As, NA) -> + case gb_sets:is_empty(As) of + true -> ok; + false -> flush_multiple(As, WriterPid, case gb_sets:is_empty(NA) of + false -> gb_sets:smallest(NA); + true -> gb_sets:largest(As)+1 + end) + end. + + flush_multiple(Acks, WriterPid, SmallestNotAcked) -> [First | Rest] = gb_sets:to_list(Acks), Remaining = case Rest of -- cgit v1.2.1 From b8f85f578441d3cec72a9255aa6a64bb3ef6bacd Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Thu, 2 Sep 2010 18:14:46 +0100 Subject: moved most of the confirm logic from amqqueue_process to variable_queue --- src/rabbit_amqqueue_process.erl | 68 ++++++----------------------- src/rabbit_variable_queue.erl | 94 ++++++++++++++++++++++++++++++----------- 2 files changed, 83 insertions(+), 79 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index ce5c8162..3df59de6 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -61,9 +61,7 @@ rate_timer_ref, expiry_timer_ref, stats_timer, - guid_to_channel, - msgs_on_disk, - msg_indices_on_disk + guid_to_channel }). -record(consumer, {tag, ack_required}). @@ -126,9 +124,7 @@ init(Q) -> rate_timer_ref = undefined, expiry_timer_ref = undefined, stats_timer = rabbit_event:init_stats_timer(), - guid_to_channel = dict:new(), - msgs_on_disk = gb_sets:new(), - msg_indices_on_disk = gb_sets:new()}, hibernate, + guid_to_channel = dict:new()}, hibernate, {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}. terminate(shutdown, State = #q{backing_queue = BQ}) -> @@ -406,17 +402,13 @@ deliver_from_queue_deliver(AckRequired, false, {{Message, IsDelivered, AckTag}, 0 == Remaining, State #q { backing_queue_state = BQS1 }}. -confirm_message_internal(Guid, State = #q { guid_to_channel = GTC, - msgs_on_disk = MOD, - msg_indices_on_disk = MIOD }) -> +confirm_message_internal(Guid, State = #q { guid_to_channel = GTC }) -> case dict:find(Guid, GTC) of {ok, {_ , undefined}} -> ok; {ok, {ChPid, MsgSeqNo}} -> rabbit_channel:confirm(ChPid, MsgSeqNo); _ -> ok end, - State #q { guid_to_channel = dict:erase(Guid, GTC), - msgs_on_disk = gb_sets:delete_any(Guid, MOD), - msg_indices_on_disk = gb_sets:delete_any(Guid, MIOD) }. + State #q { guid_to_channel = dict:erase(Guid, GTC) }. maybe_record_confirm_message(undefined, _, _, State) -> State; @@ -553,7 +545,15 @@ maybe_send_reply(ChPid, Msg) -> ok = rabbit_channel:send_command(ChPid, Msg). qname(#q{q = #amqqueue{name = QName}}) -> QName. maybe_run_queue_via_backing_queue(Fun, State = #q{backing_queue_state = BQS}) -> - run_message_queue(State#q{backing_queue_state = Fun(BQS)}). + case Fun(BQS) of + {BQS1, {confirm, Guids}} -> + State1 = lists:foldl(fun (Guid, State0) -> + confirm_message_internal(Guid, State0) end, + State, Guids), + State1 #q { backing_queue_state = BQS1}; + BQS1 -> + run_message_queue(State#q{backing_queue_state = BQS1}) + end. commit_transaction(Txn, From, ChPid, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> @@ -851,48 +851,6 @@ handle_cast({ack, Txn, AckTags, ChPid}, noreply(State#q{backing_queue_state = BQS1}) end; -%% Called when variable queue gets ack from a consumer. -handle_cast({confirm_messages, Guids}, State) -> - noreply(lists:foldl(fun (Guid, State0) -> - confirm_message_internal(Guid, State0) - end, State, Guids)); - -handle_cast({msgs_written_to_disk, Guids}, - State = #q{guid_to_channel = GTC, - msgs_on_disk = MOD, - msg_indices_on_disk = MIOD}) -> - GuidSet = gb_sets:from_list( - lists:filter(fun(Guid) -> - dict:is_key(Guid, GTC) - end, Guids)), - ToConfirmMsgs = gb_sets:intersection(GuidSet, MIOD), - gb_sets:fold(fun (Guid, State0) -> - confirm_message_internal(Guid, State0) - end, State, ToConfirmMsgs), - noreply(State#q{msgs_on_disk = - gb_sets:difference(gb_sets:union(MOD, GuidSet), - ToConfirmMsgs), - msg_indices_on_disk = - gb_sets:difference(MIOD, ToConfirmMsgs)}); - -handle_cast({msg_indices_written_to_disk, Guids}, - State = #q{guid_to_channel = GTC, - msgs_on_disk = MOD, - msg_indices_on_disk = MIOD}) -> - GuidSet = gb_sets:from_list( - lists:filter(fun(Guid) -> - dict:is_key(Guid, GTC) - end, Guids)), - ToConfirmMsgs = gb_sets:intersection(GuidSet, MOD), - gb_sets:fold(fun (Guid, State0) -> - confirm_message_internal(Guid, State0) - end, State, ToConfirmMsgs), - noreply(State#q{msgs_on_disk = - gb_sets:difference(MOD, ToConfirmMsgs), - msg_indices_on_disk = - gb_sets:difference(gb_sets:union(MIOD, GuidSet), - ToConfirmMsgs)}); - handle_cast({reject, AckTags, Requeue, ChPid}, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> case lookup_ch(ChPid) of diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 0b0cec46..448e8c93 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -31,7 +31,7 @@ -module(rabbit_variable_queue). --export([init/3, init/4, terminate/1, delete_and_terminate/1, +-export([init/3, terminate/1, delete_and_terminate/1, purge/1, publish/2, publish_delivered/3, fetch/2, ack/2, tx_publish/3, tx_ack/3, tx_rollback/2, tx_commit/3, requeue/2, len/1, is_empty/1, @@ -237,8 +237,9 @@ out_counter, in_counter, rates, - confirm_functions - }). + msgs_on_disk, + msg_indices_on_disk + }). -record(rates, { egress, ingress, avg_egress, avg_ingress, timestamp }). @@ -324,7 +325,8 @@ out_counter :: non_neg_integer(), in_counter :: non_neg_integer(), rates :: rates(), - confirm_functions :: {any(), any(), any()} }). + msgs_on_disk :: gb_set(), + msg_indices_on_disk :: gb_set()}). -include("rabbit_backing_queue_spec.hrl"). @@ -371,20 +373,6 @@ stop_msg_store() -> ok = rabbit_sup:stop_child(?TRANSIENT_MSG_STORE). init(QueueName, IsDurable, Recover) -> - Self = self(), - init(QueueName, IsDurable, Recover, - { fun(Guids) -> %% index-on-disk fun - gen_server2:cast(Self, - {msg_indices_written_to_disk, Guids}) - end, - fun (Guids) -> %% msg-on-disk fun - gen_server2:cast(Self, {msgs_written_to_disk, Guids}) - end, - fun (Guids) -> %% ack-received fun - gen_server2:cast(Self, {confirm_messages, Guids}) - end }). - -init(QueueName, IsDurable, Recover, {IndicesOnDisk, MsgsOnDisk, _} = CF) -> {DeltaCount, Terms, IndexState} = rabbit_queue_index:init( QueueName, Recover, @@ -392,7 +380,7 @@ init(QueueName, IsDurable, Recover, {IndicesOnDisk, MsgsOnDisk, _} = CF) -> fun (Guid) -> rabbit_msg_store:contains(?PERSISTENT_MSG_STORE, Guid) end, - IndicesOnDisk), + fun msg_indices_written_to_disk/1), {LowSeqId, NextSeqId, IndexState1} = rabbit_queue_index:bounds(IndexState), {PRef, TRef, Terms1} = @@ -416,9 +404,12 @@ init(QueueName, IsDurable, Recover, {IndicesOnDisk, MsgsOnDisk, _} = CF) -> false -> undefined end, + Self = self(), rabbit_msg_store:register_sync_callback( ?PERSISTENT_MSG_STORE, - MsgsOnDisk), + fun (Guids) -> + msgs_written_to_disk(Self, Guids) + end), TransientClient = rabbit_msg_store:client_init(?TRANSIENT_MSG_STORE, TRef), State = #vqstate { @@ -451,7 +442,8 @@ init(QueueName, IsDurable, Recover, {IndicesOnDisk, MsgsOnDisk, _} = CF) -> avg_egress = 0.0, avg_ingress = 0.0, timestamp = Now }, - confirm_functions = CF}, + msgs_on_disk = gb_sets:new(), + msg_indices_on_disk = gb_sets:new()}, a(maybe_deltas_to_betas(State)). terminate(State) -> @@ -1126,8 +1118,7 @@ remove_pending_ack(KeepPersistent, ack(_MsgStoreFun, _Fun, [], State) -> State; -ack(MsgStoreFun, Fun, AckTags, - State = #vqstate { confirm_functions = {_, _, AcksReceived} }) -> +ack(MsgStoreFun, Fun, AckTags, State) -> {{SeqIds, GuidsByStore}, State1 = #vqstate { index_state = IndexState, persistent_count = PCount }} = lists:foldl( @@ -1139,7 +1130,7 @@ ack(MsgStoreFun, Fun, AckTags, end, {{[], orddict:new()}, State}, AckTags), IndexState1 = rabbit_queue_index:ack(SeqIds, IndexState), ok = orddict:fold(fun (MsgStore, Guids, ok) -> - AcksReceived(Guids), + confirm_messages(Guids), MsgStoreFun(MsgStore, Guids) end, ok, GuidsByStore), PCount1 = PCount - case orddict:find(?PERSISTENT_MSG_STORE, GuidsByStore) of @@ -1157,6 +1148,61 @@ accumulate_ack(SeqId, {IsPersistent, Guid}, {SeqIdsAcc, Dict}) -> {cons_if(IsPersistent, SeqId, SeqIdsAcc), rabbit_misc:orddict_cons(find_msg_store(IsPersistent), Guid, Dict)}. + +%%---------------------------------------------------------------------------- +%% Internal plumbing for confirms (aka publisher acks) +%%---------------------------------------------------------------------------- + +confirm_messages(Guids) -> + Self = self(), + spawn(fun() -> rabbit_amqqueue:maybe_run_queue_via_backing_queue( + Self, + fun (State = #vqstate { msgs_on_disk = MOD, + msg_indices_on_disk = MIOD }) -> + { State #vqstate { + msgs_on_disk = + gb_sets:difference(MOD, gb_sets:from_list(Guids)), + msg_indices_on_disk = + gb_sets:delete_any(MIOD, gb_sets:from_list(Guids)) }, + {confirm, Guids} } + end) + end). + +msgs_written_to_disk(QPid, Guids) -> + spawn(fun() -> rabbit_amqqueue:maybe_run_queue_via_backing_queue( + QPid, + fun(State = #vqstate { msgs_on_disk = MOD, + msg_indices_on_disk = MIOD}) -> + GuidSet = gb_sets:from_list(Guids), + ToConfirmMsgs = gb_sets:intersection(GuidSet, MIOD), + { State #vqstate { + msgs_on_disk = + gb_sets:difference(gb_sets:union(MOD, GuidSet), + ToConfirmMsgs), + msg_indices_on_disk = + gb_sets:difference(MIOD, ToConfirmMsgs) }, + {confirm, gb_sets:to_list(ToConfirmMsgs)} } + end) + end). + +msg_indices_written_to_disk(Guids) -> + Self = self(), + spawn(fun() -> rabbit_amqqueue:maybe_run_queue_via_backing_queue( + Self, + fun(State = #vqstate { msgs_on_disk = MOD, + msg_indices_on_disk = MIOD}) -> + GuidSet = gb_sets:from_list(Guids), + ToConfirmMsgs = gb_sets:intersection(GuidSet, MOD), + { State #vqstate { + msgs_on_disk = + gb_sets:difference(MOD, ToConfirmMsgs), + msg_indices_on_disk = + gb_sets:difference(gb_sets:union(MIOD, GuidSet), + ToConfirmMsgs) }, + {confirm, gb_sets:to_list(ToConfirmMsgs)} } + end) + end). + %%---------------------------------------------------------------------------- %% Phase changes %%---------------------------------------------------------------------------- -- cgit v1.2.1 From d4fe1b9276f57b078df0287a25d4f57bd6e23387 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Fri, 3 Sep 2010 10:36:22 +0100 Subject: when a consumer ack is received, the publisher ack is sent by the amqqueue_process, rather than the backing_queue --- src/rabbit_amqqueue_process.erl | 34 ++++++++++++++++++++++----------- src/rabbit_tests.erl | 12 ++++-------- src/rabbit_variable_queue.erl | 42 ++++++++++++++++++++++------------------- 3 files changed, 50 insertions(+), 38 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 3df59de6..6eeb397c 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -402,6 +402,11 @@ deliver_from_queue_deliver(AckRequired, false, {{Message, IsDelivered, AckTag}, 0 == Remaining, State #q { backing_queue_state = BQS1 }}. +confirm_messages_internal(Guids, State) -> + lists:foldl(fun(Guid, State0) -> + confirm_message_internal(Guid, State0) + end, State, Guids). + confirm_message_internal(Guid, State = #q { guid_to_channel = GTC }) -> case dict:find(Guid, GTC) of {ok, {_ , undefined}} -> ok; @@ -547,10 +552,8 @@ qname(#q{q = #amqqueue{name = QName}}) -> QName. maybe_run_queue_via_backing_queue(Fun, State = #q{backing_queue_state = BQS}) -> case Fun(BQS) of {BQS1, {confirm, Guids}} -> - State1 = lists:foldl(fun (Guid, State0) -> - confirm_message_internal(Guid, State0) end, - State, Guids), - State1 #q { backing_queue_state = BQS1}; + confirm_messages_internal(Guids, + State #q { backing_queue_state = BQS1 }); BQS1 -> run_message_queue(State#q{backing_queue_state = BQS1}) end. @@ -841,14 +844,22 @@ handle_cast({ack, Txn, AckTags, ChPid}, not_found -> noreply(State); C = #cr{acktags = ChAckTags} -> - {C1, BQS1} = + {C1, State1} = case Txn of - none -> ChAckTags1 = subtract_acks(ChAckTags, AckTags), - {C#cr{acktags = ChAckTags1}, BQ:ack(AckTags, BQS)}; - _ -> {C#cr{txn = Txn}, BQ:tx_ack(Txn, AckTags, BQS)} + none -> + ChAckTags1 = subtract_acks(ChAckTags, AckTags), + NewC = C#cr{acktags = ChAckTags1}, + {NewBQS, AckdGuids} = BQ:ack(AckTags, BQS), + NewState = + confirm_messages_internal(AckdGuids, + State #q { backing_queue_state = NewBQS }), + {NewC, NewState}; + _ -> + {C#cr{txn = Txn}, + State #q { backing_queue_state = BQ:tx_ack(Txn, AckTags, BQS) }} end, store_ch_record(C1), - noreply(State#q{backing_queue_state = BQS1}) + noreply(State1) end; handle_cast({reject, AckTags, Requeue, ChPid}, @@ -861,8 +872,9 @@ handle_cast({reject, AckTags, Requeue, ChPid}, store_ch_record(C#cr{acktags = ChAckTags1}), noreply(case Requeue of true -> requeue_and_run(AckTags, State); - false -> BQS1 = BQ:ack(AckTags, BQS), - State #q { backing_queue_state = BQS1 } + false -> {BQS1, AckdGuids} = BQ:ack(AckTags, BQS), + confirm_messages_internal(AckdGuids, + State #q { backing_queue_state = BQS1 }) end) end; diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index fcc3d92c..c8af7c10 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -1801,8 +1801,7 @@ assert_props(List, PropVals) -> with_fresh_variable_queue(Fun) -> ok = empty_test_queue(), - VQ = rabbit_variable_queue:init(test_queue(), true, false, - {fun nop/1, fun nop/1, fun nop/1}), + VQ = rabbit_variable_queue:init(test_queue(), true, false), S0 = rabbit_variable_queue:status(VQ), assert_props(S0, [{q1, 0}, {q2, 0}, {delta, {delta, undefined, 0, undefined}}, @@ -1914,8 +1913,7 @@ test_variable_queue_all_the_bits_not_covered_elsewhere1(VQ0) -> {VQ5, _AckTags1} = variable_queue_fetch(Count, false, false, Count, VQ4), _VQ6 = rabbit_variable_queue:terminate(VQ5), - VQ7 = rabbit_variable_queue:init(test_queue(), true, true, - {fun nop/1, fun nop/1, fun nop/1}), + VQ7 = rabbit_variable_queue:init(test_queue(), true, true), {{_Msg1, true, _AckTag1, Count1}, VQ8} = rabbit_variable_queue:fetch(true, VQ7), VQ9 = variable_queue_publish(false, 1, VQ8), @@ -1931,8 +1929,7 @@ test_variable_queue_all_the_bits_not_covered_elsewhere2(VQ0) -> VQ4 = rabbit_variable_queue:requeue(AckTags, VQ3), VQ5 = rabbit_variable_queue:idle_timeout(VQ4), _VQ6 = rabbit_variable_queue:terminate(VQ5), - VQ7 = rabbit_variable_queue:init(test_queue(), true, true, - {fun nop/1, fun nop/1, fun nop/1}), + VQ7 = rabbit_variable_queue:init(test_queue(), true, true), {empty, VQ8} = rabbit_variable_queue:fetch(false, VQ7), VQ8. @@ -1962,8 +1959,7 @@ test_queue_recover() -> {ok, CountMinusOne, {QName, QPid1, _AckTag, true, _Msg}} = rabbit_amqqueue:basic_get(Q1, self(), false), exit(QPid1, shutdown), - VQ1 = rabbit_variable_queue:init(QName, true, true, - {fun nop/1, fun nop/1, fun nop/1}), + VQ1 = rabbit_variable_queue:init(QName, true, true), {{_Msg1, true, _AckTag1, CountMinusOne}, VQ2} = rabbit_variable_queue:fetch(true, VQ1), _VQ3 = rabbit_variable_queue:delete_and_terminate(VQ2), diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 448e8c93..c33859ee 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -758,6 +758,10 @@ status(#vqstate { q1 = Q1, q2 = Q2, delta = Delta, q3 = Q3, q4 = Q4, %% Minor helpers %%---------------------------------------------------------------------------- +a({State, _} = I) -> + a(State), + I; + a(State = #vqstate { q1 = Q1, q2 = Q2, delta = Delta, q3 = Q3, q4 = Q4, len = Len, persistent_count = PersistentCount, @@ -966,7 +970,7 @@ tx_commit_index(State = #vqstate { on_sync = #sync { IsPersistent1 = IsDurable andalso IsPersistent, {SeqId, State3} = publish(Msg, false, IsPersistent1, State2), {cons_if(IsPersistent1, SeqId, SeqIdsAcc), State3} - end, {PAcks, ack(Acks, State)}, Pubs), + end, {PAcks, element(1, ack(Acks, State))}, Pubs), IndexState1 = rabbit_queue_index:sync(SeqIds, IndexState), [ Fun() || Fun <- lists:reverse(SFuns) ], reduce_memory_use( @@ -1117,7 +1121,7 @@ remove_pending_ack(KeepPersistent, end. ack(_MsgStoreFun, _Fun, [], State) -> - State; + {State, []}; ack(MsgStoreFun, Fun, AckTags, State) -> {{SeqIds, GuidsByStore}, State1 = #vqstate { index_state = IndexState, persistent_count = PCount }} = @@ -1130,15 +1134,19 @@ ack(MsgStoreFun, Fun, AckTags, State) -> end, {{[], orddict:new()}, State}, AckTags), IndexState1 = rabbit_queue_index:ack(SeqIds, IndexState), ok = orddict:fold(fun (MsgStore, Guids, ok) -> - confirm_messages(Guids), MsgStoreFun(MsgStore, Guids) end, ok, GuidsByStore), + AckdGuids = lists:append(orddict:fold(fun (_, Guids, Acc) -> + [Guids || Acc] + end, [], GuidsByStore)), + State2 = msgs_confirmed(AckdGuids, State1), PCount1 = PCount - case orddict:find(?PERSISTENT_MSG_STORE, GuidsByStore) of error -> 0; {ok, Guids} -> length(Guids) end, - State1 #vqstate { index_state = IndexState1, - persistent_count = PCount1 }. + {State2 #vqstate { index_state = IndexState1, + persistent_count = PCount1 }, + AckdGuids}. accumulate_ack(_SeqId, #msg_status { is_persistent = false, %% ASSERTIONS msg_on_disk = false, @@ -1153,20 +1161,13 @@ accumulate_ack(SeqId, {IsPersistent, Guid}, {SeqIdsAcc, Dict}) -> %% Internal plumbing for confirms (aka publisher acks) %%---------------------------------------------------------------------------- -confirm_messages(Guids) -> - Self = self(), - spawn(fun() -> rabbit_amqqueue:maybe_run_queue_via_backing_queue( - Self, - fun (State = #vqstate { msgs_on_disk = MOD, - msg_indices_on_disk = MIOD }) -> - { State #vqstate { - msgs_on_disk = - gb_sets:difference(MOD, gb_sets:from_list(Guids)), - msg_indices_on_disk = - gb_sets:delete_any(MIOD, gb_sets:from_list(Guids)) }, - {confirm, Guids} } - end) - end). +msgs_confirmed(Guids, State = #vqstate { msgs_on_disk = MOD, + msg_indices_on_disk = MIOD }) -> + State #vqstate { + msgs_on_disk = + gb_sets:difference(MOD, gb_sets:from_list(Guids)), + msg_indices_on_disk = + gb_sets:delete_any(MIOD, gb_sets:from_list(Guids)) }. msgs_written_to_disk(QPid, Guids) -> spawn(fun() -> rabbit_amqqueue:maybe_run_queue_via_backing_queue( @@ -1242,6 +1243,9 @@ reduce_memory_use(AlphaBetaFun, BetaGammaFun, BetaDeltaFun, State) -> end end. +reduce_memory_use({State, Other}) -> + {reduce_memory_use(State), Other}; + reduce_memory_use(State) -> {_, State1} = reduce_memory_use(fun push_alphas_to_betas/2, fun limit_ram_index/2, -- cgit v1.2.1 From a41ff26583a4a34df17cdb6bc880ebee99016d89 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Fri, 3 Sep 2010 10:45:27 +0100 Subject: moved confirm extension into the 0-9-1 json --- Makefile | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index ee3f198b..7ec8009e 100644 --- a/Makefile +++ b/Makefile @@ -57,7 +57,6 @@ SIBLING_CODEGEN_DIR=../rabbitmq-codegen/ AMQP_CODEGEN_DIR=$(shell [ -d $(SIBLING_CODEGEN_DIR) ] && echo $(SIBLING_CODEGEN_DIR) || echo codegen) AMQP_SPEC_JSON_FILES_0_9_1=$(AMQP_CODEGEN_DIR)/amqp-rabbitmq-0.9.1.json AMQP_SPEC_JSON_FILES_0_8=$(AMQP_CODEGEN_DIR)/amqp-rabbitmq-0.8.json -RABBIT_SPEC_EXTENSIONS=$(AMQP_CODEGEN_DIR)/confirm-extension.json ERL_CALL=erl_call -sname $(RABBITMQ_NODENAME) -e @@ -100,14 +99,14 @@ $(EBIN_DIR)/rabbit.app: $(EBIN_DIR)/rabbit_app.in $(BEAM_TARGETS) generate_app $(EBIN_DIR)/%.beam: erlc $(ERLC_OPTS) -pa $(EBIN_DIR) $< -$(INCLUDE_DIR)/rabbit_framing.hrl: codegen.py $(AMQP_CODEGEN_DIR)/amqp_codegen.py $(AMQP_SPEC_JSON_FILES_0_9_1) $(AMQP_SPEC_JSON_FILES_0_8) $(RABBIT_SPEC_EXTENSIONS) - $(PYTHON) codegen.py --ignore-conflicts header $(AMQP_SPEC_JSON_FILES_0_9_1) $(AMQP_SPEC_JSON_FILES_0_8) $(RABBIT_SPEC_EXTENSIONS) $@ +$(INCLUDE_DIR)/rabbit_framing.hrl: codegen.py $(AMQP_CODEGEN_DIR)/amqp_codegen.py $(AMQP_SPEC_JSON_FILES_0_9_1) $(AMQP_SPEC_JSON_FILES_0_8) + $(PYTHON) codegen.py --ignore-conflicts header $(AMQP_SPEC_JSON_FILES_0_9_1) $(AMQP_SPEC_JSON_FILES_0_8) $@ -$(SOURCE_DIR)/rabbit_framing_amqp_0_9_1.erl: codegen.py $(AMQP_CODEGEN_DIR)/amqp_codegen.py $(AMQP_SPEC_JSON_FILES_0_9_1) $(RABBIT_SPEC_EXTENSIONS) - $(PYTHON) codegen.py body $(AMQP_SPEC_JSON_FILES_0_9_1) $(RABBIT_SPEC_EXTENSIONS) $@ +$(SOURCE_DIR)/rabbit_framing_amqp_0_9_1.erl: codegen.py $(AMQP_CODEGEN_DIR)/amqp_codegen.py $(AMQP_SPEC_JSON_FILES_0_9_1) + $(PYTHON) codegen.py body $(AMQP_SPEC_JSON_FILES_0_9_1) $@ -$(SOURCE_DIR)/rabbit_framing_amqp_0_8.erl: codegen.py $(AMQP_CODEGEN_DIR)/amqp_codegen.py $(AMQP_SPEC_JSON_FILES_0_8) $(RABBIT_SPEC_EXTENSIONS) - $(PYTHON) codegen.py body $(AMQP_SPEC_JSON_FILES_0_8) $(RABBIT_SPEC_EXTENSIONS) $@ +$(SOURCE_DIR)/rabbit_framing_amqp_0_8.erl: codegen.py $(AMQP_CODEGEN_DIR)/amqp_codegen.py $(AMQP_SPEC_JSON_FILES_0_8) + $(PYTHON) codegen.py body $(AMQP_SPEC_JSON_FILES_0_8) $@ dialyze: $(BEAM_TARGETS) $(BASIC_PLT) $(ERL_EBIN) -eval \ -- cgit v1.2.1 From e758afefc3048f98e5f3d605a3b9889e50dd92d6 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Fri, 3 Sep 2010 10:57:45 +0100 Subject: remove rabbit_basic:delivery/4 since it's not used by anything --- src/rabbit_basic.erl | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/rabbit_basic.erl b/src/rabbit_basic.erl index b7cfa09a..348310d9 100644 --- a/src/rabbit_basic.erl +++ b/src/rabbit_basic.erl @@ -33,7 +33,7 @@ -include("rabbit.hrl"). -include("rabbit_framing.hrl"). --export([publish/1, message/4, properties/1, delivery/4, delivery/5]). +-export([publish/1, message/4, properties/1, delivery/5]). -export([publish/4, publish/7]). -export([build_content/2, from_content/1]). -export([is_message_persistent/1]). @@ -50,9 +50,6 @@ -spec(publish/1 :: (rabbit_types:delivery()) -> publish_result()). --spec(delivery/4 :: - (boolean(), boolean(), rabbit_types:maybe(rabbit_types:txn()), - rabbit_types:message()) -> rabiit_types:delivery()). -spec(delivery/5 :: (boolean(), boolean(), rabbit_types:maybe(rabbit_types:txn()), rabbit_types:message(), undefined | integer()) @@ -96,9 +93,6 @@ publish(Delivery = #delivery{ Other end. -delivery(Mandatory, Immediate, Txn, Message) -> - delivery(Mandatory, Immediate, Txn, Message, undefined). - delivery(Mandatory, Immediate, Txn, Message, MsgSeqNo) -> #delivery{mandatory = Mandatory, immediate = Immediate, txn = Txn, sender = self(), message = Message, msg_seq_no = MsgSeqNo}. @@ -168,7 +162,8 @@ publish(ExchangeName, RoutingKeyBin, Mandatory, Immediate, Txn, Properties, BodyBin) -> publish(delivery(Mandatory, Immediate, Txn, message(ExchangeName, RoutingKeyBin, - properties(Properties), BodyBin))). + properties(Properties), BodyBin), + undefined)). is_message_persistent(#content{properties = #'P_basic'{ delivery_mode = Mode}}) -> -- cgit v1.2.1 From f46c237a6fa7081256742c5d89e06a478706a026 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Fri, 3 Sep 2010 11:05:49 +0100 Subject: cosmetic renames --- src/rabbit_channel.erl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index bebb59a6..590bf2d6 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -70,7 +70,7 @@ -define(INFO_KEYS, ?CREATION_EVENT_KEYS ++ ?STATISTICS_KEYS -- [pid]). --define(MULTIPLE_ACK_FLUSH_INTERVAL, 1000). +-define(FLUSH_MULTIPLE_ACKS_INTERVAL, 1000). %%---------------------------------------------------------------------------- @@ -158,7 +158,7 @@ flush(Pid) -> gen_server2:call(Pid, flush). flush_multiple_acks(Pid) -> - gen_server2:cast(Pid, multiple_ack_flush). + gen_server2:cast(Pid, flush_multiple_acks). confirm(Pid, MsgSeqNo) -> gen_server2:cast(Pid, {confirm, MsgSeqNo}). @@ -262,7 +262,7 @@ handle_cast(emit_stats, State) -> internal_emit_stats(State), {noreply, State}; -handle_cast(multiple_ack_flush, +handle_cast(flush_multiple_acks, State = #ch{writer_pid = WriterPid, held_confirms = As, need_confirming = NA}) -> @@ -963,7 +963,7 @@ handle_method(#'queue.purge'{queue = QueueNameBin, handle_method(#'tx.select'{}, _, #ch{confirm_enabled = true}) -> rabbit_misc:protocol_error( - precondition_failed, "a confirm channel cannot be made transactional", []); + precondition_failed, "cannot switch from confirm to tx mode", []); handle_method(#'tx.select'{}, _, State = #ch{transaction_id = none}) -> {reply, #'tx.select_ok'{}, new_tx(State)}; @@ -988,7 +988,7 @@ handle_method(#'tx.rollback'{}, _, State) -> handle_method(#'confirm.select'{}, _, #ch{transaction_id = TxId}) when TxId =/= none -> rabbit_misc:protocol_error( - precondition_failed, "transactional channel cannot be made confirm", []); + precondition_failed, "cannot switch from tx to confirm mode", []); handle_method(#'confirm.select'{multiple = Multiple, nowait = NoWait}, _, @@ -1341,7 +1341,7 @@ erase_queue_stats(QPid) -> {{queue_exchange_stats, QX = {QPid0, _}}, _} <- get(), QPid =:= QPid0]. start_ack_timer(State = #ch{confirm_tref = undefined}) -> - {ok, TRef} = timer:apply_after(?MULTIPLE_ACK_FLUSH_INTERVAL, + {ok, TRef} = timer:apply_after(?FLUSH_MULTIPLE_ACKS_INTERVAL, ?MODULE, flush_multiple_acks, [self()]), State #ch { confirm_tref = TRef }; start_ack_timer(State) -> -- cgit v1.2.1 From 89fd68fd50d75f7c24cc18b8d9e7637eeaf2269d Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Fri, 3 Sep 2010 11:35:37 +0100 Subject: cosmetic --- src/rabbit_msg_store.erl | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/rabbit_msg_store.erl b/src/rabbit_msg_store.erl index 9be895ed..10e46fc7 100644 --- a/src/rabbit_msg_store.erl +++ b/src/rabbit_msg_store.erl @@ -663,10 +663,11 @@ handle_cast({write, Pid, Guid}, NextOffset, State #msstate { sum_valid_data = SumValid + TotalSize, sum_file_size = SumFileSize + TotalSize, - pid_to_guids = case dict:find(Pid, PTF) of - {ok, _} -> dict:append(Pid, Guid, PTG); - error -> PTG - end})); + pid_to_guids = + case dict:find(Pid, PTF) of + {ok, _} -> rabbit_misc:dict_cons(Pid, Guid, PTG); + error -> PTG + end})); #msg_location { ref_count = RefCount } -> %% We already know about it, just update counter. Only %% update field otherwise bad interaction with concurrent GC -- cgit v1.2.1 From 6c948c258227476d84045f95fe81263a23cbe5a0 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Fri, 3 Sep 2010 14:59:16 +0100 Subject: use client_terminate instead of monitoring for DOWN --- src/rabbit_msg_store.erl | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/rabbit_msg_store.erl b/src/rabbit_msg_store.erl index 10e46fc7..1d53787a 100644 --- a/src/rabbit_msg_store.erl +++ b/src/rabbit_msg_store.erl @@ -377,7 +377,10 @@ client_init(Server, Ref) -> cur_file_cache_ets = CurFileCacheEts }. client_terminate(CState) -> - close_all_handles(CState), + Self = self(), + spawn(fun() -> + gen_server2:call(self(), {client_terminate, CState}) + end), ok. client_delete_and_terminate(CState, Server, Ref) -> @@ -616,10 +619,16 @@ handle_call(successfully_recovered_state, _From, State) -> handle_call({register_sync_callback, Fun}, {Pid, _}, State = #msstate { pid_to_fun = PTF }) -> - erlang:monitor(process, Pid), reply(ok, - State #msstate { pid_to_fun = dict:store(Pid, Fun, PTF) }). + State #msstate { pid_to_fun = dict:store(Pid, Fun, PTF) }); +handle_call({client_terminate, CState}, {Pid, _}, + State = #msstate { pid_to_fun = PTF, + pid_to_guids = PTG }) -> + ok = close_all_handles(CState), + reply(ok, + State #msstate { pid_to_fun = dict:erase(Pid, PTF), + pid_to_guids = dict:erase(Pid, PTG) }). handle_cast({write, Pid, Guid}, State = #msstate { current_file_handle = CurHdl, @@ -751,13 +760,6 @@ handle_cast({delete_client, CRef}, handle_info(timeout, State) -> noreply(internal_sync(State)); -handle_info({'DOWN', _MRef, process, Pid, _Reason}, - State = #msstate { pid_to_fun = PTF, - pid_to_guids = PTG }) -> - % A queue with a callback has died, so remove it from dicts. - {noreply, State #msstate { pid_to_fun = dict:erase(Pid, PTF), - pid_to_guids = dict:erase(Pid, PTG) }}; - handle_info({'EXIT', _Pid, Reason}, State) -> {stop, Reason, State}. -- cgit v1.2.1 From c587f9b2b7c9007d2a2d57b43bdc05804ce4eb5c Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Fri, 3 Sep 2010 15:12:29 +0100 Subject: moved some confirm logic from router to channel --- src/rabbit_channel.erl | 8 ++++---- src/rabbit_router.erl | 8 -------- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 590bf2d6..917188d4 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -554,10 +554,10 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, State2 = case RoutingRes of %% Confirm transient messages now routed -> - case IsPersistent of - true -> State1; - false -> send_or_enqueue_ack( - MsgSeqNo, State1) + case {IsPersistent, DeliveredQPids} of + {_, []} -> send_or_enqueue_ack(MsgSeqNo, State1); + {true, _} -> State1; + {false, _} -> send_or_enqueue_ack(MsgSeqNo, State1) end; %% Confirm after basic.returns unroutable -> diff --git a/src/rabbit_router.erl b/src/rabbit_router.erl index 3d270916..aaf3ae92 100644 --- a/src/rabbit_router.erl +++ b/src/rabbit_router.erl @@ -65,14 +65,6 @@ deliver(QPids, Delivery = #delivery{mandatory = false, %% case below. delegate:invoke_no_result( QPids, fun (Pid) -> rabbit_amqqueue:deliver(Pid, Delivery) end), - case {QPids, MsgSeqNo} of - {_, undefined} -> ok; - {[], _} -> - %% No queues will get the message. This is fine, so we - %% just confirm it. - rabbit_channel:confirm(self(), MsgSeqNo); - _ -> ok - end, maybe_inform_channel(MsgSeqNo, QPids), {routed, QPids}; -- cgit v1.2.1 From 566f2ea31151284777825a1cb0ce3cee94ba2716 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Fri, 3 Sep 2010 17:17:58 +0100 Subject: stopped a race --- src/rabbit_variable_queue.erl | 57 ++++++++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 20 deletions(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index c33859ee..9e7b1d2c 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -238,7 +238,8 @@ in_counter, rates, msgs_on_disk, - msg_indices_on_disk + msg_indices_on_disk, + need_acking }). -record(rates, { egress, ingress, avg_egress, avg_ingress, timestamp }). @@ -326,7 +327,8 @@ in_counter :: non_neg_integer(), rates :: rates(), msgs_on_disk :: gb_set(), - msg_indices_on_disk :: gb_set()}). + msg_indices_on_disk :: gb_set(), + need_acking :: gb_set()}). -include("rabbit_backing_queue_spec.hrl"). @@ -443,7 +445,8 @@ init(QueueName, IsDurable, Recover) -> avg_ingress = 0.0, timestamp = Now }, msgs_on_disk = gb_sets:new(), - msg_indices_on_disk = gb_sets:new()}, + msg_indices_on_disk = gb_sets:new(), + need_acking = gb_sets:new()}, a(maybe_deltas_to_betas(State)). terminate(State) -> @@ -511,7 +514,8 @@ publish(Msg, State) -> publish_delivered(false, _Msg, State = #vqstate { len = 0 }) -> {blank_ack, a(State)}; -publish_delivered(true, Msg = #basic_message { is_persistent = IsPersistent }, +publish_delivered(true, Msg = #basic_message { is_persistent = IsPersistent, + guid = Guid }, State = #vqstate { len = 0, next_seq_id = SeqId, out_counter = OutCount, @@ -529,7 +533,8 @@ publish_delivered(true, Msg = #basic_message { is_persistent = IsPersistent }, out_counter = OutCount + 1, in_counter = InCount + 1, persistent_count = PCount1, - pending_ack = PA1 })}. + pending_ack = PA1, + need_acking = gb_sets:insert(Guid, State1#vqstate.need_acking)})}. fetch(AckRequired, State = #vqstate { q4 = Q4, ram_msg_count = RamMsgCount, @@ -1014,7 +1019,8 @@ remove_queue_entries1( %% Internal gubbins for publishing %%---------------------------------------------------------------------------- -publish(Msg = #basic_message { is_persistent = IsPersistent }, +publish(Msg = #basic_message { is_persistent = IsPersistent, + guid = Guid }, IsDelivered, MsgOnDisk, State = #vqstate { q1 = Q1, q3 = Q3, q4 = Q4, next_seq_id = SeqId, @@ -1036,7 +1042,8 @@ publish(Msg = #basic_message { is_persistent = IsPersistent }, len = Len + 1, in_counter = InCount + 1, persistent_count = PCount1, - ram_msg_count = RamMsgCount + 1}}. + ram_msg_count = RamMsgCount + 1, + need_acking = gb_sets:add(Guid, State2#vqstate.need_acking) }}. maybe_write_msg_to_disk(_Force, MsgStatus = #msg_status { msg_on_disk = true }, MSCState) -> @@ -1136,9 +1143,8 @@ ack(MsgStoreFun, Fun, AckTags, State) -> ok = orddict:fold(fun (MsgStore, Guids, ok) -> MsgStoreFun(MsgStore, Guids) end, ok, GuidsByStore), - AckdGuids = lists:append(orddict:fold(fun (_, Guids, Acc) -> - [Guids || Acc] - end, [], GuidsByStore)), + AckdGuids = lists:append([Guids || + {_Store, Guids} <- orddict:to_list(GuidsByStore)]), State2 = msgs_confirmed(AckdGuids, State1), PCount1 = PCount - case orddict:find(?PERSISTENT_MSG_STORE, GuidsByStore) of error -> 0; @@ -1162,26 +1168,33 @@ accumulate_ack(SeqId, {IsPersistent, Guid}, {SeqIdsAcc, Dict}) -> %%---------------------------------------------------------------------------- msgs_confirmed(Guids, State = #vqstate { msgs_on_disk = MOD, - msg_indices_on_disk = MIOD }) -> + msg_indices_on_disk = MIOD, + need_acking = NA }) -> + GuidSet = gb_sets:from_list(Guids), State #vqstate { msgs_on_disk = - gb_sets:difference(MOD, gb_sets:from_list(Guids)), + gb_sets:difference(MOD, GuidSet), msg_indices_on_disk = - gb_sets:delete_any(MIOD, gb_sets:from_list(Guids)) }. + gb_sets:difference(MIOD, GuidSet), + need_acking = + gb_sets:difference(NA, GuidSet) }. msgs_written_to_disk(QPid, Guids) -> spawn(fun() -> rabbit_amqqueue:maybe_run_queue_via_backing_queue( QPid, fun(State = #vqstate { msgs_on_disk = MOD, - msg_indices_on_disk = MIOD}) -> + msg_indices_on_disk = MIOD, + need_acking = NA }) -> GuidSet = gb_sets:from_list(Guids), ToConfirmMsgs = gb_sets:intersection(GuidSet, MIOD), + MOD1 = gb_sets:intersection(gb_sets:union(MOD, GuidSet), NA), { State #vqstate { msgs_on_disk = - gb_sets:difference(gb_sets:union(MOD, GuidSet), - ToConfirmMsgs), + gb_sets:difference(MOD1, ToConfirmMsgs), msg_indices_on_disk = - gb_sets:difference(MIOD, ToConfirmMsgs) }, + gb_sets:difference(MIOD, ToConfirmMsgs), + need_acking = + gb_sets:difference(NA, ToConfirmMsgs) }, {confirm, gb_sets:to_list(ToConfirmMsgs)} } end) end). @@ -1191,15 +1204,19 @@ msg_indices_written_to_disk(Guids) -> spawn(fun() -> rabbit_amqqueue:maybe_run_queue_via_backing_queue( Self, fun(State = #vqstate { msgs_on_disk = MOD, - msg_indices_on_disk = MIOD}) -> + msg_indices_on_disk = MIOD, + need_acking = NA }) -> GuidSet = gb_sets:from_list(Guids), ToConfirmMsgs = gb_sets:intersection(GuidSet, MOD), + MIOD1 = + gb_sets:intersection(gb_sets:union(MIOD, GuidSet), NA), { State #vqstate { msgs_on_disk = gb_sets:difference(MOD, ToConfirmMsgs), msg_indices_on_disk = - gb_sets:difference(gb_sets:union(MIOD, GuidSet), - ToConfirmMsgs) }, + gb_sets:difference(MIOD1, ToConfirmMsgs), + need_acking = + gb_sets:difference(NA, ToConfirmMsgs) }, {confirm, gb_sets:to_list(ToConfirmMsgs)} } end) end). -- cgit v1.2.1 From 1c56ee01693c6db88469e455f41ec403195dbe4c Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Mon, 6 Sep 2010 18:09:15 +0100 Subject: message store keeps track of queues through their client refs --- src/rabbit_msg_store.erl | 108 ++++++++++++++++++++++-------------------- src/rabbit_variable_queue.erl | 1 + 2 files changed, 58 insertions(+), 51 deletions(-) diff --git a/src/rabbit_msg_store.erl b/src/rabbit_msg_store.erl index 1d53787a..b2768a13 100644 --- a/src/rabbit_msg_store.erl +++ b/src/rabbit_msg_store.erl @@ -36,7 +36,7 @@ -export([start_link/4, write/4, read/3, contains/2, remove/2, release/2, sync/3, client_init/2, client_terminate/1, client_delete_and_terminate/3, successfully_recovered_state/1, - register_sync_callback/2]). + register_sync_callback/3]). -export([sync/1, gc_done/4, set_maximum_since_use/2, gc/3]). %% internal @@ -84,8 +84,8 @@ client_refs, %% set of references of all registered clients successfully_recovered, %% boolean: did we recover state? file_size_limit, %% how big are our files allowed to get? - pid_to_fun, %% pid to callback function mapping - pid_to_guids %% pid to synced messages mapping + client_ondisk_callback, %% client ref to callback function mapping + cref_to_guids %% client ref to synced messages mapping }). -record(client_msstate, @@ -97,7 +97,8 @@ file_handles_ets, file_summary_ets, dedup_cache_ets, - cur_file_cache_ets + cur_file_cache_ets, + client_ref }). -record(file_summary, @@ -119,7 +120,8 @@ file_handles_ets :: ets:tid(), file_summary_ets :: ets:tid(), dedup_cache_ets :: ets:tid(), - cur_file_cache_ets :: ets:tid() }). + cur_file_cache_ets :: ets:tid(), + client_ref :: rabbit_guid:guid()}). -type(startup_fun_state() :: {(fun ((A) -> 'finished' | {rabbit_guid:guid(), non_neg_integer(), A})), A}). @@ -127,8 +129,9 @@ -spec(start_link/4 :: (atom(), file:filename(), [binary()] | 'undefined', startup_fun_state()) -> rabbit_types:ok_pid_or_error()). --spec(write/4 :: (server(), rabbit_guid:guid(), msg(), client_msstate()) -> - rabbit_types:ok(client_msstate())). +-spec(write/4 :: (server(), rabbit_guid:guid(), + msg(), client_msstate()) + -> rabbit_types:ok(client_msstate())). -spec(read/3 :: (server(), rabbit_guid:guid(), client_msstate()) -> {rabbit_types:ok(msg()) | 'not_found', client_msstate()}). -spec(contains/2 :: (server(), rabbit_guid:guid()) -> boolean()). @@ -138,10 +141,10 @@ -spec(gc_done/4 :: (server(), non_neg_integer(), file_num(), file_num()) -> 'ok'). -spec(set_maximum_since_use/2 :: (server(), non_neg_integer()) -> 'ok'). --spec(client_init/2 :: (server(), binary()) -> client_msstate()). +-spec(client_init/2 :: (server(), rabbit_guid:guid()) -> client_msstate()). -spec(client_terminate/1 :: (client_msstate()) -> 'ok'). -spec(client_delete_and_terminate/3 :: - (client_msstate(), server(), binary()) -> 'ok'). + (client_msstate(), server(), rabbit_guid:guid()) -> 'ok'). -spec(successfully_recovered_state/1 :: (server()) -> boolean()). -spec(gc/3 :: (non_neg_integer(), non_neg_integer(), @@ -318,9 +321,10 @@ start_link(Server, Dir, ClientRefs, StartupFunState) -> [{timeout, infinity}]). write(Server, Guid, Msg, - CState = #client_msstate { cur_file_cache_ets = CurFileCacheEts }) -> + CState = #client_msstate { cur_file_cache_ets = CurFileCacheEts, + client_ref = CRef }) -> ok = update_msg_cache(CurFileCacheEts, Guid, Msg), - {gen_server2:cast(Server, {write, self(), Guid}), CState}. + {gen_server2:cast(Server, {write, CRef, Guid}), CState}. read(Server, Guid, CState = #client_msstate { dedup_cache_ets = DedupCacheEts, @@ -374,12 +378,13 @@ client_init(Server, Ref) -> file_handles_ets = FileHandlesEts, file_summary_ets = FileSummaryEts, dedup_cache_ets = DedupCacheEts, - cur_file_cache_ets = CurFileCacheEts }. + cur_file_cache_ets = CurFileCacheEts, + client_ref = Ref}. client_terminate(CState) -> Self = self(), spawn(fun() -> - gen_server2:call(self(), {client_terminate, CState}) + gen_server2:call(Self, {client_terminate, CState}) end), ok. @@ -390,8 +395,8 @@ client_delete_and_terminate(CState, Server, Ref) -> successfully_recovered_state(Server) -> gen_server2:pcall(Server, 7, successfully_recovered_state, infinity). -register_sync_callback(Server, Fun) -> - gen_server2:call(Server, {register_sync_callback, Fun}, infinity). +register_sync_callback(Server, ClientRef, Fun) -> + gen_server2:call(Server, {register_sync_callback, ClientRef, Fun}, infinity). %%---------------------------------------------------------------------------- %% Client-side-only helpers @@ -569,8 +574,8 @@ init([Server, BaseDir, ClientRefs, StartupFunState]) -> client_refs = ClientRefs1, successfully_recovered = CleanShutdown, file_size_limit = FileSizeLimit, - pid_to_fun = dict:new(), - pid_to_guids = dict:new() + client_ondisk_callback = dict:new(), + cref_to_guids = dict:new() }, %% If we didn't recover the msg location index then we need to @@ -617,28 +622,29 @@ handle_call({new_client_state, CRef}, _From, handle_call(successfully_recovered_state, _From, State) -> reply(State #msstate.successfully_recovered, State); -handle_call({register_sync_callback, Fun}, {Pid, _}, - State = #msstate { pid_to_fun = PTF }) -> - reply(ok, - State #msstate { pid_to_fun = dict:store(Pid, Fun, PTF) }); - -handle_call({client_terminate, CState}, {Pid, _}, - State = #msstate { pid_to_fun = PTF, - pid_to_guids = PTG }) -> +handle_call({register_sync_callback, ClientRef, Fun}, _From, + State = #msstate { client_ondisk_callback = CODC }) -> + reply(ok, State #msstate { client_ondisk_callback = + dict:store(ClientRef, Fun, CODC) }); +handle_call({client_terminate, CState = #client_msstate { client_ref = CRef }}, + _From, + State = #msstate { client_ondisk_callback = CODC, + cref_to_guids = CTG }) -> ok = close_all_handles(CState), reply(ok, - State #msstate { pid_to_fun = dict:erase(Pid, PTF), - pid_to_guids = dict:erase(Pid, PTG) }). - -handle_cast({write, Pid, Guid}, - State = #msstate { current_file_handle = CurHdl, - current_file = CurFile, - sum_valid_data = SumValid, - sum_file_size = SumFileSize, - file_summary_ets = FileSummaryEts, - cur_file_cache_ets = CurFileCacheEts, - pid_to_fun = PTF, - pid_to_guids = PTG}) -> + State #msstate { client_ondisk_callback = dict:erase(CRef, CODC), + cref_to_guids = dict:erase(CRef, CTG) }). + +handle_cast({write, CRef, Guid}, + State = #msstate { current_file_handle = CurHdl, + current_file = CurFile, + sum_valid_data = SumValid, + sum_file_size = SumFileSize, + file_summary_ets = FileSummaryEts, + cur_file_cache_ets = CurFileCacheEts, + client_ondisk_callback = CODC, + cref_to_guids = CTG}) -> + true = 0 =< ets:update_counter(CurFileCacheEts, Guid, {3, -1}), [{Guid, Msg, _CacheRefCount}] = ets:lookup(CurFileCacheEts, Guid), case index_lookup(Guid, State) of @@ -672,10 +678,10 @@ handle_cast({write, Pid, Guid}, NextOffset, State #msstate { sum_valid_data = SumValid + TotalSize, sum_file_size = SumFileSize + TotalSize, - pid_to_guids = - case dict:find(Pid, PTF) of - {ok, _} -> rabbit_misc:dict_cons(Pid, Guid, PTG); - error -> PTG + cref_to_guids = + case dict:find(CRef, CODC) of + {ok, _} -> rabbit_misc:dict_cons(CRef, Guid, CTG); + error -> CRef end})); #msg_location { ref_count = RefCount } -> %% We already know about it, just update counter. Only @@ -809,14 +815,14 @@ reply(Reply, State) -> next_state(State = #msstate { sync_timer_ref = undefined, on_sync = OS, - pid_to_guids = PTG }) -> - case {OS, dict:size(PTG)} of + cref_to_guids = CTG }) -> + case {OS, dict:size(CTG)} of {[], 0} -> {State, hibernate}; _ -> {start_sync_timer(State), 0} end; next_state(State = #msstate { on_sync = OS, - pid_to_guids = PTG }) -> - case {OS, dict:size(PTG)} of + cref_to_guids = CTG }) -> + case {OS, dict:size(CTG)} of {[], 0} -> {stop_sync_timer(State), hibernate}; _ -> {State, 0} end. @@ -833,8 +839,8 @@ stop_sync_timer(State = #msstate { sync_timer_ref = TRef }) -> internal_sync(State = #msstate { current_file_handle = CurHdl, on_sync = Syncs, - pid_to_fun = PTF, - pid_to_guids = PTG }) -> + client_ondisk_callback = CODC, + cref_to_guids = CTG }) -> State1 = stop_sync_timer(State), State2 = case Syncs of [] -> State1; @@ -842,13 +848,13 @@ internal_sync(State = #msstate { current_file_handle = CurHdl, lists:foreach(fun (K) -> K() end, lists:reverse(Syncs)), State1 #msstate { on_sync = [] } end, - dict:map(fun(Pid, Guids) -> - case dict:find(Pid, PTF) of + dict:map(fun(CRef, Guids) -> + case dict:find(CRef, CODC) of {ok, Fun} -> Fun(Guids); error -> ok %% shouldn't happen end - end, PTG), - State2 #msstate { pid_to_guids = dict:new() }. + end, CTG), + State2 #msstate { cref_to_guids = dict:new() }. read_message(Guid, From, diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 9e7b1d2c..62bd38f2 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -409,6 +409,7 @@ init(QueueName, IsDurable, Recover) -> Self = self(), rabbit_msg_store:register_sync_callback( ?PERSISTENT_MSG_STORE, + PRef, fun (Guids) -> msgs_written_to_disk(Self, Guids) end), -- cgit v1.2.1 From 78090afc673f83ca12d2a3b085146a00ba2307c5 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Thu, 16 Sep 2010 16:18:36 +0100 Subject: Avoid starting rabbit app on plugin activation --- scripts/rabbitmq-server | 5 +---- scripts/rabbitmq-server.bat | 6 ++---- src/rabbit_plugin_activator.erl | 6 +----- 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/scripts/rabbitmq-server b/scripts/rabbitmq-server index 9310752f..92ce3209 100755 --- a/scripts/rabbitmq-server +++ b/scripts/rabbitmq-server @@ -88,13 +88,10 @@ RABBITMQ_EBIN_ROOT="${RABBITMQ_HOME}/ebin" if [ "x" = "x$RABBITMQ_NODE_ONLY" ]; then if erl \ -pa "$RABBITMQ_EBIN_ROOT" \ - -rabbit plugins_dir "\"$RABBITMQ_PLUGINS_DIR\"" \ - -rabbit plugins_expand_dir "\"${RABBITMQ_MNESIA_DIR}/plugins-scratch\"" \ - -rabbit rabbit_ebin "\"$RABBITMQ_EBIN_ROOT\"" \ -noinput \ -hidden \ -s rabbit_plugin_activator \ - -extra "$@" + -extra "$RABBITMQ_PLUGINS_DIR" "${RABBITMQ_MNESIA_DIR}/plugins-scratch" then RABBITMQ_BOOT_FILE="${RABBITMQ_MNESIA_DIR}/plugins-scratch/rabbit" RABBITMQ_EBIN_PATH="" diff --git a/scripts/rabbitmq-server.bat b/scripts/rabbitmq-server.bat index 5bcbc6ba..4d90d90c 100644 --- a/scripts/rabbitmq-server.bat +++ b/scripts/rabbitmq-server.bat @@ -114,10 +114,8 @@ set RABBITMQ_EBIN_ROOT=!TDP0!..\ebin -pa "!RABBITMQ_EBIN_ROOT!" ^ -noinput -hidden ^ -s rabbit_plugin_activator ^ --rabbit plugins_dir \""!RABBITMQ_PLUGINS_DIR:\=/!"\" ^ --rabbit plugins_expand_dir \""!RABBITMQ_MNESIA_DIR:\=/!/plugins-scratch"\" ^ --rabbit rabbit_ebin \""!RABBITMQ_EBIN_ROOT:\=/!"\" ^ --extra !STAR! +-extra "!RABBITMQ_PLUGINS_DIR:\=/!" ^ + "!RABBITMQ_MNESIA_DIR:\=/!/plugins-scratch" set RABBITMQ_BOOT_FILE=!RABBITMQ_MNESIA_DIR!\plugins-scratch\rabbit if not exist "!RABBITMQ_BOOT_FILE!.boot" ( diff --git a/src/rabbit_plugin_activator.erl b/src/rabbit_plugin_activator.erl index b23776cd..3d9a8a95 100644 --- a/src/rabbit_plugin_activator.erl +++ b/src/rabbit_plugin_activator.erl @@ -52,13 +52,9 @@ start() -> io:format("Activating RabbitMQ plugins ...~n"), - %% Ensure Rabbit is loaded so we can access it's environment - application:load(rabbit), %% Determine our various directories - PluginDir = get_env(plugins_dir, ?DefaultPluginDir), - UnpackedPluginDir = get_env(plugins_expand_dir, ?DefaultUnpackedPluginDir), - + [PluginDir, UnpackedPluginDir] = init:get_plain_arguments(), RootName = UnpackedPluginDir ++ "/rabbit", %% Unpack any .ez plugins -- cgit v1.2.1 From 252e077e3c86608c22261918f4a6957da218e266 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Mon, 27 Sep 2010 08:59:06 +0100 Subject: solved a couple of bad_matches/function_clauses --- src/rabbit_amqqueue_process.erl | 21 ++++++++++++--------- src/rabbit_msg_store.erl | 2 +- src/rabbit_variable_queue.erl | 4 ++-- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 8a9a293b..eb34aeff 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -417,8 +417,8 @@ maybe_record_confirm_message(undefined, _, _, State) -> maybe_record_confirm_message(MsgSeqNo, #basic_message { guid = Guid }, ChPid, State) -> - State #q { guid_to_channel = - dict:store(Guid, {ChPid, MsgSeqNo}, State#q.guid_to_channel) }. + State #q { guid_to_channel = + dict:store(Guid, {ChPid, MsgSeqNo}, State#q.guid_to_channel) }. run_message_queue(State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> Funs = {fun deliver_from_queue_pred/2, @@ -549,8 +549,9 @@ qname(#q{q = #amqqueue{name = QName}}) -> QName. maybe_run_queue_via_backing_queue(Fun, State = #q{backing_queue_state = BQS}) -> case Fun(BQS) of {BQS1, {confirm, Guids}} -> - confirm_messages_internal(Guids, - State #q { backing_queue_state = BQS1 }); + run_message_queue( + confirm_messages_internal(Guids, + State #q { backing_queue_state = BQS1 })); BQS1 -> run_message_queue(State#q{backing_queue_state = BQS1}) end. @@ -868,10 +869,11 @@ handle_cast({ack, Txn, AckTags, ChPid}, none -> ChAckTags1 = subtract_acks(ChAckTags, AckTags), NewC = C#cr{acktags = ChAckTags1}, - {NewBQS, AckdGuids} = BQ:ack(AckTags, BQS), + {NewBQS, {confirm, AckdGuids}} = BQ:ack(AckTags, BQS), NewState = confirm_messages_internal(AckdGuids, - State #q { backing_queue_state = NewBQS }), + State #q { backing_queue_state = + NewBQS }), {NewC, NewState}; _ -> {C#cr{txn = Txn}, @@ -891,9 +893,10 @@ handle_cast({reject, AckTags, Requeue, ChPid}, store_ch_record(C#cr{acktags = ChAckTags1}), noreply(case Requeue of true -> requeue_and_run(AckTags, State); - false -> {BQS1, AckdGuids} = BQ:ack(AckTags, BQS), - confirm_messages_internal(AckdGuids, - State #q { backing_queue_state = BQS1 }) + false -> {BQS1, {confirm, AckdGuids}} = BQ:ack(AckTags, BQS), + confirm_messages_internal( + AckdGuids, + State #q { backing_queue_state = BQS1 }) end) end; diff --git a/src/rabbit_msg_store.erl b/src/rabbit_msg_store.erl index 208f71f0..9e38a976 100644 --- a/src/rabbit_msg_store.erl +++ b/src/rabbit_msg_store.erl @@ -376,6 +376,7 @@ client_init(Server, Ref) -> client_ref = Ref}. client_terminate(CState, Server) -> + close_all_handles(CState), ok = gen_server2:call(Server, {client_terminate, CState}, infinity). client_delete_and_terminate(CState, Server, Ref) -> @@ -637,7 +638,6 @@ handle_call({client_terminate, CState = #client_msstate { client_ref = CRef }}, _From, State = #msstate { client_ondisk_callback = CODC, cref_to_guids = CTG }) -> - ok = close_all_handles(CState), reply(ok, State #msstate { client_ondisk_callback = dict:erase(CRef, CODC), cref_to_guids = dict:erase(CRef, CTG) }). diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 6521c544..8b4f55c5 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -1129,7 +1129,7 @@ remove_pending_ack(KeepPersistent, end. ack(_MsgStoreFun, _Fun, [], State) -> - {State, []}; + {State, {confirm, []}}; ack(MsgStoreFun, Fun, AckTags, State) -> {{SeqIds, GuidsByStore}, State1 = #vqstate { index_state = IndexState, persistent_count = PCount }} = @@ -1153,7 +1153,7 @@ ack(MsgStoreFun, Fun, AckTags, State) -> end, {State2 #vqstate { index_state = IndexState1, persistent_count = PCount1 }, - AckdGuids}. + {confirm, AckdGuids}}. accumulate_ack(_SeqId, #msg_status { is_persistent = false, %% ASSERTIONS msg_on_disk = false, -- cgit v1.2.1 From f19c3300d2861f9c266be1d4f7c99a10b8fbe9e6 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Mon, 27 Sep 2010 10:14:54 +0100 Subject: no performance penalty when *not* using confirms --- include/rabbit_backing_queue_spec.hrl | 7 ++--- src/rabbit_amqqueue_process.erl | 23 ++++++++-------- src/rabbit_backing_queue.erl | 4 +-- src/rabbit_invariable_queue.erl | 12 ++++----- src/rabbit_msg_store.erl | 2 +- src/rabbit_variable_queue.erl | 51 +++++++++++++++++++++-------------- 6 files changed, 56 insertions(+), 43 deletions(-) diff --git a/include/rabbit_backing_queue_spec.hrl b/include/rabbit_backing_queue_spec.hrl index 005994f0..c01e9246 100644 --- a/include/rabbit_backing_queue_spec.hrl +++ b/include/rabbit_backing_queue_spec.hrl @@ -43,9 +43,10 @@ -spec(terminate/1 :: (state()) -> state()). -spec(delete_and_terminate/1 :: (state()) -> state()). -spec(purge/1 :: (state()) -> {purged_msg_count(), state()}). --spec(publish/2 :: (rabbit_types:basic_message(), state()) -> state()). --spec(publish_delivered/3 :: - (ack_required(), rabbit_types:basic_message(), state()) -> {ack(), state()}). +-spec(publish/3 :: (rabbit_types:basic_message(), boolean(), state()) -> state()). +-spec(publish_delivered/4 :: + (ack_required(), rabbit_types:basic_message(), boolean(), state()) + -> {ack(), state()}). -spec(fetch/2 :: (ack_required(), state()) -> {fetch_result(), state()}). -spec(ack/2 :: ([ack()], state()) -> state()). -spec(tx_publish/3 :: (rabbit_types:txn(), rabbit_types:basic_message(), state()) -> state()). diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index eb34aeff..bc2ffd17 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -427,28 +427,30 @@ run_message_queue(State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> {_IsEmpty1, State1} = deliver_msgs_to_consumers(Funs, IsEmpty, State), State1. -attempt_delivery(none, _ChPid, Message, State = #q{backing_queue = BQ}) -> +attempt_delivery(none, _ChPid, Message, MsgSeqNo, State = #q{backing_queue = BQ}) -> PredFun = fun (IsEmpty, _State) -> not IsEmpty end, DeliverFun = fun (AckRequired, false, State1 = #q{backing_queue_state = BQS}) -> {AckTag, BQS1} = - BQ:publish_delivered(AckRequired, Message, BQS), + BQ:publish_delivered(AckRequired, Message, + MsgSeqNo =/= undefined, BQS), {{Message, false, AckTag}, true, State1#q{backing_queue_state = BQS1}} end, deliver_msgs_to_consumers({ PredFun, DeliverFun }, false, State); -attempt_delivery(Txn, ChPid, Message, State = #q{backing_queue = BQ, - backing_queue_state = BQS}) -> +attempt_delivery(Txn, ChPid, Message, _MSN, State = #q{backing_queue = BQ, + backing_queue_state = BQS}) -> record_current_channel_tx(ChPid, Txn), {true, State#q{backing_queue_state = BQ:tx_publish(Txn, Message, BQS)}}. -deliver_or_enqueue(Txn, ChPid, Message, State = #q{backing_queue = BQ}) -> - case attempt_delivery(Txn, ChPid, Message, State) of +deliver_or_enqueue(Txn, ChPid, Message, MsgSeqNo, State = #q{backing_queue = BQ}) -> + case attempt_delivery(Txn, ChPid, Message, MsgSeqNo, State) of {true, NewState} -> {true, NewState}; {false, NewState} -> %% Txn is none and no unblocked channels with consumers - BQS = BQ:publish(Message, State #q.backing_queue_state), + BQS = BQ:publish(Message, MsgSeqNo =/= undefined, + State #q.backing_queue_state), {false, NewState#q{backing_queue_state = BQS}} end. @@ -698,14 +700,13 @@ handle_call({deliver_immediately, Txn, Message, MsgSeqNo, ChPid}, _From, State) %% queues discarding the message? %% State1 = maybe_record_confirm_message(MsgSeqNo, Message, ChPid, State), - {Delivered, State2} = attempt_delivery(Txn, ChPid, Message, State1 -), + {Delivered, State2} = attempt_delivery(Txn, ChPid, Message, MsgSeqNo, State1), reply(Delivered, State2); handle_call({deliver, Txn, Message, MsgSeqNo, ChPid}, _From, State) -> %% Synchronous, "mandatory" delivery mode State1 = maybe_record_confirm_message(MsgSeqNo, Message, ChPid, State), - {Delivered, State2} = deliver_or_enqueue(Txn, ChPid, Message, State1), + {Delivered, State2} = deliver_or_enqueue(Txn, ChPid, Message, MsgSeqNo, State1), reply(Delivered, State2); handle_call({commit, Txn, ChPid}, From, State) -> @@ -855,7 +856,7 @@ handle_call({maybe_run_queue_via_backing_queue, Fun}, _From, State) -> handle_cast({deliver, Txn, Message, MsgSeqNo, ChPid}, State) -> %% Asynchronous, non-"mandatory", non-"immediate" deliver mode. State1 = maybe_record_confirm_message(MsgSeqNo, Message, ChPid, State), - {_Delivered, State2} = deliver_or_enqueue(Txn, ChPid, Message, State1), + {_Delivered, State2} = deliver_or_enqueue(Txn, ChPid, Message, MsgSeqNo, State1), noreply(State2); handle_cast({ack, Txn, AckTags, ChPid}, diff --git a/src/rabbit_backing_queue.erl b/src/rabbit_backing_queue.erl index 2230c507..32f9f15a 100644 --- a/src/rabbit_backing_queue.erl +++ b/src/rabbit_backing_queue.erl @@ -62,12 +62,12 @@ behaviour_info(callbacks) -> {purge, 1}, %% Publish a message. - {publish, 2}, + {publish, 3}, %% Called for messages which have already been passed straight %% out to a client. The queue will be empty for these calls %% (i.e. saves the round trip through the backing queue). - {publish_delivered, 3}, + {publish_delivered, 4}, %% Produce the next message. {fetch, 2}, diff --git a/src/rabbit_invariable_queue.erl b/src/rabbit_invariable_queue.erl index 4e0dad84..664ef653 100644 --- a/src/rabbit_invariable_queue.erl +++ b/src/rabbit_invariable_queue.erl @@ -31,8 +31,8 @@ -module(rabbit_invariable_queue). --export([init/3, terminate/1, delete_and_terminate/1, purge/1, publish/2, - publish_delivered/3, fetch/2, ack/2, tx_publish/3, tx_ack/3, +-export([init/3, terminate/1, delete_and_terminate/1, purge/1, publish/3, + publish_delivered/4, fetch/2, ack/2, tx_publish/3, tx_ack/3, tx_rollback/2, tx_commit/3, requeue/2, len/1, is_empty/1, set_ram_duration_target/2, ram_duration/1, needs_idle_timeout/1, idle_timeout/1, handle_pre_hibernate/1, status/1]). @@ -99,14 +99,14 @@ purge(State = #iv_state { queue = Q, qname = QName, durable = IsDurable, ok = persist_acks(QName, IsDurable, none, AckTags, PA), {Len, State #iv_state { len = 0, queue = queue:new() }}. -publish(Msg, State = #iv_state { queue = Q, qname = QName, durable = IsDurable, - len = Len }) -> +publish(Msg, _, State = #iv_state { queue = Q, qname = QName, durable = IsDurable, + len = Len }) -> ok = persist_message(QName, IsDurable, none, Msg), State #iv_state { queue = queue:in({Msg, false}, Q), len = Len + 1 }. -publish_delivered(false, _Msg, State) -> +publish_delivered(false, _Msg, _, State) -> {blank_ack, State}; -publish_delivered(true, Msg = #basic_message { guid = Guid }, +publish_delivered(true, Msg = #basic_message { guid = Guid }, _, State = #iv_state { qname = QName, durable = IsDurable, len = 0, pending_ack = PA }) -> ok = persist_message(QName, IsDurable, none, Msg), diff --git a/src/rabbit_msg_store.erl b/src/rabbit_msg_store.erl index 9e38a976..3cf2ec4f 100644 --- a/src/rabbit_msg_store.erl +++ b/src/rabbit_msg_store.erl @@ -634,7 +634,7 @@ handle_call({register_sync_callback, ClientRef, Fun}, _From, reply(ok, State #msstate { client_ondisk_callback = dict:store(ClientRef, Fun, CODC) }); -handle_call({client_terminate, CState = #client_msstate { client_ref = CRef }}, +handle_call({client_terminate, #client_msstate { client_ref = CRef }}, _From, State = #msstate { client_ondisk_callback = CODC, cref_to_guids = CTG }) -> diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 8b4f55c5..83448c54 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -32,7 +32,7 @@ -module(rabbit_variable_queue). -export([init/3, terminate/1, delete_and_terminate/1, - purge/1, publish/2, publish_delivered/3, fetch/2, ack/2, + purge/1, publish/3, publish_delivered/4, fetch/2, ack/2, tx_publish/3, tx_ack/3, tx_rollback/2, tx_commit/3, requeue/2, len/1, is_empty/1, set_ram_duration_target/2, ram_duration/1, @@ -509,14 +509,15 @@ purge(State = #vqstate { q4 = Q4, index_state = IndexState, len = Len }) -> ram_index_count = 0, persistent_count = 0 })}. -publish(Msg, State) -> - {_SeqId, State1} = publish(Msg, false, false, State), +publish(Msg, NeedsConfirming, State) -> + {_SeqId, State1} = publish(Msg, false, false, NeedsConfirming, State), a(reduce_memory_use(State1)). -publish_delivered(false, _Msg, State = #vqstate { len = 0 }) -> +publish_delivered(false, _Msg, _NC, State = #vqstate { len = 0 }) -> {blank_ack, a(State)}; publish_delivered(true, Msg = #basic_message { is_persistent = IsPersistent, guid = Guid }, + NeedsConfirming, State = #vqstate { len = 0, next_seq_id = SeqId, out_counter = OutCount, @@ -530,12 +531,17 @@ publish_delivered(true, Msg = #basic_message { is_persistent = IsPersistent, {MsgStatus1, State1} = maybe_write_to_disk(false, false, MsgStatus, State), PA1 = record_pending_ack(m(MsgStatus1), PA), PCount1 = PCount + one_if(IsPersistent1), - {SeqId, a(State1 #vqstate { next_seq_id = SeqId + 1, - out_counter = OutCount + 1, - in_counter = InCount + 1, - persistent_count = PCount1, - pending_ack = PA1, - need_acking = gb_sets:insert(Guid, State1#vqstate.need_acking)})}. + {SeqId, a(State1 #vqstate { + next_seq_id = SeqId + 1, + out_counter = OutCount + 1, + in_counter = InCount + 1, + persistent_count = PCount1, + pending_ack = PA1, + need_acking = + case NeedsConfirming of + true -> gb_sets:insert(Guid, State1#vqstate.need_acking); + false -> State1#vqstate.need_acking + end })}. fetch(AckRequired, State = #vqstate { q4 = Q4, ram_msg_count = RamMsgCount, @@ -649,14 +655,14 @@ requeue(AckTags, State) -> a(reduce_memory_use( ack(fun rabbit_msg_store:release/2, fun (#msg_status { msg = Msg }, State1) -> - {_SeqId, State2} = publish(Msg, true, false, State1), + {_SeqId, State2} = publish(Msg, true, false, false, State1), State2; ({IsPersistent, Guid}, State1) -> #vqstate { msg_store_clients = MSCState } = State1, {{ok, Msg = #basic_message{}}, MSCState1} = read_from_msg_store(MSCState, IsPersistent, Guid), State2 = State1 #vqstate { msg_store_clients = MSCState1 }, - {_SeqId, State3} = publish(Msg, true, true, State2), + {_SeqId, State3} = publish(Msg, true, true, false, State2), State3 end, AckTags, State))). @@ -974,7 +980,7 @@ tx_commit_index(State = #vqstate { on_sync = #sync { fun (Msg = #basic_message { is_persistent = IsPersistent }, {SeqIdsAcc, State2}) -> IsPersistent1 = IsDurable andalso IsPersistent, - {SeqId, State3} = publish(Msg, false, IsPersistent1, State2), + {SeqId, State3} = publish(Msg, false, IsPersistent1, false, State2), {cons_if(IsPersistent1, SeqId, SeqIdsAcc), State3} end, {PAcks, element(1, ack(Acks, State))}, Pubs), IndexState1 = rabbit_queue_index:sync(SeqIds, IndexState), @@ -1022,7 +1028,7 @@ remove_queue_entries1( publish(Msg = #basic_message { is_persistent = IsPersistent, guid = Guid }, - IsDelivered, MsgOnDisk, + IsDelivered, MsgOnDisk, NeedsConfirming, State = #vqstate { q1 = Q1, q3 = Q3, q4 = Q4, next_seq_id = SeqId, len = Len, @@ -1039,12 +1045,17 @@ publish(Msg = #basic_message { is_persistent = IsPersistent, true -> State1 #vqstate { q4 = queue:in(m(MsgStatus1), Q4) } end, PCount1 = PCount + one_if(IsPersistent1), - {SeqId, State2 #vqstate { next_seq_id = SeqId + 1, - len = Len + 1, - in_counter = InCount + 1, - persistent_count = PCount1, - ram_msg_count = RamMsgCount + 1, - need_acking = gb_sets:add(Guid, State2#vqstate.need_acking) }}. + {SeqId, State2 #vqstate { + next_seq_id = SeqId + 1, + len = Len + 1, + in_counter = InCount + 1, + persistent_count = PCount1, + ram_msg_count = RamMsgCount + 1, + need_acking = + case NeedsConfirming of + true -> gb_sets:add(Guid, State2#vqstate.need_acking); + false -> State2#vqstate.need_acking + end }}. maybe_write_msg_to_disk(_Force, MsgStatus = #msg_status { msg_on_disk = true }, MSCState) -> -- cgit v1.2.1 From 7d6c8f26d536a4d76d37df19b3d6d8cab2ed48c5 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Mon, 27 Sep 2010 10:23:03 +0100 Subject: cosmetic --- src/rabbit_variable_queue.erl | 46 +++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 83448c54..3c0e14a0 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -239,7 +239,7 @@ rates, msgs_on_disk, msg_indices_on_disk, - need_acking + need_confirming }). -record(rates, { egress, ingress, avg_egress, avg_ingress, timestamp }). @@ -328,7 +328,7 @@ rates :: rates(), msgs_on_disk :: gb_set(), msg_indices_on_disk :: gb_set(), - need_acking :: gb_set()}). + need_confirming :: gb_set()}). -include("rabbit_backing_queue_spec.hrl"). @@ -447,7 +447,7 @@ init(QueueName, IsDurable, Recover) -> timestamp = Now }, msgs_on_disk = gb_sets:new(), msg_indices_on_disk = gb_sets:new(), - need_acking = gb_sets:new()}, + need_confirming = gb_sets:new()}, a(maybe_deltas_to_betas(State)). terminate(State) -> @@ -537,10 +537,10 @@ publish_delivered(true, Msg = #basic_message { is_persistent = IsPersistent, in_counter = InCount + 1, persistent_count = PCount1, pending_ack = PA1, - need_acking = + need_confirming = case NeedsConfirming of - true -> gb_sets:insert(Guid, State1#vqstate.need_acking); - false -> State1#vqstate.need_acking + true -> gb_sets:insert(Guid, State1#vqstate.need_confirming); + false -> State1#vqstate.need_confirming end })}. fetch(AckRequired, State = #vqstate { q4 = Q4, @@ -1051,10 +1051,10 @@ publish(Msg = #basic_message { is_persistent = IsPersistent, in_counter = InCount + 1, persistent_count = PCount1, ram_msg_count = RamMsgCount + 1, - need_acking = + need_confirming = case NeedsConfirming of - true -> gb_sets:add(Guid, State2#vqstate.need_acking); - false -> State2#vqstate.need_acking + true -> gb_sets:add(Guid, State2#vqstate.need_confirming); + false -> State2#vqstate.need_confirming end }}. maybe_write_msg_to_disk(_Force, MsgStatus = #msg_status { @@ -1179,34 +1179,34 @@ accumulate_ack(SeqId, {IsPersistent, Guid}, {SeqIdsAcc, Dict}) -> %% Internal plumbing for confirms (aka publisher acks) %%---------------------------------------------------------------------------- -msgs_confirmed(Guids, State = #vqstate { msgs_on_disk = MOD, +msgs_confirmed(Guids, State = #vqstate { msgs_on_disk = MOD, msg_indices_on_disk = MIOD, - need_acking = NA }) -> + need_confirming = NC }) -> GuidSet = gb_sets:from_list(Guids), State #vqstate { msgs_on_disk = gb_sets:difference(MOD, GuidSet), msg_indices_on_disk = gb_sets:difference(MIOD, GuidSet), - need_acking = - gb_sets:difference(NA, GuidSet) }. + need_confirming = + gb_sets:difference(NC, GuidSet) }. msgs_written_to_disk(QPid, Guids) -> spawn(fun() -> rabbit_amqqueue:maybe_run_queue_via_backing_queue( QPid, - fun(State = #vqstate { msgs_on_disk = MOD, + fun(State = #vqstate { msgs_on_disk = MOD, msg_indices_on_disk = MIOD, - need_acking = NA }) -> + need_confirming = NC }) -> GuidSet = gb_sets:from_list(Guids), ToConfirmMsgs = gb_sets:intersection(GuidSet, MIOD), - MOD1 = gb_sets:intersection(gb_sets:union(MOD, GuidSet), NA), + MOD1 = gb_sets:intersection(gb_sets:union(MOD, GuidSet), NC), { State #vqstate { msgs_on_disk = gb_sets:difference(MOD1, ToConfirmMsgs), msg_indices_on_disk = gb_sets:difference(MIOD, ToConfirmMsgs), - need_acking = - gb_sets:difference(NA, ToConfirmMsgs) }, + need_confirming = + gb_sets:difference(NC, ToConfirmMsgs) }, {confirm, gb_sets:to_list(ToConfirmMsgs)} } end) end). @@ -1215,20 +1215,20 @@ msg_indices_written_to_disk(Guids) -> Self = self(), spawn(fun() -> rabbit_amqqueue:maybe_run_queue_via_backing_queue( Self, - fun(State = #vqstate { msgs_on_disk = MOD, + fun(State = #vqstate { msgs_on_disk = MOD, msg_indices_on_disk = MIOD, - need_acking = NA }) -> + need_confirming = NC }) -> GuidSet = gb_sets:from_list(Guids), ToConfirmMsgs = gb_sets:intersection(GuidSet, MOD), MIOD1 = - gb_sets:intersection(gb_sets:union(MIOD, GuidSet), NA), + gb_sets:intersection(gb_sets:union(MIOD, GuidSet), NC), { State #vqstate { msgs_on_disk = gb_sets:difference(MOD, ToConfirmMsgs), msg_indices_on_disk = gb_sets:difference(MIOD1, ToConfirmMsgs), - need_acking = - gb_sets:difference(NA, ToConfirmMsgs) }, + need_confirming = + gb_sets:difference(NC, ToConfirmMsgs) }, {confirm, gb_sets:to_list(ToConfirmMsgs)} } end) end). -- cgit v1.2.1 From 2e85630d0caeddc330fb9d451395087e75f1d8f6 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Mon, 27 Sep 2010 11:53:57 +0100 Subject: better separation between amqqueue_process and bq --- src/rabbit_amqqueue_process.erl | 13 +++++++------ src/rabbit_msg_store.erl | 5 ++--- src/rabbit_variable_queue.erl | 26 +++++++++++--------------- 3 files changed, 20 insertions(+), 24 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index bc2ffd17..b8b0cf8d 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -870,11 +870,11 @@ handle_cast({ack, Txn, AckTags, ChPid}, none -> ChAckTags1 = subtract_acks(ChAckTags, AckTags), NewC = C#cr{acktags = ChAckTags1}, - {NewBQS, {confirm, AckdGuids}} = BQ:ack(AckTags, BQS), - NewState = - confirm_messages_internal(AckdGuids, - State #q { backing_queue_state = - NewBQS }), + AckdGuids = BQ:seqids_to_guids(AckTags, BQS), + NewBQS = BQ:ack(AckTags, BQS), + NewState = confirm_messages_internal( + AckdGuids, + State #q { backing_queue_state = NewBQS }), {NewC, NewState}; _ -> {C#cr{txn = Txn}, @@ -894,7 +894,8 @@ handle_cast({reject, AckTags, Requeue, ChPid}, store_ch_record(C#cr{acktags = ChAckTags1}), noreply(case Requeue of true -> requeue_and_run(AckTags, State); - false -> {BQS1, {confirm, AckdGuids}} = BQ:ack(AckTags, BQS), + false -> AckdGuids = BQ:seqids_to_guids(AckTags, BQS), + BQS1 = BQ:ack(AckTags, BQS), confirm_messages_internal( AckdGuids, State #q { backing_queue_state = BQS1 }) diff --git a/src/rabbit_msg_store.erl b/src/rabbit_msg_store.erl index 3cf2ec4f..91904562 100644 --- a/src/rabbit_msg_store.erl +++ b/src/rabbit_msg_store.erl @@ -638,9 +638,8 @@ handle_call({client_terminate, #client_msstate { client_ref = CRef }}, _From, State = #msstate { client_ondisk_callback = CODC, cref_to_guids = CTG }) -> - reply(ok, - State #msstate { client_ondisk_callback = dict:erase(CRef, CODC), - cref_to_guids = dict:erase(CRef, CTG) }). + reply(ok, State #msstate { client_ondisk_callback = dict:erase(CRef, CODC), + cref_to_guids = dict:erase(CRef, CTG) }). handle_cast({write, CRef, Guid}, State = #msstate { current_file_handle = CurHdl, diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 3c0e14a0..efdf34f2 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -37,7 +37,7 @@ requeue/2, len/1, is_empty/1, set_ram_duration_target/2, ram_duration/1, needs_idle_timeout/1, idle_timeout/1, handle_pre_hibernate/1, - status/1]). + status/1, seqids_to_guids/2]). -export([start/1, stop/0]). @@ -766,14 +766,16 @@ status(#vqstate { q1 = Q1, q2 = Q2, delta = Delta, q3 = Q3, q4 = Q4, {avg_egress_rate , AvgEgressRate}, {avg_ingress_rate , AvgIngressRate} ]. +seqids_to_guids(SeqIds, #vqstate{ pending_ack = PA }) -> + lists:foldl(fun(SeqId, Guids) -> + {ok, #msg_status { msg = Msg }} = dict:find(SeqId, PA), + [Msg#basic_message.guid | Guids] + end, [], SeqIds). + %%---------------------------------------------------------------------------- %% Minor helpers %%---------------------------------------------------------------------------- -a({State, _} = I) -> - a(State), - I; - a(State = #vqstate { q1 = Q1, q2 = Q2, delta = Delta, q3 = Q3, q4 = Q4, len = Len, persistent_count = PersistentCount, @@ -1140,7 +1142,7 @@ remove_pending_ack(KeepPersistent, end. ack(_MsgStoreFun, _Fun, [], State) -> - {State, {confirm, []}}; + State; ack(MsgStoreFun, Fun, AckTags, State) -> {{SeqIds, GuidsByStore}, State1 = #vqstate { index_state = IndexState, persistent_count = PCount }} = @@ -1155,16 +1157,13 @@ ack(MsgStoreFun, Fun, AckTags, State) -> ok = orddict:fold(fun (MsgStore, Guids, ok) -> MsgStoreFun(MsgStore, Guids) end, ok, GuidsByStore), - AckdGuids = lists:append([Guids || - {_Store, Guids} <- orddict:to_list(GuidsByStore)]), - State2 = msgs_confirmed(AckdGuids, State1), + State2 = msgs_confirmed(seqids_to_guids(AckTags, State), State1), PCount1 = PCount - case orddict:find(?PERSISTENT_MSG_STORE, GuidsByStore) of error -> 0; {ok, Guids} -> length(Guids) end, - {State2 #vqstate { index_state = IndexState1, - persistent_count = PCount1 }, - {confirm, AckdGuids}}. + State2 #vqstate { index_state = IndexState1, + persistent_count = PCount1 }. accumulate_ack(_SeqId, #msg_status { is_persistent = false, %% ASSERTIONS msg_on_disk = false, @@ -1272,9 +1271,6 @@ reduce_memory_use(AlphaBetaFun, BetaGammaFun, BetaDeltaFun, State) -> end end. -reduce_memory_use({State, Other}) -> - {reduce_memory_use(State), Other}; - reduce_memory_use(State) -> {_, State1} = reduce_memory_use(fun push_alphas_to_betas/2, fun limit_ram_index/2, -- cgit v1.2.1 From 1f3bd000a870ce95fc25fed3a2c61869342e0a4c Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Mon, 27 Sep 2010 14:28:32 +0100 Subject: bugfixes; passes same java tests as default The queue lease test fails with: There was 1 failure: 1) testExpireMustBeLong(com.rabbitmq.client.test.functional.QueueLease)junit.framework.AssertionFailedError: server accepted x-expires not of type long at com.rabbitmq.client.test.functional.QueueLease.testExpireMustBeLong(QueueLease.java:80) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) This also happens on default. --- src/rabbit_amqqueue_process.erl | 2 +- src/rabbit_variable_queue.erl | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index b8b0cf8d..482d2a37 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -399,7 +399,7 @@ deliver_from_queue_deliver(AckRequired, false, {{Message, IsDelivered, AckTag}, 0 == Remaining, State #q { backing_queue_state = BQS1 }}. -confirm_messages_internal(Guids, State) -> +confirm_messages_internal(Guids, State) when is_list(Guids) -> lists:foldl(fun(Guid, State0) -> confirm_message_internal(Guid, State0) end, State, Guids). diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index efdf34f2..e3a08989 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -768,8 +768,11 @@ status(#vqstate { q1 = Q1, q2 = Q2, delta = Delta, q3 = Q3, q4 = Q4, seqids_to_guids(SeqIds, #vqstate{ pending_ack = PA }) -> lists:foldl(fun(SeqId, Guids) -> - {ok, #msg_status { msg = Msg }} = dict:find(SeqId, PA), - [Msg#basic_message.guid | Guids] + {ok, AckEntry} = dict:find(SeqId, PA), + [case AckEntry of + #msg_status { msg = Msg } -> Msg#basic_message.guid; + {_, Guid} -> Guid + end | Guids] end, [], SeqIds). %%---------------------------------------------------------------------------- @@ -984,7 +987,7 @@ tx_commit_index(State = #vqstate { on_sync = #sync { IsPersistent1 = IsDurable andalso IsPersistent, {SeqId, State3} = publish(Msg, false, IsPersistent1, false, State2), {cons_if(IsPersistent1, SeqId, SeqIdsAcc), State3} - end, {PAcks, element(1, ack(Acks, State))}, Pubs), + end, {PAcks, ack(Acks, State)}, Pubs), IndexState1 = rabbit_queue_index:sync(SeqIds, IndexState), [ Fun() || Fun <- lists:reverse(SFuns) ], reduce_memory_use( -- cgit v1.2.1 From 9a2507d580f2567463e706073390e39cb281353d Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Mon, 27 Sep 2010 14:38:12 +0100 Subject: revert rabbit_tests to default --- src/rabbit_tests.erl | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 27235154..a72656b7 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -1608,8 +1608,7 @@ init_test_queue() -> test_queue(), true, false, fun (Guid) -> rabbit_msg_store:contains(?PERSISTENT_MSG_STORE, Guid) - end, - fun (_Guids) -> ok end). + end). restart_test_queue(Qi) -> _ = rabbit_queue_index:terminate([], Qi), @@ -1976,5 +1975,3 @@ test_queue_recover() -> rabbit_amqqueue:internal_delete(QName) end), passed. - -nop(_) -> ok. -- cgit v1.2.1 From 537ee7a8b46852b183c3e2f6b68ad13ade9ee602 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Mon, 27 Sep 2010 15:48:01 +0100 Subject: server tests pass --- src/rabbit_msg_store.erl | 2 +- src/rabbit_tests.erl | 21 +++++++++++++++------ src/rabbit_variable_queue.erl | 18 ++++++++++++------ 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/src/rabbit_msg_store.erl b/src/rabbit_msg_store.erl index 91904562..695b4425 100644 --- a/src/rabbit_msg_store.erl +++ b/src/rabbit_msg_store.erl @@ -681,7 +681,7 @@ handle_cast({write, CRef, Guid}, cref_to_guids = case dict:find(CRef, CODC) of {ok, _} -> rabbit_misc:dict_cons(CRef, Guid, CTG); - error -> CRef + error -> CTG end})); #msg_location { ref_count = RefCount } -> %% We already know about it, just update counter. Only diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index a72656b7..b8143900 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -75,7 +75,6 @@ all_tests() -> passed = maybe_run_cluster_dependent_tests(), passed. - maybe_run_cluster_dependent_tests() -> SecondaryNode = rabbit_misc:makenode("hare"), @@ -1608,6 +1607,9 @@ init_test_queue() -> test_queue(), true, false, fun (Guid) -> rabbit_msg_store:contains(?PERSISTENT_MSG_STORE, Guid) + end, + fun (_) -> + ok %% Sync! end). restart_test_queue(Qi) -> @@ -1790,7 +1792,8 @@ variable_queue_publish(IsPersistent, Count, VQ) -> <<>>, #'P_basic'{delivery_mode = case IsPersistent of true -> 2; false -> 1 - end}, <<>>), VQN) + end}, <<>>), + false, VQN) end, VQ, lists:seq(1, Count)). variable_queue_fetch(Count, IsPersistent, IsDelivered, Len, VQ) -> @@ -1810,7 +1813,8 @@ assert_props(List, PropVals) -> with_fresh_variable_queue(Fun) -> ok = empty_test_queue(), - VQ = rabbit_variable_queue:init(test_queue(), true, false), + VQ = rabbit_variable_queue:init(test_queue(), true, false, + fun nop/1, fun nop/1), S0 = rabbit_variable_queue:status(VQ), assert_props(S0, [{q1, 0}, {q2, 0}, {delta, {delta, undefined, 0, undefined}}, @@ -1922,7 +1926,8 @@ test_variable_queue_all_the_bits_not_covered_elsewhere1(VQ0) -> {VQ5, _AckTags1} = variable_queue_fetch(Count, false, false, Count, VQ4), _VQ6 = rabbit_variable_queue:terminate(VQ5), - VQ7 = rabbit_variable_queue:init(test_queue(), true, true), + VQ7 = rabbit_variable_queue:init(test_queue(), true, true, + fun nop/1, fun nop/1), {{_Msg1, true, _AckTag1, Count1}, VQ8} = rabbit_variable_queue:fetch(true, VQ7), VQ9 = variable_queue_publish(false, 1, VQ8), @@ -1938,7 +1943,8 @@ test_variable_queue_all_the_bits_not_covered_elsewhere2(VQ0) -> VQ4 = rabbit_variable_queue:requeue(AckTags, VQ3), VQ5 = rabbit_variable_queue:idle_timeout(VQ4), _VQ6 = rabbit_variable_queue:terminate(VQ5), - VQ7 = rabbit_variable_queue:init(test_queue(), true, true), + VQ7 = rabbit_variable_queue:init(test_queue(), true, true, + fun nop/1, fun nop/1), {empty, VQ8} = rabbit_variable_queue:fetch(false, VQ7), VQ8. @@ -1968,10 +1974,13 @@ test_queue_recover() -> {ok, CountMinusOne, {QName, QPid1, _AckTag, true, _Msg}} = rabbit_amqqueue:basic_get(Q1, self(), false), exit(QPid1, shutdown), - VQ1 = rabbit_variable_queue:init(QName, true, true), + VQ1 = rabbit_variable_queue:init(QName, true, true, + fun nop/1, fun nop/1), {{_Msg1, true, _AckTag1, CountMinusOne}, VQ2} = rabbit_variable_queue:fetch(true, VQ1), _VQ3 = rabbit_variable_queue:delete_and_terminate(VQ2), rabbit_amqqueue:internal_delete(QName) end), passed. + +nop(_) -> ok. diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index e3a08989..9256b8ac 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -31,7 +31,7 @@ -module(rabbit_variable_queue). --export([init/3, terminate/1, delete_and_terminate/1, +-export([init/5, init/3, terminate/1, delete_and_terminate/1, purge/1, publish/3, publish_delivered/4, fetch/2, ack/2, tx_publish/3, tx_ack/3, tx_rollback/2, tx_commit/3, requeue/2, len/1, is_empty/1, @@ -375,6 +375,15 @@ stop_msg_store() -> ok = rabbit_sup:stop_child(?TRANSIENT_MSG_STORE). init(QueueName, IsDurable, Recover) -> + Self = self(), + init(QueueName, IsDurable, Recover, + fun (Guids) -> + msgs_written_to_disk(Self, Guids) + end, + fun msg_indices_written_to_disk/1). + +init(QueueName, IsDurable, Recover, + MsgOnDiskFun, MsgIdxOnDiskFun) -> {DeltaCount, Terms, IndexState} = rabbit_queue_index:init( QueueName, Recover, @@ -382,7 +391,7 @@ init(QueueName, IsDurable, Recover) -> fun (Guid) -> rabbit_msg_store:contains(?PERSISTENT_MSG_STORE, Guid) end, - fun msg_indices_written_to_disk/1), + MsgIdxOnDiskFun), {LowSeqId, NextSeqId, IndexState1} = rabbit_queue_index:bounds(IndexState), {PRef, TRef, Terms1} = @@ -406,13 +415,10 @@ init(QueueName, IsDurable, Recover) -> false -> undefined end, - Self = self(), rabbit_msg_store:register_sync_callback( ?PERSISTENT_MSG_STORE, PRef, - fun (Guids) -> - msgs_written_to_disk(Self, Guids) - end), + MsgOnDiskFun), TransientClient = rabbit_msg_store:client_init(?TRANSIENT_MSG_STORE, TRef), State = #vqstate { -- cgit v1.2.1 From f3c1eb4e58519762231cf7e21c6bebfed16d6749 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Wed, 29 Sep 2010 10:48:42 +0100 Subject: refactor --- src/rabbit_queue_index.erl | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/rabbit_queue_index.erl b/src/rabbit_queue_index.erl index d4b613ff..9b4255da 100644 --- a/src/rabbit_queue_index.erl +++ b/src/rabbit_queue_index.erl @@ -228,13 +228,11 @@ init(Name, false, _MsgStoreRecovered, _ContainsCheckFun, OnSyncFun) -> State = #qistate { dir = Dir } = blank_state(Name), false = filelib:is_file(Dir), %% is_file == is file or dir - {0, [], State #qistate { on_sync = OnSyncFun, - unsynced_guids = []}}; + {0, [], State #qistate { on_sync = OnSyncFun }}; init(Name, true, MsgStoreRecovered, ContainsCheckFun, OnSyncFun) -> State = #qistate { dir = Dir } = blank_state(Name), - State1 = State #qistate { on_sync = OnSyncFun, - unsynced_guids = [] }, + State1 = State #qistate { on_sync = OnSyncFun }, Terms = case read_shutdown_terms(Dir) of {error, _} -> []; {ok, Terms1} -> Terms1 @@ -381,7 +379,8 @@ blank_state(QueueName) -> segments = segments_new(), journal_handle = undefined, dirty_count = 0, - max_journal_entries = MaxJournal }. + max_journal_entries = MaxJournal, + unsynced_guids = [] }. clean_file_name(Dir) -> filename:join(Dir, ?CLEAN_FILENAME). -- cgit v1.2.1 From 74ff67d707d3597e45d1ddd7f93a10de5bcd8a50 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 29 Sep 2010 12:40:48 +0100 Subject: cosmetics and minor refactors --- src/rabbit_channel.erl | 45 +++++++++++++++---------------- src/rabbit_msg_store.erl | 16 +++++------ src/rabbit_variable_queue.erl | 63 ++++++++++++++++--------------------------- 3 files changed, 53 insertions(+), 71 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 4bb1f13b..85ace25b 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -460,30 +460,29 @@ send_or_enqueue_ack(undefined, State) -> State; send_or_enqueue_ack(_, State = #ch{confirm_enabled = false}) -> State; -send_or_enqueue_ack(MsgSeqNo, - State = #ch{confirm_multiple = false}) -> - do_if_not_dup(MsgSeqNo, State, - fun(MSN, S = #ch{writer_pid = WriterPid, - qpid_to_msgs = QTM}) -> - ok = rabbit_writer:send_command( - WriterPid, #'basic.ack'{delivery_tag = MSN}), - S #ch { qpid_to_msgs = - dict:map(fun (_, Msgs) -> - gb_sets:delete_any(MsgSeqNo, Msgs) - end, QTM) } - end); +send_or_enqueue_ack(MsgSeqNo, State = #ch{confirm_multiple = false}) -> + do_if_not_dup( + MsgSeqNo, State, + fun(MSN, State1 = #ch{writer_pid = WriterPid, qpid_to_msgs = QTM}) -> + ok = rabbit_writer:send_command( + WriterPid, #'basic.ack'{delivery_tag = MSN}), + QTM1 = dict:map(fun (_, Msgs) -> + gb_sets:delete_any(MsgSeqNo, Msgs) + end, QTM), + State1#ch{qpid_to_msgs = QTM1} + end); send_or_enqueue_ack(MsgSeqNo, State = #ch{confirm_multiple = true}) -> - do_if_not_dup(MsgSeqNo, State, - fun(MSN, S = #ch{qpid_to_msgs = QTM}) -> - State1 = start_ack_timer(S), - State1 #ch { held_confirms = - gb_sets:add(MSN, State1#ch.held_confirms), - qpid_to_msgs = - dict:map(fun (_, Msgs) -> - gb_sets:delete_any(MsgSeqNo, - Msgs) - end, QTM) } - end). + do_if_not_dup( + MsgSeqNo, State, + fun(MSN, State1 = #ch{qpid_to_msgs = QTM}) -> + QTM1 = dict:map(fun (_, Msgs) -> + gb_sets:delete_any(MsgSeqNo, Msgs) + end, QTM), + start_ack_timer( + State1#ch{held_confirms = + gb_sets:add(MSN, State1#ch.held_confirms), + qpid_to_msgs = QTM1}) + end). msg_sent_to_queues(MsgSeqNo, QPid, State = #ch{qpid_to_msgs = QTM}) -> case dict:find(QPid, QTM) of diff --git a/src/rabbit_msg_store.erl b/src/rabbit_msg_store.erl index 695b4425..7d0af80a 100644 --- a/src/rabbit_msg_store.erl +++ b/src/rabbit_msg_store.erl @@ -814,15 +814,15 @@ reply(Reply, State) -> {reply, Reply, State1, Timeout}. next_state(State = #msstate { sync_timer_ref = undefined, - on_sync = OS, - cref_to_guids = CTG }) -> - case {OS, dict:size(CTG)} of + on_sync = Syncs, + cref_to_guids = CTG }) -> + case {Syncs, dict:size(CTG)} of {[], 0} -> {State, hibernate}; _ -> {start_sync_timer(State), 0} end; -next_state(State = #msstate { on_sync = OS, +next_state(State = #msstate { on_sync = Syncs, cref_to_guids = CTG }) -> - case {OS, dict:size(CTG)} of + case {Syncs, dict:size(CTG)} of {[], 0} -> {stop_sync_timer(State), hibernate}; _ -> {State, 0} end. @@ -837,10 +837,10 @@ stop_sync_timer(State = #msstate { sync_timer_ref = TRef }) -> {ok, cancel} = timer:cancel(TRef), State #msstate { sync_timer_ref = undefined }. -internal_sync(State = #msstate { current_file_handle = CurHdl, - on_sync = Syncs, +internal_sync(State = #msstate { current_file_handle = CurHdl, + on_sync = Syncs, client_ondisk_callback = CODC, - cref_to_guids = CTG }) -> + cref_to_guids = CTG }) -> State1 = stop_sync_timer(State), State2 = case Syncs of [] -> State1; diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 9256b8ac..c763fe4d 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -415,10 +415,8 @@ init(QueueName, IsDurable, Recover, false -> undefined end, - rabbit_msg_store:register_sync_callback( - ?PERSISTENT_MSG_STORE, - PRef, - MsgOnDiskFun), + rabbit_msg_store:register_sync_callback(?PERSISTENT_MSG_STORE, PRef, + MsgOnDiskFun), TransientClient = rabbit_msg_store:client_init(?TRANSIENT_MSG_STORE, TRef), State = #vqstate { @@ -773,13 +771,13 @@ status(#vqstate { q1 = Q1, q2 = Q2, delta = Delta, q3 = Q3, q4 = Q4, {avg_ingress_rate , AvgIngressRate} ]. seqids_to_guids(SeqIds, #vqstate{ pending_ack = PA }) -> - lists:foldl(fun(SeqId, Guids) -> - {ok, AckEntry} = dict:find(SeqId, PA), - [case AckEntry of - #msg_status { msg = Msg } -> Msg#basic_message.guid; - {_, Guid} -> Guid - end | Guids] - end, [], SeqIds). + lists:foldl( + fun(SeqId, Guids) -> + [case dict:fetch(SeqId, PA) of + #msg_status { msg = Msg } -> Msg#basic_message.guid; + {_, Guid} -> Guid + end | Guids] + end, [], SeqIds). %%---------------------------------------------------------------------------- %% Minor helpers @@ -1166,7 +1164,8 @@ ack(MsgStoreFun, Fun, AckTags, State) -> ok = orddict:fold(fun (MsgStore, Guids, ok) -> MsgStoreFun(MsgStore, Guids) end, ok, GuidsByStore), - State2 = msgs_confirmed(seqids_to_guids(AckTags, State), State1), + State2 = msgs_confirmed(gb_sets:from_list(seqids_to_guids(AckTags, State1)), + State1), PCount1 = PCount - case orddict:find(?PERSISTENT_MSG_STORE, GuidsByStore) of error -> 0; {ok, Guids} -> length(Guids) @@ -1187,17 +1186,12 @@ accumulate_ack(SeqId, {IsPersistent, Guid}, {SeqIdsAcc, Dict}) -> %% Internal plumbing for confirms (aka publisher acks) %%---------------------------------------------------------------------------- -msgs_confirmed(Guids, State = #vqstate { msgs_on_disk = MOD, - msg_indices_on_disk = MIOD, - need_confirming = NC }) -> - GuidSet = gb_sets:from_list(Guids), - State #vqstate { - msgs_on_disk = - gb_sets:difference(MOD, GuidSet), - msg_indices_on_disk = - gb_sets:difference(MIOD, GuidSet), - need_confirming = - gb_sets:difference(NC, GuidSet) }. +msgs_confirmed(GuidSet, State = #vqstate { msgs_on_disk = MOD, + msg_indices_on_disk = MIOD, + need_confirming = NC }) -> + State #vqstate { msgs_on_disk = gb_sets:difference(MOD, GuidSet), + msg_indices_on_disk = gb_sets:difference(MIOD, GuidSet), + need_confirming = gb_sets:difference(NC, GuidSet) }. msgs_written_to_disk(QPid, Guids) -> spawn(fun() -> rabbit_amqqueue:maybe_run_queue_via_backing_queue( @@ -1207,14 +1201,9 @@ msgs_written_to_disk(QPid, Guids) -> need_confirming = NC }) -> GuidSet = gb_sets:from_list(Guids), ToConfirmMsgs = gb_sets:intersection(GuidSet, MIOD), - MOD1 = gb_sets:intersection(gb_sets:union(MOD, GuidSet), NC), - { State #vqstate { - msgs_on_disk = - gb_sets:difference(MOD1, ToConfirmMsgs), - msg_indices_on_disk = - gb_sets:difference(MIOD, ToConfirmMsgs), - need_confirming = - gb_sets:difference(NC, ToConfirmMsgs) }, + State1 = State #vqstate { msgs_on_disk = + gb_sets:intersection(gb_sets:union(MOD, GuidSet), NC) }, + { msgs_confirmed(ToConfirmMsgs, State1), {confirm, gb_sets:to_list(ToConfirmMsgs)} } end) end). @@ -1228,15 +1217,9 @@ msg_indices_written_to_disk(Guids) -> need_confirming = NC }) -> GuidSet = gb_sets:from_list(Guids), ToConfirmMsgs = gb_sets:intersection(GuidSet, MOD), - MIOD1 = - gb_sets:intersection(gb_sets:union(MIOD, GuidSet), NC), - { State #vqstate { - msgs_on_disk = - gb_sets:difference(MOD, ToConfirmMsgs), - msg_indices_on_disk = - gb_sets:difference(MIOD1, ToConfirmMsgs), - need_confirming = - gb_sets:difference(NC, ToConfirmMsgs) }, + State1 = State #vqstate { msg_indices_on_disk = + gb_sets:intersection(gb_sets:union(MIOD, GuidSet), NC) }, + { msgs_confirmed(ToConfirmMsgs, State1), {confirm, gb_sets:to_list(ToConfirmMsgs)} } end) end). -- cgit v1.2.1 From 283b74d2fe78d042765f4b27602a8800f36ee069 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Wed, 29 Sep 2010 13:52:25 +0100 Subject: refactor --- include/rabbit_backing_queue_spec.hrl | 6 +++-- src/rabbit_amqqueue.erl | 18 +++++-------- src/rabbit_amqqueue_process.erl | 48 +++++++++++++++++++++-------------- src/rabbit_variable_queue.erl | 3 ++- 4 files changed, 41 insertions(+), 34 deletions(-) diff --git a/include/rabbit_backing_queue_spec.hrl b/include/rabbit_backing_queue_spec.hrl index c01e9246..8addfb9b 100644 --- a/include/rabbit_backing_queue_spec.hrl +++ b/include/rabbit_backing_queue_spec.hrl @@ -36,6 +36,7 @@ -type(attempt_recovery() :: boolean()). -type(purged_msg_count() :: non_neg_integer()). -type(ack_required() :: boolean()). +-type(confirm_required() :: boolean()). -spec(start/1 :: ([rabbit_amqqueue:name()]) -> 'ok'). -spec(stop/0 :: () -> 'ok'). @@ -43,9 +44,10 @@ -spec(terminate/1 :: (state()) -> state()). -spec(delete_and_terminate/1 :: (state()) -> state()). -spec(purge/1 :: (state()) -> {purged_msg_count(), state()}). --spec(publish/3 :: (rabbit_types:basic_message(), boolean(), state()) -> state()). +-spec(publish/3 :: + (rabbit_types:basic_message(), confirm_required(), state()) -> state()). -spec(publish_delivered/4 :: - (ack_required(), rabbit_types:basic_message(), boolean(), state()) + (ack_required(), rabbit_types:basic_message(), confirm_required(), state()) -> {ack(), state()}). -spec(fetch/2 :: (ack_required(), state()) -> {fetch_result(), state()}). -spec(ack/2 :: ([ack()], state()) -> state()). diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index d756fcb9..c7d63d08 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -370,19 +370,13 @@ delete(#amqqueue{ pid = QPid }, IfUnused, IfEmpty) -> purge(#amqqueue{ pid = QPid }) -> delegate_call(QPid, purge, infinity). -deliver(QPid, #delivery{immediate = true, - txn = Txn, sender = ChPid, message = Message, - msg_seq_no = MsgSeqNo}) -> - gen_server2:call(QPid, {deliver_immediately, Txn, Message, MsgSeqNo, ChPid}, - infinity); -deliver(QPid, #delivery{mandatory = true, - txn = Txn, sender = ChPid, message = Message, - msg_seq_no = MsgSeqNo}) -> - gen_server2:call(QPid, {deliver, Txn, Message, MsgSeqNo, ChPid}, infinity), +deliver(QPid, Delivery = #delivery{immediate = true}) -> + gen_server2:call(QPid, {deliver_immediately, Delivery}, infinity); +deliver(QPid, Delivery = #delivery{mandatory = true}) -> + gen_server2:call(QPid, {deliver, Delivery}, infinity), true; -deliver(QPid, #delivery{txn = Txn, sender = ChPid, message = Message, - msg_seq_no = MsgSeqNo}) -> - gen_server2:cast(QPid, {deliver, Txn, Message, MsgSeqNo, ChPid}), +deliver(QPid, Delivery) -> + gen_server2:cast(QPid, {deliver, Delivery}), true. requeue(QPid, MsgIds, ChPid) -> diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 824c4401..0fc7ee35 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -404,7 +404,7 @@ confirm_messages_internal(Guids, State) when is_list(Guids) -> confirm_message_internal(Guid, State0) end, State, Guids). -confirm_message_internal(Guid, State = #q { guid_to_channel = GTC }) -> +confirm_message_internal(Guid, State = #q{guid_to_channel = GTC}) -> case dict:find(Guid, GTC) of {ok, {_ , undefined}} -> ok; {ok, {ChPid, MsgSeqNo}} -> rabbit_channel:confirm(ChPid, MsgSeqNo); @@ -412,11 +412,11 @@ confirm_message_internal(Guid, State = #q { guid_to_channel = GTC }) -> end, State #q { guid_to_channel = dict:erase(Guid, GTC) }. -maybe_record_confirm_message(undefined, _, _, State) -> +maybe_record_confirm_message(#delivery{msg_seq_no = undefined }, State) -> State; -maybe_record_confirm_message(MsgSeqNo, - #basic_message { guid = Guid }, - ChPid, State) -> +maybe_record_confirm_message(#delivery{sender = ChPid, + message = #basic_message{guid = Guid}, + msg_seq_no = MsgSeqNo}, State) -> State #q { guid_to_channel = dict:store(Guid, {ChPid, MsgSeqNo}, State#q.guid_to_channel) }. @@ -427,7 +427,10 @@ run_message_queue(State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> {_IsEmpty1, State1} = deliver_msgs_to_consumers(Funs, IsEmpty, State), State1. -attempt_delivery(none, _ChPid, Message, MsgSeqNo, State = #q{backing_queue = BQ}) -> +attempt_delivery(#delivery{txn = none, + message = Message, + msg_seq_no = MsgSeqNo}, + State = #q{backing_queue = BQ}) -> PredFun = fun (IsEmpty, _State) -> not IsEmpty end, DeliverFun = fun (AckRequired, false, State1 = #q{backing_queue_state = BQS}) -> @@ -438,13 +441,20 @@ attempt_delivery(none, _ChPid, Message, MsgSeqNo, State = #q{backing_queue = BQ} State1#q{backing_queue_state = BQS1}} end, deliver_msgs_to_consumers({ PredFun, DeliverFun }, false, State); -attempt_delivery(Txn, ChPid, Message, _MSN, State = #q{backing_queue = BQ, - backing_queue_state = BQS}) -> +attempt_delivery(#delivery{txn = Txn, + sender = ChPid, + message = Message}, + State = #q{backing_queue = BQ, + backing_queue_state = BQS}) -> record_current_channel_tx(ChPid, Txn), {true, State#q{backing_queue_state = BQ:tx_publish(Txn, Message, BQS)}}. -deliver_or_enqueue(Txn, ChPid, Message, MsgSeqNo, State = #q{backing_queue = BQ}) -> - case attempt_delivery(Txn, ChPid, Message, MsgSeqNo, State) of +deliver_or_enqueue(Delivery = #delivery{txn = Txn, + sender = ChPid, + message = Message, + msg_seq_no = MsgSeqNo}, + State = #q{backing_queue = BQ}) -> + case attempt_delivery(Delivery, State) of {true, NewState} -> {true, NewState}; {false, NewState} -> @@ -690,7 +700,7 @@ handle_call(consumers, _From, [{ChPid, ConsumerTag, AckRequired} | Acc] end, [], queue:join(ActiveConsumers, BlockedConsumers)), State); -handle_call({deliver_immediately, Txn, Message, MsgSeqNo, ChPid}, _From, State) -> +handle_call({deliver_immediately, Delivery}, _From, State) -> %% Synchronous, "immediate" delivery mode %% %% FIXME: Is this correct semantics? @@ -704,14 +714,14 @@ handle_call({deliver_immediately, Txn, Message, MsgSeqNo, ChPid}, _From, State) %% just all ready-to-consume queues get the message, with unready %% queues discarding the message? %% - State1 = maybe_record_confirm_message(MsgSeqNo, Message, ChPid, State), - {Delivered, State2} = attempt_delivery(Txn, ChPid, Message, MsgSeqNo, State1), + State1 = maybe_record_confirm_message(Delivery, State), + {Delivered, State2} = attempt_delivery(Delivery, State1), reply(Delivered, State2); -handle_call({deliver, Txn, Message, MsgSeqNo, ChPid}, _From, State) -> +handle_call({deliver, Delivery}, _From, State) -> %% Synchronous, "mandatory" delivery mode - State1 = maybe_record_confirm_message(MsgSeqNo, Message, ChPid, State), - {Delivered, State2} = deliver_or_enqueue(Txn, ChPid, Message, MsgSeqNo, State1), + State1 = maybe_record_confirm_message(Delivery, State), + {Delivered, State2} = deliver_or_enqueue(Delivery, State1), reply(Delivered, State2); handle_call({commit, Txn, ChPid}, From, State) -> @@ -868,10 +878,10 @@ handle_call({maybe_run_queue_via_backing_queue, Fun}, _From, State) -> reply(ok, maybe_run_queue_via_backing_queue(Fun, State)). -handle_cast({deliver, Txn, Message, MsgSeqNo, ChPid}, State) -> +handle_cast({deliver, Delivery}, State) -> %% Asynchronous, non-"mandatory", non-"immediate" deliver mode. - State1 = maybe_record_confirm_message(MsgSeqNo, Message, ChPid, State), - {_Delivered, State2} = deliver_or_enqueue(Txn, ChPid, Message, MsgSeqNo, State1), + State1 = maybe_record_confirm_message(Delivery, State), + {_Delivered, State2} = deliver_or_enqueue(Delivery, State1), noreply(State2); handle_cast({ack, Txn, AckTags, ChPid}, diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index c763fe4d..e6a93871 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -1164,7 +1164,8 @@ ack(MsgStoreFun, Fun, AckTags, State) -> ok = orddict:fold(fun (MsgStore, Guids, ok) -> MsgStoreFun(MsgStore, Guids) end, ok, GuidsByStore), - State2 = msgs_confirmed(gb_sets:from_list(seqids_to_guids(AckTags, State1)), + %% the AckTags were removed from State1, so use State in seqids_to_guids + State2 = msgs_confirmed(gb_sets:from_list(seqids_to_guids(AckTags, State)), State1), PCount1 = PCount - case orddict:find(?PERSISTENT_MSG_STORE, GuidsByStore) of error -> 0; -- cgit v1.2.1 From ace12dbca873ed44e86ec86ffd4d13d64bf3ad92 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Wed, 29 Sep 2010 14:36:28 +0100 Subject: refactoring --- src/rabbit_amqqueue_process.erl | 73 +++++++++++++++++++---------------------- 1 file changed, 34 insertions(+), 39 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 0fc7ee35..62ac6992 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -346,7 +346,7 @@ deliver_msgs_to_consumers(Funs = {PredFun, DeliverFun}, FunAcc, {State2, ChAckTags1} = case AckRequired of true -> {State1, sets:add_element(AckTag, ChAckTags)}; - false -> {confirm_message_internal( + false -> {confirm_message( Message#basic_message.guid, State1), ChAckTags} end, @@ -399,12 +399,12 @@ deliver_from_queue_deliver(AckRequired, false, {{Message, IsDelivered, AckTag}, 0 == Remaining, State #q { backing_queue_state = BQS1 }}. -confirm_messages_internal(Guids, State) when is_list(Guids) -> +confirm_messages(Guids, State) when is_list(Guids) -> lists:foldl(fun(Guid, State0) -> - confirm_message_internal(Guid, State0) + confirm_message(Guid, State0) end, State, Guids). -confirm_message_internal(Guid, State = #q{guid_to_channel = GTC}) -> +confirm_message(Guid, State = #q{guid_to_channel = GTC}) -> case dict:find(Guid, GTC) of {ok, {_ , undefined}} -> ok; {ok, {ChPid, MsgSeqNo}} -> rabbit_channel:confirm(ChPid, MsgSeqNo); @@ -412,13 +412,21 @@ confirm_message_internal(Guid, State = #q{guid_to_channel = GTC}) -> end, State #q { guid_to_channel = dict:erase(Guid, GTC) }. -maybe_record_confirm_message(#delivery{msg_seq_no = undefined }, State) -> +record_confirm_message(#delivery{msg_seq_no = undefined }, State) -> State; -maybe_record_confirm_message(#delivery{sender = ChPid, - message = #basic_message{guid = Guid}, - msg_seq_no = MsgSeqNo}, State) -> - State #q { guid_to_channel = - dict:store(Guid, {ChPid, MsgSeqNo}, State#q.guid_to_channel) }. +record_confirm_message(#delivery{sender = ChPid, + message = #basic_message{guid = Guid}, + msg_seq_no = MsgSeqNo}, + State = #q{guid_to_channel = GTC}) -> + State #q { guid_to_channel = dict:store(Guid, {ChPid, MsgSeqNo}, GTC) }. + +ack_by_acktags(AckTags, State = #q{backing_queue = BQ, + backing_queue_state = BQS}) -> + AckdGuids = BQ:seqids_to_guids(AckTags, BQS), + BQS1 = BQ:ack(AckTags, BQS), + confirm_messages( + AckdGuids, + State #q { backing_queue_state = BQS1 }). run_message_queue(State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> Funs = {fun deliver_from_queue_pred/2, @@ -449,18 +457,17 @@ attempt_delivery(#delivery{txn = Txn, record_current_channel_tx(ChPid, Txn), {true, State#q{backing_queue_state = BQ:tx_publish(Txn, Message, BQS)}}. -deliver_or_enqueue(Delivery = #delivery{txn = Txn, - sender = ChPid, - message = Message, +deliver_or_enqueue(Delivery = #delivery{message = Message, msg_seq_no = MsgSeqNo}, State = #q{backing_queue = BQ}) -> - case attempt_delivery(Delivery, State) of + State1 = record_confirm_message(Delivery, State), + case attempt_delivery(Delivery, State1) of {true, NewState} -> {true, NewState}; {false, NewState} -> %% Txn is none and no unblocked channels with consumers BQS = BQ:publish(Message, MsgSeqNo =/= undefined, - State #q.backing_queue_state), + State1 #q.backing_queue_state), {false, NewState#q{backing_queue_state = BQS}} end. @@ -562,8 +569,7 @@ maybe_run_queue_via_backing_queue(Fun, State = #q{backing_queue_state = BQS}) -> case Fun(BQS) of {BQS1, {confirm, Guids}} -> run_message_queue( - confirm_messages_internal(Guids, - State #q { backing_queue_state = BQS1 })); + confirm_messages(Guids, State #q { backing_queue_state = BQS1 })); BQS1 -> run_message_queue(State#q{backing_queue_state = BQS1}) end. @@ -714,15 +720,15 @@ handle_call({deliver_immediately, Delivery}, _From, State) -> %% just all ready-to-consume queues get the message, with unready %% queues discarding the message? %% - State1 = maybe_record_confirm_message(Delivery, State), - {Delivered, State2} = attempt_delivery(Delivery, State1), + {Delivered, State1} = attempt_delivery(Delivery, State), + State2 = confirm_message(Delivery#delivery.message#basic_message.guid, + record_confirm_message(Delivery, State1)), reply(Delivered, State2); handle_call({deliver, Delivery}, _From, State) -> %% Synchronous, "mandatory" delivery mode - State1 = maybe_record_confirm_message(Delivery, State), - {Delivered, State2} = deliver_or_enqueue(Delivery, State1), - reply(Delivered, State2); + {Delivered, State1} = deliver_or_enqueue(Delivery, State), + reply(Delivered, State1); handle_call({commit, Txn, ChPid}, From, State) -> NewState = commit_transaction(Txn, From, ChPid, State), @@ -754,8 +760,7 @@ handle_call({basic_get, ChPid, NoAck}, _From, C#cr{acktags = sets:add_element(AckTag, ChAckTags)}), State1; false -> - confirm_message_internal(Message#basic_message.guid, - State1) + confirm_message(Message#basic_message.guid, State1) end, Msg = {QName, self(), AckTag, IsDelivered, Message}, reply({ok, Remaining, Msg}, State2#q{backing_queue_state = BQS1}) @@ -880,9 +885,8 @@ handle_call({maybe_run_queue_via_backing_queue, Fun}, _From, State) -> handle_cast({deliver, Delivery}, State) -> %% Asynchronous, non-"mandatory", non-"immediate" deliver mode. - State1 = maybe_record_confirm_message(Delivery, State), - {_Delivered, State2} = deliver_or_enqueue(Delivery, State1), - noreply(State2); + {_Delivered, State1} = deliver_or_enqueue(Delivery, State), + noreply(State1); handle_cast({ack, Txn, AckTags, ChPid}, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> @@ -895,11 +899,7 @@ handle_cast({ack, Txn, AckTags, ChPid}, none -> ChAckTags1 = subtract_acks(ChAckTags, AckTags), NewC = C#cr{acktags = ChAckTags1}, - AckdGuids = BQ:seqids_to_guids(AckTags, BQS), - NewBQS = BQ:ack(AckTags, BQS), - NewState = confirm_messages_internal( - AckdGuids, - State #q { backing_queue_state = NewBQS }), + NewState = ack_by_acktags(AckTags, State), {NewC, NewState}; _ -> {C#cr{txn = Txn}, @@ -909,8 +909,7 @@ handle_cast({ack, Txn, AckTags, ChPid}, noreply(State1) end; -handle_cast({reject, AckTags, Requeue, ChPid}, - State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> +handle_cast({reject, AckTags, Requeue, ChPid}, State) -> case lookup_ch(ChPid) of not_found -> noreply(State); @@ -919,11 +918,7 @@ handle_cast({reject, AckTags, Requeue, ChPid}, store_ch_record(C#cr{acktags = ChAckTags1}), noreply(case Requeue of true -> requeue_and_run(AckTags, State); - false -> AckdGuids = BQ:seqids_to_guids(AckTags, BQS), - BQS1 = BQ:ack(AckTags, BQS), - confirm_messages_internal( - AckdGuids, - State #q { backing_queue_state = BQS1 }) + false -> ack_by_acktags(AckTags, State) end) end; -- cgit v1.2.1 From 62f7e8a396f791b5704c2d34cc65e6275def16ac Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Wed, 29 Sep 2010 14:43:58 +0100 Subject: cosmetic --- src/rabbit_channel.erl | 45 +++++++++++++++++++++------------------------ 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 85ace25b..14f85d59 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -283,8 +283,8 @@ handle_cast(flush_multiple_acks, held_confirms = As, need_confirming = NA}) -> handle_multiple_flush(WriterPid, As, NA), - {noreply, State #ch { held_confirms = gb_sets:new(), - confirm_tref = undefined }}; + {noreply, State#ch{held_confirms = gb_sets:new(), + confirm_tref = undefined}}; handle_cast({confirm, MsgSeqNo}, State) -> {noreply, send_or_enqueue_ack(MsgSeqNo, State)}; @@ -302,25 +302,25 @@ handle_info({'DOWN', _MRef, process, QPid, _Reason}, S = gb_sets:fold(fun (MsgSeqNo, State0) -> send_or_enqueue_ack(MsgSeqNo, State0) end, State, Msgs), - S #ch {qpid_to_msgs = dict:erase(QPid, QTM)}; + S #ch{qpid_to_msgs = dict:erase(QPid, QTM)}; error -> State end, erase_queue_stats(QPid), {noreply, queue_blocked(QPid, State1)}. -handle_pre_hibernate(State = #ch { writer_pid = WriterPid, - held_confirms = As, - stats_timer = StatsTimer, - need_confirming = NA }) -> +handle_pre_hibernate(State = #ch{writer_pid = WriterPid, + held_confirms = As, + stats_timer = StatsTimer, + need_confirming = NA}) -> ok = clear_permission_cache(), handle_multiple_flush(WriterPid, As, NA), rabbit_event:if_enabled(StatsTimer, fun() -> internal_emit_stats(State) end), - {hibernate, State #ch { held_confirms = gb_sets:new(), - stats_timer = rabbit_event:stop_stats_timer(StatsTimer), - confirm_tref = undefined }}. + {hibernate, State#ch{held_confirms = gb_sets:new(), + stats_timer = rabbit_event:stop_stats_timer(StatsTimer), + confirm_tref = undefined}}. terminate(_Reason, State = #ch{state = terminating}) -> terminate(State); @@ -487,14 +487,12 @@ send_or_enqueue_ack(MsgSeqNo, State = #ch{confirm_multiple = true}) -> msg_sent_to_queues(MsgSeqNo, QPid, State = #ch{qpid_to_msgs = QTM}) -> case dict:find(QPid, QTM) of {ok, Msgs} -> - State #ch {qpid_to_msgs = dict:store(QPid, - gb_sets:add(MsgSeqNo, Msgs), - QTM) }; + State#ch{ + qpid_to_msgs = dict:store(QPid, gb_sets:add(MsgSeqNo, Msgs), QTM)}; error -> erlang:monitor(process, QPid), - State #ch { qpid_to_msgs = dict:store(QPid, - gb_sets:add(MsgSeqNo, gb_sets:new()), - QTM) } + State#ch{ + qpid_to_msgs = dict:store(QPid, gb_sets:add(MsgSeqNo, gb_sets:new()), QTM)} end. do_if_not_dup(MsgSeqNo, State = #ch{need_confirming = NA}, Fun) -> @@ -546,10 +544,9 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, true -> Count = State#ch.published_count, {Count, - State #ch { published_count = Count + 1, - need_confirming = - gb_sets:add(Count, - State#ch.need_confirming) }} + State#ch{published_count = Count + 1, + need_confirming = + gb_sets:add(Count, State#ch.need_confirming) }} end, Message = #basic_message{exchange_name = ExchangeName, routing_key = RoutingKey, @@ -1008,8 +1005,8 @@ handle_method(#'confirm.select'{multiple = Multiple, nowait = NoWait}, State = #ch{confirm_enabled = false}) -> rabbit_log:info("got confirm.select{multiple = ~p, nowait = ~p}~n", [Multiple, NoWait]), - State1 = State #ch { confirm_enabled = true, - confirm_multiple = Multiple }, + State1 = State#ch{confirm_enabled = true, + confirm_multiple = Multiple}, case NoWait of true -> {noreply, State1}; false -> {reply, #'confirm.select_ok'{}, State1} @@ -1359,7 +1356,7 @@ erase_queue_stats(QPid) -> start_ack_timer(State = #ch{confirm_tref = undefined}) -> {ok, TRef} = timer:apply_after(?FLUSH_MULTIPLE_ACKS_INTERVAL, ?MODULE, flush_multiple_acks, [self()]), - State #ch { confirm_tref = TRef }; + State#ch{confirm_tref = TRef}; start_ack_timer(State) -> State. @@ -1367,7 +1364,7 @@ stop_ack_timer(State = #ch{confirm_tref = undefined}) -> State; stop_ack_timer(State = #ch{confirm_tref = TRef}) -> {ok, cancel} = timer:cancel(TRef), - State #ch { confirm_tref = undefined }. + State#ch{confirm_tref = undefined}. handle_multiple_flush(WriterPid, As, NA) -> case gb_sets:is_empty(As) of -- cgit v1.2.1 From f1f42f3fd28518b350cf398f8e85b01ba49755a7 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Wed, 29 Sep 2010 15:03:42 +0100 Subject: refactoring --- src/rabbit_channel.erl | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 14f85d59..7a8e1a31 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -282,7 +282,7 @@ handle_cast(flush_multiple_acks, State = #ch{writer_pid = WriterPid, held_confirms = As, need_confirming = NA}) -> - handle_multiple_flush(WriterPid, As, NA), + flush_multiple(WriterPid, As, NA), {noreply, State#ch{held_confirms = gb_sets:new(), confirm_tref = undefined}}; @@ -314,7 +314,7 @@ handle_pre_hibernate(State = #ch{writer_pid = WriterPid, stats_timer = StatsTimer, need_confirming = NA}) -> ok = clear_permission_cache(), - handle_multiple_flush(WriterPid, As, NA), + flush_multiple(WriterPid, As, NA), rabbit_event:if_enabled(StatsTimer, fun() -> internal_emit_stats(State) end), @@ -1366,27 +1366,23 @@ stop_ack_timer(State = #ch{confirm_tref = TRef}) -> {ok, cancel} = timer:cancel(TRef), State#ch{confirm_tref = undefined}. -handle_multiple_flush(WriterPid, As, NA) -> +flush_multiple(WriterPid, As, NA) -> case gb_sets:is_empty(As) of true -> ok; - false -> flush_multiple(As, WriterPid, case gb_sets:is_empty(NA) of - false -> gb_sets:smallest(NA); - true -> gb_sets:largest(As)+1 - end) - end. - + false -> SmallestNotAcked = case gb_sets:is_empty(NA) of + false -> gb_sets:smallest(NA); + true -> gb_sets:largest(As)+1 + end, + [First | Rest] = gb_sets:to_list(As), + Remaining = + case Rest of + [] -> [First]; + _ -> flush_multiple(First, Rest, WriterPid, SmallestNotAcked) + end, + [rabbit_writer:send_command(WriterPid, #'basic.ack'{delivery_tag = A}) + || A <- Remaining] +end. -flush_multiple(Acks, WriterPid, SmallestNotAcked) -> - [First | Rest] = gb_sets:to_list(Acks), - Remaining = case Rest of - [] -> [First]; - _ -> flush_multiple(First, Rest, WriterPid, SmallestNotAcked) - end, - lists:foreach(fun(A) -> - ok = rabbit_writer:send_command( - WriterPid, - #'basic.ack'{delivery_tag = A}) - end, Remaining). flush_multiple(Prev, [Cur | Rest], WriterPid, SNA) -> ExpNext = Prev+1, -- cgit v1.2.1 From 2c3af402db90a169c6f9da5219d439b5707c19d0 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Wed, 29 Sep 2010 15:41:54 +0100 Subject: no more redundant message passing --- src/rabbit_channel.erl | 26 +++++++++++++------------- src/rabbit_router.erl | 18 ++++-------------- 2 files changed, 17 insertions(+), 27 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 7a8e1a31..1f8455cc 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -287,13 +287,7 @@ handle_cast(flush_multiple_acks, confirm_tref = undefined}}; handle_cast({confirm, MsgSeqNo}, State) -> - {noreply, send_or_enqueue_ack(MsgSeqNo, State)}; - -handle_cast({msg_sent_to_queues, MsgSeqNo, QPids}, State) -> - {noreply, lists:foldl(fun (QPid, State0) -> - msg_sent_to_queues(MsgSeqNo, QPid, State0) - end, State, QPids)}. - + {noreply, send_or_enqueue_ack(MsgSeqNo, State)}. handle_info({'DOWN', _MRef, process, QPid, _Reason}, State = #ch{qpid_to_msgs = QTM}) -> @@ -484,7 +478,9 @@ send_or_enqueue_ack(MsgSeqNo, State = #ch{confirm_multiple = true}) -> qpid_to_msgs = QTM1}) end). -msg_sent_to_queues(MsgSeqNo, QPid, State = #ch{qpid_to_msgs = QTM}) -> +msg_sent_to_queue(undefined, _QPid, State) -> + State; +msg_sent_to_queue(MsgSeqNo, QPid, State = #ch{qpid_to_msgs = QTM}) -> case dict:find(QPid, QTM) of {ok, Msgs} -> State#ch{ @@ -527,9 +523,10 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, routing_key = RoutingKey, mandatory = Mandatory, immediate = Immediate}, - Content, State = #ch{virtual_host = VHostPath, - transaction_id = TxnKey, - writer_pid = WriterPid}) -> + Content, State = #ch{virtual_host = VHostPath, + transaction_id = TxnKey, + writer_pid = WriterPid, + confirm_enabled = ConfirmEnabled}) -> ExchangeName = rabbit_misc:r(VHostPath, exchange, ExchangeNameBin), check_write_permitted(ExchangeName, State), Exchange = rabbit_exchange:lookup_or_die(ExchangeName), @@ -538,7 +535,7 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, DecodedContent = rabbit_binary_parser:ensure_content_decoded(Content), IsPersistent = is_message_persistent(DecodedContent), {MsgSeqNo, State1} - = case State#ch.confirm_enabled of + = case ConfirmEnabled of false -> {undefined, State}; true -> @@ -566,7 +563,10 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, routed -> case {IsPersistent, DeliveredQPids} of {_, []} -> send_or_enqueue_ack(MsgSeqNo, State1); - {true, _} -> State1; + {true, _} -> + lists:foldl(fun (QPid, State0) -> + msg_sent_to_queue(MsgSeqNo, QPid, State0) + end, State1, DeliveredQPids); {false, _} -> send_or_enqueue_ack(MsgSeqNo, State1) end; %% Confirm after basic.returns diff --git a/src/rabbit_router.erl b/src/rabbit_router.erl index e5ffe863..707698b0 100644 --- a/src/rabbit_router.erl +++ b/src/rabbit_router.erl @@ -58,8 +58,7 @@ %%---------------------------------------------------------------------------- deliver(QPids, Delivery = #delivery{mandatory = false, - immediate = false, - msg_seq_no = MsgSeqNo}) -> + immediate = false}) -> %% optimisation: when Mandatory = false and Immediate = false, %% rabbit_amqqueue:deliver will deliver the message to the queue %% process asynchronously, and return true, which means all the @@ -69,10 +68,9 @@ deliver(QPids, Delivery = #delivery{mandatory = false, %% case below. delegate:invoke_no_result( QPids, fun (Pid) -> rabbit_amqqueue:deliver(Pid, Delivery) end), - maybe_inform_channel(MsgSeqNo, QPids), {routed, QPids}; -deliver(QPids, Delivery = #delivery{msg_seq_no = MsgSeqNo}) -> +deliver(QPids, Delivery) -> {Success, _} = delegate:invoke(QPids, fun (Pid) -> @@ -82,11 +80,8 @@ deliver(QPids, Delivery = #delivery{msg_seq_no = MsgSeqNo}) -> lists:foldl(fun fold_deliveries/2, {false, []}, Success), case check_delivery(Delivery#delivery.mandatory, Delivery#delivery.immediate, {Routed, Handled}) of - {routed, Qs} -> - maybe_inform_channel(MsgSeqNo, Qs), - {routed, Qs}; - O -> - O + {routed, Qs} -> {routed, Qs}; + O -> O end. %% TODO: Maybe this should be handled by a cursor instead. @@ -125,8 +120,3 @@ fold_deliveries({_, false},{_, Handled}) -> {true, Handled}. check_delivery(true, _ , {false, []}) -> {unroutable, []}; check_delivery(_ , true, {_ , []}) -> {not_delivered, []}; check_delivery(_ , _ , {_ , Qs}) -> {routed, Qs}. - -maybe_inform_channel(undefined, _) -> - ok; -maybe_inform_channel(MsgSeqNo, QPids) -> - gen_server2:cast(self(), {msg_sent_to_queues, MsgSeqNo, QPids}). -- cgit v1.2.1 From 8b1eca75cbb8bc35f693b8c2869dec35b34541e2 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Wed, 29 Sep 2010 16:07:48 +0100 Subject: more refactoring --- src/rabbit_channel.erl | 2 -- src/rabbit_msg_store.erl | 9 +++------ src/rabbit_queue_index.erl | 32 ++++++++++++++++++-------------- 3 files changed, 21 insertions(+), 22 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 1f8455cc..a6472e7a 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -1003,8 +1003,6 @@ handle_method(#'confirm.select'{}, _, #ch{transaction_id = TxId}) handle_method(#'confirm.select'{multiple = Multiple, nowait = NoWait}, _, State = #ch{confirm_enabled = false}) -> - rabbit_log:info("got confirm.select{multiple = ~p, nowait = ~p}~n", - [Multiple, NoWait]), State1 = State#ch{confirm_enabled = true, confirm_multiple = Multiple}, case NoWait of diff --git a/src/rabbit_msg_store.erl b/src/rabbit_msg_store.erl index 7d0af80a..84853227 100644 --- a/src/rabbit_msg_store.erl +++ b/src/rabbit_msg_store.erl @@ -848,12 +848,9 @@ internal_sync(State = #msstate { current_file_handle = CurHdl, lists:foreach(fun (K) -> K() end, lists:reverse(Syncs)), State1 #msstate { on_sync = [] } end, - dict:map(fun(CRef, Guids) -> - case dict:find(CRef, CODC) of - {ok, Fun} -> Fun(Guids); - error -> ok %% shouldn't happen - end - end, CTG), + dict:map(fun(CRef, Guids) -> Fun = dict:fetch(CRef, CODC), + Fun(Guids) end, + CTG), State2 #msstate { cref_to_guids = dict:new() }. diff --git a/src/rabbit_queue_index.erl b/src/rabbit_queue_index.erl index 9b4255da..88b6e832 100644 --- a/src/rabbit_queue_index.erl +++ b/src/rabbit_queue_index.erl @@ -256,17 +256,17 @@ delete_and_terminate(State) -> ok = rabbit_misc:recursive_delete([Dir]), State1. -publish(Guid, SeqId, IsPersistent, State) when is_binary(Guid) -> +publish(Guid, SeqId, IsPersistent, + State = #qistate { unsynced_guids = UnsyncedGuids }) when is_binary(Guid) -> ?GUID_BYTES = size(Guid), - {JournalHdl, State1} = get_journal_handle(State), + {JournalHdl, State1} = + get_journal_handle(State #qistate { unsynced_guids = [Guid | UnsyncedGuids] }), ok = file_handle_cache:append( JournalHdl, [<<(case IsPersistent of true -> ?PUB_PERSIST_JPREFIX; false -> ?PUB_TRANS_JPREFIX end):?JPREFIX_BITS, SeqId:?SEQ_BITS>>, Guid]), - State2 = State1 #qistate { unsynced_guids = - [Guid | State1#qistate.unsynced_guids] }, - maybe_flush_journal(add_to_journal(SeqId, {Guid, IsPersistent}, State2)). + maybe_flush_journal(add_to_journal(SeqId, {Guid, IsPersistent}, State1)). deliver(SeqIds, State) -> deliver_or_ack(del, SeqIds, State). @@ -278,8 +278,7 @@ sync([], State) -> State; sync(_SeqIds, State = #qistate { journal_handle = undefined }) -> State; -sync(_SeqIds, State = #qistate { journal_handle = JournalHdl, - on_sync = OnSyncFun }) -> +sync(_SeqIds, State = #qistate { journal_handle = JournalHdl }) -> %% The SeqIds here contains the SeqId of every publish and ack in %% the transaction. Ideally we should go through these seqids and %% only sync the journal if the pubs or acks appear in the @@ -289,8 +288,7 @@ sync(_SeqIds, State = #qistate { journal_handle = JournalHdl, %% seqids not being in the journal, provided the transaction isn't %% emptied (handled above anyway). ok = file_handle_cache:sync(JournalHdl), - OnSyncFun(State#qistate.unsynced_guids), - State#qistate { unsynced_guids = [] }. + notify_sync(State). flush(State = #qistate { dirty_count = 0 }) -> State; flush(State) -> flush_journal(State). @@ -570,9 +568,7 @@ maybe_flush_journal(State = #qistate { dirty_count = DCount, maybe_flush_journal(State) -> State. -flush_journal(State = #qistate { segments = Segments, - on_sync = OnSyncFun, - unsynced_guids = UGs }) -> +flush_journal(State = #qistate { segments = Segments }) -> Segments1 = segment_fold( fun (#segment { unacked = 0, path = Path }, SegmentsN) -> @@ -587,8 +583,7 @@ flush_journal(State = #qistate { segments = Segments, {JournalHdl, State1} = get_journal_handle(State #qistate { segments = Segments1 }), ok = file_handle_cache:clear(JournalHdl), - OnSyncFun(UGs), - State1 #qistate { dirty_count = 0, unsynced_guids = [] }. + notify_sync(State1 #qistate { dirty_count = 0 }). append_journal_to_segment(#segment { journal_entries = JEntries, path = Path } = Segment) -> @@ -947,3 +942,12 @@ journal_minus_segment1({no_pub, del, ack}, {?PUB, del, no_ack}) -> {{no_pub, no_del, ack}, 0}; journal_minus_segment1({no_pub, del, ack}, {?PUB, del, ack}) -> {undefined, -1}. + +%%---------------------------------------------------------------------------- +%% misc +%%---------------------------------------------------------------------------- + +notify_sync(State = #qistate { unsynced_guids = UG, + on_sync = OnSyncFun }) -> + OnSyncFun(UG), + State #qistate { unsynced_guids = [] }. -- cgit v1.2.1 From ab7c3fa382cb9d62a1189fe6281442b28eb4aa90 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Wed, 29 Sep 2010 16:57:48 +0100 Subject: msg_store refactoring --- src/rabbit_msg_store.erl | 13 +++++---- src/rabbit_tests.erl | 8 +++--- src/rabbit_variable_queue.erl | 62 ++++++++++++++++++++++++------------------- 3 files changed, 46 insertions(+), 37 deletions(-) diff --git a/src/rabbit_msg_store.erl b/src/rabbit_msg_store.erl index 84853227..7249e13e 100644 --- a/src/rabbit_msg_store.erl +++ b/src/rabbit_msg_store.erl @@ -34,9 +34,8 @@ -behaviour(gen_server2). -export([start_link/4, write/4, read/3, contains/2, remove/2, release/2, - sync/3, client_init/2, client_terminate/2, - client_delete_and_terminate/3, successfully_recovered_state/1, - register_sync_callback/3]). + sync/3, client_init/3, client_terminate/2, + client_delete_and_terminate/3, successfully_recovered_state/1]). -export([sync/1, gc_done/4, set_maximum_since_use/2, gc/3]). %% internal @@ -124,6 +123,7 @@ -type(startup_fun_state() :: {(fun ((A) -> 'finished' | {rabbit_guid:guid(), non_neg_integer(), A})), A}). +-type(guid_fun() :: fun (([rabbit_guid:guid()]) -> any())). -spec(start_link/4 :: (atom(), file:filename(), [binary()] | 'undefined', @@ -140,7 +140,7 @@ -spec(gc_done/4 :: (server(), non_neg_integer(), file_num(), file_num()) -> 'ok'). -spec(set_maximum_since_use/2 :: (server(), non_neg_integer()) -> 'ok'). --spec(client_init/2 :: (server(), rabbit_guid:guid()) -> client_msstate()). +-spec(client_init/3 :: (server(), rabbit_guid:guid(), guid_fun()) -> client_msstate()). -spec(client_terminate/2 :: (client_msstate(), server()) -> 'ok'). -spec(client_delete_and_terminate/3 :: (client_msstate(), server(), rabbit_guid:guid()) -> 'ok'). @@ -360,10 +360,11 @@ gc_done(Server, Reclaimed, Source, Destination) -> set_maximum_since_use(Server, Age) -> gen_server2:cast(Server, {set_maximum_since_use, Age}). -client_init(Server, Ref) -> +client_init(Server, Ref, MsgOnDiskFun) -> {IState, IModule, Dir, GCPid, FileHandlesEts, FileSummaryEts, DedupCacheEts, CurFileCacheEts} = gen_server2:call(Server, {new_client_state, Ref}, infinity), + register_sync_callback(Server, Ref, MsgOnDiskFun), #client_msstate { file_handle_cache = dict:new(), index_state = IState, index_module = IModule, @@ -629,6 +630,8 @@ handle_call({new_client_state, CRef}, _From, handle_call(successfully_recovered_state, _From, State) -> reply(State #msstate.successfully_recovered, State); +handle_call({register_sync_callback, _ , undefined}, _From, State) -> + reply(ok, State); handle_call({register_sync_callback, ClientRef, Fun}, _From, State = #msstate { client_ondisk_callback = CODC }) -> reply(ok, State #msstate { client_ondisk_callback = diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index b8143900..ddb78aff 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -1469,7 +1469,7 @@ msg_store_remove(Guids) -> foreach_with_msg_store_client(MsgStore, Ref, Fun, L) -> rabbit_msg_store:client_terminate( lists:foldl(fun (Guid, MSCState) -> Fun(Guid, MsgStore, MSCState) end, - rabbit_msg_store:client_init(MsgStore, Ref), L), MsgStore). + rabbit_msg_store:client_init(MsgStore, Ref, undefined), L), MsgStore). test_msg_store() -> restart_msg_store_empty(), @@ -1479,7 +1479,7 @@ test_msg_store() -> %% check we don't contain any of the msgs we're about to publish false = msg_store_contains(false, Guids), Ref = rabbit_guid:guid(), - MSCState = rabbit_msg_store:client_init(?PERSISTENT_MSG_STORE, Ref), + MSCState = rabbit_msg_store:client_init(?PERSISTENT_MSG_STORE, Ref, undefined), %% publish the first half {ok, MSCState1} = msg_store_write(Guids1stHalf, MSCState), %% sync on the first half @@ -1553,7 +1553,7 @@ test_msg_store() -> %% check we don't contain any of the msgs false = msg_store_contains(false, Guids), %% publish the first half again - MSCState8 = rabbit_msg_store:client_init(?PERSISTENT_MSG_STORE, Ref), + MSCState8 = rabbit_msg_store:client_init(?PERSISTENT_MSG_STORE, Ref, undefined), {ok, MSCState9} = msg_store_write(Guids1stHalf, MSCState8), %% this should force some sort of sync internally otherwise misread ok = rabbit_msg_store:client_terminate( @@ -1645,7 +1645,7 @@ queue_index_publish(SeqIds, Persistent, Qi) -> {ok, MSCStateM} = rabbit_msg_store:write(MsgStore, Guid, Guid, MSCStateN), {QiM, [{SeqId, Guid} | SeqIdsGuidsAcc], MSCStateM} - end, {Qi, [], rabbit_msg_store:client_init(MsgStore, Ref)}, SeqIds), + end, {Qi, [], rabbit_msg_store:client_init(MsgStore, Ref, undefined)}, SeqIds), ok = rabbit_msg_store:client_delete_and_terminate( MSCStateEnd, MsgStore, Ref), {A, B}. diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index e6a93871..31fb21f6 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -377,10 +377,8 @@ stop_msg_store() -> init(QueueName, IsDurable, Recover) -> Self = self(), init(QueueName, IsDurable, Recover, - fun (Guids) -> - msgs_written_to_disk(Self, Guids) - end, - fun msg_indices_written_to_disk/1). + fun (Guids) -> msgs_written_to_disk(Self, Guids) end, + fun (Guids) -> msg_indices_written_to_disk(Self, Guids) end). init(QueueName, IsDurable, Recover, MsgOnDiskFun, MsgIdxOnDiskFun) -> @@ -409,16 +407,16 @@ init(QueueName, IsDurable, Recover, end_seq_id = NextSeqId } end, Now = now(), + PersistentClient = case IsDurable of - true -> rabbit_msg_store:client_init(?PERSISTENT_MSG_STORE, PRef); + true -> rabbit_msg_store:client_init(?PERSISTENT_MSG_STORE, PRef, + MsgOnDiskFun); false -> undefined end, + TransientClient = + rabbit_msg_store:client_init(?TRANSIENT_MSG_STORE, TRef, undefined), - rabbit_msg_store:register_sync_callback(?PERSISTENT_MSG_STORE, PRef, - MsgOnDiskFun), - - TransientClient = rabbit_msg_store:client_init(?TRANSIENT_MSG_STORE, TRef), State = #vqstate { q1 = queue:new(), q2 = bpqueue:new(), @@ -528,24 +526,25 @@ publish_delivered(true, Msg = #basic_message { is_persistent = IsPersistent, in_counter = InCount, persistent_count = PCount, pending_ack = PA, - durable = IsDurable }) -> + durable = IsDurable, + need_confirming = NeedConfirming }) -> IsPersistent1 = IsDurable andalso IsPersistent, MsgStatus = (msg_status(IsPersistent1, SeqId, Msg)) #msg_status { is_delivered = true }, {MsgStatus1, State1} = maybe_write_to_disk(false, false, MsgStatus, State), PA1 = record_pending_ack(m(MsgStatus1), PA), PCount1 = PCount + one_if(IsPersistent1), + NeedConfirming1 = case NeedsConfirming of + true -> gb_sets:add(Guid, NeedConfirming); + false -> NeedConfirming + end, {SeqId, a(State1 #vqstate { next_seq_id = SeqId + 1, out_counter = OutCount + 1, in_counter = InCount + 1, persistent_count = PCount1, pending_ack = PA1, - need_confirming = - case NeedsConfirming of - true -> gb_sets:insert(Guid, State1#vqstate.need_confirming); - false -> State1#vqstate.need_confirming - end })}. + need_confirming = NeedConfirming1 })}. fetch(AckRequired, State = #vqstate { q4 = Q4, ram_msg_count = RamMsgCount, @@ -1044,7 +1043,8 @@ publish(Msg = #basic_message { is_persistent = IsPersistent, in_counter = InCount, persistent_count = PCount, durable = IsDurable, - ram_msg_count = RamMsgCount }) -> + ram_msg_count = RamMsgCount, + need_confirming = NeedConfirming}) -> IsPersistent1 = IsDurable andalso IsPersistent, MsgStatus = (msg_status(IsPersistent1, SeqId, Msg)) #msg_status { is_delivered = IsDelivered, msg_on_disk = MsgOnDisk }, @@ -1054,17 +1054,17 @@ publish(Msg = #basic_message { is_persistent = IsPersistent, true -> State1 #vqstate { q4 = queue:in(m(MsgStatus1), Q4) } end, PCount1 = PCount + one_if(IsPersistent1), + NeedConfirming1 = case NeedsConfirming of + true -> gb_sets:add(Guid, NeedConfirming); + false -> NeedConfirming + end, {SeqId, State2 #vqstate { next_seq_id = SeqId + 1, len = Len + 1, in_counter = InCount + 1, persistent_count = PCount1, ram_msg_count = RamMsgCount + 1, - need_confirming = - case NeedsConfirming of - true -> gb_sets:add(Guid, State2#vqstate.need_confirming); - false -> State2#vqstate.need_confirming - end }}. + need_confirming = NeedConfirming1 }}. maybe_write_msg_to_disk(_Force, MsgStatus = #msg_status { msg_on_disk = true }, MSCState) -> @@ -1202,29 +1202,35 @@ msgs_written_to_disk(QPid, Guids) -> need_confirming = NC }) -> GuidSet = gb_sets:from_list(Guids), ToConfirmMsgs = gb_sets:intersection(GuidSet, MIOD), - State1 = State #vqstate { msgs_on_disk = - gb_sets:intersection(gb_sets:union(MOD, GuidSet), NC) }, + State1 = + State #vqstate { + msgs_on_disk = + gb_sets:intersection( + gb_sets:union(MOD, GuidSet), NC) }, { msgs_confirmed(ToConfirmMsgs, State1), {confirm, gb_sets:to_list(ToConfirmMsgs)} } end) end). -msg_indices_written_to_disk(Guids) -> - Self = self(), +msg_indices_written_to_disk(QPid, Guids) -> spawn(fun() -> rabbit_amqqueue:maybe_run_queue_via_backing_queue( - Self, + QPid, fun(State = #vqstate { msgs_on_disk = MOD, msg_indices_on_disk = MIOD, need_confirming = NC }) -> GuidSet = gb_sets:from_list(Guids), ToConfirmMsgs = gb_sets:intersection(GuidSet, MOD), - State1 = State #vqstate { msg_indices_on_disk = - gb_sets:intersection(gb_sets:union(MIOD, GuidSet), NC) }, + State1 = + State #vqstate { + msg_indices_on_disk = + gb_sets:intersection( + gb_sets:union(MIOD, GuidSet), NC) }, { msgs_confirmed(ToConfirmMsgs, State1), {confirm, gb_sets:to_list(ToConfirmMsgs)} } end) end). + %%---------------------------------------------------------------------------- %% Phase changes %%---------------------------------------------------------------------------- -- cgit v1.2.1 From e77820736ac47c7a8d795ce6c2bff7906fab73c3 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Thu, 30 Sep 2010 10:40:19 +0100 Subject: :%s/need_confirming/unconfirmed/g --- src/rabbit_channel.erl | 22 +++++++++++----------- src/rabbit_variable_queue.erl | 42 +++++++++++++++++++++--------------------- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index a6472e7a..40cb1a95 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -50,7 +50,7 @@ username, virtual_host, most_recently_declared_queue, consumer_mapping, blocking, queue_collector_pid, stats_timer, confirm_enabled, published_count, confirm_multiple, confirm_tref, - held_confirms, need_confirming, qpid_to_msgs}). + held_confirms, unconfirmed, qpid_to_msgs}). -define(MAX_PERMISSION_CACHE_SIZE, 12). @@ -194,7 +194,7 @@ init([Channel, ReaderPid, WriterPid, Username, VHost, CollectorPid, published_count = 0, confirm_multiple = false, held_confirms = gb_sets:new(), - need_confirming = gb_sets:new(), + unconfirmed = gb_sets:new(), qpid_to_msgs = dict:new() }, rabbit_event:notify(channel_created, infos(?CREATION_EVENT_KEYS, State)), rabbit_event:if_enabled(StatsTimer, @@ -281,8 +281,8 @@ handle_cast(emit_stats, State = #ch{stats_timer = StatsTimer}) -> handle_cast(flush_multiple_acks, State = #ch{writer_pid = WriterPid, held_confirms = As, - need_confirming = NA}) -> - flush_multiple(WriterPid, As, NA), + unconfirmed = UC}) -> + flush_multiple(WriterPid, As, UC), {noreply, State#ch{held_confirms = gb_sets:new(), confirm_tref = undefined}}; @@ -306,9 +306,9 @@ handle_info({'DOWN', _MRef, process, QPid, _Reason}, handle_pre_hibernate(State = #ch{writer_pid = WriterPid, held_confirms = As, stats_timer = StatsTimer, - need_confirming = NA}) -> + unconfirmed = UC}) -> ok = clear_permission_cache(), - flush_multiple(WriterPid, As, NA), + flush_multiple(WriterPid, As, UC), rabbit_event:if_enabled(StatsTimer, fun() -> internal_emit_stats(State) end), @@ -491,11 +491,11 @@ msg_sent_to_queue(MsgSeqNo, QPid, State = #ch{qpid_to_msgs = QTM}) -> qpid_to_msgs = dict:store(QPid, gb_sets:add(MsgSeqNo, gb_sets:new()), QTM)} end. -do_if_not_dup(MsgSeqNo, State = #ch{need_confirming = NA}, Fun) -> - case gb_sets:is_element(MsgSeqNo, NA) of +do_if_not_dup(MsgSeqNo, State = #ch{unconfirmed = UC}, Fun) -> + case gb_sets:is_element(MsgSeqNo, UC) of true -> State1 = Fun(MsgSeqNo, State), - State1 #ch { need_confirming = gb_sets:delete(MsgSeqNo, NA) }; + State1#ch{unconfirmed = gb_sets:delete(MsgSeqNo, UC)}; false -> State end. @@ -542,8 +542,8 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, Count = State#ch.published_count, {Count, State#ch{published_count = Count + 1, - need_confirming = - gb_sets:add(Count, State#ch.need_confirming) }} + unconfirmed = + gb_sets:add(Count, State#ch.unconfirmed) }} end, Message = #basic_message{exchange_name = ExchangeName, routing_key = RoutingKey, diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 31fb21f6..185617d5 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -239,7 +239,7 @@ rates, msgs_on_disk, msg_indices_on_disk, - need_confirming + unconfirmed }). -record(rates, { egress, ingress, avg_egress, avg_ingress, timestamp }). @@ -328,7 +328,7 @@ rates :: rates(), msgs_on_disk :: gb_set(), msg_indices_on_disk :: gb_set(), - need_confirming :: gb_set()}). + unconfirmed :: gb_set()}). -include("rabbit_backing_queue_spec.hrl"). @@ -449,7 +449,7 @@ init(QueueName, IsDurable, Recover, timestamp = Now }, msgs_on_disk = gb_sets:new(), msg_indices_on_disk = gb_sets:new(), - need_confirming = gb_sets:new()}, + unconfirmed = gb_sets:new()}, a(maybe_deltas_to_betas(State)). terminate(State) -> @@ -527,24 +527,24 @@ publish_delivered(true, Msg = #basic_message { is_persistent = IsPersistent, persistent_count = PCount, pending_ack = PA, durable = IsDurable, - need_confirming = NeedConfirming }) -> + unconfirmed = Unconfirmed }) -> IsPersistent1 = IsDurable andalso IsPersistent, MsgStatus = (msg_status(IsPersistent1, SeqId, Msg)) #msg_status { is_delivered = true }, {MsgStatus1, State1} = maybe_write_to_disk(false, false, MsgStatus, State), PA1 = record_pending_ack(m(MsgStatus1), PA), PCount1 = PCount + one_if(IsPersistent1), - NeedConfirming1 = case NeedsConfirming of - true -> gb_sets:add(Guid, NeedConfirming); - false -> NeedConfirming - end, + Unconfirmed1 = case NeedsConfirming of + true -> gb_sets:add(Guid, Unconfirmed); + false -> Unconfirmed + end, {SeqId, a(State1 #vqstate { next_seq_id = SeqId + 1, out_counter = OutCount + 1, in_counter = InCount + 1, persistent_count = PCount1, pending_ack = PA1, - need_confirming = NeedConfirming1 })}. + unconfirmed = Unconfirmed1 })}. fetch(AckRequired, State = #vqstate { q4 = Q4, ram_msg_count = RamMsgCount, @@ -1044,7 +1044,7 @@ publish(Msg = #basic_message { is_persistent = IsPersistent, persistent_count = PCount, durable = IsDurable, ram_msg_count = RamMsgCount, - need_confirming = NeedConfirming}) -> + unconfirmed = Unconfirmed}) -> IsPersistent1 = IsDurable andalso IsPersistent, MsgStatus = (msg_status(IsPersistent1, SeqId, Msg)) #msg_status { is_delivered = IsDelivered, msg_on_disk = MsgOnDisk }, @@ -1054,17 +1054,17 @@ publish(Msg = #basic_message { is_persistent = IsPersistent, true -> State1 #vqstate { q4 = queue:in(m(MsgStatus1), Q4) } end, PCount1 = PCount + one_if(IsPersistent1), - NeedConfirming1 = case NeedsConfirming of - true -> gb_sets:add(Guid, NeedConfirming); - false -> NeedConfirming - end, + Unconfirmed1 = case NeedsConfirming of + true -> gb_sets:add(Guid, Unconfirmed); + false -> Unconfirmed + end, {SeqId, State2 #vqstate { next_seq_id = SeqId + 1, len = Len + 1, in_counter = InCount + 1, persistent_count = PCount1, ram_msg_count = RamMsgCount + 1, - need_confirming = NeedConfirming1 }}. + unconfirmed = Unconfirmed1 }}. maybe_write_msg_to_disk(_Force, MsgStatus = #msg_status { msg_on_disk = true }, MSCState) -> @@ -1189,24 +1189,24 @@ accumulate_ack(SeqId, {IsPersistent, Guid}, {SeqIdsAcc, Dict}) -> msgs_confirmed(GuidSet, State = #vqstate { msgs_on_disk = MOD, msg_indices_on_disk = MIOD, - need_confirming = NC }) -> + unconfirmed = UC }) -> State #vqstate { msgs_on_disk = gb_sets:difference(MOD, GuidSet), msg_indices_on_disk = gb_sets:difference(MIOD, GuidSet), - need_confirming = gb_sets:difference(NC, GuidSet) }. + unconfirmed = gb_sets:difference(UC, GuidSet) }. msgs_written_to_disk(QPid, Guids) -> spawn(fun() -> rabbit_amqqueue:maybe_run_queue_via_backing_queue( QPid, fun(State = #vqstate { msgs_on_disk = MOD, msg_indices_on_disk = MIOD, - need_confirming = NC }) -> + unconfirmed = UC }) -> GuidSet = gb_sets:from_list(Guids), ToConfirmMsgs = gb_sets:intersection(GuidSet, MIOD), State1 = State #vqstate { msgs_on_disk = gb_sets:intersection( - gb_sets:union(MOD, GuidSet), NC) }, + gb_sets:union(MOD, GuidSet), UC) }, { msgs_confirmed(ToConfirmMsgs, State1), {confirm, gb_sets:to_list(ToConfirmMsgs)} } end) @@ -1217,14 +1217,14 @@ msg_indices_written_to_disk(QPid, Guids) -> QPid, fun(State = #vqstate { msgs_on_disk = MOD, msg_indices_on_disk = MIOD, - need_confirming = NC }) -> + unconfirmed = UC }) -> GuidSet = gb_sets:from_list(Guids), ToConfirmMsgs = gb_sets:intersection(GuidSet, MOD), State1 = State #vqstate { msg_indices_on_disk = gb_sets:intersection( - gb_sets:union(MIOD, GuidSet), NC) }, + gb_sets:union(MIOD, GuidSet), UC) }, { msgs_confirmed(ToConfirmMsgs, State1), {confirm, gb_sets:to_list(ToConfirmMsgs)} } end) -- cgit v1.2.1 From 77519b59e3c7e560afa239b78a1ecb2e2350b060 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 1 Oct 2010 18:17:48 +0100 Subject: cosmetics and some refactoring --- src/rabbit_amqqueue_process.erl | 78 ++++++------- src/rabbit_channel.erl | 247 +++++++++++++++++----------------------- 2 files changed, 142 insertions(+), 183 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 62ac6992..7723d722 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -397,7 +397,7 @@ deliver_from_queue_deliver(AckRequired, false, {{Message, IsDelivered, AckTag, Remaining}, BQS1} = BQ:fetch(AckRequired, BQS), {{Message, IsDelivered, AckTag}, 0 == Remaining, - State #q { backing_queue_state = BQS1 }}. + State#q{backing_queue_state = BQS1}}. confirm_messages(Guids, State) when is_list(Guids) -> lists:foldl(fun(Guid, State0) -> @@ -410,23 +410,21 @@ confirm_message(Guid, State = #q{guid_to_channel = GTC}) -> {ok, {ChPid, MsgSeqNo}} -> rabbit_channel:confirm(ChPid, MsgSeqNo); _ -> ok end, - State #q { guid_to_channel = dict:erase(Guid, GTC) }. + State#q{guid_to_channel = dict:erase(Guid, GTC)}. -record_confirm_message(#delivery{msg_seq_no = undefined }, State) -> +record_confirm_message(#delivery{msg_seq_no = undefined}, State) -> State; -record_confirm_message(#delivery{sender = ChPid, - message = #basic_message{guid = Guid}, - msg_seq_no = MsgSeqNo}, +record_confirm_message(#delivery{msg_seq_no = MsgSeqNo, + sender = ChPid, + message = #basic_message{guid = Guid}}, State = #q{guid_to_channel = GTC}) -> - State #q { guid_to_channel = dict:store(Guid, {ChPid, MsgSeqNo}, GTC) }. + State#q{guid_to_channel = dict:store(Guid, {ChPid, MsgSeqNo}, GTC)}. ack_by_acktags(AckTags, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> AckdGuids = BQ:seqids_to_guids(AckTags, BQS), - BQS1 = BQ:ack(AckTags, BQS), - confirm_messages( - AckdGuids, - State #q { backing_queue_state = BQS1 }). + confirm_messages(AckdGuids, + State#q{backing_queue_state = BQ:ack(AckTags, BQS)}). run_message_queue(State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> Funs = {fun deliver_from_queue_pred/2, @@ -460,15 +458,11 @@ attempt_delivery(#delivery{txn = Txn, deliver_or_enqueue(Delivery = #delivery{message = Message, msg_seq_no = MsgSeqNo}, State = #q{backing_queue = BQ}) -> - State1 = record_confirm_message(Delivery, State), - case attempt_delivery(Delivery, State1) of - {true, NewState} -> - {true, NewState}; - {false, NewState} -> - %% Txn is none and no unblocked channels with consumers - BQS = BQ:publish(Message, MsgSeqNo =/= undefined, - State1 #q.backing_queue_state), - {false, NewState#q{backing_queue_state = BQS}} + case attempt_delivery(Delivery, record_confirm_message(Delivery, State)) of + {true, NewState} -> {true, NewState}; + {false, NewState} -> BQS = BQ:publish(Message, MsgSeqNo =/= undefined, + NewState#q.backing_queue_state), + {false, NewState#q{backing_queue_state = BQS}} end. requeue_and_run(AckTags, State = #q{backing_queue = BQ}) -> @@ -566,13 +560,12 @@ maybe_send_reply(ChPid, Msg) -> ok = rabbit_channel:send_command(ChPid, Msg). qname(#q{q = #amqqueue{name = QName}}) -> QName. maybe_run_queue_via_backing_queue(Fun, State = #q{backing_queue_state = BQS}) -> - case Fun(BQS) of - {BQS1, {confirm, Guids}} -> - run_message_queue( - confirm_messages(Guids, State #q { backing_queue_state = BQS1 })); - BQS1 -> - run_message_queue(State#q{backing_queue_state = BQS1}) - end. + {BQS2, State1} = + case Fun(BQS) of + {BQS1, {confirm, Guids}} -> {BQS1, confirm_messages(Guids, State)}; + BQS1 -> {BQS1, State} + end, + run_message_queue(State1#q{backing_queue_state = BQS2}). commit_transaction(Txn, From, ChPid, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> @@ -720,15 +713,15 @@ handle_call({deliver_immediately, Delivery}, _From, State) -> %% just all ready-to-consume queues get the message, with unready %% queues discarding the message? %% - {Delivered, State1} = attempt_delivery(Delivery, State), - State2 = confirm_message(Delivery#delivery.message#basic_message.guid, - record_confirm_message(Delivery, State1)), - reply(Delivered, State2); + {Delivered, NewState} = attempt_delivery(Delivery, State), + reply(Delivered, + confirm_message(Delivery#delivery.message#basic_message.guid, + record_confirm_message(Delivery, NewState))); handle_call({deliver, Delivery}, _From, State) -> %% Synchronous, "mandatory" delivery mode - {Delivered, State1} = deliver_or_enqueue(Delivery, State), - reply(Delivered, State1); + {Delivered, NewState} = deliver_or_enqueue(Delivery, State), + reply(Delivered, NewState); handle_call({commit, Txn, ChPid}, From, State) -> NewState = commit_transaction(Txn, From, ChPid, State), @@ -885,8 +878,8 @@ handle_call({maybe_run_queue_via_backing_queue, Fun}, _From, State) -> handle_cast({deliver, Delivery}, State) -> %% Asynchronous, non-"mandatory", non-"immediate" deliver mode. - {_Delivered, State1} = deliver_or_enqueue(Delivery, State), - noreply(State1); + {_Delivered, NewState} = deliver_or_enqueue(Delivery, State), + noreply(NewState); handle_cast({ack, Txn, AckTags, ChPid}, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> @@ -896,14 +889,13 @@ handle_cast({ack, Txn, AckTags, ChPid}, C = #cr{acktags = ChAckTags} -> {C1, State1} = case Txn of - none -> - ChAckTags1 = subtract_acks(ChAckTags, AckTags), - NewC = C#cr{acktags = ChAckTags1}, - NewState = ack_by_acktags(AckTags, State), - {NewC, NewState}; - _ -> - {C#cr{txn = Txn}, - State #q { backing_queue_state = BQ:tx_ack(Txn, AckTags, BQS) }} + none -> ChAckTags1 = subtract_acks(ChAckTags, AckTags), + NewC = C#cr{acktags = ChAckTags1}, + NewState = ack_by_acktags(AckTags, State), + {NewC, NewState}; + _ -> BQS1 = BQ:tx_ack(Txn, AckTags, BQS), + {C#cr{txn = Txn}, + State#q{backing_queue_state = BQS1}} end, store_ch_record(C1), noreply(State1) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 20a55410..722eff37 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -165,7 +165,6 @@ flush_multiple_acks(Pid) -> confirm(Pid, MsgSeqNo) -> gen_server2:cast(Pid, {confirm, MsgSeqNo}). - %%--------------------------------------------------------------------------- init([Channel, ReaderPid, WriterPid, Username, VHost, CollectorPid, @@ -173,30 +172,30 @@ init([Channel, ReaderPid, WriterPid, Username, VHost, CollectorPid, process_flag(trap_exit, true), ok = pg_local:join(rabbit_channels, self()), StatsTimer = rabbit_event:init_stats_timer(), - State = #ch{ state = starting, - channel = Channel, - reader_pid = ReaderPid, - writer_pid = WriterPid, - limiter_pid = undefined, - start_limiter_fun = StartLimiterFun, - transaction_id = none, - tx_participants = sets:new(), - next_tag = 1, - uncommitted_ack_q = queue:new(), - unacked_message_q = queue:new(), - username = Username, - virtual_host = VHost, - most_recently_declared_queue = <<>>, - consumer_mapping = dict:new(), - blocking = dict:new(), - queue_collector_pid = CollectorPid, - stats_timer = StatsTimer, - confirm_enabled = false, - published_count = 0, - confirm_multiple = false, - held_confirms = gb_sets:new(), - unconfirmed = gb_sets:new(), - qpid_to_msgs = dict:new() }, + State = #ch{state = starting, + channel = Channel, + reader_pid = ReaderPid, + writer_pid = WriterPid, + limiter_pid = undefined, + start_limiter_fun = StartLimiterFun, + transaction_id = none, + tx_participants = sets:new(), + next_tag = 1, + uncommitted_ack_q = queue:new(), + unacked_message_q = queue:new(), + username = Username, + virtual_host = VHost, + most_recently_declared_queue = <<>>, + consumer_mapping = dict:new(), + blocking = dict:new(), + queue_collector_pid = CollectorPid, + stats_timer = StatsTimer, + confirm_enabled = false, + published_count = 0, + confirm_multiple = false, + held_confirms = gb_sets:new(), + unconfirmed = gb_sets:new(), + qpid_to_msgs = dict:new()}, rabbit_event:notify(channel_created, infos(?CREATION_EVENT_KEYS, State)), rabbit_event:if_enabled(StatsTimer, fun() -> internal_emit_stats(State) end), @@ -304,18 +303,19 @@ handle_info({'DOWN', _MRef, process, QPid, _Reason}, erase_queue_stats(QPid), {noreply, queue_blocked(QPid, State1)}. -handle_pre_hibernate(State = #ch{writer_pid = WriterPid, - held_confirms = As, - stats_timer = StatsTimer, - unconfirmed = UC}) -> +handle_pre_hibernate(State = #ch{writer_pid = WriterPid, + held_confirms = As, + stats_timer = StatsTimer, + unconfirmed = UC}) -> ok = clear_permission_cache(), flush_multiple(WriterPid, As, UC), rabbit_event:if_enabled(StatsTimer, fun() -> - internal_emit_stats(State) + internal_emit_stats(State) end), + StatsTimer1 = rabbit_event:stop_stats_timer(StatsTimer), {hibernate, State#ch{held_confirms = gb_sets:new(), - stats_timer = rabbit_event:stop_stats_timer(StatsTimer), - confirm_tref = undefined}}. + stats_timer = StatsTimer1, + confirm_tref = undefined}}. terminate(_Reason, State = #ch{state = terminating}) -> terminate(State); @@ -456,52 +456,39 @@ send_or_enqueue_ack(undefined, State) -> send_or_enqueue_ack(_, State = #ch{confirm_enabled = false}) -> State; send_or_enqueue_ack(MsgSeqNo, State = #ch{confirm_multiple = false}) -> - do_if_not_dup( - MsgSeqNo, State, - fun(MSN, State1 = #ch{writer_pid = WriterPid, qpid_to_msgs = QTM}) -> - ok = rabbit_writer:send_command( - WriterPid, #'basic.ack'{delivery_tag = MSN}), - QTM1 = dict:map(fun (_, Msgs) -> - gb_sets:delete_any(MsgSeqNo, Msgs) - end, QTM), - State1#ch{qpid_to_msgs = QTM1} - end); + do_if_not_dup(MsgSeqNo, State, + fun(MSN, State1 = #ch{writer_pid = WriterPid}) -> + ok = rabbit_writer:send_command( + WriterPid, #'basic.ack'{delivery_tag = MSN}), + State1 + end); send_or_enqueue_ack(MsgSeqNo, State = #ch{confirm_multiple = true}) -> - do_if_not_dup( - MsgSeqNo, State, - fun(MSN, State1 = #ch{qpid_to_msgs = QTM}) -> - QTM1 = dict:map(fun (_, Msgs) -> - gb_sets:delete_any(MsgSeqNo, Msgs) - end, QTM), - start_ack_timer( - State1#ch{held_confirms = - gb_sets:add(MSN, State1#ch.held_confirms), - qpid_to_msgs = QTM1}) - end). + do_if_not_dup(MsgSeqNo, State, + fun(MSN, State1 = #ch{held_confirms = As}) -> + start_ack_timer(State1#ch{held_confirms = + gb_sets:add(MSN, As)}) + end). msg_sent_to_queue(undefined, _QPid, State) -> State; msg_sent_to_queue(MsgSeqNo, QPid, State = #ch{qpid_to_msgs = QTM}) -> - case dict:find(QPid, QTM) of - {ok, Msgs} -> - State#ch{ - qpid_to_msgs = dict:store(QPid, gb_sets:add(MsgSeqNo, Msgs), QTM)}; - error -> - erlang:monitor(process, QPid), - State#ch{ - qpid_to_msgs = dict:store(QPid, gb_sets:add(MsgSeqNo, gb_sets:new()), QTM)} - end. + Msgs1 = case dict:find(QPid, QTM) of + {ok, Msgs} -> Msgs; + error -> erlang:monitor(process, QPid), + gb_sets:new() + end, + State#ch{qpid_to_msgs = dict:store(QPid, gb_sets:add(MsgSeqNo, Msgs1))}. do_if_not_dup(MsgSeqNo, State = #ch{unconfirmed = UC}, Fun) -> case gb_sets:is_element(MsgSeqNo, UC) of - true -> - State1 = Fun(MsgSeqNo, State), - State1#ch{unconfirmed = gb_sets:delete(MsgSeqNo, UC)}; - false -> - State + true -> QTM = dict:map(fun (_, Msgs) -> + gb_sets:delete_any(MsgSeqNo, Msgs) + end, State#ch.qpid_to_msgs), + State1 = Fun(MsgSeqNo, State#ch{qpid_to_msgs = QTM}), + State1#ch{unconfirmed = gb_sets:delete(MsgSeqNo, UC)}; + false -> State end. - handle_method(#'channel.open'{}, _, State = #ch{state = starting}) -> {reply, #'channel.open_ok'{}, State#ch{state = running}}; @@ -526,7 +513,6 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, immediate = Immediate}, Content, State = #ch{virtual_host = VHostPath, transaction_id = TxnKey, - writer_pid = WriterPid, confirm_enabled = ConfirmEnabled}) -> ExchangeName = rabbit_misc:r(VHostPath, exchange, ExchangeNameBin), check_write_permitted(ExchangeName, State), @@ -537,14 +523,12 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, IsPersistent = is_message_persistent(DecodedContent), {MsgSeqNo, State1} = case ConfirmEnabled of - false -> - {undefined, State}; - true -> - Count = State#ch.published_count, - {Count, - State#ch{published_count = Count + 1, - unconfirmed = - gb_sets:add(Count, State#ch.unconfirmed) }} + false -> {undefined, State}; + true -> Count = State#ch.published_count, + {Count, + State#ch{published_count = Count + 1, + unconfirmed = + gb_sets:add(Count, State#ch.unconfirmed)}} end, Message = #basic_message{exchange_name = ExchangeName, routing_key = RoutingKey, @@ -559,25 +543,8 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, true -> MsgSeqNo; false -> undefined end)), - State2 = case RoutingRes of - %% Confirm transient messages now - routed -> - case {IsPersistent, DeliveredQPids} of - {_, []} -> send_or_enqueue_ack(MsgSeqNo, State1); - {true, _} -> - lists:foldl(fun (QPid, State0) -> - msg_sent_to_queue(MsgSeqNo, QPid, State0) - end, State1, DeliveredQPids); - {false, _} -> send_or_enqueue_ack(MsgSeqNo, State1) - end; - %% Confirm after basic.returns - unroutable -> - ok = basic_return(Message, WriterPid, no_route), - send_or_enqueue_ack(MsgSeqNo, State1); - not_delivered -> - ok = basic_return(Message, WriterPid, no_consumers), - send_or_enqueue_ack(MsgSeqNo, State1) - end, + State2 = process_routing_result(RoutingRes, DeliveredQPids, IsPersistent, + MsgSeqNo, Message, State1), maybe_incr_stats([{ExchangeName, 1} | [{{QPid, ExchangeName}, 1} || QPid <- DeliveredQPids]], publish, State2), @@ -1002,30 +969,18 @@ handle_method(#'confirm.select'{}, _, #ch{transaction_id = TxId}) precondition_failed, "cannot switch from tx to confirm mode", []); handle_method(#'confirm.select'{multiple = Multiple, nowait = NoWait}, - _, - State = #ch{confirm_enabled = false}) -> - State1 = State#ch{confirm_enabled = true, - confirm_multiple = Multiple}, - case NoWait of - true -> {noreply, State1}; - false -> {reply, #'confirm.select_ok'{}, State1} - end; + _, State = #ch{confirm_enabled = false}) -> + return_ok(State#ch{confirm_enabled = true, confirm_multiple = Multiple}, + NoWait, #'confirm.select_ok'{}); handle_method(#'confirm.select'{multiple = Multiple, nowait = NoWait}, - _, - State = #ch{confirm_enabled = true, - confirm_multiple = Multiple}) -> - rabbit_log:info("got a confirm.select with same options~n"), - case NoWait of - true -> {noreply, State}; - false -> {reply, #'confirm.select_ok'{}, State} - end; + _, State = #ch{confirm_enabled = true, + confirm_multiple = Multiple}) -> + return_ok(State, NoWait, #'confirm.select_ok'{}); -handle_method(#'confirm.select'{}, - _, - #ch{confirm_enabled = true}) -> +handle_method(#'confirm.select'{}, _, #ch{confirm_enabled = true}) -> rabbit_misc:protocol_error( - precondition_failed, "cannot change confirm channel multiple setting", []); + precondition_failed, "cannot change confirm_multiple setting", []); handle_method(#'channel.flow'{active = true}, _, State = #ch{limiter_pid = LimiterPid}) -> @@ -1249,6 +1204,21 @@ is_message_persistent(Content) -> IsPersistent end. +process_routing_result(unroutable, _, _, MsgSeqNo, Message, State) -> + ok = basic_return(Message, State#ch.writer_pid, no_route), + send_or_enqueue_ack(MsgSeqNo, State); +process_routing_result(not_delivered, _, _, MsgSeqNo, Message, State) -> + ok = basic_return(Message, State#ch.writer_pid, no_consumers), + send_or_enqueue_ack(MsgSeqNo, State); +process_routing_result(routed, [], _, MsgSeqNo, _, State) -> + send_or_enqueue_ack(MsgSeqNo, State); +process_routing_result(routed, _, false, MsgSeqNo, _, State) -> + send_or_enqueue_ack(MsgSeqNo, State); +process_routing_result(routed, QPids, true, MsgSeqNo, _, State) -> + lists:foldl(fun (QPid, State0) -> + msg_sent_to_queue(MsgSeqNo, QPid, State0) + end, State, QPids). + lock_message(true, MsgStruct, State = #ch{unacked_message_q = UAMQ}) -> State#ch{unacked_message_q = queue:in(MsgStruct, UAMQ)}; lock_message(false, _MsgStruct, State) -> @@ -1370,33 +1340,30 @@ stop_ack_timer(State = #ch{confirm_tref = TRef}) -> flush_multiple(WriterPid, As, NA) -> case gb_sets:is_empty(As) of true -> ok; - false -> SmallestNotAcked = case gb_sets:is_empty(NA) of - false -> gb_sets:smallest(NA); - true -> gb_sets:largest(As)+1 - end, - [First | Rest] = gb_sets:to_list(As), - Remaining = - case Rest of - [] -> [First]; - _ -> flush_multiple(First, Rest, WriterPid, SmallestNotAcked) - end, - [rabbit_writer:send_command(WriterPid, #'basic.ack'{delivery_tag = A}) - || A <- Remaining] -end. - + false -> [First | Rest] = gb_sets:to_list(As), + [rabbit_writer:send_command(WriterPid, + #'basic.ack'{delivery_tag = A}) || + A <- case Rest of + [] -> [First]; + _ -> flush_multiple( + First, Rest, WriterPid, + case gb_sets:is_empty(NA) of + false -> gb_sets:smallest(NA); + true -> gb_sets:largest(As) + 1 + end) + end], + ok + end. flush_multiple(Prev, [Cur | Rest], WriterPid, SNA) -> - ExpNext = Prev+1, + ExpNext = Prev + 1, case {SNA >= Cur, Cur} of - {true, ExpNext} -> - flush_multiple(Cur, Rest, WriterPid, SNA); - _ -> - flush_multiple(Prev, [], WriterPid, SNA), - [Cur | Rest] + {true, ExpNext} -> flush_multiple(Cur, Rest, WriterPid, SNA); + _ -> flush_multiple(Prev, [], WriterPid, SNA), + [Cur | Rest] end; flush_multiple(Prev, [], WriterPid, _) -> - ok = rabbit_writer:send_command( - WriterPid, - #'basic.ack'{delivery_tag = Prev, - multiple = true}), + ok = rabbit_writer:send_command(WriterPid, + #'basic.ack'{delivery_tag = Prev, + multiple = true}), []. -- cgit v1.2.1 From f4c642ab2985c94531e0794658e56336965edf09 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 2 Oct 2010 03:12:47 +0100 Subject: cosmetics and some minor refactoring --- src/rabbit_msg_store.erl | 30 ++++++++++---------- src/rabbit_queue_index.erl | 21 ++++++-------- src/rabbit_router.erl | 19 ++++++------- src/rabbit_variable_queue.erl | 65 +++++++++++++++++++++---------------------- 4 files changed, 64 insertions(+), 71 deletions(-) diff --git a/src/rabbit_msg_store.erl b/src/rabbit_msg_store.erl index 7249e13e..cb4768bd 100644 --- a/src/rabbit_msg_store.erl +++ b/src/rabbit_msg_store.erl @@ -637,12 +637,11 @@ handle_call({register_sync_callback, ClientRef, Fun}, _From, reply(ok, State #msstate { client_ondisk_callback = dict:store(ClientRef, Fun, CODC) }); -handle_call({client_terminate, #client_msstate { client_ref = CRef }}, - _From, +handle_call({client_terminate, #client_msstate { client_ref = CRef }}, _From, State = #msstate { client_ondisk_callback = CODC, cref_to_guids = CTG }) -> reply(ok, State #msstate { client_ondisk_callback = dict:erase(CRef, CODC), - cref_to_guids = dict:erase(CRef, CTG) }). + cref_to_guids = dict:erase(CRef, CTG)}). handle_cast({write, CRef, Guid}, State = #msstate { current_file_handle = CurHdl, @@ -652,7 +651,7 @@ handle_cast({write, CRef, Guid}, file_summary_ets = FileSummaryEts, cur_file_cache_ets = CurFileCacheEts, client_ondisk_callback = CODC, - cref_to_guids = CTG}) -> + cref_to_guids = CTG }) -> true = 0 =< ets:update_counter(CurFileCacheEts, Guid, {3, -1}), [{Guid, Msg, _CacheRefCount}] = ets:lookup(CurFileCacheEts, Guid), @@ -676,16 +675,15 @@ handle_cast({write, CRef, Guid}, [{#file_summary.valid_total_size, ValidTotalSize1}, {#file_summary.file_size, FileSize + TotalSize}]), NextOffset = CurOffset + TotalSize, - noreply( - maybe_roll_to_new_file( - NextOffset, State #msstate { - sum_valid_data = SumValid + TotalSize, - sum_file_size = SumFileSize + TotalSize, - cref_to_guids = - case dict:find(CRef, CODC) of - {ok, _} -> rabbit_misc:dict_cons(CRef, Guid, CTG); - error -> CTG - end})); + CTG1 = case dict:find(CRef, CODC) of + {ok, _} -> rabbit_misc:dict_cons(CRef, Guid, CTG); + error -> CTG + end, + noreply(maybe_roll_to_new_file( + NextOffset, State #msstate { + sum_valid_data = SumValid + TotalSize, + sum_file_size = SumFileSize + TotalSize, + cref_to_guids = CTG1 })); #msg_location { ref_count = RefCount } -> %% We already know about it, just update counter. Only %% update field otherwise bad interaction with concurrent GC @@ -852,8 +850,8 @@ internal_sync(State = #msstate { current_file_handle = CurHdl, State1 #msstate { on_sync = [] } end, dict:map(fun(CRef, Guids) -> Fun = dict:fetch(CRef, CODC), - Fun(Guids) end, - CTG), + Fun(Guids) + end, CTG), State2 #msstate { cref_to_guids = dict:new() }. diff --git a/src/rabbit_queue_index.erl b/src/rabbit_queue_index.erl index 88b6e832..ea3a9fbf 100644 --- a/src/rabbit_queue_index.erl +++ b/src/rabbit_queue_index.erl @@ -257,10 +257,12 @@ delete_and_terminate(State) -> State1. publish(Guid, SeqId, IsPersistent, - State = #qistate { unsynced_guids = UnsyncedGuids }) when is_binary(Guid) -> + State = #qistate { unsynced_guids = UnsyncedGuids }) + when is_binary(Guid) -> ?GUID_BYTES = size(Guid), - {JournalHdl, State1} = - get_journal_handle(State #qistate { unsynced_guids = [Guid | UnsyncedGuids] }), + {JournalHdl, State1} = get_journal_handle( + State #qistate { + unsynced_guids = [Guid | UnsyncedGuids] }), ok = file_handle_cache:append( JournalHdl, [<<(case IsPersistent of true -> ?PUB_PERSIST_JPREFIX; @@ -675,6 +677,10 @@ deliver_or_ack(Kind, SeqIds, State) -> add_to_journal(SeqId, Kind, StateN) end, State1, SeqIds)). +notify_sync(State = #qistate { unsynced_guids = UG, on_sync = OnSyncFun }) -> + OnSyncFun(UG), + State #qistate { unsynced_guids = [] }. + %%---------------------------------------------------------------------------- %% segment manipulation %%---------------------------------------------------------------------------- @@ -942,12 +948,3 @@ journal_minus_segment1({no_pub, del, ack}, {?PUB, del, no_ack}) -> {{no_pub, no_del, ack}, 0}; journal_minus_segment1({no_pub, del, ack}, {?PUB, del, ack}) -> {undefined, -1}. - -%%---------------------------------------------------------------------------- -%% misc -%%---------------------------------------------------------------------------- - -notify_sync(State = #qistate { unsynced_guids = UG, - on_sync = OnSyncFun }) -> - OnSyncFun(UG), - State #qistate { unsynced_guids = [] }. diff --git a/src/rabbit_router.erl b/src/rabbit_router.erl index 707698b0..a1a341a9 100644 --- a/src/rabbit_router.erl +++ b/src/rabbit_router.erl @@ -70,16 +70,15 @@ deliver(QPids, Delivery = #delivery{mandatory = false, QPids, fun (Pid) -> rabbit_amqqueue:deliver(Pid, Delivery) end), {routed, QPids}; -deliver(QPids, Delivery) -> - {Success, _} = - delegate:invoke(QPids, - fun (Pid) -> - rabbit_amqqueue:deliver(Pid, Delivery) - end), - {Routed, Handled} = - lists:foldl(fun fold_deliveries/2, {false, []}, Success), - case check_delivery(Delivery#delivery.mandatory, Delivery#delivery.immediate, - {Routed, Handled}) of +deliver(QPids, Delivery = #delivery{mandatory = Mandatory, + immediate = Immediate}) -> + {Success, _} = delegate:invoke( + QPids, fun (Pid) -> + rabbit_amqqueue:deliver(Pid, Delivery) + end), + case check_delivery(Mandatory, Immediate, + lists:foldl(fun fold_deliveries/2, + {false, []}, Success)) of {routed, Qs} -> {routed, Qs}; O -> O end. diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 9b8dd23c..a72ec2f7 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -34,10 +34,10 @@ -export([init/5, init/3, terminate/1, delete_and_terminate/1, purge/1, publish/3, publish_delivered/4, fetch/2, ack/2, tx_publish/3, tx_ack/3, tx_rollback/2, tx_commit/3, - requeue/2, len/1, is_empty/1, + requeue/2, len/1, is_empty/1, seqids_to_guids/2, set_ram_duration_target/2, ram_duration/1, needs_idle_timeout/1, idle_timeout/1, handle_pre_hibernate/1, - status/1, seqids_to_guids/2]). + status/1]). -export([start/1, stop/0]). @@ -449,7 +449,7 @@ init(QueueName, IsDurable, Recover, timestamp = Now }, msgs_on_disk = gb_sets:new(), msg_indices_on_disk = gb_sets:new(), - unconfirmed = gb_sets:new()}, + unconfirmed = gb_sets:new() }, a(maybe_deltas_to_betas(State)). terminate(State) -> @@ -681,6 +681,15 @@ len(#vqstate { len = Len }) -> Len. is_empty(State) -> 0 == len(State). +seqids_to_guids(SeqIds, #vqstate{ pending_ack = PA }) -> + lists:foldl( + fun(SeqId, Guids) -> + [case dict:fetch(SeqId, PA) of + #msg_status { msg = Msg } -> Msg#basic_message.guid; + {_, Guid} -> Guid + end | Guids] + end, [], SeqIds). + set_ram_duration_target(DurationTarget, State = #vqstate { rates = #rates { avg_egress = AvgEgressRate, @@ -776,15 +785,6 @@ status(#vqstate { q1 = Q1, q2 = Q2, delta = Delta, q3 = Q3, q4 = Q4, {avg_egress_rate , AvgEgressRate}, {avg_ingress_rate , AvgIngressRate} ]. -seqids_to_guids(SeqIds, #vqstate{ pending_ack = PA }) -> - lists:foldl( - fun(SeqId, Guids) -> - [case dict:fetch(SeqId, PA) of - #msg_status { msg = Msg } -> Msg#basic_message.guid; - {_, Guid} -> Guid - end | Guids] - end, [], SeqIds). - %%---------------------------------------------------------------------------- %% Minor helpers %%---------------------------------------------------------------------------- @@ -1182,8 +1182,8 @@ ack(MsgStoreFun, Fun, AckTags, State) -> MsgStoreFun(MsgStore, Guids) end, ok, GuidsByStore), %% the AckTags were removed from State1, so use State in seqids_to_guids - State2 = msgs_confirmed(gb_sets:from_list(seqids_to_guids(AckTags, State)), - State1), + State2 = remove_confirms( + gb_sets:from_list(seqids_to_guids(AckTags, State)), State1), PCount1 = PCount - find_persistent_count(sum_guids_by_store_to_len( orddict:new(), GuidsByStore)), State2 #vqstate { index_state = IndexState1, @@ -1207,13 +1207,16 @@ find_persistent_count(LensByStore) -> %% Internal plumbing for confirms (aka publisher acks) %%---------------------------------------------------------------------------- -msgs_confirmed(GuidSet, State = #vqstate { msgs_on_disk = MOD, - msg_indices_on_disk = MIOD, - unconfirmed = UC }) -> +remove_confirms(GuidSet, State = #vqstate { msgs_on_disk = MOD, + msg_indices_on_disk = MIOD, + unconfirmed = UC }) -> State #vqstate { msgs_on_disk = gb_sets:difference(MOD, GuidSet), msg_indices_on_disk = gb_sets:difference(MIOD, GuidSet), unconfirmed = gb_sets:difference(UC, GuidSet) }. +msgs_confirmed(GuidSet, State) -> + {remove_confirms(GuidSet, State), {confirm, gb_sets:to_list(GuidSet)}}. + msgs_written_to_disk(QPid, Guids) -> spawn(fun() -> rabbit_amqqueue:maybe_run_queue_via_backing_queue( QPid, @@ -1221,14 +1224,12 @@ msgs_written_to_disk(QPid, Guids) -> msg_indices_on_disk = MIOD, unconfirmed = UC }) -> GuidSet = gb_sets:from_list(Guids), - ToConfirmMsgs = gb_sets:intersection(GuidSet, MIOD), - State1 = - State #vqstate { - msgs_on_disk = - gb_sets:intersection( - gb_sets:union(MOD, GuidSet), UC) }, - { msgs_confirmed(ToConfirmMsgs, State1), - {confirm, gb_sets:to_list(ToConfirmMsgs)} } + msgs_confirmed( + gb_sets:intersection(GuidSet, MIOD), + State #vqstate { + msgs_on_disk = + gb_sets:intersection( + gb_sets:union(MOD, GuidSet), UC) }) end) end). @@ -1239,14 +1240,12 @@ msg_indices_written_to_disk(QPid, Guids) -> msg_indices_on_disk = MIOD, unconfirmed = UC }) -> GuidSet = gb_sets:from_list(Guids), - ToConfirmMsgs = gb_sets:intersection(GuidSet, MOD), - State1 = - State #vqstate { - msg_indices_on_disk = - gb_sets:intersection( - gb_sets:union(MIOD, GuidSet), UC) }, - { msgs_confirmed(ToConfirmMsgs, State1), - {confirm, gb_sets:to_list(ToConfirmMsgs)} } + msgs_confirmed( + gb_sets:intersection(GuidSet, MOD), + State #vqstate { + msg_indices_on_disk = + gb_sets:intersection( + gb_sets:union(MIOD, GuidSet), UC) }) end) end). -- cgit v1.2.1 From 78377bc08a5674a8e051caa17ecfb72c8a12eaa3 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Mon, 4 Oct 2010 10:35:21 +0100 Subject: only confirm immediate messages if they haven't been delivered --- src/rabbit_amqqueue_process.erl | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 7723d722..5b31c8ce 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -699,7 +699,8 @@ handle_call(consumers, _From, [{ChPid, ConsumerTag, AckRequired} | Acc] end, [], queue:join(ActiveConsumers, BlockedConsumers)), State); -handle_call({deliver_immediately, Delivery}, _From, State) -> +handle_call({deliver_immediately, Delivery = #delivery{message = Msg}} + , _From, State) -> %% Synchronous, "immediate" delivery mode %% %% FIXME: Is this correct semantics? @@ -713,10 +714,13 @@ handle_call({deliver_immediately, Delivery}, _From, State) -> %% just all ready-to-consume queues get the message, with unready %% queues discarding the message? %% - {Delivered, NewState} = attempt_delivery(Delivery, State), - reply(Delivered, - confirm_message(Delivery#delivery.message#basic_message.guid, - record_confirm_message(Delivery, NewState))); + State1 = record_confirm_message(Delivery, State), + {Delivered, State2} = attempt_delivery(Delivery, State1), + State3 = case Delivered of + true -> State2, + false -> confirm_message(Msg#basic_message.guid, State2) + end, + reply(Delivered, State3); handle_call({deliver, Delivery}, _From, State) -> %% Synchronous, "mandatory" delivery mode -- cgit v1.2.1 From 171d7882d6cc7992b539a8eb1b629d7a84d31ba5 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Mon, 4 Oct 2010 10:48:21 +0100 Subject: do_if_not_dup -> do_if_unconfirmed; bugfixes --- src/rabbit_amqqueue_process.erl | 2 +- src/rabbit_channel.erl | 26 +++++++++++++------------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 5b31c8ce..f1085a0c 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -717,7 +717,7 @@ handle_call({deliver_immediately, Delivery = #delivery{message = Msg}} State1 = record_confirm_message(Delivery, State), {Delivered, State2} = attempt_delivery(Delivery, State1), State3 = case Delivered of - true -> State2, + true -> State2; false -> confirm_message(Msg#basic_message.guid, State2) end, reply(Delivered, State3); diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index a755b073..d641e824 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -452,18 +452,18 @@ send_or_enqueue_ack(undefined, State) -> send_or_enqueue_ack(_, State = #ch{confirm_enabled = false}) -> State; send_or_enqueue_ack(MsgSeqNo, State = #ch{confirm_multiple = false}) -> - do_if_not_dup(MsgSeqNo, State, - fun(MSN, State1 = #ch{writer_pid = WriterPid}) -> - ok = rabbit_writer:send_command( - WriterPid, #'basic.ack'{delivery_tag = MSN}), - State1 - end); + do_if_unconfirmed(MsgSeqNo, State, + fun(MSN, State1 = #ch{writer_pid = WriterPid}) -> + ok = rabbit_writer:send_command( + WriterPid, #'basic.ack'{delivery_tag = MSN}), + State1 + end); send_or_enqueue_ack(MsgSeqNo, State = #ch{confirm_multiple = true}) -> - do_if_not_dup(MsgSeqNo, State, - fun(MSN, State1 = #ch{held_confirms = As}) -> - start_ack_timer(State1#ch{held_confirms = - gb_sets:add(MSN, As)}) - end). + do_if_unconfirmed(MsgSeqNo, State, + fun(MSN, State1 = #ch{held_confirms = As}) -> + start_ack_timer(State1#ch{held_confirms = + gb_sets:add(MSN, As)}) + end). msg_sent_to_queue(undefined, _QPid, State) -> State; @@ -473,9 +473,9 @@ msg_sent_to_queue(MsgSeqNo, QPid, State = #ch{qpid_to_msgs = QTM}) -> error -> erlang:monitor(process, QPid), gb_sets:new() end, - State#ch{qpid_to_msgs = dict:store(QPid, gb_sets:add(MsgSeqNo, Msgs1))}. + State#ch{qpid_to_msgs = dict:store(QPid, gb_sets:add(MsgSeqNo, Msgs1), QTM)}. -do_if_not_dup(MsgSeqNo, State = #ch{unconfirmed = UC}, Fun) -> +do_if_unconfirmed(MsgSeqNo, State = #ch{unconfirmed = UC}, Fun) -> case gb_sets:is_element(MsgSeqNo, UC) of true -> QTM = dict:map(fun (_, Msgs) -> gb_sets:delete_any(MsgSeqNo, Msgs) -- cgit v1.2.1 From 230ee330d2edb37ecb3a8d140753a25b944520da Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Mon, 4 Oct 2010 11:11:31 +0100 Subject: inlined register_sync_callback into new_client_state --- src/rabbit_msg_store.erl | 49 ++++++++++++++++++++++-------------------------- 1 file changed, 22 insertions(+), 27 deletions(-) diff --git a/src/rabbit_msg_store.erl b/src/rabbit_msg_store.erl index cb4768bd..73565847 100644 --- a/src/rabbit_msg_store.erl +++ b/src/rabbit_msg_store.erl @@ -363,8 +363,7 @@ set_maximum_since_use(Server, Age) -> client_init(Server, Ref, MsgOnDiskFun) -> {IState, IModule, Dir, GCPid, FileHandlesEts, FileSummaryEts, DedupCacheEts, CurFileCacheEts} = - gen_server2:call(Server, {new_client_state, Ref}, infinity), - register_sync_callback(Server, Ref, MsgOnDiskFun), + gen_server2:call(Server, {new_client_state, Ref, MsgOnDiskFun}, infinity), #client_msstate { file_handle_cache = dict:new(), index_state = IState, index_module = IModule, @@ -387,9 +386,6 @@ client_delete_and_terminate(CState, Server, Ref) -> successfully_recovered_state(Server) -> gen_server2:call(Server, successfully_recovered_state, infinity). -register_sync_callback(Server, ClientRef, Fun) -> - gen_server2:call(Server, {register_sync_callback, ClientRef, Fun}, infinity). - %%---------------------------------------------------------------------------- %% Client-side-only helpers %%---------------------------------------------------------------------------- @@ -591,10 +587,10 @@ init([Server, BaseDir, ClientRefs, StartupFunState]) -> prioritise_call(Msg, _From, _State) -> case Msg of - {new_client_state, _Ref} -> 7; - successfully_recovered_state -> 7; - {read, _Guid} -> 2; - _ -> 0 + {new_client_state, _Ref, _MODC} -> 7; + successfully_recovered_state -> 7; + {read, _Guid} -> 2; + _ -> 0 end. prioritise_cast(Msg, _State) -> @@ -613,30 +609,29 @@ handle_call({contains, Guid}, From, State) -> State1 = contains_message(Guid, From, State), noreply(State1); -handle_call({new_client_state, CRef}, _From, - State = #msstate { dir = Dir, - index_state = IndexState, - index_module = IndexModule, - file_handles_ets = FileHandlesEts, - file_summary_ets = FileSummaryEts, - dedup_cache_ets = DedupCacheEts, - cur_file_cache_ets = CurFileCacheEts, - client_refs = ClientRefs, - gc_pid = GCPid }) -> +handle_call({new_client_state, CRef, Callback}, _From, + State = #msstate { dir = Dir, + index_state = IndexState, + index_module = IndexModule, + file_handles_ets = FileHandlesEts, + file_summary_ets = FileSummaryEts, + dedup_cache_ets = DedupCacheEts, + cur_file_cache_ets = CurFileCacheEts, + client_refs = ClientRefs, + client_ondisk_callback = CODC, + gc_pid = GCPid }) -> + CODC1 = case Callback of + undefined -> CODC; + _ -> dict:store(CRef, Callback, CODC) + end, reply({IndexState, IndexModule, Dir, GCPid, FileHandlesEts, FileSummaryEts, DedupCacheEts, CurFileCacheEts}, - State #msstate { client_refs = sets:add_element(CRef, ClientRefs) }); + State #msstate { client_refs = sets:add_element(CRef, ClientRefs), + client_ondisk_callback = CODC1 }); handle_call(successfully_recovered_state, _From, State) -> reply(State #msstate.successfully_recovered, State); -handle_call({register_sync_callback, _ , undefined}, _From, State) -> - reply(ok, State); -handle_call({register_sync_callback, ClientRef, Fun}, _From, - State = #msstate { client_ondisk_callback = CODC }) -> - reply(ok, State #msstate { client_ondisk_callback = - dict:store(ClientRef, Fun, CODC) }); - handle_call({client_terminate, #client_msstate { client_ref = CRef }}, _From, State = #msstate { client_ondisk_callback = CODC, cref_to_guids = CTG }) -> -- cgit v1.2.1 From e813894056d29bbbfda7905e1e4144ec35abca72 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Mon, 4 Oct 2010 11:29:03 +0100 Subject: both client_terminate and client_delete clear the msg_store's on_disk callbacks --- src/rabbit_msg_store.erl | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/rabbit_msg_store.erl b/src/rabbit_msg_store.erl index 73565847..1a25e790 100644 --- a/src/rabbit_msg_store.erl +++ b/src/rabbit_msg_store.erl @@ -493,6 +493,13 @@ client_read3(Server, #msg_location { guid = Guid, file = File }, Defer, end end. +clear_client_callback(CRef, + State = #msstate { client_ondisk_callback = CODC, + cref_to_guids = CTG }) -> + State #msstate { client_ondisk_callback = dict:erase(CRef, CODC), + cref_to_guids = dict:erase(CRef, CTG)}. + + %%---------------------------------------------------------------------------- %% gen_server callbacks %%---------------------------------------------------------------------------- @@ -632,11 +639,8 @@ handle_call({new_client_state, CRef, Callback}, _From, handle_call(successfully_recovered_state, _From, State) -> reply(State #msstate.successfully_recovered, State); -handle_call({client_terminate, #client_msstate { client_ref = CRef }}, _From, - State = #msstate { client_ondisk_callback = CODC, - cref_to_guids = CTG }) -> - reply(ok, State #msstate { client_ondisk_callback = dict:erase(CRef, CODC), - cref_to_guids = dict:erase(CRef, CTG)}). +handle_call({client_terminate, #client_msstate { client_ref = CRef }}, _From, State) -> + reply(ok, clear_client_callback(CRef, State)). handle_cast({write, CRef, Guid}, State = #msstate { current_file_handle = CurHdl, @@ -754,10 +758,9 @@ handle_cast({set_maximum_since_use, Age}, State) -> ok = file_handle_cache:set_maximum_since_use(Age), noreply(State); -handle_cast({client_delete, CRef}, - State = #msstate { client_refs = ClientRefs }) -> - noreply( - State #msstate { client_refs = sets:del_element(CRef, ClientRefs) }). +handle_cast({client_delete, CRef}, State = #msstate { client_refs = ClientRefs }) -> + State1 = clear_client_callback(CRef, State), + noreply(State1 #msstate { client_refs = sets:del_element(CRef, ClientRefs) }). handle_info(timeout, State) -> noreply(internal_sync(State)); -- cgit v1.2.1 From 47dab7c168da252139a71a1fc44a8673d4f342e0 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Mon, 4 Oct 2010 11:54:16 +0100 Subject: confirm messages when they get written do disk in *any* queue; confirm messages immediately if they've already been written to disk --- src/rabbit_msg_store.erl | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/rabbit_msg_store.erl b/src/rabbit_msg_store.erl index 1a25e790..0dba4dd2 100644 --- a/src/rabbit_msg_store.erl +++ b/src/rabbit_msg_store.erl @@ -683,13 +683,18 @@ handle_cast({write, CRef, Guid}, sum_valid_data = SumValid + TotalSize, sum_file_size = SumFileSize + TotalSize, cref_to_guids = CTG1 })); - #msg_location { ref_count = RefCount } -> + #msg_location { ref_count = RefCount, file = File } -> %% We already know about it, just update counter. Only %% update field otherwise bad interaction with concurrent GC ok = index_update_fields(Guid, {#msg_location.ref_count, RefCount + 1}, State), - noreply(State) + CTG1 = case {dict:find(CRef, CODC), File =:= CurFile} of + {{ok, _} , true} -> rabbit_misc:dict_cons(CRef, Guid, CTG); + {{ok, Fun}, false} -> Fun([Guid]), CTG; + _ -> CTG + end, + noreply(State #msstate { cref_to_guids = CTG1 }) end; handle_cast({remove, Guids}, State) -> -- cgit v1.2.1 From aac6fd66885fbbef065458f35b45dd8741f7e987 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Mon, 4 Oct 2010 12:36:13 +0100 Subject: schedule a fsync if necessary before confirming messages --- src/rabbit_msg_store.erl | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/rabbit_msg_store.erl b/src/rabbit_msg_store.erl index 0dba4dd2..521c1c09 100644 --- a/src/rabbit_msg_store.erl +++ b/src/rabbit_msg_store.erl @@ -846,15 +846,21 @@ internal_sync(State = #msstate { current_file_handle = CurHdl, client_ondisk_callback = CODC, cref_to_guids = CTG }) -> State1 = stop_sync_timer(State), + CGs = dict:fold(fun (_CRef, [], NS) -> NS; + (CRef, Guids, NS) -> [{CRef, Guids} | NS] + end, [], CTG), + if Syncs =/= [] orelse CGs =/= [] -> ok = file_handle_cache:sync(CurHdl); + true -> ok + end, State2 = case Syncs of [] -> State1; - _ -> ok = file_handle_cache:sync(CurHdl), - lists:foreach(fun (K) -> K() end, lists:reverse(Syncs)), + _ -> lists:foreach(fun (K) -> K() end, lists:reverse(Syncs)), State1 #msstate { on_sync = [] } end, - dict:map(fun(CRef, Guids) -> Fun = dict:fetch(CRef, CODC), - Fun(Guids) - end, CTG), + [begin + Fun = dict:fetch(CRef, CODC), + Fun(Guids) + end || {CRef, Guids} <- CGs], State2 #msstate { cref_to_guids = dict:new() }. -- cgit v1.2.1 From 4581a548767b6361aacfff80baf6ca5325c46a1a Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Mon, 4 Oct 2010 13:15:48 +0100 Subject: refactor --- src/rabbit_channel.erl | 2 +- src/rabbit_queue_index.erl | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index d641e824..787fc82c 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -91,7 +91,7 @@ -spec(do/3 :: (pid(), rabbit_framing:amqp_method_record(), rabbit_types:maybe(rabbit_types:content())) -> 'ok'). -spec(shutdown/1 :: (pid()) -> 'ok'). --spec(send_command/2 :: (pid(), rabbit_framing:amqp_method()) -> 'ok'). +-spec(send_command/2 :: (pid(), rabbit_framing:amqp_method_record()) -> 'ok'). -spec(deliver/4 :: (pid(), rabbit_types:ctag(), boolean(), rabbit_amqqueue:qmsg()) -> 'ok'). diff --git a/src/rabbit_queue_index.erl b/src/rabbit_queue_index.erl index ea3a9fbf..81b6daf9 100644 --- a/src/rabbit_queue_index.erl +++ b/src/rabbit_queue_index.erl @@ -185,12 +185,13 @@ })). -type(seq_id() :: integer()). -type(seg_dict() :: {dict:dictionary(), [segment()]}). +-type(on_sync_fun() :: fun (([rabbit_guid:guid()]) -> ok)). -type(qistate() :: #qistate { dir :: file:filename(), segments :: 'undefined' | seg_dict(), journal_handle :: hdl(), dirty_count :: integer(), max_journal_entries :: non_neg_integer(), - on_sync :: fun (([rabbit_guid:guid()]) -> ok), + on_sync :: on_sync_fun(), unsynced_guids :: [rabbit_guid:guid()] }). -type(startup_fun_state() :: @@ -198,8 +199,7 @@ A}). -spec(init/5 :: (rabbit_amqqueue:name(), boolean(), boolean(), - fun ((rabbit_guid:guid()) -> boolean()), - fun (([seq_id()]) -> ok)) + fun ((rabbit_guid:guid()) -> boolean()), on_sync_fun()) -> {'undefined' | non_neg_integer(), [any()], qistate()}). -spec(terminate/2 :: ([any()], qistate()) -> qistate()). -spec(delete_and_terminate/1 :: (qistate()) -> qistate()). @@ -232,19 +232,18 @@ init(Name, false, _MsgStoreRecovered, _ContainsCheckFun, OnSyncFun) -> init(Name, true, MsgStoreRecovered, ContainsCheckFun, OnSyncFun) -> State = #qistate { dir = Dir } = blank_state(Name), - State1 = State #qistate { on_sync = OnSyncFun }, Terms = case read_shutdown_terms(Dir) of {error, _} -> []; {ok, Terms1} -> Terms1 end, CleanShutdown = detect_clean_shutdown(Dir), - {Count, State2} = + {Count, State1} = case CleanShutdown andalso MsgStoreRecovered of true -> RecoveredCounts = proplists:get_value(segments, Terms, []), - init_clean(RecoveredCounts, State1); - false -> init_dirty(CleanShutdown, ContainsCheckFun, State1) + init_clean(RecoveredCounts, State); + false -> init_dirty(CleanShutdown, ContainsCheckFun, State) end, - {Count, Terms, State2}. + {Count, Terms, State1 #qistate { on_sync = OnSyncFun }}. terminate(Terms, State) -> {SegmentCounts, State1 = #qistate { dir = Dir }} = terminate(State), @@ -380,6 +379,7 @@ blank_state(QueueName) -> journal_handle = undefined, dirty_count = 0, max_journal_entries = MaxJournal, + on_sync = fun (_) -> ok end, unsynced_guids = [] }. clean_file_name(Dir) -> filename:join(Dir, ?CLEAN_FILENAME). -- cgit v1.2.1 From 3cbbcb57949ec4a29f289596420904aa0f4af694 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Mon, 4 Oct 2010 15:33:26 +0100 Subject: refactor --- include/rabbit_backing_queue_spec.hrl | 2 +- src/rabbit_amqqueue_process.erl | 5 +-- src/rabbit_channel.erl | 2 +- src/rabbit_tests.erl | 7 ++-- src/rabbit_variable_queue.erl | 69 +++++++++++++++++------------------ 5 files changed, 41 insertions(+), 44 deletions(-) diff --git a/include/rabbit_backing_queue_spec.hrl b/include/rabbit_backing_queue_spec.hrl index 8addfb9b..9d532656 100644 --- a/include/rabbit_backing_queue_spec.hrl +++ b/include/rabbit_backing_queue_spec.hrl @@ -50,7 +50,7 @@ (ack_required(), rabbit_types:basic_message(), confirm_required(), state()) -> {ack(), state()}). -spec(fetch/2 :: (ack_required(), state()) -> {fetch_result(), state()}). --spec(ack/2 :: ([ack()], state()) -> state()). +-spec(ack/2 :: ([ack()], state()) -> {state(), [rabbit_guid:guid()]}). -spec(tx_publish/3 :: (rabbit_types:txn(), rabbit_types:basic_message(), state()) -> state()). -spec(tx_ack/3 :: (rabbit_types:txn(), [ack()], state()) -> state()). -spec(tx_rollback/2 :: (rabbit_types:txn(), state()) -> {[ack()], state()}). diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index f1085a0c..72b9b49d 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -422,9 +422,8 @@ record_confirm_message(#delivery{msg_seq_no = MsgSeqNo, ack_by_acktags(AckTags, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> - AckdGuids = BQ:seqids_to_guids(AckTags, BQS), - confirm_messages(AckdGuids, - State#q{backing_queue_state = BQ:ack(AckTags, BQS)}). + {BQS1, AckdGuids} = BQ:ack(AckTags, BQS), + confirm_messages(AckdGuids, State#q{backing_queue_state = BQS1}). run_message_queue(State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> Funs = {fun deliver_from_queue_pred/2, diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 787fc82c..d641e824 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -91,7 +91,7 @@ -spec(do/3 :: (pid(), rabbit_framing:amqp_method_record(), rabbit_types:maybe(rabbit_types:content())) -> 'ok'). -spec(shutdown/1 :: (pid()) -> 'ok'). --spec(send_command/2 :: (pid(), rabbit_framing:amqp_method_record()) -> 'ok'). +-spec(send_command/2 :: (pid(), rabbit_framing:amqp_method()) -> 'ok'). -spec(deliver/4 :: (pid(), rabbit_types:ctag(), boolean(), rabbit_amqqueue:qmsg()) -> 'ok'). diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index ddb78aff..e03d1e94 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -1854,7 +1854,7 @@ test_variable_queue_dynamic_duration_change(VQ0) -> %% drain {VQ8, AckTags} = variable_queue_fetch(Len, false, false, Len, VQ7), - VQ9 = rabbit_variable_queue:ack(AckTags, VQ8), + {VQ9, _} = rabbit_variable_queue:ack(AckTags, VQ8), {empty, VQ10} = rabbit_variable_queue:fetch(true, VQ9), VQ10. @@ -1864,7 +1864,8 @@ publish_fetch_and_ack(0, _Len, 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), - publish_fetch_and_ack(N-1, Len, rabbit_variable_queue:ack([AckTag], VQ2)). + {VQ3, _} = rabbit_variable_queue:ack([AckTag], VQ2), + publish_fetch_and_ack(N-1, Len, VQ3). test_variable_queue_partial_segments_delta_thing(VQ0) -> SegmentSize = rabbit_queue_index:next_segment_boundary(0), @@ -1897,7 +1898,7 @@ test_variable_queue_partial_segments_delta_thing(VQ0) -> {len, HalfSegment + 1}]), {VQ8, AckTags1} = variable_queue_fetch(HalfSegment + 1, true, false, HalfSegment + 1, VQ7), - VQ9 = rabbit_variable_queue:ack(AckTags ++ AckTags1, VQ8), + {VQ9, _} = rabbit_variable_queue:ack(AckTags ++ AckTags1, VQ8), %% should be empty now {empty, VQ10} = rabbit_variable_queue:fetch(true, VQ9), VQ10. diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index a72ec2f7..c687ae02 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -34,7 +34,7 @@ -export([init/5, init/3, terminate/1, delete_and_terminate/1, purge/1, publish/3, publish_delivered/4, fetch/2, ack/2, tx_publish/3, tx_ack/3, tx_rollback/2, tx_commit/3, - requeue/2, len/1, is_empty/1, seqids_to_guids/2, + requeue/2, len/1, is_empty/1, set_ram_duration_target/2, ram_duration/1, needs_idle_timeout/1, idle_timeout/1, handle_pre_hibernate/1, status/1]). @@ -662,34 +662,27 @@ tx_commit(Txn, Fun, State = #vqstate { durable = IsDurable }) -> end)}. requeue(AckTags, State) -> - a(reduce_memory_use( - ack(fun rabbit_msg_store:release/2, - fun (#msg_status { msg = Msg }, State1) -> - {_SeqId, State2} = publish(Msg, true, false, false, State1), - State2; - ({IsPersistent, Guid}, State1) -> - #vqstate { msg_store_clients = MSCState } = State1, - {{ok, Msg = #basic_message{}}, MSCState1} = - read_from_msg_store(MSCState, IsPersistent, Guid), - State2 = State1 #vqstate { msg_store_clients = MSCState1 }, - {_SeqId, State3} = publish(Msg, true, true, false, State2), - State3 - end, - AckTags, State))). + {State1, _Guids} = + a(reduce_memory_use( + ack(fun rabbit_msg_store:release/2, + fun (#msg_status { msg = Msg }, State1) -> + {_SeqId, State2} = publish(Msg, true, false, false, State1), + State2; + ({IsPersistent, Guid}, State1) -> + #vqstate { msg_store_clients = MSCState } = State1, + {{ok, Msg = #basic_message{}}, MSCState1} = + read_from_msg_store(MSCState, IsPersistent, Guid), + State2 = State1 #vqstate { msg_store_clients = MSCState1 }, + {_SeqId, State3} = publish(Msg, true, true, false, State2), + State3 + end, + AckTags, State))), + State1. len(#vqstate { len = Len }) -> Len. is_empty(State) -> 0 == len(State). -seqids_to_guids(SeqIds, #vqstate{ pending_ack = PA }) -> - lists:foldl( - fun(SeqId, Guids) -> - [case dict:fetch(SeqId, PA) of - #msg_status { msg = Msg } -> Msg#basic_message.guid; - {_, Guid} -> Guid - end | Guids] - end, [], SeqIds). - set_ram_duration_target(DurationTarget, State = #vqstate { rates = #rates { avg_egress = AvgEgressRate, @@ -789,6 +782,8 @@ status(#vqstate { q1 = Q1, q2 = Q2, delta = Delta, q3 = Q3, q4 = Q4, %% Minor helpers %%---------------------------------------------------------------------------- +a({State, Other}) -> + {a(State), Other}; a(State = #vqstate { q1 = Q1, q2 = Q2, delta = Delta, q3 = Q3, q4 = Q4, len = Len, persistent_count = PersistentCount, @@ -990,6 +985,7 @@ tx_commit_index(State = #vqstate { on_sync = #sync { PAcks = lists:append(SPAcks), Acks = lists:append(SAcks), Pubs = lists:append(lists:reverse(SPubs)), + {NewState, _Guids} = ack(Acks, State), {SeqIds, State1 = #vqstate { index_state = IndexState }} = lists:foldl( fun (Msg = #basic_message { is_persistent = IsPersistent }, @@ -997,7 +993,7 @@ tx_commit_index(State = #vqstate { on_sync = #sync { IsPersistent1 = IsDurable andalso IsPersistent, {SeqId, State3} = publish(Msg, false, IsPersistent1, false, State2), {cons_if(IsPersistent1, SeqId, SeqIdsAcc), State3} - end, {PAcks, ack(Acks, State)}, Pubs), + end, {PAcks, NewState}, Pubs), IndexState1 = rabbit_queue_index:sync(SeqIds, IndexState), [ Fun() || Fun <- lists:reverse(SFuns) ], reduce_memory_use( @@ -1166,28 +1162,27 @@ remove_pending_ack(KeepPersistent, end. ack(_MsgStoreFun, _Fun, [], State) -> - State; + {State, []}; ack(MsgStoreFun, Fun, AckTags, State) -> - {{SeqIds, GuidsByStore}, State1 = #vqstate { index_state = IndexState, - persistent_count = PCount }} = + {Guids, {SeqIds, GuidsByStore}, State1 = #vqstate { index_state = IndexState, + persistent_count = PCount }} = lists:foldl( - fun (SeqId, {Acc, State2 = #vqstate { pending_ack = PA }}) -> + fun (SeqId, {Gs, Acc, State2 = #vqstate { pending_ack = PA }}) -> {ok, AckEntry} = dict:find(SeqId, PA), - {accumulate_ack(SeqId, AckEntry, Acc), + {[AckEntry | Gs], + accumulate_ack(SeqId, AckEntry, Acc), Fun(AckEntry, State2 #vqstate { pending_ack = dict:erase(SeqId, PA) })} - end, {{[], orddict:new()}, State}, AckTags), + end, {[], {[], orddict:new()}, State}, AckTags), IndexState1 = rabbit_queue_index:ack(SeqIds, IndexState), ok = orddict:fold(fun (MsgStore, Guids, ok) -> MsgStoreFun(MsgStore, Guids) end, ok, GuidsByStore), - %% the AckTags were removed from State1, so use State in seqids_to_guids - State2 = remove_confirms( - gb_sets:from_list(seqids_to_guids(AckTags, State)), State1), + State2 = remove_confirms(gb_sets:from_list(Guids), State1), PCount1 = PCount - find_persistent_count(sum_guids_by_store_to_len( orddict:new(), GuidsByStore)), - State2 #vqstate { index_state = IndexState1, - persistent_count = PCount1 }. + {State2 #vqstate { index_state = IndexState1, + persistent_count = PCount1 }, Guids}. accumulate_ack(_SeqId, #msg_status { is_persistent = false, %% ASSERTIONS msg_on_disk = false, @@ -1289,6 +1284,8 @@ reduce_memory_use(AlphaBetaFun, BetaGammaFun, BetaDeltaFun, State) -> end end. +reduce_memory_use({State, Other}) -> + {reduce_memory_use(State), Other}; reduce_memory_use(State) -> {_, State1} = reduce_memory_use(fun push_alphas_to_betas/2, fun limit_ram_index/2, -- cgit v1.2.1 From 0273fbe1480005c8364b6fd8ab6f890ab7547f63 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Mon, 4 Oct 2010 15:43:46 +0100 Subject: gb:add -> gb:insert --- 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 c687ae02..900f325b 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -542,7 +542,7 @@ publish_delivered(true, Msg = #basic_message { is_persistent = IsPersistent, PA1 = record_pending_ack(m(MsgStatus1), PA), PCount1 = PCount + one_if(IsPersistent1), Unconfirmed1 = case NeedsConfirming of - true -> gb_sets:add(Guid, Unconfirmed); + true -> gb_sets:insert(Guid, Unconfirmed); false -> Unconfirmed end, {SeqId, a(State1 #vqstate { @@ -1068,7 +1068,7 @@ publish(Msg = #basic_message { is_persistent = IsPersistent, end, PCount1 = PCount + one_if(IsPersistent1), Unconfirmed1 = case NeedsConfirming of - true -> gb_sets:add(Guid, Unconfirmed); + true -> gb_sets:insert(Guid, Unconfirmed); false -> Unconfirmed end, {SeqId, State2 #vqstate { -- cgit v1.2.1 From 8e3eb40582ee16898eaaf65babe0e9d945621578 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Mon, 4 Oct 2010 15:51:56 +0100 Subject: clean up code --- src/rabbit_variable_queue.erl | 41 +++++++++++++++++++---------------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 900f325b..f29cf5f1 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -611,9 +611,11 @@ fetch(AckRequired, State = #vqstate { q4 = Q4, end. ack(AckTags, State) -> - a(ack(fun rabbit_msg_store:remove/2, - fun (_AckEntry, State1) -> State1 end, - AckTags, State)). + {State1, Guids} = + ack(fun rabbit_msg_store:remove/2, + fun (_AckEntry, State1) -> State1 end, + AckTags, State), + {a(State1), Guids}. tx_publish(Txn, Msg = #basic_message { is_persistent = IsPersistent }, State = #vqstate { durable = IsDurable, @@ -663,21 +665,20 @@ tx_commit(Txn, Fun, State = #vqstate { durable = IsDurable }) -> requeue(AckTags, State) -> {State1, _Guids} = - a(reduce_memory_use( - ack(fun rabbit_msg_store:release/2, - fun (#msg_status { msg = Msg }, State1) -> - {_SeqId, State2} = publish(Msg, true, false, false, State1), - State2; - ({IsPersistent, Guid}, State1) -> - #vqstate { msg_store_clients = MSCState } = State1, - {{ok, Msg = #basic_message{}}, MSCState1} = - read_from_msg_store(MSCState, IsPersistent, Guid), - State2 = State1 #vqstate { msg_store_clients = MSCState1 }, - {_SeqId, State3} = publish(Msg, true, true, false, State2), - State3 - end, - AckTags, State))), - State1. + ack(fun rabbit_msg_store:release/2, + fun (#msg_status { msg = Msg }, State1) -> + {_SeqId, State2} = publish(Msg, true, false, false, State1), + State2; + ({IsPersistent, Guid}, State1) -> + #vqstate { msg_store_clients = MSCState } = State1, + {{ok, Msg = #basic_message{}}, MSCState1} = + read_from_msg_store(MSCState, IsPersistent, Guid), + State2 = State1 #vqstate { msg_store_clients = MSCState1 }, + {_SeqId, State3} = publish(Msg, true, true, false, State2), + State3 + end, + AckTags, State), + a(reduce_memory_use(State1)). len(#vqstate { len = Len }) -> Len. @@ -782,8 +783,6 @@ status(#vqstate { q1 = Q1, q2 = Q2, delta = Delta, q3 = Q3, q4 = Q4, %% Minor helpers %%---------------------------------------------------------------------------- -a({State, Other}) -> - {a(State), Other}; a(State = #vqstate { q1 = Q1, q2 = Q2, delta = Delta, q3 = Q3, q4 = Q4, len = Len, persistent_count = PersistentCount, @@ -1284,8 +1283,6 @@ reduce_memory_use(AlphaBetaFun, BetaGammaFun, BetaDeltaFun, State) -> end end. -reduce_memory_use({State, Other}) -> - {reduce_memory_use(State), Other}; reduce_memory_use(State) -> {_, State1} = reduce_memory_use(fun push_alphas_to_betas/2, fun limit_ram_index/2, -- cgit v1.2.1 From b1b6201cd23233b9aa9b727d43c386819007155f Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Mon, 4 Oct 2010 16:05:48 +0100 Subject: refactor --- src/rabbit_amqqueue_process.erl | 17 ++++++++--------- src/rabbit_msg_store.erl | 18 ++++++------------ 2 files changed, 14 insertions(+), 21 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 72b9b49d..ee74b3d2 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -698,8 +698,8 @@ handle_call(consumers, _From, [{ChPid, ConsumerTag, AckRequired} | Acc] end, [], queue:join(ActiveConsumers, BlockedConsumers)), State); -handle_call({deliver_immediately, Delivery = #delivery{message = Msg}} - , _From, State) -> +handle_call({deliver_immediately, Delivery = #delivery{message = Msg}}, + _From, State) -> %% Synchronous, "immediate" delivery mode %% %% FIXME: Is this correct semantics? @@ -713,13 +713,12 @@ handle_call({deliver_immediately, Delivery = #delivery{message = Msg}} %% just all ready-to-consume queues get the message, with unready %% queues discarding the message? %% - State1 = record_confirm_message(Delivery, State), - {Delivered, State2} = attempt_delivery(Delivery, State1), - State3 = case Delivered of - true -> State2; - false -> confirm_message(Msg#basic_message.guid, State2) - end, - reply(Delivered, State3); + {Delivered, State1} = attempt_delivery(Delivery, + record_confirm_message(Delivery, State)), + reply(Delivered, case Delivered of + true -> State1; + false -> confirm_message(Msg#basic_message.guid, State1) + end); handle_call({deliver, Delivery}, _From, State) -> %% Synchronous, "mandatory" delivery mode diff --git a/src/rabbit_msg_store.erl b/src/rabbit_msg_store.erl index 521c1c09..ff6ff96c 100644 --- a/src/rabbit_msg_store.erl +++ b/src/rabbit_msg_store.erl @@ -849,19 +849,13 @@ internal_sync(State = #msstate { current_file_handle = CurHdl, CGs = dict:fold(fun (_CRef, [], NS) -> NS; (CRef, Guids, NS) -> [{CRef, Guids} | NS] end, [], CTG), - if Syncs =/= [] orelse CGs =/= [] -> ok = file_handle_cache:sync(CurHdl); - true -> ok + if Syncs =:= [] andalso CGs =:= [] -> ok; + true -> file_handle_cache:sync(CurHdl) end, - State2 = case Syncs of - [] -> State1; - _ -> lists:foreach(fun (K) -> K() end, lists:reverse(Syncs)), - State1 #msstate { on_sync = [] } - end, - [begin - Fun = dict:fetch(CRef, CODC), - Fun(Guids) - end || {CRef, Guids} <- CGs], - State2 #msstate { cref_to_guids = dict:new() }. + lists:foreach(fun (K) -> K() end, lists:reverse(Syncs)), + [(dict:fetch(CRef, CODC))(Guids) || {CRef, Guids} <- CGs], + State #msstate { cref_to_guids = dict:new(), + on_sync = [] }. read_message(Guid, From, -- cgit v1.2.1 From 0f8d9a753491fbd52393f398765a31394cc0a731 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Mon, 4 Oct 2010 16:22:17 +0100 Subject: refactor + add async version of maybe_run_queue_via_backing_queue/2 --- src/rabbit_amqqueue.erl | 6 ++++ src/rabbit_amqqueue_process.erl | 3 ++ src/rabbit_variable_queue.erl | 67 +++++++++++++++++++---------------------- 3 files changed, 40 insertions(+), 36 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index c7d63d08..e1dc73e1 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -34,6 +34,7 @@ -export([start/0, stop/0, declare/5, delete_exclusive/1, delete/3, purge/1]). -export([internal_declare/2, internal_delete/1, maybe_run_queue_via_backing_queue/2, + maybe_run_queue_via_backing_queue_async/2, update_ram_duration/1, set_ram_duration_target/2, set_maximum_since_use/2, maybe_expire/1]). -export([pseudo_queue/2]). @@ -159,6 +160,8 @@ rabbit_types:connection_exit()). -spec(maybe_run_queue_via_backing_queue/2 :: (pid(), (fun ((A) -> A))) -> 'ok'). +-spec(maybe_run_queue_via_backing_queue_async/2 :: + (pid(), (fun ((A) -> A))) -> 'ok'). -spec(update_ram_duration/1 :: (pid()) -> 'ok'). -spec(set_ram_duration_target/2 :: (pid(), number() | 'infinity') -> 'ok'). -spec(set_maximum_since_use/2 :: (pid(), non_neg_integer()) -> 'ok'). @@ -457,6 +460,9 @@ internal_delete(QueueName) -> maybe_run_queue_via_backing_queue(QPid, Fun) -> gen_server2:call(QPid, {maybe_run_queue_via_backing_queue, Fun}, infinity). +maybe_run_queue_via_backing_queue_async(QPid, Fun) -> + gen_server2:cast(QPid, {maybe_run_queue_via_backing_queue, Fun}). + update_ram_duration(QPid) -> gen_server2:cast(QPid, update_ram_duration). diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index ee74b3d2..d0d971ac 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -878,6 +878,9 @@ handle_call({maybe_run_queue_via_backing_queue, Fun}, _From, State) -> reply(ok, maybe_run_queue_via_backing_queue(Fun, State)). +handle_cast({maybe_run_queue_via_backing_queue, Fun}, State) -> + noreply(maybe_run_queue_via_backing_queue(Fun, State)); + handle_cast({deliver, Delivery}, State) -> %% Asynchronous, non-"mandatory", non-"immediate" deliver mode. {_Delivered, NewState} = deliver_or_enqueue(Delivery, State), diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index f29cf5f1..7d84c840 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -541,10 +541,7 @@ publish_delivered(true, Msg = #basic_message { is_persistent = IsPersistent, {MsgStatus1, State1} = maybe_write_to_disk(false, false, MsgStatus, State), PA1 = record_pending_ack(m(MsgStatus1), PA), PCount1 = PCount + one_if(IsPersistent1), - Unconfirmed1 = case NeedsConfirming of - true -> gb_sets:insert(Guid, Unconfirmed); - false -> Unconfirmed - end, + Unconfirmed1 = gb_sets_maybe_insert(NeedsConfirming, Guid, Unconfirmed), {SeqId, a(State1 #vqstate { next_seq_id = SeqId + 1, out_counter = OutCount + 1, @@ -823,6 +820,9 @@ one_if(false) -> 0. cons_if(true, E, L) -> [E | L]; cons_if(false, _E, L) -> L. +gb_sets_maybe_insert(false, _Val, Set) -> Set; +gb_sets_maybe_insert(true, Val, Set) -> gb_sets:insert(Val, Set). + msg_status(IsPersistent, SeqId, Msg = #basic_message { guid = Guid }) -> #msg_status { seq_id = SeqId, guid = Guid, msg = Msg, is_persistent = IsPersistent, is_delivered = false, @@ -1066,10 +1066,7 @@ publish(Msg = #basic_message { is_persistent = IsPersistent, true -> State1 #vqstate { q4 = queue:in(m(MsgStatus1), Q4) } end, PCount1 = PCount + one_if(IsPersistent1), - Unconfirmed1 = case NeedsConfirming of - true -> gb_sets:insert(Guid, Unconfirmed); - false -> Unconfirmed - end, + Unconfirmed1 = gb_sets_maybe_insert(NeedsConfirming, Guid, Unconfirmed), {SeqId, State2 #vqstate { next_seq_id = SeqId + 1, len = Len + 1, @@ -1212,36 +1209,34 @@ msgs_confirmed(GuidSet, State) -> {remove_confirms(GuidSet, State), {confirm, gb_sets:to_list(GuidSet)}}. msgs_written_to_disk(QPid, Guids) -> - spawn(fun() -> rabbit_amqqueue:maybe_run_queue_via_backing_queue( - QPid, - fun(State = #vqstate { msgs_on_disk = MOD, - msg_indices_on_disk = MIOD, - unconfirmed = UC }) -> - GuidSet = gb_sets:from_list(Guids), - msgs_confirmed( - gb_sets:intersection(GuidSet, MIOD), - State #vqstate { - msgs_on_disk = - gb_sets:intersection( - gb_sets:union(MOD, GuidSet), UC) }) - end) - end). + rabbit_amqqueue:maybe_run_queue_via_backing_queue_async( + QPid, + fun(State = #vqstate { msgs_on_disk = MOD, + msg_indices_on_disk = MIOD, + unconfirmed = UC }) -> + GuidSet = gb_sets:from_list(Guids), + msgs_confirmed( + gb_sets:intersection(GuidSet, MIOD), + State #vqstate { + msgs_on_disk = + gb_sets:intersection( + gb_sets:union(MOD, GuidSet), UC) }) + end). msg_indices_written_to_disk(QPid, Guids) -> - spawn(fun() -> rabbit_amqqueue:maybe_run_queue_via_backing_queue( - QPid, - fun(State = #vqstate { msgs_on_disk = MOD, - msg_indices_on_disk = MIOD, - unconfirmed = UC }) -> - GuidSet = gb_sets:from_list(Guids), - msgs_confirmed( - gb_sets:intersection(GuidSet, MOD), - State #vqstate { - msg_indices_on_disk = - gb_sets:intersection( - gb_sets:union(MIOD, GuidSet), UC) }) - end) - end). + rabbit_amqqueue:maybe_run_queue_via_backing_queue_async( + QPid, + fun(State = #vqstate { msgs_on_disk = MOD, + msg_indices_on_disk = MIOD, + unconfirmed = UC }) -> + GuidSet = gb_sets:from_list(Guids), + msgs_confirmed( + gb_sets:intersection(GuidSet, MOD), + State #vqstate { + msg_indices_on_disk = + gb_sets:intersection( + gb_sets:union(MIOD, GuidSet), UC) }) + end). %%---------------------------------------------------------------------------- -- cgit v1.2.1 From 0e37ec53b67b503fbd968737186caccb10d34baf Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Mon, 4 Oct 2010 16:46:01 +0100 Subject: no more warnings --- src/rabbit_msg_store.erl | 2 +- src/rabbit_variable_queue.erl | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/rabbit_msg_store.erl b/src/rabbit_msg_store.erl index ff6ff96c..17f8fb1d 100644 --- a/src/rabbit_msg_store.erl +++ b/src/rabbit_msg_store.erl @@ -854,7 +854,7 @@ internal_sync(State = #msstate { current_file_handle = CurHdl, end, lists:foreach(fun (K) -> K() end, lists:reverse(Syncs)), [(dict:fetch(CRef, CODC))(Guids) || {CRef, Guids} <- CGs], - State #msstate { cref_to_guids = dict:new(), + State1 #msstate { cref_to_guids = dict:new(), on_sync = [] }. diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 7d84c840..61654444 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -1160,8 +1160,8 @@ remove_pending_ack(KeepPersistent, ack(_MsgStoreFun, _Fun, [], State) -> {State, []}; ack(MsgStoreFun, Fun, AckTags, State) -> - {Guids, {SeqIds, GuidsByStore}, State1 = #vqstate { index_state = IndexState, - persistent_count = PCount }} = + {AckdGuids, {SeqIds, GuidsByStore}, State1 = #vqstate { index_state = IndexState, + persistent_count = PCount }} = lists:foldl( fun (SeqId, {Gs, Acc, State2 = #vqstate { pending_ack = PA }}) -> {ok, AckEntry} = dict:find(SeqId, PA), @@ -1174,11 +1174,11 @@ ack(MsgStoreFun, Fun, AckTags, State) -> ok = orddict:fold(fun (MsgStore, Guids, ok) -> MsgStoreFun(MsgStore, Guids) end, ok, GuidsByStore), - State2 = remove_confirms(gb_sets:from_list(Guids), State1), + State2 = remove_confirms(gb_sets:from_list(AckdGuids), State1), PCount1 = PCount - find_persistent_count(sum_guids_by_store_to_len( orddict:new(), GuidsByStore)), {State2 #vqstate { index_state = IndexState1, - persistent_count = PCount1 }, Guids}. + persistent_count = PCount1 }, AckdGuids}. accumulate_ack(_SeqId, #msg_status { is_persistent = false, %% ASSERTIONS msg_on_disk = false, -- cgit v1.2.1 From 44614dcf3a8e651e26e99fe1b7f5176bc90cdd7f Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Mon, 4 Oct 2010 16:57:00 +0100 Subject: improve performance Reverting an earlier change improves throughput by about 3x. --- src/rabbit_variable_queue.erl | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 61654444..86ed4b57 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -1159,21 +1159,27 @@ remove_pending_ack(KeepPersistent, ack(_MsgStoreFun, _Fun, [], State) -> {State, []}; -ack(MsgStoreFun, Fun, AckTags, State) -> - {AckdGuids, {SeqIds, GuidsByStore}, State1 = #vqstate { index_state = IndexState, - persistent_count = PCount }} = +ack(MsgStoreFun, Fun, AckTags, State = #vqstate { pending_ack = PendAck }) -> + {{SeqIds, GuidsByStore}, State1 = #vqstate { index_state = IndexState, + persistent_count = PCount }} = lists:foldl( - fun (SeqId, {Gs, Acc, State2 = #vqstate { pending_ack = PA }}) -> + fun (SeqId, {Acc, State2 = #vqstate { pending_ack = PA }}) -> {ok, AckEntry} = dict:find(SeqId, PA), - {[AckEntry | Gs], - accumulate_ack(SeqId, AckEntry, Acc), + {accumulate_ack(SeqId, AckEntry, Acc), Fun(AckEntry, State2 #vqstate { pending_ack = dict:erase(SeqId, PA) })} - end, {[], {[], orddict:new()}, State}, AckTags), + end, {{[], orddict:new()}, State}, AckTags), IndexState1 = rabbit_queue_index:ack(SeqIds, IndexState), ok = orddict:fold(fun (MsgStore, Guids, ok) -> MsgStoreFun(MsgStore, Guids) end, ok, GuidsByStore), + AckdGuids = lists:foldl( + fun(SeqId, Guids) -> + [case dict:fetch(SeqId, PendAck) of + #msg_status { msg = Msg } -> Msg#basic_message.guid; + {_, Guid} -> Guid + end | Guids] + end, [], SeqIds), State2 = remove_confirms(gb_sets:from_list(AckdGuids), State1), PCount1 = PCount - find_persistent_count(sum_guids_by_store_to_len( orddict:new(), GuidsByStore)), -- cgit v1.2.1 From 4922c67a549b57d89e0ced8d735591e6fd64c8c0 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Mon, 4 Oct 2010 17:17:28 +0100 Subject: refactor --- src/rabbit_variable_queue.erl | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 86ed4b57..1d6b324d 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -1170,16 +1170,11 @@ ack(MsgStoreFun, Fun, AckTags, State = #vqstate { pending_ack = PendAck }) -> pending_ack = dict:erase(SeqId, PA) })} end, {{[], orddict:new()}, State}, AckTags), IndexState1 = rabbit_queue_index:ack(SeqIds, IndexState), - ok = orddict:fold(fun (MsgStore, Guids, ok) -> - MsgStoreFun(MsgStore, Guids) - end, ok, GuidsByStore), - AckdGuids = lists:foldl( - fun(SeqId, Guids) -> - [case dict:fetch(SeqId, PendAck) of - #msg_status { msg = Msg } -> Msg#basic_message.guid; - {_, Guid} -> Guid - end | Guids] - end, [], SeqIds), + AckdGuids = lists:concat( + orddict:fold(fun (MsgStore, Guids, Gs) -> + MsgStoreFun(MsgStore, Guids), + [Guids | Gs] + end, [], GuidsByStore)), State2 = remove_confirms(gb_sets:from_list(AckdGuids), State1), PCount1 = PCount - find_persistent_count(sum_guids_by_store_to_len( orddict:new(), GuidsByStore)), -- cgit v1.2.1 From ef9272f109db7fe83af5619c0a4219ae655b3a15 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 5 Oct 2010 09:36:25 +0100 Subject: cosmetics and minor refactoring --- include/rabbit.hrl | 3 +- include/rabbit_backing_queue_spec.hrl | 6 ++-- src/rabbit_amqqueue_process.erl | 58 +++++++++++++++--------------- src/rabbit_basic.erl | 4 +-- src/rabbit_channel.erl | 42 +++++++++++----------- src/rabbit_msg_store.erl | 26 ++++++++------ src/rabbit_queue_index.erl | 6 ++-- src/rabbit_variable_queue.erl | 68 ++++++++++++++++------------------- 8 files changed, 106 insertions(+), 107 deletions(-) diff --git a/include/rabbit.hrl b/include/rabbit.hrl index 700523d7..5770cb95 100644 --- a/include/rabbit.hrl +++ b/include/rabbit.hrl @@ -69,7 +69,8 @@ is_persistent}). -record(ssl_socket, {tcp, ssl}). --record(delivery, {mandatory, immediate, txn, sender, message, origin, msg_seq_no}). +-record(delivery, {mandatory, immediate, txn, sender, message, + origin, msg_seq_no}). -record(amqp_error, {name, explanation, method = none}). -record(event, {type, props, timestamp}). diff --git a/include/rabbit_backing_queue_spec.hrl b/include/rabbit_backing_queue_spec.hrl index 9d532656..cc086d3c 100644 --- a/include/rabbit_backing_queue_spec.hrl +++ b/include/rabbit_backing_queue_spec.hrl @@ -46,9 +46,9 @@ -spec(purge/1 :: (state()) -> {purged_msg_count(), state()}). -spec(publish/3 :: (rabbit_types:basic_message(), confirm_required(), state()) -> state()). --spec(publish_delivered/4 :: - (ack_required(), rabbit_types:basic_message(), confirm_required(), state()) - -> {ack(), state()}). +-spec(publish_delivered/4 :: (ack_required(), rabbit_types:basic_message(), + confirm_required(), state()) -> + {ack(), state()}). -spec(fetch/2 :: (ack_required(), state()) -> {fetch_result(), state()}). -spec(ack/2 :: ([ack()], state()) -> {state(), [rabbit_guid:guid()]}). -spec(tx_publish/3 :: (rabbit_types:txn(), rabbit_types:basic_message(), state()) -> state()). diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index d0d971ac..9abe9069 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -345,10 +345,10 @@ deliver_msgs_to_consumers(Funs = {PredFun, DeliverFun}, FunAcc, {QName, self(), AckTag, IsDelivered, Message}), {State2, ChAckTags1} = case AckRequired of - true -> {State1, sets:add_element(AckTag, ChAckTags)}; - false -> {confirm_message( - Message#basic_message.guid, - State1), ChAckTags} + true -> {State1, + sets:add_element(AckTag, ChAckTags)}; + false -> {confirm_message(Message, State1), + ChAckTags} end, NewC = C#cr{unsent_message_count = Count + 1, acktags = ChAckTags1}, @@ -399,12 +399,10 @@ deliver_from_queue_deliver(AckRequired, false, {{Message, IsDelivered, AckTag}, 0 == Remaining, State#q{backing_queue_state = BQS1}}. -confirm_messages(Guids, State) when is_list(Guids) -> - lists:foldl(fun(Guid, State0) -> - confirm_message(Guid, State0) - end, State, Guids). +confirm_messages(Guids, State) -> + lists:foldl(fun confirm_message_by_guid/2, State, Guids). -confirm_message(Guid, State = #q{guid_to_channel = GTC}) -> +confirm_message_by_guid(Guid, State = #q{guid_to_channel = GTC}) -> case dict:find(Guid, GTC) of {ok, {_ , undefined}} -> ok; {ok, {ChPid, MsgSeqNo}} -> rabbit_channel:confirm(ChPid, MsgSeqNo); @@ -412,6 +410,9 @@ confirm_message(Guid, State = #q{guid_to_channel = GTC}) -> end, State#q{guid_to_channel = dict:erase(Guid, GTC)}. +confirm_message(#basic_message{guid = Guid}, State) -> + confirm_message_by_guid(Guid, State). + record_confirm_message(#delivery{msg_seq_no = undefined}, State) -> State; record_confirm_message(#delivery{msg_seq_no = MsgSeqNo, @@ -454,14 +455,14 @@ attempt_delivery(#delivery{txn = Txn, record_current_channel_tx(ChPid, Txn), {true, State#q{backing_queue_state = BQ:tx_publish(Txn, Message, BQS)}}. -deliver_or_enqueue(Delivery = #delivery{message = Message, - msg_seq_no = MsgSeqNo}, - State = #q{backing_queue = BQ}) -> +deliver_or_enqueue(Delivery, State) -> case attempt_delivery(Delivery, record_confirm_message(Delivery, State)) of - {true, NewState} -> {true, NewState}; - {false, NewState} -> BQS = BQ:publish(Message, MsgSeqNo =/= undefined, - NewState#q.backing_queue_state), - {false, NewState#q{backing_queue_state = BQS}} + {true, State1} -> + {true, State1}; + {false, State1 = #q{backing_queue = BQ, backing_queue_state = BQS}} -> + #delivery{message = Message, msg_seq_no = MsgSeqNo} = Delivery, + BQS1 = BQ:publish(Message, MsgSeqNo =/= undefined, BQS), + {false, State1#q{backing_queue_state = BQS1}} end. requeue_and_run(AckTags, State = #q{backing_queue = BQ}) -> @@ -698,7 +699,7 @@ handle_call(consumers, _From, [{ChPid, ConsumerTag, AckRequired} | Acc] end, [], queue:join(ActiveConsumers, BlockedConsumers)), State); -handle_call({deliver_immediately, Delivery = #delivery{message = Msg}}, +handle_call({deliver_immediately, Delivery = #delivery{message = Message}}, _From, State) -> %% Synchronous, "immediate" delivery mode %% @@ -713,11 +714,11 @@ handle_call({deliver_immediately, Delivery = #delivery{message = Msg}}, %% just all ready-to-consume queues get the message, with unready %% queues discarding the message? %% - {Delivered, State1} = attempt_delivery(Delivery, - record_confirm_message(Delivery, State)), + {Delivered, State1} = + attempt_delivery(Delivery, record_confirm_message(Delivery, State)), reply(Delivered, case Delivered of true -> State1; - false -> confirm_message(Msg#basic_message.guid, State1) + false -> confirm_message(Message, State1) end); handle_call({deliver, Delivery}, _From, State) -> @@ -748,15 +749,14 @@ handle_call({basic_get, ChPid, NoAck}, _From, case BQ:fetch(AckRequired, BQS) of {empty, BQS1} -> reply(empty, State1#q{backing_queue_state = BQS1}); {{Message, IsDelivered, AckTag, Remaining}, BQS1} -> - State2 = case AckRequired of - true -> - C = #cr{acktags = ChAckTags} = ch_record(ChPid), - store_ch_record( - C#cr{acktags = sets:add_element(AckTag, ChAckTags)}), - State1; - false -> - confirm_message(Message#basic_message.guid, State1) - end, + State2 = + case AckRequired of + true -> C = #cr{acktags = ChAckTags} = ch_record(ChPid), + ChAckTags1 = sets:add_element(AckTag, ChAckTags), + store_ch_record(C#cr{acktags = ChAckTags1}), + State1; + false -> confirm_message(Message, State1) + end, Msg = {QName, self(), AckTag, IsDelivered, Message}, reply({ok, Remaining, Msg}, State2#q{backing_queue_state = BQS1}) end; diff --git a/src/rabbit_basic.erl b/src/rabbit_basic.erl index 348310d9..8ca98f6e 100644 --- a/src/rabbit_basic.erl +++ b/src/rabbit_basic.erl @@ -52,8 +52,8 @@ (rabbit_types:delivery()) -> publish_result()). -spec(delivery/5 :: (boolean(), boolean(), rabbit_types:maybe(rabbit_types:txn()), - rabbit_types:message(), undefined | integer()) - -> rabiit_types:delivery()). + rabbit_types:message(), undefined | integer()) -> + rabbit_types:delivery()). -spec(message/4 :: (rabbit_exchange:name(), rabbit_router:routing_key(), properties_input(), binary()) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index d641e824..7d9f8064 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -291,17 +291,14 @@ handle_cast({confirm, MsgSeqNo}, State) -> handle_info({'DOWN', _MRef, process, QPid, _Reason}, State = #ch{qpid_to_msgs = QTM}) -> - State1 = case dict:find(QPid, QTM) of - {ok, Msgs} -> - S = gb_sets:fold(fun (MsgSeqNo, State0) -> - send_or_enqueue_ack(MsgSeqNo, State0) - end, State, Msgs), - S #ch{qpid_to_msgs = dict:erase(QPid, QTM)}; - error -> - State + State2 = case dict:find(QPid, QTM) of + {ok, Msgs} -> State1 = gb_sets:fold(fun send_or_enqueue_ack/2, + State, Msgs), + State1 #ch{qpid_to_msgs = dict:erase(QPid, QTM)}; + error -> State end, erase_queue_stats(QPid), - {noreply, queue_blocked(QPid, State1)}. + {noreply, queue_blocked(QPid, State2)}. handle_pre_hibernate(State = #ch{writer_pid = WriterPid, held_confirms = As, @@ -452,18 +449,20 @@ send_or_enqueue_ack(undefined, State) -> send_or_enqueue_ack(_, State = #ch{confirm_enabled = false}) -> State; send_or_enqueue_ack(MsgSeqNo, State = #ch{confirm_multiple = false}) -> - do_if_unconfirmed(MsgSeqNo, State, - fun(MSN, State1 = #ch{writer_pid = WriterPid}) -> - ok = rabbit_writer:send_command( - WriterPid, #'basic.ack'{delivery_tag = MSN}), - State1 - end); + do_if_unconfirmed( + MsgSeqNo, State, + fun(MSN, State1 = #ch{writer_pid = WriterPid}) -> + ok = rabbit_writer:send_command( + WriterPid, #'basic.ack'{delivery_tag = MSN}), + State1 + end); send_or_enqueue_ack(MsgSeqNo, State = #ch{confirm_multiple = true}) -> - do_if_unconfirmed(MsgSeqNo, State, - fun(MSN, State1 = #ch{held_confirms = As}) -> - start_ack_timer(State1#ch{held_confirms = - gb_sets:add(MSN, As)}) - end). + do_if_unconfirmed( + MsgSeqNo, State, + fun(MSN, State1 = #ch{held_confirms = As}) -> + start_ack_timer(State1#ch{held_confirms = + gb_sets:add(MSN, As)}) + end). msg_sent_to_queue(undefined, _QPid, State) -> State; @@ -473,7 +472,8 @@ msg_sent_to_queue(MsgSeqNo, QPid, State = #ch{qpid_to_msgs = QTM}) -> error -> erlang:monitor(process, QPid), gb_sets:new() end, - State#ch{qpid_to_msgs = dict:store(QPid, gb_sets:add(MsgSeqNo, Msgs1), QTM)}. + QTM1 = dict:store(QPid, gb_sets:add(MsgSeqNo, Msgs1), QTM), + State#ch{qpid_to_msgs = QTM1}. do_if_unconfirmed(MsgSeqNo, State = #ch{unconfirmed = UC}, Fun) -> case gb_sets:is_element(MsgSeqNo, UC) of diff --git a/src/rabbit_msg_store.erl b/src/rabbit_msg_store.erl index 17f8fb1d..c2e74a23 100644 --- a/src/rabbit_msg_store.erl +++ b/src/rabbit_msg_store.erl @@ -128,9 +128,8 @@ -spec(start_link/4 :: (atom(), file:filename(), [binary()] | 'undefined', startup_fun_state()) -> rabbit_types:ok_pid_or_error()). --spec(write/4 :: (server(), rabbit_guid:guid(), - msg(), client_msstate()) - -> rabbit_types:ok(client_msstate())). +-spec(write/4 :: (server(), rabbit_guid:guid(), msg(), client_msstate()) -> + rabbit_types:ok(client_msstate())). -spec(read/3 :: (server(), rabbit_guid:guid(), client_msstate()) -> {rabbit_types:ok(msg()) | 'not_found', client_msstate()}). -spec(contains/2 :: (server(), rabbit_guid:guid()) -> boolean()). @@ -140,7 +139,8 @@ -spec(gc_done/4 :: (server(), non_neg_integer(), file_num(), file_num()) -> 'ok'). -spec(set_maximum_since_use/2 :: (server(), non_neg_integer()) -> 'ok'). --spec(client_init/3 :: (server(), rabbit_guid:guid(), guid_fun()) -> client_msstate()). +-spec(client_init/3 :: (server(), rabbit_guid:guid(), guid_fun()) -> + client_msstate()). -spec(client_terminate/2 :: (client_msstate(), server()) -> 'ok'). -spec(client_delete_and_terminate/3 :: (client_msstate(), server(), rabbit_guid:guid()) -> 'ok'). @@ -363,7 +363,8 @@ set_maximum_since_use(Server, Age) -> client_init(Server, Ref, MsgOnDiskFun) -> {IState, IModule, Dir, GCPid, FileHandlesEts, FileSummaryEts, DedupCacheEts, CurFileCacheEts} = - gen_server2:call(Server, {new_client_state, Ref, MsgOnDiskFun}, infinity), + gen_server2:call(Server, {new_client_state, Ref, MsgOnDiskFun}, + infinity), #client_msstate { file_handle_cache = dict:new(), index_state = IState, index_module = IModule, @@ -639,7 +640,8 @@ handle_call({new_client_state, CRef, Callback}, _From, handle_call(successfully_recovered_state, _From, State) -> reply(State #msstate.successfully_recovered, State); -handle_call({client_terminate, #client_msstate { client_ref = CRef }}, _From, State) -> +handle_call({client_terminate, #client_msstate { client_ref = CRef }}, _From, + State) -> reply(ok, clear_client_callback(CRef, State)). handle_cast({write, CRef, Guid}, @@ -690,7 +692,8 @@ handle_cast({write, CRef, Guid}, {#msg_location.ref_count, RefCount + 1}, State), CTG1 = case {dict:find(CRef, CODC), File =:= CurFile} of - {{ok, _} , true} -> rabbit_misc:dict_cons(CRef, Guid, CTG); + {{ok, _} , true} -> rabbit_misc:dict_cons(CRef, Guid, + CTG); {{ok, Fun}, false} -> Fun([Guid]), CTG; _ -> CTG end, @@ -763,9 +766,11 @@ handle_cast({set_maximum_since_use, Age}, State) -> ok = file_handle_cache:set_maximum_since_use(Age), noreply(State); -handle_cast({client_delete, CRef}, State = #msstate { client_refs = ClientRefs }) -> +handle_cast({client_delete, CRef}, + State = #msstate { client_refs = ClientRefs }) -> State1 = clear_client_callback(CRef, State), - noreply(State1 #msstate { client_refs = sets:del_element(CRef, ClientRefs) }). + noreply(State1 #msstate { + client_refs = sets:del_element(CRef, ClientRefs) }). handle_info(timeout, State) -> noreply(internal_sync(State)); @@ -854,8 +859,7 @@ internal_sync(State = #msstate { current_file_handle = CurHdl, end, lists:foreach(fun (K) -> K() end, lists:reverse(Syncs)), [(dict:fetch(CRef, CODC))(Guids) || {CRef, Guids} <- CGs], - State1 #msstate { cref_to_guids = dict:new(), - on_sync = [] }. + State1 #msstate { cref_to_guids = dict:new(), on_sync = [] }. read_message(Guid, From, diff --git a/src/rabbit_queue_index.erl b/src/rabbit_queue_index.erl index 81b6daf9..6f196336 100644 --- a/src/rabbit_queue_index.erl +++ b/src/rabbit_queue_index.erl @@ -196,11 +196,11 @@ }). -type(startup_fun_state() :: {fun ((A) -> 'finished' | {rabbit_guid:guid(), non_neg_integer(), A}), - A}). + A}). -spec(init/5 :: (rabbit_amqqueue:name(), boolean(), boolean(), - fun ((rabbit_guid:guid()) -> boolean()), on_sync_fun()) - -> {'undefined' | non_neg_integer(), [any()], qistate()}). + fun ((rabbit_guid:guid()) -> boolean()), on_sync_fun()) -> + {'undefined' | non_neg_integer(), [any()], qistate()}). -spec(terminate/2 :: ([any()], qistate()) -> qistate()). -spec(delete_and_terminate/1 :: (qistate()) -> qistate()). -spec(publish/4 :: (rabbit_guid:guid(), seq_id(), boolean(), qistate()) -> diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 1d6b324d..b07ee279 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -990,7 +990,8 @@ tx_commit_index(State = #vqstate { on_sync = #sync { fun (Msg = #basic_message { is_persistent = IsPersistent }, {SeqIdsAcc, State2}) -> IsPersistent1 = IsDurable andalso IsPersistent, - {SeqId, State3} = publish(Msg, false, IsPersistent1, false, State2), + {SeqId, State3} = publish(Msg, false, IsPersistent1, false, + State2), {cons_if(IsPersistent1, SeqId, SeqIdsAcc), State3} end, {PAcks, NewState}, Pubs), IndexState1 = rabbit_queue_index:sync(SeqIds, IndexState), @@ -1046,8 +1047,7 @@ sum_guids_by_store_to_len(LensByStore, GuidsByStore) -> %% Internal gubbins for publishing %%---------------------------------------------------------------------------- -publish(Msg = #basic_message { is_persistent = IsPersistent, - guid = Guid }, +publish(Msg = #basic_message { is_persistent = IsPersistent, guid = Guid }, IsDelivered, MsgOnDisk, NeedsConfirming, State = #vqstate { q1 = Q1, q3 = Q3, q4 = Q4, next_seq_id = SeqId, @@ -1067,13 +1067,12 @@ publish(Msg = #basic_message { is_persistent = IsPersistent, end, PCount1 = PCount + one_if(IsPersistent1), Unconfirmed1 = gb_sets_maybe_insert(NeedsConfirming, Guid, Unconfirmed), - {SeqId, State2 #vqstate { - next_seq_id = SeqId + 1, - len = Len + 1, - in_counter = InCount + 1, - persistent_count = PCount1, - ram_msg_count = RamMsgCount + 1, - unconfirmed = Unconfirmed1 }}. + {SeqId, State2 #vqstate { next_seq_id = SeqId + 1, + len = Len + 1, + in_counter = InCount + 1, + persistent_count = PCount1, + ram_msg_count = RamMsgCount + 1, + unconfirmed = Unconfirmed1 }}. maybe_write_msg_to_disk(_Force, MsgStatus = #msg_status { msg_on_disk = true }, MSCState) -> @@ -1159,7 +1158,7 @@ remove_pending_ack(KeepPersistent, ack(_MsgStoreFun, _Fun, [], State) -> {State, []}; -ack(MsgStoreFun, Fun, AckTags, State = #vqstate { pending_ack = PendAck }) -> +ack(MsgStoreFun, Fun, AckTags, State) -> {{SeqIds, GuidsByStore}, State1 = #vqstate { index_state = IndexState, persistent_count = PCount }} = lists:foldl( @@ -1179,7 +1178,7 @@ ack(MsgStoreFun, Fun, AckTags, State = #vqstate { pending_ack = PendAck }) -> PCount1 = PCount - find_persistent_count(sum_guids_by_store_to_len( orddict:new(), GuidsByStore)), {State2 #vqstate { index_state = IndexState1, - persistent_count = PCount1 }, AckdGuids}. + persistent_count = PCount1 }, AckdGuids}. accumulate_ack(_SeqId, #msg_status { is_persistent = false, %% ASSERTIONS msg_on_disk = false, @@ -1211,34 +1210,29 @@ msgs_confirmed(GuidSet, State) -> msgs_written_to_disk(QPid, Guids) -> rabbit_amqqueue:maybe_run_queue_via_backing_queue_async( - QPid, - fun(State = #vqstate { msgs_on_disk = MOD, - msg_indices_on_disk = MIOD, - unconfirmed = UC }) -> - GuidSet = gb_sets:from_list(Guids), - msgs_confirmed( - gb_sets:intersection(GuidSet, MIOD), - State #vqstate { - msgs_on_disk = - gb_sets:intersection( - gb_sets:union(MOD, GuidSet), UC) }) - end). + QPid, fun(State = #vqstate { msgs_on_disk = MOD, + msg_indices_on_disk = MIOD, + unconfirmed = UC }) -> + GuidSet = gb_sets:from_list(Guids), + msgs_confirmed(gb_sets:intersection(GuidSet, MIOD), + State #vqstate { + msgs_on_disk = + gb_sets:intersection( + gb_sets:union(MOD, GuidSet), UC) }) + end). msg_indices_written_to_disk(QPid, Guids) -> rabbit_amqqueue:maybe_run_queue_via_backing_queue_async( - QPid, - fun(State = #vqstate { msgs_on_disk = MOD, - msg_indices_on_disk = MIOD, - unconfirmed = UC }) -> - GuidSet = gb_sets:from_list(Guids), - msgs_confirmed( - gb_sets:intersection(GuidSet, MOD), - State #vqstate { - msg_indices_on_disk = - gb_sets:intersection( - gb_sets:union(MIOD, GuidSet), UC) }) - end). - + QPid, fun(State = #vqstate { msgs_on_disk = MOD, + msg_indices_on_disk = MIOD, + unconfirmed = UC }) -> + GuidSet = gb_sets:from_list(Guids), + msgs_confirmed(gb_sets:intersection(GuidSet, MOD), + State #vqstate { + msg_indices_on_disk = + gb_sets:intersection( + gb_sets:union(MIOD, GuidSet), UC) }) + end). %%---------------------------------------------------------------------------- %% Phase changes -- cgit v1.2.1 From ce21bc83e70cfc8eb89aecc22d84a77c0175024b Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Tue, 5 Oct 2010 18:03:56 +0100 Subject: swap the arguments of ack/2 --- include/rabbit_backing_queue_spec.hrl | 2 +- src/rabbit_amqqueue_process.erl | 2 +- src/rabbit_variable_queue.erl | 14 +++++++------- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/include/rabbit_backing_queue_spec.hrl b/include/rabbit_backing_queue_spec.hrl index 98d7ee9b..9f3a0227 100644 --- a/include/rabbit_backing_queue_spec.hrl +++ b/include/rabbit_backing_queue_spec.hrl @@ -51,7 +51,7 @@ -spec(publish_delivered/4 :: (ack_required(), rabbit_types:basic_message(), confirm_required(), state()) -> {ack(), state()}). -spec(fetch/2 :: (ack_required(), state()) -> {fetch_result(), state()}). --spec(ack/2 :: ([ack()], state()) -> {state(), [rabbit_guid:guid()]}). +-spec(ack/2 :: ([ack()], state()) -> {[rabbit_guid:guid()], state()}). -spec(tx_publish/3 :: (rabbit_types:txn(), rabbit_types:basic_message(), state()) -> state()). -spec(tx_ack/3 :: (rabbit_types:txn(), [ack()], state()) -> state()). diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 9abe9069..cab77c1b 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -423,7 +423,7 @@ record_confirm_message(#delivery{msg_seq_no = MsgSeqNo, ack_by_acktags(AckTags, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> - {BQS1, AckdGuids} = BQ:ack(AckTags, BQS), + {AckdGuids, BQS1} = BQ:ack(AckTags, BQS), confirm_messages(AckdGuids, State#q{backing_queue_state = BQS1}). run_message_queue(State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index b07ee279..8c498a79 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -608,11 +608,11 @@ fetch(AckRequired, State = #vqstate { q4 = Q4, end. ack(AckTags, State) -> - {State1, Guids} = + {Guids, State1} = ack(fun rabbit_msg_store:remove/2, fun (_AckEntry, State1) -> State1 end, AckTags, State), - {a(State1), Guids}. + {Guids, a(State1)}. tx_publish(Txn, Msg = #basic_message { is_persistent = IsPersistent }, State = #vqstate { durable = IsDurable, @@ -661,7 +661,7 @@ tx_commit(Txn, Fun, State = #vqstate { durable = IsDurable }) -> end)}. requeue(AckTags, State) -> - {State1, _Guids} = + {_Guids, State1} = ack(fun rabbit_msg_store:release/2, fun (#msg_status { msg = Msg }, State1) -> {_SeqId, State2} = publish(Msg, true, false, false, State1), @@ -984,7 +984,7 @@ tx_commit_index(State = #vqstate { on_sync = #sync { PAcks = lists:append(SPAcks), Acks = lists:append(SAcks), Pubs = lists:append(lists:reverse(SPubs)), - {NewState, _Guids} = ack(Acks, State), + {_Guids, NewState} = ack(Acks, State), {SeqIds, State1 = #vqstate { index_state = IndexState }} = lists:foldl( fun (Msg = #basic_message { is_persistent = IsPersistent }, @@ -1157,7 +1157,7 @@ remove_pending_ack(KeepPersistent, end. ack(_MsgStoreFun, _Fun, [], State) -> - {State, []}; + {[], State}; ack(MsgStoreFun, Fun, AckTags, State) -> {{SeqIds, GuidsByStore}, State1 = #vqstate { index_state = IndexState, persistent_count = PCount }} = @@ -1177,8 +1177,8 @@ ack(MsgStoreFun, Fun, AckTags, State) -> State2 = remove_confirms(gb_sets:from_list(AckdGuids), State1), PCount1 = PCount - find_persistent_count(sum_guids_by_store_to_len( orddict:new(), GuidsByStore)), - {State2 #vqstate { index_state = IndexState1, - persistent_count = PCount1 }, AckdGuids}. + {AckdGuids, State2 #vqstate { index_state = IndexState1, + persistent_count = PCount1 }}. accumulate_ack(_SeqId, #msg_status { is_persistent = false, %% ASSERTIONS msg_on_disk = false, -- cgit v1.2.1 From 5e62212375a4292f311f045f3287c6cf7f3dab9c Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 5 Oct 2010 18:11:15 +0100 Subject: cosmetic --- src/rabbit_variable_queue.erl | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 8c498a79..cf61ecbe 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -608,10 +608,9 @@ fetch(AckRequired, State = #vqstate { q4 = Q4, end. ack(AckTags, State) -> - {Guids, State1} = - ack(fun rabbit_msg_store:remove/2, - fun (_AckEntry, State1) -> State1 end, - AckTags, State), + {Guids, State1} = ack(fun rabbit_msg_store:remove/2, + fun (_AckEntry, State1) -> State1 end, + AckTags, State), {Guids, a(State1)}. tx_publish(Txn, Msg = #basic_message { is_persistent = IsPersistent }, -- cgit v1.2.1 From 63c9e1de03ec055598beed856aef4f2cddc50ff0 Mon Sep 17 00:00:00 2001 From: Rob Harrop Date: Mon, 11 Oct 2010 15:54:48 +0100 Subject: Started recording the acks that are stored as full messages in memory --- src/rabbit_variable_queue.erl | 61 +++++++++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 25 deletions(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index cbc71bcc..208f3d56 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -220,6 +220,7 @@ q4, next_seq_id, pending_ack, + pending_ack_index, index_state, msg_store_clients, on_sync, @@ -305,6 +306,7 @@ q4 :: queue(), next_seq_id :: seq_id(), pending_ack :: dict:dictionary(), + pending_ack_index :: gb_trees:gb_tree(), index_state :: any(), msg_store_clients :: 'undefined' | {{any(), binary()}, {any(), binary()}}, @@ -407,6 +409,7 @@ init(QueueName, IsDurable, Recover) -> q4 = queue:new(), next_seq_id = NextSeqId, pending_ack = dict:new(), + pending_ack_index = gb_trees:empty(), index_state = IndexState1, msg_store_clients = {{PersistentClient, PRef}, {TransientClient, TRef}}, @@ -509,27 +512,24 @@ publish_delivered(true, Msg = #basic_message { is_persistent = IsPersistent }, out_counter = OutCount, in_counter = InCount, persistent_count = PCount, - pending_ack = PA, durable = IsDurable }) -> IsPersistent1 = IsDurable andalso IsPersistent, MsgStatus = (msg_status(IsPersistent1, SeqId, Msg)) #msg_status { is_delivered = true }, {MsgStatus1, State1} = maybe_write_to_disk(false, false, MsgStatus, State), - PA1 = record_pending_ack(m(MsgStatus1), PA), + State2 = record_pending_ack(m(MsgStatus1), State1), PCount1 = PCount + one_if(IsPersistent1), - {SeqId, a(State1 #vqstate { next_seq_id = SeqId + 1, + {SeqId, a(State2 #vqstate { next_seq_id = SeqId + 1, out_counter = OutCount + 1, in_counter = InCount + 1, - persistent_count = PCount1, - pending_ack = PA1 })}. + persistent_count = PCount1 })}. fetch(AckRequired, State = #vqstate { q4 = Q4, ram_msg_count = RamMsgCount, out_counter = OutCount, index_state = IndexState, len = Len, - persistent_count = PCount, - pending_ack = PA }) -> + persistent_count = PCount }) -> case queue:out(Q4) of {empty, _Q4} -> case fetch_from_q3_to_q4(State) of @@ -560,24 +560,24 @@ fetch(AckRequired, State = #vqstate { q4 = Q4, end, %% 3. If an ack is required, add something sensible to PA - {AckTag, PA1} = case AckRequired of - true -> PA2 = record_pending_ack( - MsgStatus #msg_status { - is_delivered = true }, PA), - {SeqId, PA2}; - false -> {blank_ack, PA} + {AckTag, State1} = case AckRequired of + true -> StateN = record_pending_ack( + MsgStatus #msg_status { + is_delivered = true }, + State), + {SeqId, StateN}; + false -> {blank_ack, State} end, PCount1 = PCount - one_if(IsPersistent andalso not AckRequired), Len1 = Len - 1, {{Msg, IsDelivered, AckTag, Len1}, - a(State #vqstate { q4 = Q4a, + a(State1 #vqstate { q4 = Q4a, ram_msg_count = RamMsgCount - 1, out_counter = OutCount + 1, index_state = IndexState2, len = Len1, - persistent_count = PCount1, - pending_ack = PA1 })} + persistent_count = PCount1})} end. ack(AckTags, State) -> @@ -1090,19 +1090,27 @@ maybe_write_to_disk(ForceMsg, ForceIndex, MsgStatus, record_pending_ack(#msg_status { guid = Guid, seq_id = SeqId, is_persistent = IsPersistent, - msg_on_disk = MsgOnDisk } = MsgStatus, PA) -> - AckEntry = case MsgOnDisk of - true -> {IsPersistent, Guid}; - false -> MsgStatus - end, - dict:store(SeqId, AckEntry, PA). + msg_on_disk = MsgOnDisk } = MsgStatus, + State = #vqstate { pending_ack = PA, + pending_ack_index = PAI }) -> + {AckEntry, PAI1} = + case MsgOnDisk of + true -> + {{IsPersistent, Guid}, PAI}; + false -> + {MsgStatus, gb_trees:insert(SeqId, Guid, PAI)} + end, + PA1 = dict:store(SeqId, AckEntry, PA), + State #vqstate { pending_ack = PA1, pending_ack_index = PAI1 }. +%% TODO: On remove, need to prevent any seqids that remove_pending_ack(KeepPersistent, State = #vqstate { pending_ack = PA, index_state = IndexState }) -> {SeqIds, GuidsByStore} = dict:fold(fun accumulate_ack/3, {[], orddict:new()}, PA), - State1 = State #vqstate { pending_ack = dict:new() }, + State1 = State #vqstate { pending_ack = dict:new(), + pending_ack_index = gb_trees:empty() }, case KeepPersistent of true -> case orddict:find(?TRANSIENT_MSG_STORE, GuidsByStore) of error -> State1; @@ -1124,11 +1132,14 @@ ack(MsgStoreFun, Fun, AckTags, State) -> {{SeqIds, GuidsByStore}, State1 = #vqstate { index_state = IndexState, persistent_count = PCount }} = lists:foldl( - fun (SeqId, {Acc, State2 = #vqstate { pending_ack = PA }}) -> + fun (SeqId, {Acc, State2 = #vqstate { pending_ack = PA, + pending_ack_index = PAI }}) -> {ok, AckEntry} = dict:find(SeqId, PA), {accumulate_ack(SeqId, AckEntry, Acc), Fun(AckEntry, State2 #vqstate { - pending_ack = dict:erase(SeqId, PA) })} + pending_ack = dict:erase(SeqId, PA), + pending_ack_index = + gb_trees:delete_any(SeqId, PAI)})} end, {{[], orddict:new()}, State}, AckTags), IndexState1 = rabbit_queue_index:ack(SeqIds, IndexState), ok = orddict:fold(fun (MsgStore, Guids, ok) -> -- cgit v1.2.1 From e18fb9312251a11250f58442f7848c16f91cf746 Mon Sep 17 00:00:00 2001 From: Matthew Sackman Date: Wed, 13 Oct 2010 13:35:59 +0100 Subject: Don't do a dirty_read within a transaction --- src/rabbit_binding.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rabbit_binding.erl b/src/rabbit_binding.erl index 53c9c663..62700893 100644 --- a/src/rabbit_binding.erl +++ b/src/rabbit_binding.erl @@ -350,10 +350,10 @@ group_bindings_fold(Fun, SrcName, Acc, Removed, Bindings) -> group_bindings_fold(Fun, Fun(SrcName, Bindings, Acc), Removed). maybe_auto_delete(XName, Bindings, Deletions) -> - case rabbit_exchange:lookup(XName) of - {error, not_found} -> + case mnesia:read(rabbit_exchange, XName) of + [] -> add_deletion(XName, {undefined, not_deleted, Bindings}, Deletions); - {ok, X} -> + [X] -> add_deletion(XName, {X, not_deleted, Bindings}, case rabbit_exchange:maybe_auto_delete(X) of not_deleted -> Deletions; -- cgit v1.2.1 From aad2fe5095630ca36311e705c6ceb37e6ebeb6cd Mon Sep 17 00:00:00 2001 From: Rob Harrop Date: Wed, 13 Oct 2010 18:21:30 +0100 Subject: Support for requeue=false removed. No longer storing messages along with ack records --- src/rabbit_channel.erl | 41 +++++++++++++---------------------------- 1 file changed, 13 insertions(+), 28 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 64f84f34..0900f3eb 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -245,7 +245,9 @@ handle_cast({command, Msg}, State = #ch{writer_pid = WriterPid}) -> handle_cast({deliver, ConsumerTag, AckRequired, Msg}, State = #ch{writer_pid = WriterPid, next_tag = DeliveryTag}) -> - State1 = lock_message(AckRequired, {DeliveryTag, ConsumerTag, Msg}, State), + State1 = lock_message(AckRequired, + ack_record(DeliveryTag, ConsumerTag, Msg), + State), ok = internal_deliver(WriterPid, true, ConsumerTag, DeliveryTag, Msg), {_QName, QPid, _MsgId, _Redelivered, _Msg} = Msg, maybe_incr_stats([{QPid, 1}], @@ -490,7 +492,9 @@ handle_method(#'basic.get'{queue = QueueNameBin, #basic_message{exchange_name = ExchangeName, routing_key = RoutingKey, content = Content}}} -> - State1 = lock_message(not(NoAck), {DeliveryTag, none, Msg}, State), + State1 = lock_message(not(NoAck), + ack_record(DeliveryTag, none, Msg), + State), maybe_incr_stats([{QPid, 1}], case NoAck of true -> get_no_ack; @@ -626,30 +630,8 @@ handle_method(#'basic.recover_async'{requeue = true}, %% variant of this method {noreply, State#ch{unacked_message_q = queue:new()}}; -handle_method(#'basic.recover_async'{requeue = false}, - _, State = #ch{writer_pid = WriterPid, - unacked_message_q = UAMQ}) -> - ok = rabbit_misc:queue_fold( - fun ({_DeliveryTag, none, _Msg}, ok) -> - %% Was sent as a basic.get_ok. Don't redeliver - %% it. FIXME: appropriate? - ok; - ({DeliveryTag, ConsumerTag, - {QName, QPid, MsgId, _Redelivered, Message}}, ok) -> - %% Was sent as a proper consumer delivery. Resend - %% it as before. - %% - %% FIXME: What should happen if the consumer's been - %% cancelled since? - %% - %% FIXME: should we allocate a fresh DeliveryTag? - internal_deliver( - WriterPid, false, ConsumerTag, DeliveryTag, - {QName, QPid, MsgId, true, Message}) - end, ok, UAMQ), - %% No answer required - basic.recover is the newer, synchronous - %% variant of this method - {noreply, State}; +handle_method(#'basic.recover_async'{requeue = false}, _, _State) -> + rabbit_misc:protocol_error(not_implemented, "requeue=false", []); handle_method(#'basic.recover'{requeue = Requeue}, Content, State) -> {noreply, State2 = #ch{writer_pid = WriterPid}} = @@ -948,6 +930,10 @@ basic_return(#basic_message{exchange_name = ExchangeName, routing_key = RoutingKey}, Content). +ack_record(DeliveryTag, ConsumerTag, + _MsgStruct = {_QName, QPid, MsgId, _Redelivered, _Msg}) -> + {DeliveryTag, ConsumerTag, {QPid, MsgId}}. + collect_acks(Q, 0, true) -> {Q, queue:new()}; collect_acks(Q, DeliveryTag, Multiple) -> @@ -1020,8 +1006,7 @@ rollback_and_notify(State) -> fold_per_queue(F, Acc0, UAQ) -> D = rabbit_misc:queue_fold( - fun ({_DTag, _CTag, - {_QName, QPid, MsgId, _Redelivered, _Message}}, D) -> + fun ({_DTag, _CTag, {QPid, MsgId}}, D) -> %% dict:append would avoid the lists:reverse in %% handle_message({recover, true}, ...). However, it %% is significantly slower when going beyond a few -- cgit v1.2.1 From 13b4bea3b3f437cb1f066dd9e16b7611cf4b2a7c Mon Sep 17 00:00:00 2001 From: Rob Harrop Date: Thu, 14 Oct 2010 12:57:44 +0100 Subject: rename pending_ack_index to ram_ack_index --- src/rabbit_variable_queue.erl | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 208f3d56..28055af3 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -220,7 +220,7 @@ q4, next_seq_id, pending_ack, - pending_ack_index, + ram_ack_index, index_state, msg_store_clients, on_sync, @@ -306,7 +306,7 @@ q4 :: queue(), next_seq_id :: seq_id(), pending_ack :: dict:dictionary(), - pending_ack_index :: gb_trees:gb_tree(), + ram_ack_index :: gb_tree(), index_state :: any(), msg_store_clients :: 'undefined' | {{any(), binary()}, {any(), binary()}}, @@ -409,7 +409,7 @@ init(QueueName, IsDurable, Recover) -> q4 = queue:new(), next_seq_id = NextSeqId, pending_ack = dict:new(), - pending_ack_index = gb_trees:empty(), + ram_ack_index = gb_trees:empty(), index_state = IndexState1, msg_store_clients = {{PersistentClient, PRef}, {TransientClient, TRef}}, @@ -1092,16 +1092,16 @@ record_pending_ack(#msg_status { guid = Guid, seq_id = SeqId, is_persistent = IsPersistent, msg_on_disk = MsgOnDisk } = MsgStatus, State = #vqstate { pending_ack = PA, - pending_ack_index = PAI }) -> - {AckEntry, PAI1} = + ram_ack_index = RAI }) -> + {AckEntry, RAI1} = case MsgOnDisk of true -> - {{IsPersistent, Guid}, PAI}; + {{IsPersistent, Guid}, RAI}; false -> - {MsgStatus, gb_trees:insert(SeqId, Guid, PAI)} + {MsgStatus, gb_trees:insert(SeqId, Guid, RAI)} end, PA1 = dict:store(SeqId, AckEntry, PA), - State #vqstate { pending_ack = PA1, pending_ack_index = PAI1 }. + State #vqstate { pending_ack = PA1, ram_ack_index = RAI1 }. %% TODO: On remove, need to prevent any seqids that remove_pending_ack(KeepPersistent, @@ -1110,7 +1110,7 @@ remove_pending_ack(KeepPersistent, {SeqIds, GuidsByStore} = dict:fold(fun accumulate_ack/3, {[], orddict:new()}, PA), State1 = State #vqstate { pending_ack = dict:new(), - pending_ack_index = gb_trees:empty() }, + ram_ack_index = gb_trees:empty() }, case KeepPersistent of true -> case orddict:find(?TRANSIENT_MSG_STORE, GuidsByStore) of error -> State1; @@ -1133,13 +1133,13 @@ ack(MsgStoreFun, Fun, AckTags, State) -> persistent_count = PCount }} = lists:foldl( fun (SeqId, {Acc, State2 = #vqstate { pending_ack = PA, - pending_ack_index = PAI }}) -> + ram_ack_index = RAI }}) -> {ok, AckEntry} = dict:find(SeqId, PA), {accumulate_ack(SeqId, AckEntry, Acc), Fun(AckEntry, State2 #vqstate { pending_ack = dict:erase(SeqId, PA), - pending_ack_index = - gb_trees:delete_any(SeqId, PAI)})} + ram_ack_index = + gb_trees:delete_any(SeqId, RAI)})} end, {{[], orddict:new()}, State}, AckTags), IndexState1 = rabbit_queue_index:ack(SeqIds, IndexState), ok = orddict:fold(fun (MsgStore, Guids, ok) -> -- cgit v1.2.1 From ae551e5eed1320800f0851d267f3c8192abf64ec Mon Sep 17 00:00:00 2001 From: Rob Harrop Date: Thu, 14 Oct 2010 14:19:21 +0100 Subject: experimenting with moving acks to disk --- src/rabbit_variable_queue.erl | 61 ++++++++++++++++++++++++++++++++----------- 1 file changed, 46 insertions(+), 15 deletions(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 28055af3..90fbcf81 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -232,6 +232,7 @@ duration_target, target_ram_msg_count, + target_ram_ack_count, ram_msg_count, ram_msg_count_prev, ram_index_count, @@ -319,6 +320,7 @@ transient_threshold :: non_neg_integer(), duration_target :: number() | 'infinity', target_ram_msg_count :: non_neg_integer() | 'infinity', + target_ram_ack_count :: non_neg_integer() | 'infinity', ram_msg_count :: non_neg_integer(), ram_msg_count_prev :: non_neg_integer(), ram_index_count :: non_neg_integer(), @@ -422,6 +424,7 @@ init(QueueName, IsDurable, Recover) -> duration_target = infinity, target_ram_msg_count = infinity, + target_ram_ack_count = infinity, ram_msg_count = 0, ram_msg_count_prev = 0, ram_index_count = 0, @@ -651,10 +654,16 @@ len(#vqstate { len = Len }) -> Len. is_empty(State) -> 0 == len(State). +within_limits(Previous, Current) -> + Current == infinity + orelse (Previous =/= infinity andalso Current >= Previous). + set_ram_duration_target(DurationTarget, State = #vqstate { + len = Len, pending_ack = PA, rates = #rates { avg_egress = AvgEgressRate, avg_ingress = AvgIngressRate }, + target_ram_ack_count = TargetRamAckCount, target_ram_msg_count = TargetRamMsgCount }) -> Rate = AvgEgressRate + AvgIngressRate, TargetRamMsgCount1 = @@ -662,13 +671,24 @@ set_ram_duration_target(DurationTarget, infinity -> infinity; _ -> trunc(DurationTarget * Rate) %% msgs = sec * msgs/sec end, - State1 = State #vqstate { target_ram_msg_count = TargetRamMsgCount1, + + io:format("MSG: ~p[~p]~n", [TargetRamMsgCount1, Len]), + + TargetRamAckCount1 = case TargetRamMsgCount1 == infinity orelse Len == 0 of + true -> TargetRamMsgCount1; + _ -> (TargetRamMsgCount1 / Len) * dict:size(PA) + end, + + io:format("ACK: ~p~n", [TargetRamAckCount1]), + + State1 = State #vqstate { target_ram_ack_count = TargetRamAckCount1, + target_ram_msg_count = TargetRamMsgCount1, duration_target = DurationTarget }, - a(case TargetRamMsgCount1 == infinity orelse - (TargetRamMsgCount =/= infinity andalso - TargetRamMsgCount1 >= TargetRamMsgCount) of + + a(case within_limits(TargetRamMsgCount, TargetRamMsgCount1) andalso + within_limits(TargetRamAckCount, TargetRamAckCount1) of true -> State1; - false -> reduce_memory_use(State1) + false -> io:format("Reducing~n"), reduce_memory_use(State1) end). ram_duration(State = #vqstate { @@ -1188,21 +1208,32 @@ find_persistent_count(LensByStore) -> %% 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, BetaGammaFun, BetaDeltaFun, State) -> - {Reduce, State1} = case chunk_size(State #vqstate.ram_msg_count, + Size = gb_trees:size(State #vqstate.ram_ack_index), + State1 = case chunk_size(Size, State #vqstate.target_ram_ack_count) of + 0 -> State; + S -> limit_ram_acks(S, State) + end, + {Reduce, State2} = case chunk_size(State #vqstate.ram_msg_count, State #vqstate.target_ram_msg_count) of - 0 -> {false, State}; - S1 -> {true, AlphaBetaFun(S1, State)} + 0 -> {false, State1}; + S1 -> {true, AlphaBetaFun(S1, State1)} end, - case State1 #vqstate.target_ram_msg_count of - infinity -> {Reduce, State1}; - 0 -> {Reduce, BetaDeltaFun(State1)}; - _ -> case chunk_size(State1 #vqstate.ram_index_count, - permitted_ram_index_count(State1)) of - ?IO_BATCH_SIZE = S2 -> {true, BetaGammaFun(S2, State1)}; - _ -> {Reduce, State1} + case State2 #vqstate.target_ram_msg_count of + infinity -> {Reduce, State2}; + 0 -> {Reduce, BetaDeltaFun(State2)}; + _ -> case chunk_size(State2 #vqstate.ram_index_count, + permitted_ram_index_count(State2)) of + ?IO_BATCH_SIZE = S2 -> {true, BetaGammaFun(S2, State2)}; + _ -> {Reduce, State2} end end. +limit_ram_acks(0, State) -> + State; +limit_ram_acks(Quota, State) -> + + + reduce_memory_use(State) -> {_, State1} = reduce_memory_use(fun push_alphas_to_betas/2, fun limit_ram_index/2, -- cgit v1.2.1 From 3288425642fb401db913e5bf783515ebf218957c Mon Sep 17 00:00:00 2001 From: Rob Harrop Date: Thu, 14 Oct 2010 19:22:11 +0100 Subject: rough sketch of pushing ram acks to disk. doesnt actually work... --- src/rabbit_variable_queue.erl | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 90fbcf81..f09fb4d4 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -1211,7 +1211,7 @@ reduce_memory_use(AlphaBetaFun, BetaGammaFun, BetaDeltaFun, State) -> Size = gb_trees:size(State #vqstate.ram_ack_index), State1 = case chunk_size(Size, State #vqstate.target_ram_ack_count) of 0 -> State; - S -> limit_ram_acks(S, State) + S -> io:format("Limiting~n"), limit_ram_acks(S, State) end, {Reduce, State2} = case chunk_size(State #vqstate.ram_msg_count, State #vqstate.target_ram_msg_count) of @@ -1230,8 +1230,24 @@ reduce_memory_use(AlphaBetaFun, BetaGammaFun, BetaDeltaFun, State) -> limit_ram_acks(0, State) -> State; -limit_ram_acks(Quota, State) -> - +limit_ram_acks(Quota, State = #vqstate { pending_ack = PA, + ram_ack_index = RAI }) -> + io:format("Limiting acks~p~n", [Quota]), + case gb_trees:is_empty(RAI) of + true -> + State; + false -> + {SeqId, Guid, RAI1} = gb_trees:take_largest(RAI), + io:format("Largest~p~n", [SeqId]), + MsgStatus = dict:fetch(SeqId, PA), + State1 = maybe_write_to_disk(true, false, MsgStatus, State), + io:format("Wrote~n"), + limit_ram_acks(Quota - 1, + State1 #vqstate { + pending_ack = + dict:update(SeqId, {false, Guid}, PA), + ram_ack_index = RAI1 }) + end. reduce_memory_use(State) -> -- cgit v1.2.1 From 5fc27ddfe468be603804df4c7a49e2831b8c3f00 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Wed, 20 Oct 2010 13:11:18 +0100 Subject: only confirm when all queues have dealt with the message --- src/rabbit_channel.erl | 108 +++++++++++++++++++++++++++---------------------- 1 file changed, 60 insertions(+), 48 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index ee9cc0df..746f4ca6 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -50,7 +50,7 @@ username, virtual_host, most_recently_declared_queue, consumer_mapping, blocking, queue_collector_pid, stats_timer, confirm_enabled, published_count, confirm_multiple, confirm_tref, - held_confirms, unconfirmed, qpid_to_msgs}). + held_confirms, unconfirmed, queues_for_msg}). -define(MAX_PERMISSION_CACHE_SIZE, 12). @@ -163,7 +163,7 @@ flush_multiple_acks(Pid) -> gen_server2:cast(Pid, flush_multiple_acks). confirm(Pid, MsgSeqNo) -> - gen_server2:cast(Pid, {confirm, MsgSeqNo}). + gen_server2:cast(Pid, {confirm, MsgSeqNo, self()}). %%--------------------------------------------------------------------------- @@ -195,7 +195,7 @@ init([Channel, ReaderPid, WriterPid, Username, VHost, CollectorPid, confirm_multiple = false, held_confirms = gb_sets:new(), unconfirmed = gb_sets:new(), - qpid_to_msgs = dict:new()}, + queues_for_msg = dict:new()}, rabbit_event:notify(channel_created, infos(?CREATION_EVENT_KEYS, State)), rabbit_event:if_enabled(StatsTimer, fun() -> internal_emit_stats(State) end), @@ -286,19 +286,22 @@ handle_cast(flush_multiple_acks, {noreply, State#ch{held_confirms = gb_sets:new(), confirm_tref = undefined}}; -handle_cast({confirm, MsgSeqNo}, State) -> - {noreply, send_or_enqueue_ack(MsgSeqNo, State)}. +handle_cast({confirm, MsgSeqNo, From}, State) -> + {noreply, send_or_enqueue_ack(MsgSeqNo, From, State)}. handle_info({'DOWN', _MRef, process, QPid, _Reason}, - State = #ch{qpid_to_msgs = QTM}) -> - State2 = case dict:find(QPid, QTM) of - {ok, Msgs} -> State1 = gb_sets:fold(fun send_or_enqueue_ack/2, - State, Msgs), - State1 #ch{qpid_to_msgs = dict:erase(QPid, QTM)}; - error -> State - end, + State = #ch{queues_for_msg = QFM}) -> + State1 = dict:fold( + fun(Msg, QPids, State0 = #ch{queues_for_msg = QFM0}) -> + Qs = sets:del_elem(QPid, QPids), + case sets:size(Qs) of + 0 -> send_or_enqueue_ack(Msg, QPid, State0); + _ -> State0#ch{queues_for_msg = + dict:store(Msg, Qs, QFM0)} + end + end, State, QFM), erase_queue_stats(QPid), - {noreply, queue_blocked(QPid, State2)}. + {noreply, queue_blocked(QPid, State1)}. handle_pre_hibernate(State = #ch{writer_pid = WriterPid, held_confirms = As, @@ -457,44 +460,50 @@ queue_blocked(QPid, State = #ch{blocking = Blocking}) -> State#ch{blocking = Blocking1} end. -send_or_enqueue_ack(undefined, State) -> +send_or_enqueue_ack(undefined, _QPid, State) -> State; -send_or_enqueue_ack(_, State = #ch{confirm_enabled = false}) -> +send_or_enqueue_ack(_MsgSeqNo, _QPid, State = #ch{confirm_enabled = false}) -> State; -send_or_enqueue_ack(MsgSeqNo, State = #ch{confirm_multiple = false}) -> +send_or_enqueue_ack(MsgSeqNo, QPid, State = #ch{confirm_multiple = false}) -> do_if_unconfirmed( - MsgSeqNo, State, + MsgSeqNo, QPid, fun(MSN, State1 = #ch{writer_pid = WriterPid}) -> ok = rabbit_writer:send_command( WriterPid, #'basic.ack'{delivery_tag = MSN}), State1 - end); -send_or_enqueue_ack(MsgSeqNo, State = #ch{confirm_multiple = true}) -> + end, State); +send_or_enqueue_ack(MsgSeqNo, QPid, State = #ch{confirm_multiple = true}) -> do_if_unconfirmed( - MsgSeqNo, State, + MsgSeqNo, QPid, fun(MSN, State1 = #ch{held_confirms = As}) -> - start_ack_timer(State1#ch{held_confirms = - gb_sets:add(MSN, As)}) - end). + start_ack_timer(State1#ch{held_confirms = gb_sets:add(MSN, As)}) + end, State). -msg_sent_to_queue(undefined, _QPid, State) -> - State; -msg_sent_to_queue(MsgSeqNo, QPid, State = #ch{qpid_to_msgs = QTM}) -> - Msgs1 = case dict:find(QPid, QTM) of - {ok, Msgs} -> Msgs; - error -> erlang:monitor(process, QPid), - gb_sets:new() - end, - QTM1 = dict:store(QPid, gb_sets:add(MsgSeqNo, Msgs1), QTM), - State#ch{qpid_to_msgs = QTM1}. - -do_if_unconfirmed(MsgSeqNo, State = #ch{unconfirmed = UC}, Fun) -> +do_if_unconfirmed(MsgSeqNo, QPid, ConfirmFun, + State = #ch{unconfirmed = UC, + queues_for_msg = QFM}) -> + %% clears references to MsgSeqNo and does ConfirmFun case gb_sets:is_element(MsgSeqNo, UC) of - true -> QTM = dict:map(fun (_, Msgs) -> - gb_sets:delete_any(MsgSeqNo, Msgs) - end, State#ch.qpid_to_msgs), - State1 = Fun(MsgSeqNo, State#ch{qpid_to_msgs = QTM}), - State1#ch{unconfirmed = gb_sets:delete(MsgSeqNo, UC)}; + true -> + case QPid of + undefined -> + ConfirmFun(MsgSeqNo, + State#ch{unconfirmed = + gb_sets:delete(MsgSeqNo, UC)}); + _ -> + {ok, Qs} = dict:find(MsgSeqNo, QFM), + Qs1 = sets:del_element(QPid, Qs), + case sets:size(Qs1) of + 0 -> ConfirmFun(MsgSeqNo, + State#ch{ + queues_for_msg = + dict:erase(MsgSeqNo, QFM), + unconfirmed = + gb_sets:delete(MsgSeqNo, UC)}); + _ -> State#ch{queues_for_msg = + dict:store(MsgSeqNo, Qs1, QFM)} + end + end; false -> State end. @@ -1238,18 +1247,21 @@ is_message_persistent(Content) -> process_routing_result(unroutable, _, _, MsgSeqNo, Message, State) -> ok = basic_return(Message, State#ch.writer_pid, no_route), - send_or_enqueue_ack(MsgSeqNo, State); + send_or_enqueue_ack(MsgSeqNo, undefined, State); process_routing_result(not_delivered, _, _, MsgSeqNo, Message, State) -> ok = basic_return(Message, State#ch.writer_pid, no_consumers), - send_or_enqueue_ack(MsgSeqNo, State); + send_or_enqueue_ack(MsgSeqNo, undefined, State); process_routing_result(routed, [], _, MsgSeqNo, _, State) -> - send_or_enqueue_ack(MsgSeqNo, State); + send_or_enqueue_ack(MsgSeqNo, undefined, State); +process_routing_result(routed, _, _, undefined, _, State) -> + State; process_routing_result(routed, _, false, MsgSeqNo, _, State) -> - send_or_enqueue_ack(MsgSeqNo, State); -process_routing_result(routed, QPids, true, MsgSeqNo, _, State) -> - lists:foldl(fun (QPid, State0) -> - msg_sent_to_queue(MsgSeqNo, QPid, State0) - end, State, QPids). + send_or_enqueue_ack(MsgSeqNo, undefined, State); +process_routing_result(routed, QPids, true, MsgSeqNo, _, + State = #ch{queues_for_msg = QFM}) -> + QFM1 = dict:store(MsgSeqNo, sets:from_list(QPids), QFM), + [maybe_monitor(QPid) || QPid <- QPids], + State#ch{queues_for_msg = QFM1}. lock_message(true, MsgStruct, State = #ch{unacked_message_q = UAMQ}) -> State#ch{unacked_message_q = queue:in(MsgStruct, UAMQ)}; -- cgit v1.2.1 From bb00cd49396eaa9a03837545028c151a04e95746 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Tue, 26 Oct 2010 11:37:46 +0100 Subject: Plugin arguments for windows service --- scripts/rabbitmq-service.bat | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/scripts/rabbitmq-service.bat b/scripts/rabbitmq-service.bat index 4b3961d4..ab825a55 100644 --- a/scripts/rabbitmq-service.bat +++ b/scripts/rabbitmq-service.bat @@ -184,10 +184,8 @@ set RABBITMQ_EBIN_ROOT=!TDP0!..\ebin -pa "!RABBITMQ_EBIN_ROOT!" ^ -noinput -hidden ^ -s rabbit_plugin_activator ^ --rabbit plugins_dir \""!RABBITMQ_PLUGINS_DIR:\=/!"\" ^ --rabbit plugins_expand_dir \""!RABBITMQ_MNESIA_DIR:\=/!/plugins-scratch"\" ^ --rabbit rabbit_ebin \""!RABBITMQ_EBIN_ROOT:\=/!"\" ^ --extra !STAR! +-extra "!RABBITMQ_PLUGINS_DIR:\=/!" ^ + "!RABBITMQ_MNESIA_DIR:\=/!/plugins-scratch" set RABBITMQ_BOOT_FILE=!RABBITMQ_MNESIA_DIR!\plugins-scratch\rabbit if not exist "!RABBITMQ_BOOT_FILE!.boot" ( -- cgit v1.2.1 From ce7d7897efa0b0ecbef4eb0ae5dada44e300b901 Mon Sep 17 00:00:00 2001 From: Jim Apperly Date: Fri, 29 Oct 2010 16:00:12 +0100 Subject: more man page namespacery --- docs/html-to-website-xml.xsl | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/docs/html-to-website-xml.xsl b/docs/html-to-website-xml.xsl index 15e31ca0..1fd9cea1 100644 --- a/docs/html-to-website-xml.xsl +++ b/docs/html-to-website-xml.xsl @@ -1,6 +1,7 @@ @@ -10,10 +11,16 @@ - - + + + + + + + + type="text/xml" href="page.xsl" -- cgit v1.2.1 From f27016fde8999bd0829cb4c4846c722f4bd8daf1 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Sun, 31 Oct 2010 14:32:30 +0000 Subject: confirm transient messages in the queues Transient messages are now handled exactly like persistent messages: they are ack'd only after all of the queues have ack'd. Unroutable messages and messages routed to 0 queues are still ack'd by the channel. --- src/rabbit_amqqueue_process.erl | 12 ++++++++++-- src/rabbit_channel.erl | 20 +++++++------------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index f6255d2e..e78eb06f 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -426,6 +426,9 @@ confirm_message(#basic_message{guid = Guid}, State) -> record_confirm_message(#delivery{msg_seq_no = undefined}, State) -> State; +record_confirm_message(#delivery{message = #basic_message{ + is_persistent = false}}, State) -> + State; record_confirm_message(#delivery{msg_seq_no = MsgSeqNo, sender = ChPid, message = #basic_message{guid = Guid}}, @@ -447,9 +450,15 @@ run_message_queue(State) -> State2. attempt_delivery(#delivery{txn = none, + sender = ChPid, message = Message, msg_seq_no = MsgSeqNo}, State = #q{backing_queue = BQ}) -> + IsPersistent = Message#basic_message.is_persistent, + case IsPersistent of + false -> rabbit_channel:confirm(ChPid, MsgSeqNo); + _ -> ok + end, PredFun = fun (IsEmpty, _State) -> not IsEmpty end, DeliverFun = fun (AckRequired, false, State1 = #q{backing_queue_state = BQS}) -> @@ -460,8 +469,7 @@ attempt_delivery(#delivery{txn = none, BQ:publish_delivered(AckRequired, Message, ?BASE_MESSAGE_PROPERTIES #message_properties { - needs_confirming = - (MsgSeqNo =/= undefined)}, + needs_confirming = IsPersistent }, BQS), {{Message, false, AckTag}, true, State1#q{backing_queue_state = BQS1}} diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 7c45b52d..fdf4bdc2 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -556,12 +556,8 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, {RoutingRes, DeliveredQPids} = rabbit_exchange:publish( Exchange, - rabbit_basic:delivery(Mandatory, Immediate, TxnKey, Message, - case IsPersistent of - true -> MsgSeqNo; - false -> undefined - end)), - State2 = process_routing_result(RoutingRes, DeliveredQPids, IsPersistent, + rabbit_basic:delivery(Mandatory, Immediate, TxnKey, Message, MsgSeqNo)), + State2 = process_routing_result(RoutingRes, DeliveredQPids, MsgSeqNo, Message, State1), maybe_incr_stats([{ExchangeName, 1} | [{{QPid, ExchangeName}, 1} || @@ -1245,19 +1241,17 @@ is_message_persistent(Content) -> IsPersistent end. -process_routing_result(unroutable, _, _, MsgSeqNo, Message, State) -> +process_routing_result(unroutable, _, MsgSeqNo, Message, State) -> ok = basic_return(Message, State#ch.writer_pid, no_route), send_or_enqueue_ack(MsgSeqNo, undefined, State); -process_routing_result(not_delivered, _, _, MsgSeqNo, Message, State) -> +process_routing_result(not_delivered, _, MsgSeqNo, Message, State) -> ok = basic_return(Message, State#ch.writer_pid, no_consumers), send_or_enqueue_ack(MsgSeqNo, undefined, State); -process_routing_result(routed, [], _, MsgSeqNo, _, State) -> +process_routing_result(routed, [], MsgSeqNo, _, State) -> send_or_enqueue_ack(MsgSeqNo, undefined, State); -process_routing_result(routed, _, _, undefined, _, State) -> +process_routing_result(routed, _, undefined, _, State) -> State; -process_routing_result(routed, _, false, MsgSeqNo, _, State) -> - send_or_enqueue_ack(MsgSeqNo, undefined, State); -process_routing_result(routed, QPids, true, MsgSeqNo, _, +process_routing_result(routed, QPids, MsgSeqNo, _, State = #ch{queues_for_msg = QFM}) -> QFM1 = dict:store(MsgSeqNo, sets:from_list(QPids), QFM), [maybe_monitor(QPid) || QPid <- QPids], -- cgit v1.2.1 From 1a8ff3cd239fe4698cd9b65a9b6f34931857092f Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Mon, 1 Nov 2010 14:11:41 +0000 Subject: only wait for disk syncs for persistent messages on durable queues --- src/rabbit_amqqueue_process.erl | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index e78eb06f..adae926c 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -453,9 +453,10 @@ attempt_delivery(#delivery{txn = none, sender = ChPid, message = Message, msg_seq_no = MsgSeqNo}, - State = #q{backing_queue = BQ}) -> - IsPersistent = Message#basic_message.is_persistent, - case IsPersistent of + State = #q{backing_queue = BQ, q = Q}) -> + NeedsConfirming = Message#basic_message.is_persistent andalso + Q#amqqueue.durable, + case NeedsConfirming of false -> rabbit_channel:confirm(ChPid, MsgSeqNo); _ -> ok end, @@ -469,7 +470,7 @@ attempt_delivery(#delivery{txn = none, BQ:publish_delivered(AckRequired, Message, ?BASE_MESSAGE_PROPERTIES #message_properties { - needs_confirming = IsPersistent }, + needs_confirming = NeedsConfirming }, BQS), {{Message, false, AckTag}, true, State1#q{backing_queue_state = BQS1}} -- cgit v1.2.1 From d87631580658864e7212765a640617594c7e53d0 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Mon, 1 Nov 2010 14:29:15 +0000 Subject: tests pass --- 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 8a300527..842a942c 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -1634,7 +1634,8 @@ init_test_queue() -> TestQueue, Terms, false, fun (Guid) -> rabbit_msg_store:contains(Guid, PersistentClient) - end), + end, + fun nop/1), ok = rabbit_msg_store:client_delete_and_terminate(PersistentClient), Res. -- cgit v1.2.1 From 423a27191f248d75362ba6675c401d6968cf8497 Mon Sep 17 00:00:00 2001 From: Rob Harrop Date: Tue, 2 Nov 2010 16:57:38 +0000 Subject: Calculating RAM duration takes into consideration ack rates --- src/rabbit_variable_queue.erl | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 97833991..d2f79eb6 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -235,6 +235,7 @@ target_ram_ack_count, ram_msg_count, ram_msg_count_prev, + ram_ack_count_prev, ram_index_count, out_counter, in_counter, @@ -706,19 +707,34 @@ ram_duration(State = #vqstate { rates = #rates { egress = Egress, ingress = Ingress, timestamp = Timestamp } = Rates, + ack_rates = #rates { egress = AckEgress, + ingress = AckIngress } = ARates, in_counter = InCount, out_counter = OutCount, + ack_in_counter = AckInCount, + ack_out_counter = AckOutCount, ram_msg_count = RamMsgCount, - ram_msg_count_prev = RamMsgCountPrev }) -> + ram_msg_count_prev = RamMsgCountPrev, + ram_ack_index = RamAckIndex, + ram_ack_count_prev = RamAckCountPrev }) -> Now = now(), {AvgEgressRate, Egress1} = update_rate(Now, Timestamp, OutCount, Egress), {AvgIngressRate, Ingress1} = update_rate(Now, Timestamp, InCount, Ingress), - Duration = %% msgs / (msgs/sec) == sec + {AvgAckEgressRate, AckEgress1} = + update_rate(Now, Timestamp, AckOutCount, AckEgress), + {AvgAckIngressRate, AckIngress1} = + update_rate(Now, Timestamp, AckInCount, AckIngress), + + RamAckCount = gb_trees:size(RamAckIndex), + + Duration = %% msgs+acks / (msgs+acks/sec) == sec case AvgEgressRate == 0 andalso AvgIngressRate == 0 of true -> infinity; - false -> (RamMsgCountPrev + RamMsgCount) / - (2 * (AvgEgressRate + AvgIngressRate)) + false -> (RamMsgCountPrev + RamMsgCount + + RamAckCount + RamAckCountPrev) / + (2 * (AvgEgressRate + AvgIngressRate + + AvgAckEgressRate + AvgAckIngressRate)) end, {Duration, State #vqstate { @@ -728,9 +744,15 @@ ram_duration(State = #vqstate { avg_egress = AvgEgressRate, avg_ingress = AvgIngressRate, timestamp = Now }, + ack_rates = ARates #rates { + egress = AckEgress1, + ingress = AckIngress1, + avg_egress = AvgAckEgressRate, + avg_ingress = AvgAckIngressRate }, in_counter = 0, out_counter = 0, - ram_msg_count_prev = RamMsgCount }}. + ram_msg_count_prev = RamMsgCount, + ram_ack_count_prev = RamAckCount }}. needs_idle_timeout(State = #vqstate { on_sync = ?BLANK_SYNC }) -> {Res, _State} = reduce_memory_use(fun (_Quota, State1) -> State1 end, @@ -982,6 +1004,7 @@ init(IsDurable, IndexState, DeltaCount, Terms, target_ram_msg_count = infinity, ram_msg_count = 0, ram_msg_count_prev = 0, + ram_ack_count_prev = 0, ram_index_count = 0, out_counter = 0, in_counter = 0, @@ -996,7 +1019,7 @@ init(IsDurable, IndexState, DeltaCount, Terms, ingress = {Now, 0}, avg_egress = 0.0, avg_ingress = 0.0, - timestamp = Now } }, + timestamp = undefined } }, a(maybe_deltas_to_betas(State)). msg_store_callback(PersistentGuids, Pubs, AckTags, Fun, MsgPropsFun) -> -- cgit v1.2.1 From 3499cb05a36e41b456a807935811e097bd4a676d Mon Sep 17 00:00:00 2001 From: Rob Harrop Date: Wed, 3 Nov 2010 11:29:38 +0000 Subject: Further experimentation. Still not quite working --- src/rabbit_amqqueue_process.erl | 3 +++ src/rabbit_variable_queue.erl | 42 +++++++++++++++++++++++++++++++---------- 2 files changed, 35 insertions(+), 10 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index fe2c975b..83b360b8 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -239,6 +239,7 @@ stop_sync_timer(State = #q{sync_timer_ref = TRef}) -> State#q{sync_timer_ref = undefined}. ensure_rate_timer(State = #q{rate_timer_ref = undefined}) -> + io:format("Ensuring rate timer~n"), {ok, TRef} = timer:apply_after( ?RAM_DURATION_UPDATE_INTERVAL, rabbit_amqqueue, update_ram_duration, @@ -963,7 +964,9 @@ handle_cast({flush, ChPid}, State) -> handle_cast(update_ram_duration, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> + io:format("Before ram duration~n"), {RamDuration, BQS1} = BQ:ram_duration(BQS), + io:format("RamDuration~p~n", [RamDuration]), DesiredDuration = rabbit_memory_monitor:report_ram_duration(self(), RamDuration), BQS2 = BQ:set_ram_duration_target(DesiredDuration, BQS1), diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index d2f79eb6..ad62248a 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -688,8 +688,13 @@ set_ram_duration_target(DurationTarget, State = #vqstate { rates = #rates { avg_egress = AvgEgressRate, avg_ingress = AvgIngressRate }, + ack_rates = + #rates { avg_egress = AvgAckEgressRate, + avg_ingress = AvgAckIngressRate }, target_ram_msg_count = TargetRamMsgCount }) -> - Rate = AvgEgressRate + AvgIngressRate, + io:format("set:~p~n", [DurationTarget]), + Rate = AvgEgressRate + AvgIngressRate + AvgAckEgressRate + + AvgAckIngressRate, TargetRamMsgCount1 = case DurationTarget of infinity -> infinity; @@ -717,6 +722,7 @@ ram_duration(State = #vqstate { ram_msg_count_prev = RamMsgCountPrev, ram_ack_index = RamAckIndex, ram_ack_count_prev = RamAckCountPrev }) -> + io:format("Setting ram duration~n"), Now = now(), {AvgEgressRate, Egress1} = update_rate(Now, Timestamp, OutCount, Egress), {AvgIngressRate, Ingress1} = update_rate(Now, Timestamp, InCount, Ingress), @@ -729,7 +735,8 @@ ram_duration(State = #vqstate { RamAckCount = gb_trees:size(RamAckIndex), Duration = %% msgs+acks / (msgs+acks/sec) == sec - case AvgEgressRate == 0 andalso AvgIngressRate == 0 of + case AvgEgressRate == 0 andalso AvgIngressRate == 0 andalso + AvgAckEgressRate == 0 andalso AvgAckIngressRate == 0 of true -> infinity; false -> (RamMsgCountPrev + RamMsgCount + RamAckCount + RamAckCountPrev) / @@ -737,6 +744,7 @@ ram_duration(State = #vqstate { AvgAckEgressRate + AvgAckIngressRate)) end, + io:format("Duration:~p~n", [Duration]), {Duration, State #vqstate { rates = Rates #rates { egress = Egress1, @@ -1335,14 +1343,11 @@ find_persistent_count(LensByStore) -> %% 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, BetaGammaFun, BetaDeltaFun, State) -> - Size = gb_trees:size(State #vqstate.ram_ack_index), - State1 = case chunk_size(Size, State #vqstate.target_ram_ack_count) of - 0 -> State; - S -> io:format("Limiting~n"), limit_ram_acks(S, State) - end, - {Reduce, State2} = case chunk_size(State #vqstate.ram_msg_count, - State #vqstate.target_ram_msg_count) of - 0 -> {false, State1}; + %%io:format("Reducing mem~p~p~n", [State #vqstate.target_ram_msg_count, State #vqstate.ram_msg_count]), + {ReduceAck, State1} = {false, State}, %%reduce_ack_memory_use(State), + {Reduce, State2} = case chunk_size(State1 #vqstate.ram_msg_count, + State1 #vqstate.target_ram_msg_count) of + 0 -> {ReduceAck, State1}; S1 -> {true, AlphaBetaFun(S1, State1)} end, case State2 #vqstate.target_ram_msg_count of @@ -1355,6 +1360,23 @@ reduce_memory_use(AlphaBetaFun, BetaGammaFun, BetaDeltaFun, State) -> end end. +reduce_ack_memory_use(State = #vqstate { target_ram_msg_count = infinity }) -> + {false, State}; +reduce_ack_memory_use(State = #vqstate {target_ram_msg_count = TargetRamMsgCount, + ram_msg_count = RamMsgCount, + ram_ack_index = RamAckIndex} ) -> + io:format("RAI:~p,TRMC:~p,RMC:~p~n", [gb_trees:size(RamAckIndex), TargetRamMsgCount, RamMsgCount]), + PermittedAckCount = case TargetRamMsgCount > RamMsgCount of + true -> TargetRamMsgCount - RamMsgCount; + false -> 0 + end, + case chunk_size(gb_trees:size(RamAckIndex), PermittedAckCount) of + 0 -> {false, State}; + C -> {true, limit_ram_acks(C, State)} + end. + + + limit_ram_acks(0, State) -> State; limit_ram_acks(Quota, State = #vqstate { pending_ack = PA, -- cgit v1.2.1 From a14752efdaac19e4f771673e06c26c7b2037db39 Mon Sep 17 00:00:00 2001 From: Rob Harrop Date: Wed, 3 Nov 2010 14:30:39 +0000 Subject: Roughly working ack shedding --- src/rabbit_amqqueue_process.erl | 5 +++++ src/rabbit_variable_queue.erl | 37 ++++++++++++++++++++++--------------- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 83b360b8..9699ac32 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -969,6 +969,7 @@ handle_cast(update_ram_duration, State = #q{backing_queue = BQ, io:format("RamDuration~p~n", [RamDuration]), DesiredDuration = rabbit_memory_monitor:report_ram_duration(self(), RamDuration), + io:format("Desired duration:~p~n", [DesiredDuration]), BQS2 = BQ:set_ram_duration_target(DesiredDuration, BQS1), noreply(State#q{rate_timer_ref = just_measured, backing_queue_state = BQS2}); @@ -1025,11 +1026,15 @@ handle_info(Info, State) -> ?LOGDEBUG("Info in queue: ~p~n", [Info]), {stop, {unhandled_info, Info}, State}. +handle_post_hibernate(_) -> + io:format("hello~n"). + handle_pre_hibernate(State = #q{backing_queue_state = undefined}) -> {hibernate, State}; handle_pre_hibernate(State = #q{backing_queue = BQ, backing_queue_state = BQS, stats_timer = StatsTimer}) -> + io:format("Hibernating~p~n", [self()]), BQS1 = BQ:handle_pre_hibernate(BQS), %% no activity for a while == 0 egress and ingress rates DesiredDuration = diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index ad62248a..491d4e38 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -722,7 +722,7 @@ ram_duration(State = #vqstate { ram_msg_count_prev = RamMsgCountPrev, ram_ack_index = RamAckIndex, ram_ack_count_prev = RamAckCountPrev }) -> - io:format("Setting ram duration~n"), + io:format("Ram duration~n"), Now = now(), {AvgEgressRate, Egress1} = update_rate(Now, Timestamp, OutCount, Egress), {AvgIngressRate, Ingress1} = update_rate(Now, Timestamp, InCount, Ingress), @@ -766,6 +766,7 @@ needs_idle_timeout(State = #vqstate { on_sync = ?BLANK_SYNC }) -> {Res, _State} = reduce_memory_use(fun (_Quota, State1) -> State1 end, fun (_Quota, State1) -> State1 end, fun (State1) -> State1 end, + fun (_Quota, State1) -> State1 end, State), Res; needs_idle_timeout(_State) -> @@ -779,6 +780,7 @@ 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, on_sync = #sync { funs = From }, target_ram_msg_count = TargetRamMsgCount, ram_msg_count = RamMsgCount, @@ -787,7 +789,10 @@ status(#vqstate { q1 = Q1, q2 = Q2, delta = Delta, q3 = Q3, q4 = Q4, persistent_count = PersistentCount, rates = #rates { avg_egress = AvgEgressRate, - avg_ingress = AvgIngressRate } }) -> + avg_ingress = AvgIngressRate }, + ack_rates = #rates { + avg_egress = AvgAckEgressRate, + avg_ingress = AvgAckIngressRate } }) -> [ {q1 , queue:len(Q1)}, {q2 , bpqueue:len(Q2)}, {delta , Delta}, @@ -795,6 +800,7 @@ status(#vqstate { q1 = Q1, q2 = Q2, delta = Delta, q3 = Q3, q4 = Q4, {q4 , queue:len(Q4)}, {len , Len}, {pending_acks , dict:size(PA)}, + {ram_ack_count , gb_trees:size(RAI)}, {outstanding_txns , length(From)}, {target_ram_msg_count , TargetRamMsgCount}, {ram_msg_count , RamMsgCount}, @@ -802,7 +808,9 @@ status(#vqstate { q1 = Q1, q2 = Q2, delta = Delta, q3 = Q3, q4 = Q4, {next_seq_id , NextSeqId}, {persistent_count , PersistentCount}, {avg_egress_rate , AvgEgressRate}, - {avg_ingress_rate , AvgIngressRate} ]. + {avg_ingress_rate , AvgIngressRate}, + {avg_ack_egress_rate , AvgAckEgressRate}, + {avg_ack_ingress_rate , AvgAckIngressRate}]. %%---------------------------------------------------------------------------- %% Minor helpers @@ -1342,9 +1350,8 @@ find_persistent_count(LensByStore) -> %% 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, BetaGammaFun, BetaDeltaFun, State) -> - %%io:format("Reducing mem~p~p~n", [State #vqstate.target_ram_msg_count, State #vqstate.ram_msg_count]), - {ReduceAck, State1} = {false, State}, %%reduce_ack_memory_use(State), +reduce_memory_use(AlphaBetaFun, BetaGammaFun, BetaDeltaFun, AckFun, State) -> + {ReduceAck, State1} = reduce_ack_memory_use(AckFun, State), {Reduce, State2} = case chunk_size(State1 #vqstate.ram_msg_count, State1 #vqstate.target_ram_msg_count) of 0 -> {ReduceAck, State1}; @@ -1360,9 +1367,9 @@ reduce_memory_use(AlphaBetaFun, BetaGammaFun, BetaDeltaFun, State) -> end end. -reduce_ack_memory_use(State = #vqstate { target_ram_msg_count = infinity }) -> +reduce_ack_memory_use(_AckFun, State = #vqstate { target_ram_msg_count = infinity }) -> {false, State}; -reduce_ack_memory_use(State = #vqstate {target_ram_msg_count = TargetRamMsgCount, +reduce_ack_memory_use(AckFun, State = #vqstate {target_ram_msg_count = TargetRamMsgCount, ram_msg_count = RamMsgCount, ram_ack_index = RamAckIndex} ) -> io:format("RAI:~p,TRMC:~p,RMC:~p~n", [gb_trees:size(RamAckIndex), TargetRamMsgCount, RamMsgCount]), @@ -1372,7 +1379,7 @@ reduce_ack_memory_use(State = #vqstate {target_ram_msg_count = TargetRamMsgCount end, case chunk_size(gb_trees:size(RamAckIndex), PermittedAckCount) of 0 -> {false, State}; - C -> {true, limit_ram_acks(C, State)} + C -> {true, AckFun(C, State)} end. @@ -1381,20 +1388,19 @@ limit_ram_acks(0, State) -> State; limit_ram_acks(Quota, State = #vqstate { pending_ack = PA, ram_ack_index = RAI }) -> - io:format("Limiting acks~p~n", [Quota]), case gb_trees:is_empty(RAI) of true -> State; false -> {SeqId, Guid, RAI1} = gb_trees:take_largest(RAI), - io:format("Largest~p~n", [SeqId]), - MsgStatus = dict:fetch(SeqId, PA), - State1 = maybe_write_to_disk(true, false, MsgStatus, State), - io:format("Wrote~n"), + MsgStatus = #msg_status { + guid = Guid, %% ASSERTION + msg_props = MsgProps } = dict:fetch(SeqId, PA), + {_, State1} = maybe_write_to_disk(true, false, MsgStatus, State), limit_ram_acks(Quota - 1, State1 #vqstate { pending_ack = - dict:update(SeqId, {false, Guid}, PA), + dict:store(SeqId, {false, Guid, MsgProps}, PA), ram_ack_index = RAI1 }) end. @@ -1403,6 +1409,7 @@ reduce_memory_use(State) -> {_, State1} = reduce_memory_use(fun push_alphas_to_betas/2, fun limit_ram_index/2, fun push_betas_to_deltas/1, + fun limit_ram_acks/2, State), State1. -- cgit v1.2.1 From 1a45fb36b79da3c4c2531f36fd3647fd0e8b20ca Mon Sep 17 00:00:00 2001 From: Rob Harrop Date: Wed, 3 Nov 2010 15:19:05 +0000 Subject: cleaned up some io:format --- src/rabbit_amqqueue_process.erl | 3 --- src/rabbit_variable_queue.erl | 6 +----- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 9699ac32..2cf7f268 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1026,9 +1026,6 @@ handle_info(Info, State) -> ?LOGDEBUG("Info in queue: ~p~n", [Info]), {stop, {unhandled_info, Info}, State}. -handle_post_hibernate(_) -> - io:format("hello~n"). - handle_pre_hibernate(State = #q{backing_queue_state = undefined}) -> {hibernate, State}; handle_pre_hibernate(State = #q{backing_queue = BQ, diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 491d4e38..3a8a913f 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -692,7 +692,6 @@ set_ram_duration_target(DurationTarget, #rates { avg_egress = AvgAckEgressRate, avg_ingress = AvgAckIngressRate }, target_ram_msg_count = TargetRamMsgCount }) -> - io:format("set:~p~n", [DurationTarget]), Rate = AvgEgressRate + AvgIngressRate + AvgAckEgressRate + AvgAckIngressRate, TargetRamMsgCount1 = @@ -705,7 +704,7 @@ set_ram_duration_target(DurationTarget, (TargetRamMsgCount =/= infinity andalso TargetRamMsgCount1 >= TargetRamMsgCount) of true -> State1; - false -> io:format("Reducing~n"), reduce_memory_use(State1) + false -> reduce_memory_use(State1) end). ram_duration(State = #vqstate { @@ -722,7 +721,6 @@ ram_duration(State = #vqstate { ram_msg_count_prev = RamMsgCountPrev, ram_ack_index = RamAckIndex, ram_ack_count_prev = RamAckCountPrev }) -> - io:format("Ram duration~n"), Now = now(), {AvgEgressRate, Egress1} = update_rate(Now, Timestamp, OutCount, Egress), {AvgIngressRate, Ingress1} = update_rate(Now, Timestamp, InCount, Ingress), @@ -744,7 +742,6 @@ ram_duration(State = #vqstate { AvgAckEgressRate + AvgAckIngressRate)) end, - io:format("Duration:~p~n", [Duration]), {Duration, State #vqstate { rates = Rates #rates { egress = Egress1, @@ -1372,7 +1369,6 @@ reduce_ack_memory_use(_AckFun, State = #vqstate { target_ram_msg_count = infinit reduce_ack_memory_use(AckFun, State = #vqstate {target_ram_msg_count = TargetRamMsgCount, ram_msg_count = RamMsgCount, ram_ack_index = RamAckIndex} ) -> - io:format("RAI:~p,TRMC:~p,RMC:~p~n", [gb_trees:size(RamAckIndex), TargetRamMsgCount, RamMsgCount]), PermittedAckCount = case TargetRamMsgCount > RamMsgCount of true -> TargetRamMsgCount - RamMsgCount; false -> 0 -- cgit v1.2.1 From f0eaffef9b6eefb01721deb88d8c7b98c5753875 Mon Sep 17 00:00:00 2001 From: Rob Harrop Date: Thu, 4 Nov 2010 12:25:43 +0000 Subject: Removed io:format calls --- src/rabbit_amqqueue_process.erl | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 2cf7f268..fe2c975b 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -239,7 +239,6 @@ stop_sync_timer(State = #q{sync_timer_ref = TRef}) -> State#q{sync_timer_ref = undefined}. ensure_rate_timer(State = #q{rate_timer_ref = undefined}) -> - io:format("Ensuring rate timer~n"), {ok, TRef} = timer:apply_after( ?RAM_DURATION_UPDATE_INTERVAL, rabbit_amqqueue, update_ram_duration, @@ -964,12 +963,9 @@ handle_cast({flush, ChPid}, State) -> handle_cast(update_ram_duration, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> - io:format("Before ram duration~n"), {RamDuration, BQS1} = BQ:ram_duration(BQS), - io:format("RamDuration~p~n", [RamDuration]), DesiredDuration = rabbit_memory_monitor:report_ram_duration(self(), RamDuration), - io:format("Desired duration:~p~n", [DesiredDuration]), BQS2 = BQ:set_ram_duration_target(DesiredDuration, BQS1), noreply(State#q{rate_timer_ref = just_measured, backing_queue_state = BQS2}); @@ -1031,7 +1027,6 @@ handle_pre_hibernate(State = #q{backing_queue_state = undefined}) -> handle_pre_hibernate(State = #q{backing_queue = BQ, backing_queue_state = BQS, stats_timer = StatsTimer}) -> - io:format("Hibernating~p~n", [self()]), BQS1 = BQ:handle_pre_hibernate(BQS), %% no activity for a while == 0 egress and ingress rates DesiredDuration = -- cgit v1.2.1 From c70c248fb698be85b8c30e4b9d20dd154e353091 Mon Sep 17 00:00:00 2001 From: Rob Harrop Date: Thu, 4 Nov 2010 12:32:21 +0000 Subject: clean up --- src/rabbit_variable_queue.erl | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 3a8a913f..3b3f9c8d 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -1248,7 +1248,7 @@ record_pending_ack(#msg_status { seq_id = SeqId, ack_in_counter = AckInCount}) -> {AckEntry, RAI1} = case MsgOnDisk of - true -> + true -> {{IsPersistent, Guid, MsgProps}, RAI}; false -> {MsgStatus, gb_trees:insert(SeqId, Guid, RAI)} @@ -1364,11 +1364,14 @@ reduce_memory_use(AlphaBetaFun, BetaGammaFun, BetaDeltaFun, AckFun, State) -> end end. -reduce_ack_memory_use(_AckFun, State = #vqstate { target_ram_msg_count = infinity }) -> +reduce_ack_memory_use(_AckFun, + State = #vqstate { target_ram_msg_count = infinity }) -> {false, State}; -reduce_ack_memory_use(AckFun, State = #vqstate {target_ram_msg_count = TargetRamMsgCount, - ram_msg_count = RamMsgCount, - ram_ack_index = RamAckIndex} ) -> +reduce_ack_memory_use(AckFun, + State = #vqstate { + target_ram_msg_count = TargetRamMsgCount, + ram_msg_count = RamMsgCount, + ram_ack_index = RamAckIndex} ) -> PermittedAckCount = case TargetRamMsgCount > RamMsgCount of true -> TargetRamMsgCount - RamMsgCount; false -> 0 @@ -1395,7 +1398,7 @@ limit_ram_acks(Quota, State = #vqstate { pending_ack = PA, {_, State1} = maybe_write_to_disk(true, false, MsgStatus, State), limit_ram_acks(Quota - 1, State1 #vqstate { - pending_ack = + pending_ack = dict:store(SeqId, {false, Guid, MsgProps}, PA), ram_ack_index = RAI1 }) end. -- cgit v1.2.1 From 39740b60706338a45fec7c856c40026d086e5d0f Mon Sep 17 00:00:00 2001 From: Rob Harrop Date: Thu, 4 Nov 2010 12:52:46 +0000 Subject: Removed stray TODO --- 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 3b3f9c8d..2ef4ee27 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -1258,7 +1258,6 @@ record_pending_ack(#msg_status { seq_id = SeqId, ram_ack_index = RAI1, ack_in_counter = AckInCount + 1}. -%% TODO: On remove, need to prevent any seqids that remove_pending_ack(KeepPersistent, State = #vqstate { pending_ack = PA, index_state = IndexState, -- cgit v1.2.1 From 867c225ccf18ae85529f3125c82e651e26ab6632 Mon Sep 17 00:00:00 2001 From: Rob Harrop Date: Thu, 4 Nov 2010 16:00:08 +0000 Subject: Added a test for the ram ack flushing. Tweaked the way RAM is shared between acks and msgs --- src/rabbit_tests.erl | 33 ++++++++++++++++++++++++++++++++- src/rabbit_variable_queue.erl | 20 +++++++++++++++----- 2 files changed, 47 insertions(+), 6 deletions(-) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 71b23e01..8efcf239 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -1865,9 +1865,40 @@ 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_dropwhile/1]], + fun test_dropwhile/1, + fun test_variable_queue_ack_limiting/1]], passed. +test_variable_queue_ack_limiting(VQ0) -> + %% start by sending in a bunch of messages < + Len = 1024, + VQ1 = variable_queue_publish(false, Len, VQ0), + + %% squeeze and relax queue + Churn = Len div 32, + VQ2 = publish_fetch_and_ack(Churn, Len, VQ1), + + %% update stats for duration + {Duration, VQ3} = rabbit_variable_queue:ram_duration(VQ2), + + %% fetch half the messages + {VQ4, _AckTags} = variable_queue_fetch(Len div 2, false, false, Len, VQ3), + + VQ5 = check_variable_queue_status(VQ4, [{len , Len div 2}, + {ram_ack_count , Len div 2}, + {ram_msg_count , Len div 2}]), + + %% quarter the allowed duration + VQ6 = check_variable_queue_status( + rabbit_variable_queue:set_ram_duration_target(Duration / 4, VQ5), + [{len, Len div 2}, + {target_ram_msg_count, Len div 8}, + {ram_msg_count, Len div 8}, + {ram_ack_count, 0}]), + + VQ6. + + test_dropwhile(VQ0) -> Count = 10, diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 2ef4ee27..57d1344e 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -1307,7 +1307,7 @@ ack(MsgStoreFun, Fun, AckTags, State) -> orddict:new(), GuidsByStore)), State1 #vqstate { index_state = IndexState1, persistent_count = PCount1, - ack_out_counter = AckOutCount - length(AckTags) }. + ack_out_counter = AckOutCount + length(AckTags) }. accumulate_ack(_SeqId, #msg_status { is_persistent = false, %% ASSERTIONS msg_on_disk = false, @@ -1348,10 +1348,20 @@ find_persistent_count(LensByStore) -> %% conversion is needed. That in turn could cause an infinite loop. reduce_memory_use(AlphaBetaFun, BetaGammaFun, BetaDeltaFun, AckFun, State) -> {ReduceAck, State1} = reduce_ack_memory_use(AckFun, State), - {Reduce, State2} = case chunk_size(State1 #vqstate.ram_msg_count, - State1 #vqstate.target_ram_msg_count) of - 0 -> {ReduceAck, State1}; - S1 -> {true, AlphaBetaFun(S1, State1)} + + {Reduce, State2} = case ReduceAck of + true -> + %% Don't want to reduce the number of + %% ram messages if we might yet be able + %% to reduce more acks. + {true, State1}; + false -> + case chunk_size( + State1 #vqstate.ram_msg_count, + State1 #vqstate.target_ram_msg_count) of + 0 -> {false, State1}; + S1 -> {true, AlphaBetaFun(S1, State1)} + end end, case State2 #vqstate.target_ram_msg_count of infinity -> {Reduce, State2}; -- cgit v1.2.1 From e8821b7480c0c2df2536571647b1ed012ddc58f3 Mon Sep 17 00:00:00 2001 From: Rob Harrop Date: Thu, 4 Nov 2010 22:51:56 +0000 Subject: Purely cosmetic --- src/rabbit_tests.erl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 8efcf239..eecc27b3 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -1898,7 +1898,6 @@ test_variable_queue_ack_limiting(VQ0) -> VQ6. - test_dropwhile(VQ0) -> Count = 10, -- cgit v1.2.1 From 6fde9cbafc73040b33cb750a8e429209df1ae473 Mon Sep 17 00:00:00 2001 From: Rob Harrop Date: Fri, 5 Nov 2010 00:11:30 +0000 Subject: Cosmetic change and first draft of comment updates --- src/rabbit_variable_queue.erl | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 57d1344e..8b36767c 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -168,6 +168,22 @@ %% the latter) are both cheap and do require any scanning through qi %% segments. %% +%% Pending acks are recorded in memory either as the tuple {SeqId, +%% Guid, MsgProps} (tuple form) or as the message itself (message +%% form). Acks for persistent messages are always stored in the tuple +%% form. Acks for transient messages are also stored in tuple form if +%% the message has been forgotten to disk as part of the memory +%% reduction process. For transient messages that haven't already been +%% written to disk, acks are stored in message form to avoid the +%% overhead of writing to disk. +%% +%% During memory reduction, messages stored as transient ack records +%% are pushed out to disk before messages in the queue. More +%% precisely, messages from the queue will not be pushed out to disk +%% while the number of messages stored for acks is greater than +%% zero. Messages for acks are written to disk in batches of at most +%% ?IO_BATCH_SIZE. +%% %% Notes on Clean Shutdown %% (This documents behaviour in variable_queue, queue_index and %% msg_store.) @@ -1347,7 +1363,20 @@ find_persistent_count(LensByStore) -> %% 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, BetaGammaFun, BetaDeltaFun, AckFun, State) -> - {ReduceAck, State1} = reduce_ack_memory_use(AckFun, State), + {Reduce, State2} = case reduce_ack_memory_use(AckFun, State) of + {true, State1} -> + %% Don't want to reduce the number of + %% ram messages if we might yet be able + %% to reduce more acks. + {true, State1}; + {false, State1} -> + case chunk_size( + State1 #vqstate.ram_msg_count, + State1 #vqstate.target_ram_msg_count) of + 0 -> {false, State1}; + S1 -> {true, AlphaBetaFun(S1, State1)} + end + end, {Reduce, State2} = case ReduceAck of true -> -- cgit v1.2.1 From 5d600413dee1ad71d4bb6efc1597e1c3e334fb9b Mon Sep 17 00:00:00 2001 From: Rob Harrop Date: Fri, 5 Nov 2010 00:43:08 +0000 Subject: a bit more clarification on the comments --- src/rabbit_variable_queue.erl | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 8b36767c..11e55353 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -89,12 +89,14 @@ %% %% The duration indicated to us by the memory_monitor is used to %% calculate, given our current ingress and egress rates, how many -%% messages we should hold in RAM. When we need to push alphas to -%% betas or betas to gammas, we favour writing out messages that are -%% further from the head of the queue. This minimises writes to disk, -%% as the messages closer to the tail of the queue stay in the queue -%% for longer, thus do not need to be replaced as quickly by sending -%% other messages to disk. +%% messages we should hold in RAM. We track the ingress and egress +%% rates for both messages and pending acks and rates for both are +%% included when calculating the number of messages to hold in +%% RAM. When we need to push alphas to betas or betas to gammas, we +%% favour writing out messages that are further from the head of the +%% queue. This minimises writes to disk, as the messages closer to the +%% tail of the queue stay in the queue for longer, thus do not need to +%% be replaced as quickly by sending other messages to disk. %% %% Whilst messages are pushed to disk and forgotten from RAM as soon %% as requested by a new setting of the queue RAM duration, the -- cgit v1.2.1 From 8a7809f7d02db1354a8d1d335031c76592a5e468 Mon Sep 17 00:00:00 2001 From: Rob Harrop Date: Fri, 5 Nov 2010 09:18:05 +0000 Subject: Clarification of the comments in VQ --- src/rabbit_variable_queue.erl | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 11e55353..1b86320b 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -91,7 +91,7 @@ %% calculate, given our current ingress and egress rates, how many %% messages we should hold in RAM. We track the ingress and egress %% rates for both messages and pending acks and rates for both are -%% included when calculating the number of messages to hold in +%% considered when calculating the number of messages to hold in %% RAM. When we need to push alphas to betas or betas to gammas, we %% favour writing out messages that are further from the head of the %% queue. This minimises writes to disk, as the messages closer to the @@ -179,12 +179,13 @@ %% written to disk, acks are stored in message form to avoid the %% overhead of writing to disk. %% -%% During memory reduction, messages stored as transient ack records -%% are pushed out to disk before messages in the queue. More -%% precisely, messages from the queue will not be pushed out to disk -%% while the number of messages stored for acks is greater than -%% zero. Messages for acks are written to disk in batches of at most -%% ?IO_BATCH_SIZE. +%% During memory reduction, acks stored in message form are converted +%% to tuple form, and the corresponding messages are pushed out to +%% disk. Message form acks are always pushed to disk before messages +%% stored in the queue. More precisely, messages from the queue will +%% not be pushed out to disk while the number of messages form acks is +%% greater than zero. Message form acks are converted to tuple form in +%% batches of at most ?IO_BATCH_SIZE. %% %% Notes on Clean Shutdown %% (This documents behaviour in variable_queue, queue_index and -- cgit v1.2.1 From 4767dd7933fa5343d17bbeb1befc3fbbeb04298b Mon Sep 17 00:00:00 2001 From: Rob Harrop Date: Fri, 5 Nov 2010 09:30:37 +0000 Subject: That was quite special --- src/rabbit_variable_queue.erl | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 1b86320b..b3f58ef9 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -1381,20 +1381,6 @@ reduce_memory_use(AlphaBetaFun, BetaGammaFun, BetaDeltaFun, AckFun, State) -> end end, - {Reduce, State2} = case ReduceAck of - true -> - %% Don't want to reduce the number of - %% ram messages if we might yet be able - %% to reduce more acks. - {true, State1}; - false -> - case chunk_size( - State1 #vqstate.ram_msg_count, - State1 #vqstate.target_ram_msg_count) of - 0 -> {false, State1}; - S1 -> {true, AlphaBetaFun(S1, State1)} - end - end, case State2 #vqstate.target_ram_msg_count of infinity -> {Reduce, State2}; 0 -> {Reduce, BetaDeltaFun(State2)}; -- cgit v1.2.1 From 40f91556e3d9a673d4a1c34f9e46f98052b92b45 Mon Sep 17 00:00:00 2001 From: Rob Harrop Date: Fri, 5 Nov 2010 12:54:03 +0000 Subject: Reworked to test to be more predictable --- src/rabbit_tests.erl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index eecc27b3..4f543704 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -1879,7 +1879,7 @@ test_variable_queue_ack_limiting(VQ0) -> VQ2 = publish_fetch_and_ack(Churn, Len, VQ1), %% update stats for duration - {Duration, VQ3} = rabbit_variable_queue:ram_duration(VQ2), + {_Duration, VQ3} = rabbit_variable_queue:ram_duration(VQ2), %% fetch half the messages {VQ4, _AckTags} = variable_queue_fetch(Len div 2, false, false, Len, VQ3), @@ -1890,10 +1890,10 @@ test_variable_queue_ack_limiting(VQ0) -> %% quarter the allowed duration VQ6 = check_variable_queue_status( - rabbit_variable_queue:set_ram_duration_target(Duration / 4, VQ5), + rabbit_variable_queue:set_ram_duration_target(0, VQ5), [{len, Len div 2}, - {target_ram_msg_count, Len div 8}, - {ram_msg_count, Len div 8}, + {target_ram_msg_count, 0}, + {ram_msg_count, 0}, {ram_ack_count, 0}]), VQ6. -- cgit v1.2.1 From 3ed9259814c38bc0b65317ab306740dacdea7c21 Mon Sep 17 00:00:00 2001 From: Rob Harrop Date: Fri, 5 Nov 2010 14:59:21 +0000 Subject: Reworked ack shedding algorithm based on discussion with Matthew --- src/rabbit_variable_queue.erl | 125 ++++++++++++++++++++++++------------------ 1 file changed, 71 insertions(+), 54 deletions(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index b3f58ef9..9c25540e 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -181,11 +181,18 @@ %% %% During memory reduction, acks stored in message form are converted %% to tuple form, and the corresponding messages are pushed out to -%% disk. Message form acks are always pushed to disk before messages -%% stored in the queue. More precisely, messages from the queue will -%% not be pushed out to disk while the number of messages form acks is -%% greater than zero. Message form acks are converted to tuple form in -%% batches of at most ?IO_BATCH_SIZE. +%% disk. +%% +%% The order in which alphas are pushed to betas and message form acks +%% are pushed to disk is determined dynamically. We always prefer to +%% push messages for the source (alphas or acks) that is growing the +%% fastest (with growth measured as avg. ingress - avg. egress). In +%% each round of memory reduction a chunk of messages at most +%% ?IO_BATCH_SIZE in size is allocated to be pushed to disk. The +%% fastest growing source will be reduced by as much of this chunk as +%% possible. If there is any remaining allocation in the chunk after +%% the first source has been reduced to zero, the second source will +%% be reduced by as much of the remaining chunk as possible. %% %% Notes on Clean Shutdown %% (This documents behaviour in variable_queue, queue_index and @@ -779,10 +786,10 @@ ram_duration(State = #vqstate { ram_ack_count_prev = RamAckCount }}. needs_idle_timeout(State = #vqstate { on_sync = ?BLANK_SYNC }) -> - {Res, _State} = reduce_memory_use(fun (_Quota, State1) -> State1 end, + {Res, _State} = reduce_memory_use(fun (_Quota, State1) -> {0, State1} end, fun (_Quota, State1) -> State1 end, fun (State1) -> State1 end, - fun (_Quota, State1) -> State1 end, + fun (_Quota, State1) -> {0, State1} end, State), Res; needs_idle_timeout(_State) -> @@ -1365,58 +1372,68 @@ find_persistent_count(LensByStore) -> %% 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, BetaGammaFun, BetaDeltaFun, AckFun, State) -> - {Reduce, State2} = case reduce_ack_memory_use(AckFun, State) of - {true, State1} -> - %% Don't want to reduce the number of - %% ram messages if we might yet be able - %% to reduce more acks. - {true, State1}; - {false, State1} -> - case chunk_size( - State1 #vqstate.ram_msg_count, - State1 #vqstate.target_ram_msg_count) of - 0 -> {false, State1}; - S1 -> {true, AlphaBetaFun(S1, State1)} - end - end, - - case State2 #vqstate.target_ram_msg_count of - infinity -> {Reduce, State2}; - 0 -> {Reduce, BetaDeltaFun(State2)}; - _ -> case chunk_size(State2 #vqstate.ram_index_count, - permitted_ram_index_count(State2)) of - ?IO_BATCH_SIZE = S2 -> {true, BetaGammaFun(S2, State2)}; - _ -> {Reduce, State2} - end - end. - -reduce_ack_memory_use(_AckFun, - State = #vqstate { target_ram_msg_count = infinity }) -> +reduce_memory_use(_AlphaBetaFun, _BetaGammaFun, _BetaDeltaFun, _AckFun, + State = #vqstate {target_ram_msg_count = infinity}) -> {false, State}; -reduce_ack_memory_use(AckFun, - State = #vqstate { - target_ram_msg_count = TargetRamMsgCount, - ram_msg_count = RamMsgCount, - ram_ack_index = RamAckIndex} ) -> - PermittedAckCount = case TargetRamMsgCount > RamMsgCount of - true -> TargetRamMsgCount - RamMsgCount; - false -> 0 - end, - case chunk_size(gb_trees:size(RamAckIndex), PermittedAckCount) of - 0 -> {false, State}; - C -> {true, AckFun(C, State)} - end. +reduce_memory_use(AlphaBetaFun, BetaGammaFun, BetaDeltaFun, AckFun, + State = #vqstate { + ram_ack_index = RamAckIndex, + ram_msg_count = RamMsgCount, + target_ram_msg_count = TargetRamMsgCount, + rates = #rates { + avg_ingress = AvgIngress, + avg_egress = AvgEgress }, + ack_rates = #rates { + avg_ingress = AvgAckIngress, + avg_egress = AvgAckEgress } }) -> + + {Reduce, State1} = + case chunk_size(RamMsgCount + gb_trees:size(RamAckIndex), + TargetRamMsgCount) of + 0 -> + {false, State}; + S1 -> + ReduceFuns = + case (AvgAckIngress - AvgAckEgress) > + (AvgIngress - AvgEgress) of + true -> + %% ACKs are growing faster than the queue, + %% push messages from there first. + [AckFun, AlphaBetaFun]; + false -> + %% The queue is growing faster than the + %% acks, push queue messages first. + [AlphaBetaFun, AckFun] + end, + {_, StateOut} = + %% Both reduce functions get a chance to reduce + %% memory. The second may very well get a quota of + %% 0 if the first function managed to push out the + %% maximum number of messages. + lists:foldl( + fun(ReduceFun, {QuotaN, StateN}) -> + ReduceFun(QuotaN, StateN) + end, {S1, State}, ReduceFuns), + {true, StateOut} + end, + case State1 #vqstate.target_ram_msg_count of + 0 -> {Reduce, BetaDeltaFun(State1)}; + _ -> case chunk_size(State1 #vqstate.ram_index_count, + permitted_ram_index_count(State1)) of + ?IO_BATCH_SIZE = S2 -> {true, BetaGammaFun(S2, State1)}; + _ -> {Reduce, State1} + end + end. limit_ram_acks(0, State) -> - State; + {0, State}; limit_ram_acks(Quota, State = #vqstate { pending_ack = PA, ram_ack_index = RAI }) -> case gb_trees:is_empty(RAI) of true -> - State; + {Quota, State}; false -> {SeqId, Guid, RAI1} = gb_trees:take_largest(RAI), MsgStatus = #msg_status { @@ -1480,7 +1497,7 @@ permitted_ram_index_count(#vqstate { len = Len, BetaLen - trunc(BetaLen * BetaLen / (Len - DeltaCount)). chunk_size(Current, Permitted) - when Permitted =:= infinity orelse Permitted >= Current -> + when Permitted >= Current -> 0; chunk_size(Current, Permitted) -> lists:min([Current - Permitted, ?IO_BATCH_SIZE]). @@ -1568,9 +1585,9 @@ maybe_deltas_to_betas(State = #vqstate { end. push_alphas_to_betas(Quota, State) -> - { Quota1, State1} = maybe_push_q1_to_betas(Quota, State), - {_Quota2, State2} = maybe_push_q4_to_betas(Quota1, State1), - State2. + {Quota1, State1} = maybe_push_q1_to_betas(Quota, State), + {Quota2, State2} = maybe_push_q4_to_betas(Quota1, State1), + {Quota2, State2}. maybe_push_q1_to_betas(Quota, State = #vqstate { q1 = Q1 }) -> maybe_push_alphas_to_betas( -- cgit v1.2.1 From f4b9b0114a467b8edab326be43212a857b1dc05d Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Mon, 8 Nov 2010 11:27:24 +0000 Subject: confirm messages on queue purge --- src/rabbit_msg_store.erl | 46 +++++++++++++++++++++++++++++++++---------- src/rabbit_queue_index.erl | 4 ++-- src/rabbit_variable_queue.erl | 6 ++---- 3 files changed, 40 insertions(+), 16 deletions(-) diff --git a/src/rabbit_msg_store.erl b/src/rabbit_msg_store.erl index d04f679f..e84e3d8f 100644 --- a/src/rabbit_msg_store.erl +++ b/src/rabbit_msg_store.erl @@ -144,7 +144,7 @@ -type(startup_fun_state() :: {(fun ((A) -> 'finished' | {rabbit_guid:guid(), non_neg_integer(), A})), A}). --type(guid_fun() :: fun (([rabbit_guid:guid()]) -> any())). +-type(guid_fun() :: fun ((gb_set()) -> any())). -spec(start_link/4 :: (atom(), file:filename(), [binary()] | 'undefined', @@ -397,7 +397,8 @@ read(Guid, contains(Guid, CState) -> server_call(CState, {contains, Guid}). remove([], _CState) -> ok; -remove(Guids, CState) -> server_cast(CState, {remove, Guids}). +remove(Guids, CState = #client_msstate { client_ref = CRef }) -> + server_cast(CState, {remove, CRef, Guids}). release([], _CState) -> ok; release(Guids, CState) -> server_cast(CState, {release, Guids}). sync(Guids, K, CState) -> server_cast(CState, {sync, Guids, K}). @@ -696,7 +697,10 @@ handle_cast({write, CRef, Guid}, true = 0 =< ets:update_counter(CurFileCacheEts, Guid, {3, -1}), [{Guid, Msg, _CacheRefCount}] = ets:lookup(CurFileCacheEts, Guid), CTG1 = case dict:find(CRef, CODC) of - {ok, _} -> rabbit_misc:dict_cons(CRef, Guid, CTG); + {ok, _} -> dict:update(CRef, fun(Guids) -> + gb_sets:add(Guid, Guids) + end, + gb_sets:empty(), CTG); error -> CTG end, State1 = State #msstate { cref_to_guids = CTG1 }, @@ -722,17 +726,18 @@ handle_cast({write, CRef, Guid}, ok = index_update_ref_count(Guid, RefCount + 1, State1), CTG2 = case {dict:find(CRef, CODC), File} of {{ok, _}, CurFile} -> CTG1; - {{ok, Fun}, _} -> Fun([Guid]), CTG; + {{ok, Fun}, _} -> Fun(gb_sets:singleton(Guid)), CTG; _ -> CTG1 end, noreply(State #msstate { cref_to_guids = CTG2 }) end; -handle_cast({remove, Guids}, State) -> +handle_cast({remove, CRef, Guids}, State) -> State1 = lists:foldl( fun (Guid, State2) -> remove_message(Guid, State2) end, State, Guids), - noreply(maybe_compact(State1)); + State2 = client_confirm(CRef, gb_sets:from_list(Guids), State1), + noreply(maybe_compact(State2)); handle_cast({release, Guids}, State = #msstate { dedup_cache_ets = DedupCacheEts }) -> @@ -856,17 +861,19 @@ stop_sync_timer(State = #msstate { sync_timer_ref = TRef }) -> internal_sync(State = #msstate { current_file_handle = CurHdl, on_sync = Syncs, - client_ondisk_callback = CODC, cref_to_guids = CTG }) -> State1 = stop_sync_timer(State), - CGs = dict:fold(fun (_CRef, [], NS) -> NS; - (CRef, Guids, NS) -> [{CRef, Guids} | NS] + CGs = dict:fold(fun (CRef, Guids, NS) -> + case gb_sets:is_empty(Guids) of + true -> NS; + false -> [{CRef, Guids} | NS] + end end, [], CTG), if Syncs =:= [] andalso CGs =:= [] -> ok; true -> file_handle_cache:sync(CurHdl) end, lists:foreach(fun (K) -> K() end, lists:reverse(Syncs)), - [(dict:fetch(CRef, CODC))(Guids) || {CRef, Guids} <- CGs], + [client_confirm(CRef, Guids, State1) || {CRef, Guids} <- CGs], State1 #msstate { cref_to_guids = dict:new(), on_sync = [] }. @@ -1046,6 +1053,25 @@ orddict_store(Key, Val, Dict) -> false = orddict:is_key(Key, Dict), orddict:store(Key, Val, Dict). +client_confirm(CRef, Guids, + State = #msstate { client_ondisk_callback = CODC, + cref_to_guids = CTG }) -> + case dict:find(CRef, CODC) of + {ok, Fun} -> Fun(Guids), + CTG1 = case dict:find(CRef, CTG) of + {ok, Gs} -> + Guids1 = gb_sets:difference(Gs, Guids), + case gb_sets:is_empty(Guids1) of + true -> dict:erase(CRef, CTG); + false -> dict:store(CRef, Guids1, CTG) + end; + error -> CTG + end, + State #msstate { cref_to_guids = CTG1 }; + error -> State + end. + + %%---------------------------------------------------------------------------- %% file helper functions %%---------------------------------------------------------------------------- diff --git a/src/rabbit_queue_index.erl b/src/rabbit_queue_index.erl index 8a4bb801..f61707cc 100644 --- a/src/rabbit_queue_index.erl +++ b/src/rabbit_queue_index.erl @@ -191,7 +191,7 @@ })). -type(seq_id() :: integer()). -type(seg_dict() :: {dict:dictionary(), [segment()]}). --type(on_sync_fun() :: fun (([rabbit_guid:guid()]) -> ok)). +-type(on_sync_fun() :: fun ((gb_set()) -> ok)). -type(qistate() :: #qistate { dir :: file:filename(), segments :: 'undefined' | seg_dict(), journal_handle :: hdl(), @@ -717,7 +717,7 @@ deliver_or_ack(Kind, SeqIds, State) -> end, State1, SeqIds)). notify_sync(State = #qistate { unsynced_guids = UG, on_sync = OnSyncFun }) -> - OnSyncFun(UG), + OnSyncFun(gb_sets:from_list(UG)), State #qistate { unsynced_guids = [] }. %%---------------------------------------------------------------------------- diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 8463d3d5..f065e5ea 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -1312,12 +1312,11 @@ remove_confirms(GuidSet, State = #vqstate { msgs_on_disk = MOD, msgs_confirmed(GuidSet, State) -> {remove_confirms(GuidSet, State), {confirm, gb_sets:to_list(GuidSet)}}. -msgs_written_to_disk(QPid, Guids) -> +msgs_written_to_disk(QPid, GuidSet) -> rabbit_amqqueue:maybe_run_queue_via_backing_queue_async( QPid, fun(State = #vqstate { msgs_on_disk = MOD, msg_indices_on_disk = MIOD, unconfirmed = UC }) -> - GuidSet = gb_sets:from_list(Guids), msgs_confirmed(gb_sets:intersection(GuidSet, MIOD), State #vqstate { msgs_on_disk = @@ -1325,12 +1324,11 @@ msgs_written_to_disk(QPid, Guids) -> gb_sets:union(MOD, GuidSet), UC) }) end). -msg_indices_written_to_disk(QPid, Guids) -> +msg_indices_written_to_disk(QPid, GuidSet) -> rabbit_amqqueue:maybe_run_queue_via_backing_queue_async( QPid, fun(State = #vqstate { msgs_on_disk = MOD, msg_indices_on_disk = MIOD, unconfirmed = UC }) -> - GuidSet = gb_sets:from_list(Guids), msgs_confirmed(gb_sets:intersection(GuidSet, MOD), State #vqstate { msg_indices_on_disk = -- cgit v1.2.1 From a63c7bec40f7dfa0532bd25c092d2052e0352a17 Mon Sep 17 00:00:00 2001 From: John DeTreville Date: Wed, 10 Nov 2010 18:42:09 +0000 Subject: Removed rabbit_misc:intersperse; changed clients to call string:join instead; hope that's okay. --- src/rabbit_control.erl | 6 ++---- src/rabbit_misc.erl | 7 +------ src/rabbit_multi.erl | 3 +-- src/rabbit_ssl.erl | 9 +++------ 4 files changed, 7 insertions(+), 18 deletions(-) diff --git a/src/rabbit_control.erl b/src/rabbit_control.erl index 6b212745..277faeda 100644 --- a/src/rabbit_control.erl +++ b/src/rabbit_control.erl @@ -94,9 +94,7 @@ start() -> halt(); {'EXIT', {function_clause, [{?MODULE, action, _} | _]}} -> print_error("invalid command '~s'", - [lists:flatten( - rabbit_misc:intersperse( - " ", [atom_to_list(Command) | Args]))]), + [string:join([atom_to_list(Command) | Args], " ")]), usage(); {error, Reason} -> print_error("~p", [Reason]), @@ -321,7 +319,7 @@ display_info_list(Other, _) -> Other. display_row(Row) -> - io:fwrite(lists:flatten(rabbit_misc:intersperse("\t", Row))), + io:fwrite(string:join(Row, "\t")), io:nl(). -define(IS_U8(X), (X >= 0 andalso X =< 255)). diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index e5c30c06..7451116d 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -50,7 +50,7 @@ -export([execute_mnesia_transaction/1]). -export([ensure_ok/2]). -export([makenode/1, nodeparts/1, cookie_hash/0, tcp_name/3]). --export([intersperse/2, upmap/2, map_in_order/2]). +-export([upmap/2, map_in_order/2]). -export([table_fold/3]). -export([dirty_read_all/1, dirty_foreach_key/2, dirty_dump_log/1]). -export([read_term_file/1, write_term_file/2]). @@ -148,7 +148,6 @@ -spec(tcp_name/3 :: (atom(), inet:ip_address(), rabbit_networking:ip_port()) -> atom()). --spec(intersperse/2 :: (A, [A]) -> [A]). -spec(upmap/2 :: (fun ((A) -> B), [A]) -> [B]). -spec(map_in_order/2 :: (fun ((A) -> B), [A]) -> [B]). -spec(table_fold/3 :: (fun ((any(), A) -> A), A, atom()) -> A). @@ -404,10 +403,6 @@ tcp_name(Prefix, IPAddress, Port) io_lib:format("~w_~s:~w", [Prefix, inet_parse:ntoa(IPAddress), Port]))). -intersperse(_, []) -> []; -intersperse(_, [E]) -> [E]; -intersperse(Sep, [E|T]) -> [E, Sep | intersperse(Sep, T)]. - %% This is a modified version of Luke Gorrie's pmap - %% http://lukego.livejournal.com/6753.html - that doesn't care about %% the order in which results are received. diff --git a/src/rabbit_multi.erl b/src/rabbit_multi.erl index 0440dbe4..0030216e 100644 --- a/src/rabbit_multi.erl +++ b/src/rabbit_multi.erl @@ -65,8 +65,7 @@ start() -> halt(); {'EXIT', {function_clause, [{?MODULE, action, _} | _]}} -> print_error("invalid command '~s'", - [lists:flatten( - rabbit_misc:intersperse(" ", FullCommand))]), + [string:join(FullCommand, " ")]), usage(); timeout -> print_error("timeout starting some nodes.", []), diff --git a/src/rabbit_ssl.erl b/src/rabbit_ssl.erl index 4335dd2e..ce218b7b 100644 --- a/src/rabbit_ssl.erl +++ b/src/rabbit_ssl.erl @@ -95,14 +95,11 @@ cert_info(F, Cert) -> %% Format and rdnSequence as a RFC4514 subject string. format_rdn_sequence({rdnSequence, Seq}) -> - lists:flatten( - rabbit_misc:intersperse( - ",", lists:reverse([format_complex_rdn(RDN) || RDN <- Seq]))). + string:join(lists:reverse([format_complex_rdn(RDN) || RDN <- Seq]), ","). %% Format an RDN set. format_complex_rdn(RDNs) -> - lists:flatten( - rabbit_misc:intersperse("+", [format_rdn(RDN) || RDN <- RDNs])). + string:join([format_rdn(RDN) || RDN <- RDNs], "+"). %% Format an RDN. If the type name is unknown, use the dotted decimal %% representation. See RFC4514, section 2.3. @@ -129,7 +126,7 @@ format_rdn(#'AttributeTypeAndValue'{type = T, value = V}) -> io_lib:format(Fmt ++ "=~s", [FV]); none when is_tuple(T) -> TypeL = [io_lib:format("~w", [X]) || X <- tuple_to_list(T)], - io_lib:format("~s:~s", [rabbit_misc:intersperse(".", TypeL), FV]); + io_lib:format("~s:~s", [string:join(TypeL, "."), FV]); none -> io_lib:format("~p:~s", [T, FV]) end. -- cgit v1.2.1 From 4563c988b0d6c218499435074fd02f1d7e128f11 Mon Sep 17 00:00:00 2001 From: John DeTreville Date: Thu, 11 Nov 2010 11:34:38 +0000 Subject: Added some extra polymorphism inside rabbit_ssl.erl. --- src/rabbit_ssl.erl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/rabbit_ssl.erl b/src/rabbit_ssl.erl index ce218b7b..ff79d433 100644 --- a/src/rabbit_ssl.erl +++ b/src/rabbit_ssl.erl @@ -99,7 +99,7 @@ format_rdn_sequence({rdnSequence, Seq}) -> %% Format an RDN set. format_complex_rdn(RDNs) -> - string:join([format_rdn(RDN) || RDN <- RDNs], "+"). + string:join([lists:flatten(format_rdn(RDN)) || RDN <- RDNs], "+"). %% Format an RDN. If the type name is unknown, use the dotted decimal %% representation. See RFC4514, section 2.3. @@ -126,7 +126,8 @@ format_rdn(#'AttributeTypeAndValue'{type = T, value = V}) -> io_lib:format(Fmt ++ "=~s", [FV]); none when is_tuple(T) -> TypeL = [io_lib:format("~w", [X]) || X <- tuple_to_list(T)], - io_lib:format("~s:~s", [string:join(TypeL, "."), FV]); + io_lib:format("~s:~s", + [string:join(lists:flatten(TypeL), "."), FV]); none -> io_lib:format("~p:~s", [T, FV]) end. -- cgit v1.2.1 From c2206e2db98730e6102b5d774e3af3dc8c743b3f Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Thu, 11 Nov 2010 12:02:44 +0000 Subject: don't confirm messages on requeue --- src/rabbit_variable_queue.erl | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 54cda76f..9b3cc58a 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -621,9 +621,13 @@ internal_fetch(AckRequired, MsgStatus = #msg_status { pending_ack = PA1 })}. ack(AckTags, State) -> - {Guids, State1} = ack(fun msg_store_remove/3, - fun (_AckEntry, State1) -> State1 end, - AckTags, State), + {Guids, State1} = + ack(fun msg_store_remove/3, + fun ({_IsPersistent, Guid, _MsgProps}, State1) -> + remove_confirms(gb_sets:singleton(Guid), State1), + State1 + end, + AckTags, State), {Guids, a(State1)}. tx_publish(Txn, Msg = #basic_message { is_persistent = IsPersistent }, MsgProps, @@ -832,7 +836,8 @@ cons_if(true, E, L) -> [E | L]; cons_if(false, _E, L) -> L. gb_sets_maybe_insert(false, _Val, Set) -> Set; -gb_sets_maybe_insert(true, Val, Set) -> gb_sets:insert(Val, Set). +%% when requeueing, we re-add a guid to the unconfimred set +gb_sets_maybe_insert(true, Val, Set) -> gb_sets:add(Val, Set). msg_status(IsPersistent, SeqId, Msg = #basic_message { guid = Guid }, MsgProps) -> @@ -1271,10 +1276,9 @@ ack(MsgStoreFun, Fun, AckTags, State) -> MsgStoreFun(MSCState, IsPersistent, Guids), [Guids | Gs] end, [], GuidsByStore)), - State2 = remove_confirms(gb_sets:from_list(AckdGuids), State1), PCount1 = PCount - find_persistent_count(sum_guids_by_store_to_len( orddict:new(), GuidsByStore)), - {AckdGuids, State2 #vqstate { index_state = IndexState1, + {AckdGuids, State1 #vqstate { index_state = IndexState1, persistent_count = PCount1 }}. accumulate_ack(_SeqId, #msg_status { is_persistent = false, %% ASSERTIONS -- cgit v1.2.1 From acb016fa9b238856e32d48be6833af8867518c3f Mon Sep 17 00:00:00 2001 From: Marek Majkowski Date: Thu, 11 Nov 2010 12:20:50 +0000 Subject: not_allowed -> precondition_failed --- src/rabbit_amqqueue.erl | 2 +- src/rabbit_exchange.erl | 2 +- src/rabbit_misc.erl | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 5cdd0e3c..fa417544 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -279,7 +279,7 @@ assert_equivalence(#amqqueue{durable = Durable, assert_equivalence(#amqqueue{name = QueueName}, _Durable, _AutoDelete, _RequiredArgs, _Owner) -> rabbit_misc:protocol_error( - not_allowed, "parameters for ~s not equivalent", + precondition_failed, "parameters for ~s not equivalent", [rabbit_misc:rs(QueueName)]). check_exclusive_access(Q, Owner) -> check_exclusive_access(Q, Owner, lax). diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index 61a24388..8a28241f 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -176,7 +176,7 @@ assert_equivalence(X = #exchange{ durable = Durable, assert_equivalence(#exchange{ name = Name }, _Type, _Durable, _AutoDelete, _Args) -> rabbit_misc:protocol_error( - not_allowed, + precondition_failed, "cannot redeclare ~s with different type, durable or autodelete value", [rabbit_misc:rs(Name)]). diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index d5c8bd49..eafc7a70 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -229,7 +229,7 @@ assert_args_equivalence1(Orig, New, Name, Key) -> case {table_lookup(Orig, Key), table_lookup(New, Key)} of {Same, Same} -> ok; {Orig1, New1} -> protocol_error( - not_allowed, + precondition_failed, "inequivalent arg '~s' for ~s: " "required ~w, received ~w", [Key, rabbit_misc:rs(Name), New1, Orig1]) -- cgit v1.2.1 From b4ce991122d79a1b25de0f5bdd3f50af6fa0b032 Mon Sep 17 00:00:00 2001 From: Rob Harrop Date: Thu, 11 Nov 2010 18:44:41 +0000 Subject: Added assertion in limit_ram_acks for is_persistent=false --- src/rabbit_variable_queue.erl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 9c25540e..6f8fd3bc 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -1437,8 +1437,9 @@ limit_ram_acks(Quota, State = #vqstate { pending_ack = PA, false -> {SeqId, Guid, RAI1} = gb_trees:take_largest(RAI), MsgStatus = #msg_status { - guid = Guid, %% ASSERTION - msg_props = MsgProps } = dict:fetch(SeqId, PA), + guid = Guid, %% ASSERTION + is_persistent = false, %% ASSERTION + msg_props = MsgProps } = dict:fetch(SeqId, PA), {_, State1} = maybe_write_to_disk(true, false, MsgStatus, State), limit_ram_acks(Quota - 1, State1 #vqstate { -- cgit v1.2.1 From 89f14ce3f48e8c47cc2407bfb0c50d8d784d69fe Mon Sep 17 00:00:00 2001 From: Rob Harrop Date: Thu, 11 Nov 2010 18:45:22 +0000 Subject: Reinstated the infinity check for chunk_size --- 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 6f8fd3bc..4b57779b 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -1498,7 +1498,7 @@ permitted_ram_index_count(#vqstate { len = Len, BetaLen - trunc(BetaLen * BetaLen / (Len - DeltaCount)). chunk_size(Current, Permitted) - when Permitted >= Current -> + when Permitted =:= infinity orelse Permitted >= Current -> 0; chunk_size(Current, Permitted) -> lists:min([Current - Permitted, ?IO_BATCH_SIZE]). -- cgit v1.2.1 From 2885ee4b3f2c8b12415aa0a3808b04ba7c410576 Mon Sep 17 00:00:00 2001 From: Rob Harrop Date: Thu, 11 Nov 2010 18:46:15 +0000 Subject: Removed stray references to target_ram_ack_count --- src/rabbit_variable_queue.erl | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 4b57779b..cbeb9a75 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -258,7 +258,6 @@ persistent_count, target_ram_msg_count, - target_ram_ack_count, ram_msg_count, ram_msg_count_prev, ram_ack_count_prev, @@ -351,7 +350,6 @@ transient_threshold :: non_neg_integer(), target_ram_msg_count :: non_neg_integer() | 'infinity', - target_ram_ack_count :: non_neg_integer() | 'infinity', ram_msg_count :: non_neg_integer(), ram_msg_count_prev :: non_neg_integer(), ram_index_count :: non_neg_integer(), -- cgit v1.2.1 From 77b9aa447953dbb760c8e0bc766b089a5bdc9e79 Mon Sep 17 00:00:00 2001 From: Rob Harrop Date: Thu, 11 Nov 2010 19:33:43 +0000 Subject: Rename target_ram_msg_count to target_ram_item_count --- src/rabbit_tests.erl | 2 +- src/rabbit_variable_queue.erl | 225 +++++++++++++++++++++--------------------- 2 files changed, 114 insertions(+), 113 deletions(-) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 4f543704..3130bca3 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -1892,7 +1892,7 @@ test_variable_queue_ack_limiting(VQ0) -> VQ6 = check_variable_queue_status( rabbit_variable_queue:set_ram_duration_target(0, VQ5), [{len, Len div 2}, - {target_ram_msg_count, 0}, + {target_ram_item_count, 0}, {ram_msg_count, 0}, {ram_ack_count, 0}]), diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index cbeb9a75..833fc789 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -158,7 +158,7 @@ %% The conversion from alphas to betas is also chunked, but only to %% ensure no more than ?IO_BATCH_SIZE alphas are converted to betas at %% any one time. This further smooths the effects of changes to the -%% target_ram_msg_count and ensures the queue remains responsive +%% target_ram_item_count and ensures the queue remains responsive %% even when there is a large amount of IO work to do. The %% idle_timeout callback is utilised to ensure that conversions are %% done as promptly as possible whilst ensuring the queue remains @@ -257,7 +257,7 @@ len, persistent_count, - target_ram_msg_count, + target_ram_item_count, ram_msg_count, ram_msg_count_prev, ram_ack_count_prev, @@ -331,34 +331,34 @@ funs :: [fun (() -> any())] }). -type(state() :: #vqstate { - q1 :: queue(), - q2 :: bpqueue:bpqueue(), - delta :: delta(), - q3 :: bpqueue:bpqueue(), - q4 :: queue(), - next_seq_id :: seq_id(), - pending_ack :: dict:dictionary(), - ram_ack_index :: gb_tree(), - index_state :: any(), - msg_store_clients :: 'undefined' | {{any(), binary()}, + q1 :: queue(), + q2 :: bpqueue:bpqueue(), + delta :: delta(), + q3 :: bpqueue:bpqueue(), + q4 :: queue(), + next_seq_id :: seq_id(), + pending_ack :: dict:dictionary(), + ram_ack_index :: gb_tree(), + index_state :: any(), + msg_store_clients :: 'undefined' | {{any(), binary()}, {any(), binary()}}, - on_sync :: sync(), - durable :: boolean(), - - len :: non_neg_integer(), - persistent_count :: non_neg_integer(), - - transient_threshold :: non_neg_integer(), - target_ram_msg_count :: non_neg_integer() | 'infinity', - ram_msg_count :: non_neg_integer(), - ram_msg_count_prev :: non_neg_integer(), - ram_index_count :: non_neg_integer(), - out_counter :: non_neg_integer(), - in_counter :: non_neg_integer(), - ack_out_counter :: non_neg_integer(), - ack_in_counter :: non_neg_integer(), - rates :: rates(), - ack_rates :: rates() }). + on_sync :: sync(), + durable :: boolean(), + + len :: non_neg_integer(), + persistent_count :: non_neg_integer(), + + transient_threshold :: non_neg_integer(), + target_ram_item_count :: non_neg_integer() | 'infinity', + ram_msg_count :: non_neg_integer(), + ram_msg_count_prev :: non_neg_integer(), + ram_index_count :: non_neg_integer(), + out_counter :: non_neg_integer(), + in_counter :: non_neg_integer(), + ack_out_counter :: non_neg_integer(), + ack_in_counter :: non_neg_integer(), + rates :: rates(), + ack_rates :: rates() }). -include("rabbit_backing_queue_spec.hrl"). @@ -715,18 +715,18 @@ set_ram_duration_target(DurationTarget, ack_rates = #rates { avg_egress = AvgAckEgressRate, avg_ingress = AvgAckIngressRate }, - target_ram_msg_count = TargetRamMsgCount }) -> + target_ram_item_count = TargetRamItemCount }) -> Rate = AvgEgressRate + AvgIngressRate + AvgAckEgressRate + AvgAckIngressRate, - TargetRamMsgCount1 = + TargetRamItemCount1 = case DurationTarget of infinity -> infinity; _ -> trunc(DurationTarget * Rate) %% msgs = sec * msgs/sec end, - State1 = State #vqstate { target_ram_msg_count = TargetRamMsgCount1 }, - a(case TargetRamMsgCount1 == infinity orelse - (TargetRamMsgCount =/= infinity andalso - TargetRamMsgCount1 >= TargetRamMsgCount) of + State1 = State #vqstate { target_ram_item_count = TargetRamItemCount1 }, + a(case TargetRamItemCount1 == infinity orelse + (TargetRamItemCount =/= infinity andalso + TargetRamItemCount1 >= TargetRamItemCount) of true -> State1; false -> reduce_memory_use(State1) end). @@ -799,39 +799,39 @@ handle_pre_hibernate(State = #vqstate { index_state = IndexState }) -> State #vqstate { index_state = rabbit_queue_index:flush(IndexState) }. status(#vqstate { q1 = Q1, q2 = Q2, delta = Delta, q3 = Q3, q4 = Q4, - len = Len, - pending_ack = PA, - ram_ack_index = RAI, - on_sync = #sync { funs = From }, - target_ram_msg_count = TargetRamMsgCount, - ram_msg_count = RamMsgCount, - ram_index_count = RamIndexCount, - next_seq_id = NextSeqId, - persistent_count = PersistentCount, - rates = #rates { + len = Len, + pending_ack = PA, + ram_ack_index = RAI, + on_sync = #sync { funs = From }, + target_ram_item_count = TargetRamItemCount, + ram_msg_count = RamMsgCount, + ram_index_count = RamIndexCount, + next_seq_id = NextSeqId, + persistent_count = PersistentCount, + rates = #rates { avg_egress = AvgEgressRate, avg_ingress = AvgIngressRate }, - ack_rates = #rates { + ack_rates = #rates { avg_egress = AvgAckEgressRate, avg_ingress = AvgAckIngressRate } }) -> - [ {q1 , queue:len(Q1)}, - {q2 , bpqueue:len(Q2)}, - {delta , Delta}, - {q3 , bpqueue:len(Q3)}, - {q4 , queue:len(Q4)}, - {len , Len}, - {pending_acks , dict:size(PA)}, - {ram_ack_count , gb_trees:size(RAI)}, - {outstanding_txns , length(From)}, - {target_ram_msg_count , TargetRamMsgCount}, - {ram_msg_count , RamMsgCount}, - {ram_index_count , RamIndexCount}, - {next_seq_id , NextSeqId}, - {persistent_count , PersistentCount}, - {avg_egress_rate , AvgEgressRate}, - {avg_ingress_rate , AvgIngressRate}, - {avg_ack_egress_rate , AvgAckEgressRate}, - {avg_ack_ingress_rate , AvgAckIngressRate}]. + [ {q1 , queue:len(Q1)}, + {q2 , bpqueue:len(Q2)}, + {delta , Delta}, + {q3 , bpqueue:len(Q3)}, + {q4 , queue:len(Q4)}, + {len , Len}, + {pending_acks , dict:size(PA)}, + {ram_ack_count , gb_trees:size(RAI)}, + {outstanding_txns , length(From)}, + {target_ram_item_count , TargetRamItemCount}, + {ram_msg_count , RamMsgCount}, + {ram_index_count , RamIndexCount}, + {next_seq_id , NextSeqId}, + {persistent_count , PersistentCount}, + {avg_egress_rate , AvgEgressRate}, + {avg_ingress_rate , AvgIngressRate}, + {avg_ack_egress_rate , AvgAckEgressRate}, + {avg_ack_ingress_rate , AvgAckIngressRate}]. %%---------------------------------------------------------------------------- %% Minor helpers @@ -1021,42 +1021,42 @@ init(IsDurable, IndexState, DeltaCount, Terms, end, Now = now(), State = #vqstate { - q1 = queue:new(), - q2 = bpqueue:new(), - delta = Delta, - q3 = bpqueue:new(), - q4 = queue:new(), - next_seq_id = NextSeqId, - pending_ack = dict:new(), - ram_ack_index = gb_trees:empty(), - index_state = IndexState1, - msg_store_clients = {PersistentClient, TransientClient}, - on_sync = ?BLANK_SYNC, - durable = IsDurable, - transient_threshold = NextSeqId, - - len = DeltaCount1, - persistent_count = DeltaCount1, - - target_ram_msg_count = infinity, - ram_msg_count = 0, - ram_msg_count_prev = 0, - ram_ack_count_prev = 0, - ram_index_count = 0, - out_counter = 0, - in_counter = 0, - ack_out_counter = 0, - ack_in_counter = 0, - rates = #rates { egress = {Now, 0}, - ingress = {Now, DeltaCount1}, - avg_egress = 0.0, - avg_ingress = 0.0, - timestamp = Now }, - ack_rates = #rates { egress = {Now, 0}, - ingress = {Now, 0}, - avg_egress = 0.0, - avg_ingress = 0.0, - timestamp = undefined } }, + q1 = queue:new(), + q2 = bpqueue:new(), + delta = Delta, + q3 = bpqueue:new(), + q4 = queue:new(), + next_seq_id = NextSeqId, + pending_ack = dict:new(), + ram_ack_index = gb_trees:empty(), + index_state = IndexState1, + msg_store_clients = {PersistentClient, TransientClient}, + on_sync = ?BLANK_SYNC, + durable = IsDurable, + transient_threshold = NextSeqId, + + len = DeltaCount1, + persistent_count = DeltaCount1, + + target_ram_item_count = infinity, + ram_msg_count = 0, + ram_msg_count_prev = 0, + ram_ack_count_prev = 0, + ram_index_count = 0, + out_counter = 0, + in_counter = 0, + ack_out_counter = 0, + ack_in_counter = 0, + rates = #rates { egress = {Now, 0}, + ingress = {Now, DeltaCount1}, + avg_egress = 0.0, + avg_ingress = 0.0, + timestamp = Now }, + ack_rates = #rates { egress = {Now, 0}, + ingress = {Now, 0}, + avg_egress = 0.0, + avg_ingress = 0.0, + timestamp = undefined } }, a(maybe_deltas_to_betas(State)). msg_store_callback(PersistentGuids, Pubs, AckTags, Fun, MsgPropsFun) -> @@ -1360,7 +1360,7 @@ find_persistent_count(LensByStore) -> %% though the conversion function for that is called as necessary. The %% reason is twofold. Firstly, this is safe because the conversion is %% only ever necessary just after a transition to a -%% target_ram_msg_count of zero or after an incremental alpha->beta +%% target_ram_item_count of zero or after an incremental alpha->beta %% conversion. In the former case the conversion is performed straight %% away (i.e. any betas present at the time are converted to deltas), %% and in the latter case the need for a conversion is flagged up @@ -1371,23 +1371,23 @@ find_persistent_count(LensByStore) -> %% 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, _BetaGammaFun, _BetaDeltaFun, _AckFun, - State = #vqstate {target_ram_msg_count = infinity}) -> + State = #vqstate {target_ram_item_count = infinity}) -> {false, State}; reduce_memory_use(AlphaBetaFun, BetaGammaFun, BetaDeltaFun, AckFun, State = #vqstate { - ram_ack_index = RamAckIndex, - ram_msg_count = RamMsgCount, - target_ram_msg_count = TargetRamMsgCount, - rates = #rates { + ram_ack_index = RamAckIndex, + ram_msg_count = RamMsgCount, + target_ram_item_count = TargetRamItemCount, + rates = #rates { avg_ingress = AvgIngress, avg_egress = AvgEgress }, - ack_rates = #rates { + ack_rates = #rates { avg_ingress = AvgAckIngress, avg_egress = AvgAckEgress } }) -> {Reduce, State1} = case chunk_size(RamMsgCount + gb_trees:size(RamAckIndex), - TargetRamMsgCount) of + TargetRamItemCount) of 0 -> {false, State}; S1 -> @@ -1415,7 +1415,7 @@ reduce_memory_use(AlphaBetaFun, BetaGammaFun, BetaDeltaFun, AckFun, {true, StateOut} end, - case State1 #vqstate.target_ram_msg_count of + case State1 #vqstate.target_ram_item_count of 0 -> {Reduce, BetaDeltaFun(State1)}; _ -> case chunk_size(State1 #vqstate.ram_index_count, permitted_ram_index_count(State1)) of @@ -1612,10 +1612,11 @@ maybe_push_q4_to_betas(Quota, State = #vqstate { q4 = Q4 }) -> maybe_push_alphas_to_betas(_Generator, _Consumer, Quota, _Q, State = #vqstate { - ram_msg_count = RamMsgCount, - target_ram_msg_count = TargetRamMsgCount }) + ram_msg_count = RamMsgCount, + target_ram_item_count = TargetRamItemCount }) when Quota =:= 0 orelse - TargetRamMsgCount =:= infinity orelse TargetRamMsgCount >= RamMsgCount -> + TargetRamItemCount =:= infinity orelse + TargetRamItemCount >= RamMsgCount -> {Quota, State}; maybe_push_alphas_to_betas(Generator, Consumer, Quota, Q, State) -> case Generator(Q) of -- cgit v1.2.1 From 0e57e76c7c66cee842655b005b0745534cc43cc6 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 11 Nov 2010 20:50:47 +0000 Subject: exit->throw, *_or_exit -> ensure_*, and some cosmetics --- src/rabbit_mnesia.erl | 35 +++++++++++++++-------------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 9d172269..95699c76 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -378,24 +378,22 @@ init_db(ClusterNodes, Force) -> %% True single disc node, attempt upgrade wait_for_tables(), case rabbit_upgrade:maybe_upgrade() of - ok -> - schema_ok_or_exit(); - version_not_available -> - schema_ok_or_move() + ok -> ensure_schema_ok(); + version_not_available -> schema_ok_or_move() end; {[], true, _} -> %% "Master" (i.e. without config) disc node in cluster, %% verify schema wait_for_tables(), - version_ok_or_exit(rabbit_upgrade:read_version()), - schema_ok_or_exit(); + ensure_version_ok(rabbit_upgrade:read_version()), + ensure_schema_ok(); {[], false, _} -> %% First RAM node in cluster, start from scratch ok = create_schema(); {[AnotherNode|_], _, _} -> %% Subsequent node in cluster, catch up - version_ok_or_exit(rabbit_upgrade:read_version()), - version_ok_or_exit( + ensure_version_ok(rabbit_upgrade:read_version()), + ensure_version_ok( rpc:call(AnotherNode, rabbit_upgrade, read_version, [])), IsDiskNode = ClusterNodes == [] orelse lists:member(node(), ClusterNodes), @@ -405,7 +403,7 @@ init_db(ClusterNodes, Force) -> true -> disc; false -> ram end), - schema_ok_or_exit() + ensure_schema_ok() end; {error, Reason} -> %% one reason we may end up here is if we try to join @@ -430,22 +428,19 @@ schema_ok_or_move() -> ok = create_schema() end. -version_ok_or_exit({ok, DiscVersion}) -> +ensure_version_ok({ok, DiscVersion}) -> case rabbit_upgrade:desired_version() of - DiscVersion -> - ok; - DesiredVersion -> - exit({schema_mismatch, DesiredVersion, DiscVersion}) + DiscVersion -> ok; + DesiredVersion -> throw({error, {schema_mismatch, + DesiredVersion, DiscVersion}}) end; -version_ok_or_exit({error, _}) -> +ensure_version_ok({error, _}) -> ok = rabbit_upgrade:write_version(). -schema_ok_or_exit() -> +ensure_schema_ok() -> case check_schema_integrity() of - ok -> - ok; - {error, Reason} -> - exit({schema_invalid, Reason}) + ok -> ok; + {error, Reason} -> throw({error, {schema_invalid, Reason}}) end. create_schema() -> -- cgit v1.2.1 From 7c6d98d78b224a9b489b86f91854f6a95b9fe056 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 12 Nov 2010 15:45:32 +0000 Subject: use {error,_} returns and throws instead of exits --- src/rabbit_mnesia.erl | 8 ++++++-- src/rabbit_upgrade.erl | 37 ++++++++++++++++++++----------------- 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 95699c76..331ad120 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -378,8 +378,12 @@ init_db(ClusterNodes, Force) -> %% True single disc node, attempt upgrade wait_for_tables(), case rabbit_upgrade:maybe_upgrade() of - ok -> ensure_schema_ok(); - version_not_available -> schema_ok_or_move() + ok -> + ensure_schema_ok(); + version_not_available -> + schema_ok_or_move(); + {error, Reason} -> + throw({error, {upgrade_failed, Reason}}) end; {[], true, _} -> %% "Master" (i.e. without config) disc node in cluster, diff --git a/src/rabbit_upgrade.erl b/src/rabbit_upgrade.erl index 0071a08a..ba06ade8 100644 --- a/src/rabbit_upgrade.erl +++ b/src/rabbit_upgrade.erl @@ -32,7 +32,8 @@ -ifdef(use_specs). --spec(maybe_upgrade/0 :: () -> 'ok' | 'version_not_available'). +-spec(maybe_upgrade/0 :: () -> 'ok' | 'version_not_available' | + rabbit_types:error(any())). -spec(read_version/0 :: () -> {'ok', [any()]} | rabbit_types:error(any())). -spec(write_version/0 :: () -> 'ok'). @@ -49,17 +50,17 @@ maybe_upgrade() -> case read_version() of {ok, CurrentHeads} -> G = load_graph(), - case unknown_heads(CurrentHeads, G) of - [] -> - case upgrades_to_apply(CurrentHeads, G) of - [] -> ok; - Upgrades -> apply_upgrades(Upgrades) - end; - Unknown -> - exit({future_upgrades_found, Unknown}) - end, - true = digraph:delete(G), - ok; + try + case unknown_heads(CurrentHeads, G) of + [] -> case upgrades_to_apply(CurrentHeads, G) of + [] -> ok; + Upgrades -> apply_upgrades(Upgrades) + end; + Unknown -> {error, {future_upgrades_found, Unknown}} + end + after + true = digraph:delete(G) + end; {error, enoent} -> version_not_available end. @@ -94,9 +95,11 @@ edges(_Module, Steps) -> [{Require, StepName} || {StepName, Requires} <- Steps, Require <- Requires]. graph_build_error({vertex, duplicate, StepName}) -> - exit({duplicate_upgrade, StepName}); -graph_build_error({edge, E, From, To}) -> - exit({E, From, To}). + throw({error, {duplicate_upgrade, StepName}}); +graph_build_error({edge, {bad_vertex, StepName}, _From, _To}) -> + throw({error, {dependency_on_unknown_upgrade_step, StepName}}); +graph_build_error({edge, {bad_edge, Path}, _From, _To}) -> + throw({error, {cycle_in_upgrade_steps, Path}}). unknown_heads(Heads, G) -> [H || H <- Heads, digraph:vertex(G, H) =:= false]. @@ -130,9 +133,9 @@ apply_upgrades(Upgrades) -> ok = write_version(), ok = file:delete(LockFile); {error, eexist} -> - exit(previous_upgrade_failed); + {error, previous_upgrade_failed}; {error, _} = Error -> - exit(Error) + Error end. apply_upgrade({M, F}) -> -- cgit v1.2.1 From 5b7457c08325814cea0c69434d52e040ca2409bc Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 12 Nov 2010 15:46:42 +0000 Subject: cosmetic --- src/rabbit_mnesia.erl | 22 ++++++++++------------ src/rabbit_upgrade.erl | 4 ++-- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 9d172269..da81f884 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -361,16 +361,15 @@ init_db(ClusterNodes, Force) -> case mnesia:change_config(extra_db_nodes, ProperClusterNodes) of {ok, Nodes} -> case Force of - false -> - FailedClusterNodes = ProperClusterNodes -- Nodes, - case FailedClusterNodes of - [] -> ok; - _ -> - throw({error, {failed_to_cluster_with, - FailedClusterNodes, - "Mnesia could not connect to some nodes."}}) - end; - _ -> ok + false -> FailedClusterNodes = ProperClusterNodes -- Nodes, + case FailedClusterNodes of + [] -> ok; + _ -> throw({error, {failed_to_cluster_with, + FailedClusterNodes, + "Mnesia could not connect " + "to some nodes."}}) + end; + true -> ok end, case {Nodes, mnesia:system_info(use_dir), mnesia:system_info(db_nodes)} of @@ -411,8 +410,7 @@ init_db(ClusterNodes, Force) -> %% one reason we may end up here is if we try to join %% nodes together that are currently running standalone or %% are members of a different cluster - throw({error, {unable_to_join_cluster, - ClusterNodes, Reason}}) + throw({error, {unable_to_join_cluster, ClusterNodes, Reason}}) end. schema_ok_or_move() -> diff --git a/src/rabbit_upgrade.erl b/src/rabbit_upgrade.erl index 0071a08a..4ecef7ee 100644 --- a/src/rabbit_upgrade.erl +++ b/src/rabbit_upgrade.erl @@ -66,8 +66,8 @@ maybe_upgrade() -> read_version() -> case rabbit_misc:read_term_file(schema_filename()) of - {ok, [Heads]} -> {ok, Heads}; - {error, E} -> {error, E} + {ok, [Heads]} -> {ok, Heads}; + {error, _E} = Err -> Err end. write_version() -> -- cgit v1.2.1 From 00338bf9285163471b98c252c3a863948670d5ae Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 12 Nov 2010 17:44:02 +0000 Subject: make build_acyclic_graph return errors and refine types --- src/rabbit.erl | 72 +++++++++++++++++++++++++------------------------- src/rabbit_misc.erl | 42 +++++++++++++++++------------ src/rabbit_upgrade.erl | 71 ++++++++++++++++++++++++++++--------------------- 3 files changed, 102 insertions(+), 83 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index a1dd2c2e..a5713106 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -315,43 +315,43 @@ edges(_Module, Steps) -> {Key, OtherStep} <- Atts, Key =:= requires orelse Key =:= enables]. -graph_build_error({vertex, duplicate, StepName}) -> - boot_error("Duplicate boot step name: ~w~n", [StepName]); -graph_build_error({edge, Reason, From, To}) -> - boot_error( - "Could not add boot step dependency of ~w on ~w:~n~s", - [To, From, - case Reason of - {bad_vertex, V} -> - io_lib:format("Boot step not registered: ~w~n", [V]); - {bad_edge, [First | Rest]} -> - [io_lib:format("Cyclic dependency: ~w", [First]), - [io_lib:format(" depends on ~w", [Next]) || Next <- Rest], - io_lib:format(" depends on ~w~n", [First])] - end]). - sort_boot_steps(UnsortedSteps) -> - G = rabbit_misc:build_acyclic_graph( - fun vertices/2, fun edges/2, fun graph_build_error/1, UnsortedSteps), - - %% Use topological sort to find a consistent ordering (if there is - %% one, otherwise fail). - SortedStepsRev = [begin - {StepName, Step} = digraph:vertex(G, StepName), - Step - end || StepName <- digraph_utils:topsort(G)], - SortedSteps = lists:reverse(SortedStepsRev), - - digraph:delete(G), - - %% Check that all mentioned {M,F,A} triples are exported. - case [{StepName, {M,F,A}} - || {StepName, Attributes} <- SortedSteps, - {mfa, {M,F,A}} <- Attributes, - not erlang:function_exported(M, F, length(A))] of - [] -> SortedSteps; - MissingFunctions -> boot_error("Boot step functions not exported: ~p~n", - [MissingFunctions]) + case rabbit_misc:build_acyclic_graph(fun vertices/2, fun edges/2, + UnsortedSteps) of + {ok, G} -> + %% Use topological sort to find a consistent ordering (if + %% there is one, otherwise fail). + SortedSteps = lists:reverse( + [begin + {StepName, Step} = digraph:vertex(G, StepName), + Step + end || StepName <- digraph_utils:topsort(G)]), + digraph:delete(G), + %% Check that all mentioned {M,F,A} triples are exported. + case [{StepName, {M,F,A}} || + {StepName, Attributes} <- SortedSteps, + {mfa, {M,F,A}} <- Attributes, + not erlang:function_exported(M, F, length(A))] of + [] -> SortedSteps; + MissingFunctions -> boot_error( + "Boot step functions not exported: ~p~n", + [MissingFunctions]) + end; + {error, {vertex, duplicate, StepName}} -> + boot_error("Duplicate boot step name: ~w~n", [StepName]); + {error, {edge, Reason, From, To}} -> + boot_error( + "Could not add boot step dependency of ~w on ~w:~n~s", + [To, From, + case Reason of + {bad_vertex, V} -> + io_lib:format("Boot step not registered: ~w~n", [V]); + {bad_edge, [First | Rest]} -> + [io_lib:format("Cyclic dependency: ~w", [First]), + [io_lib:format(" depends on ~w", [Next]) || + Next <- Rest], + io_lib:format(" depends on ~w~n", [First])] + end]) end. %%--------------------------------------------------------------------------- diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 0522afdc..fe12167a 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -64,7 +64,7 @@ -export([recursive_delete/1, dict_cons/3, orddict_cons/3, unlink_and_capture_exit/1]). -export([get_options/2]). --export([all_module_attributes/1, build_acyclic_graph/4]). +-export([all_module_attributes/1, build_acyclic_graph/3]). -export([now_ms/0]). -import(mnesia). @@ -89,7 +89,6 @@ fun ((atom(), [term()]) -> {digraph:vertex(), digraph_label()})). -type(graph_edge_fun() :: fun ((atom(), [term()]) -> {digraph:vertex(), digraph:vertex()})). --type(graph_error_fun() :: fun ((any()) -> any() | no_return())). -spec(method_record_type/1 :: (rabbit_framing:amqp_method_record()) -> rabbit_framing:amqp_method_name()). @@ -191,9 +190,13 @@ -spec(get_options/2 :: ([optdef()], [string()]) -> {[string()], [{string(), any()}]}). -spec(all_module_attributes/1 :: (atom()) -> [{atom(), [term()]}]). --spec(build_acyclic_graph/4 :: (graph_vertex_fun(), graph_edge_fun(), - graph_error_fun(), [{atom(), [term()]}]) -> - digraph()). +-spec(build_acyclic_graph/3 :: + (graph_vertex_fun(), graph_edge_fun(), [{atom(), [term()]}]) + -> rabbit_types:ok_or_error2(digraph(), + {'vertex', 'duplicate', digraph:vertex()} | + {'edge', ({bad_vertex, digraph:vertex()} | + {bad_edge, [digraph:vertex()]}), + digraph:vertex(), digraph:vertex()})). -spec(now_ms/0 :: () -> non_neg_integer()). -endif. @@ -765,16 +768,21 @@ all_module_attributes(Name) -> end, [], Modules). -build_acyclic_graph(VertexFun, EdgeFun, ErrorFun, Graph) -> +build_acyclic_graph(VertexFun, EdgeFun, Graph) -> G = digraph:new([acyclic]), - [ case digraph:vertex(G, Vertex) of - false -> digraph:add_vertex(G, Vertex, Label); - _ -> ErrorFun({vertex, duplicate, Vertex}) - end || {Module, Atts} <- Graph, - {Vertex, Label} <- VertexFun(Module, Atts) ], - [ case digraph:add_edge(G, From, To) of - {error, E} -> ErrorFun({edge, E, From, To}); - _ -> ok - end || {Module, Atts} <- Graph, - {From, To} <- EdgeFun(Module, Atts) ], - G. + try + [case digraph:vertex(G, Vertex) of + false -> digraph:add_vertex(G, Vertex, Label); + _ -> ok = throw({graph_error, {vertex, duplicate, Vertex}}) + end || {Module, Atts} <- Graph, + {Vertex, Label} <- VertexFun(Module, Atts)], + [case digraph:add_edge(G, From, To) of + {error, E} -> throw({graph_error, {edge, E, From, To}}); + _ -> ok + end || {Module, Atts} <- Graph, + {From, To} <- EdgeFun(Module, Atts)], + {ok, G} + catch {graph_error, Reason} -> + true = digraph:delete(G), + {error, Reason} + end. diff --git a/src/rabbit_upgrade.erl b/src/rabbit_upgrade.erl index f4016d81..52470275 100644 --- a/src/rabbit_upgrade.erl +++ b/src/rabbit_upgrade.erl @@ -32,12 +32,22 @@ -ifdef(use_specs). +-type(step() :: atom()). +-type(version() :: [step()]). + +-type(upgrade_definition_error() :: + {'duplicate_upgrade_step', step()} | + {'dependency_on_unknown_upgrade_step', step()} | + {'cycle_in_upgrade_steps', [step()]}). + -spec(maybe_upgrade/0 :: () -> 'ok' | 'version_not_available' | - rabbit_types:error(any())). --spec(read_version/0 :: - () -> {'ok', [any()]} | rabbit_types:error(any())). + rabbit_types:error( + upgrade_definition_error() | + {'future_upgrades_founds', [step()]})). +-spec(read_version/0 :: () -> rabbit_types:ok_or_error2(version(), any())). -spec(write_version/0 :: () -> 'ok'). --spec(desired_version/0 :: () -> [atom()]). +-spec(desired_version/0 :: () -> rabbit_types:ok_or_error2( + version(), upgrade_definition_error())). -endif. @@ -49,18 +59,16 @@ maybe_upgrade() -> case read_version() of {ok, CurrentHeads} -> - G = load_graph(), - try - case unknown_heads(CurrentHeads, G) of - [] -> case upgrades_to_apply(CurrentHeads, G) of - [] -> ok; - Upgrades -> apply_upgrades(Upgrades) - end; - Unknown -> {error, {future_upgrades_found, Unknown}} - end - after - true = digraph:delete(G) - end; + with_upgrade_graph( + fun (G) -> + case unknown_heads(CurrentHeads, G) of + [] -> case upgrades_to_apply(CurrentHeads, G) of + [] -> ok; + Upgrades -> apply_upgrades(Upgrades) + end; + Unknown -> {error, {future_upgrades_found, Unknown}} + end + end); {error, enoent} -> version_not_available end. @@ -76,17 +84,26 @@ write_version() -> ok. desired_version() -> - G = load_graph(), - Version = heads(G), - true = digraph:delete(G), - Version. + with_upgrade_graph(fun (G) -> {ok, heads(G)} end). %% ------------------------------------------------------------------- -load_graph() -> - Upgrades = rabbit_misc:all_module_attributes(rabbit_upgrade), - rabbit_misc:build_acyclic_graph( - fun vertices/2, fun edges/2, fun graph_build_error/1, Upgrades). +with_upgrade_graph(Fun) -> + case rabbit_misc:build_acyclic_graph( + fun vertices/2, fun edges/2, + rabbit_misc:all_module_attributes(rabbit_upgrade)) of + {ok, G} -> try + Fun(G) + after + true = digraph:delete(G) + end; + {error, {vertex, duplicate, StepName}} -> + {error, {duplicate_upgrade_step, StepName}}; + {error, {edge, {bad_vertex, StepName}, _From, _To}} -> + {error, {dependency_on_unknown_upgrade_step, StepName}}; + {error, {edge, {bad_edge, StepNames}, _From, _To}} -> + {error, {cycle_in_upgrade_steps, StepNames}} + end. vertices(Module, Steps) -> [{StepName, {Module, StepName}} || {StepName, _Reqs} <- Steps]. @@ -94,12 +111,6 @@ vertices(Module, Steps) -> edges(_Module, Steps) -> [{Require, StepName} || {StepName, Requires} <- Steps, Require <- Requires]. -graph_build_error({vertex, duplicate, StepName}) -> - throw({error, {duplicate_upgrade, StepName}}); -graph_build_error({edge, {bad_vertex, StepName}, _From, _To}) -> - throw({error, {dependency_on_unknown_upgrade_step, StepName}}); -graph_build_error({edge, {bad_edge, Path}, _From, _To}) -> - throw({error, {cycle_in_upgrade_steps, Path}}). unknown_heads(Heads, G) -> [H || H <- Heads, digraph:vertex(G, H) =:= false]. -- cgit v1.2.1 From 7a35932177e46efc96f45250832c39350063ceee Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 12 Nov 2010 17:45:39 +0000 Subject: cosmetic --- src/rabbit_upgrade.erl | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/rabbit_upgrade.erl b/src/rabbit_upgrade.erl index 4ecef7ee..9522227e 100644 --- a/src/rabbit_upgrade.erl +++ b/src/rabbit_upgrade.erl @@ -66,8 +66,8 @@ maybe_upgrade() -> read_version() -> case rabbit_misc:read_term_file(schema_filename()) of - {ok, [Heads]} -> {ok, Heads}; - {error, _E} = Err -> Err + {ok, [Heads]} -> {ok, Heads}; + {error, _} = Err -> Err end. write_version() -> @@ -111,8 +111,8 @@ upgrades_to_apply(Heads, G) -> sets:from_list(digraph_utils:reaching(Heads, G)))), %% Form a subgraph from that list and find a topological ordering %% so we can invoke them in order. - [element(2, digraph:vertex(G, StepName)) - || StepName <- digraph_utils:topsort(digraph_utils:subgraph(G, Unsorted))]. + [element(2, digraph:vertex(G, StepName)) || + StepName <- digraph_utils:topsort(digraph_utils:subgraph(G, Unsorted))]. heads(G) -> lists:sort([V || V <- digraph:vertices(G), digraph:out_degree(G, V) =:= 0]). @@ -141,16 +141,12 @@ apply_upgrade({M, F}) -> %% ------------------------------------------------------------------- -schema_filename() -> - filename:join(dir(), ?VERSION_FILENAME). +dir() -> rabbit_mnesia:dir(). -lock_filename() -> - filename:join(dir(), ?LOCK_FILENAME). +schema_filename() -> filename:join(dir(), ?VERSION_FILENAME). + +lock_filename() -> filename:join(dir(), ?LOCK_FILENAME). %% NB: we cannot use rabbit_log here since it may not have been %% started yet -info(Msg, Args) -> - error_logger:info_msg(Msg, Args). - -dir() -> - rabbit_mnesia:dir(). +info(Msg, Args) -> error_logger:info_msg(Msg, Args). -- cgit v1.2.1 From 5f6de6827bbe336edc77a4d5c64532045d1ed70d Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 13 Nov 2010 21:38:57 +0000 Subject: attempt to correct specs --- src/rabbit_misc.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index fe12167a..5c85b99a 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -86,9 +86,9 @@ :: rabbit_types:channel_exit() | rabbit_types:connection_exit()). -type(digraph_label() :: term()). -type(graph_vertex_fun() :: - fun ((atom(), [term()]) -> {digraph:vertex(), digraph_label()})). + fun ((atom(), [term()]) -> [{digraph:vertex(), digraph_label()}])). -type(graph_edge_fun() :: - fun ((atom(), [term()]) -> {digraph:vertex(), digraph:vertex()})). + fun ((atom(), [term()]) -> [{digraph:vertex(), digraph:vertex()}])). -spec(method_record_type/1 :: (rabbit_framing:amqp_method_record()) -> rabbit_framing:amqp_method_name()). -- cgit v1.2.1 From f6fc65d5fbae0f8b9a7715ed4cbfbe8596908c0e Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 15 Nov 2010 11:25:25 +0000 Subject: Replace catch with try; we want to show all kinds of boot errors in a readable way. --- src/rabbit.erl | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index a1dd2c2e..983a8453 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -291,11 +291,10 @@ run_boot_step({StepName, Attributes}) -> io:format("-- ~s~n", [Description]); MFAs -> io:format("starting ~-60s ...", [Description]), - [case catch apply(M,F,A) of - {'EXIT', Reason} -> - boot_error("FAILED~nReason: ~p~n", [Reason]); - ok -> - ok + [try + apply(M,F,A) + catch + _:Reason -> boot_error("FAILED~nReason: ~p~n", [Reason]) end || {M,F,A} <- MFAs], io:format("done~n"), ok -- cgit v1.2.1 From 44d4ec091ec09626ae865ed9d0a2b615854243ad Mon Sep 17 00:00:00 2001 From: John DeTreville Date: Mon, 15 Nov 2010 16:27:22 +0000 Subject: Fixed thinkos in calls to string:join. --- src/rabbit_ssl.erl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/rabbit_ssl.erl b/src/rabbit_ssl.erl index ff79d433..ce218b7b 100644 --- a/src/rabbit_ssl.erl +++ b/src/rabbit_ssl.erl @@ -99,7 +99,7 @@ format_rdn_sequence({rdnSequence, Seq}) -> %% Format an RDN set. format_complex_rdn(RDNs) -> - string:join([lists:flatten(format_rdn(RDN)) || RDN <- RDNs], "+"). + string:join([format_rdn(RDN) || RDN <- RDNs], "+"). %% Format an RDN. If the type name is unknown, use the dotted decimal %% representation. See RFC4514, section 2.3. @@ -126,8 +126,7 @@ format_rdn(#'AttributeTypeAndValue'{type = T, value = V}) -> io_lib:format(Fmt ++ "=~s", [FV]); none when is_tuple(T) -> TypeL = [io_lib:format("~w", [X]) || X <- tuple_to_list(T)], - io_lib:format("~s:~s", - [string:join(lists:flatten(TypeL), "."), FV]); + io_lib:format("~s:~s", [string:join(TypeL, "."), FV]); none -> io_lib:format("~p:~s", [T, FV]) end. -- cgit v1.2.1 From deed6a5b61798924f492538a4f6f6a5034998ffb Mon Sep 17 00:00:00 2001 From: Matthew Sackman Date: Tue, 16 Nov 2010 12:09:10 +0000 Subject: Fixed windows scripts --- scripts/rabbitmq-server.bat | 2 +- scripts/rabbitmq-service.bat | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/rabbitmq-server.bat b/scripts/rabbitmq-server.bat index bd4120fa..fc2d0c13 100644 --- a/scripts/rabbitmq-server.bat +++ b/scripts/rabbitmq-server.bat @@ -123,7 +123,7 @@ set RABBITMQ_EBIN_ROOT=!TDP0!..\ebin -rabbit rabbit_ebin \""!RABBITMQ_EBIN_ROOT:\=/!"\" ^ -extra !STAR! -set RABBITMQ_BOOT_FILE=!RABBITMQ_MNESIA_DIR!\plugins-scratch\rabbit +set RABBITMQ_BOOT_FILE=!RABBITMQ_PLUGINS_EXPAND_DIR!\rabbit if not exist "!RABBITMQ_BOOT_FILE!.boot" ( echo Custom Boot File "!RABBITMQ_BOOT_FILE!.boot" is missing. exit /B 1 diff --git a/scripts/rabbitmq-service.bat b/scripts/rabbitmq-service.bat index ff25b146..361ffea4 100644 --- a/scripts/rabbitmq-service.bat +++ b/scripts/rabbitmq-service.bat @@ -192,7 +192,7 @@ set RABBITMQ_EBIN_ROOT=!TDP0!..\ebin -rabbit rabbit_ebin \""!RABBITMQ_EBIN_ROOT:\=/!"\" ^ -extra !STAR! -set RABBITMQ_BOOT_FILE=!RABBITMQ_MNESIA_DIR!\plugins-scratch\rabbit +set RABBITMQ_BOOT_FILE=!RABBITMQ_PLUGINS_EXPAND_DIR!\rabbit if not exist "!RABBITMQ_BOOT_FILE!.boot" ( echo Custom Boot File "!RABBITMQ_BOOT_FILE!.boot" is missing. exit /B 1 -- cgit v1.2.1 From 6ff4239a3f8d17cc3ab705076dfa0bf100786db9 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 16 Nov 2010 13:39:35 +0000 Subject: Fix --- src/rabbit_upgrade.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rabbit_upgrade.erl b/src/rabbit_upgrade.erl index 5ed7277f..9e95bb98 100644 --- a/src/rabbit_upgrade.erl +++ b/src/rabbit_upgrade.erl @@ -84,7 +84,8 @@ write_version() -> ok. desired_version() -> - with_upgrade_graph(fun (G) -> {ok, heads(G)} end). + {ok, Version} = with_upgrade_graph(fun (G) -> {ok, heads(G)} end), + Version. %% ------------------------------------------------------------------- -- cgit v1.2.1 From 96b148c4a8217d5e771978af3a31722953aec6d4 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 16 Nov 2010 15:40:18 +0000 Subject: Reoprt idle_since pseudo-info-item. --- src/rabbit_amqqueue_process.erl | 11 +++++++++-- src/rabbit_channel.erl | 18 ++++++++++++------ 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 75f285df..183570a6 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -669,7 +669,10 @@ i(Item, _) -> throw({bad_argument, Item}). emit_stats(State) -> - rabbit_event:notify(queue_stats, infos(?STATISTICS_KEYS, State)). + emit_stats(State, []). + +emit_stats(State, Extra) -> + rabbit_event:notify(queue_stats, Extra ++ infos(?STATISTICS_KEYS, State)). %--------------------------------------------------------------------------- @@ -1053,7 +1056,11 @@ handle_pre_hibernate(State = #q{backing_queue = BQ, DesiredDuration = rabbit_memory_monitor:report_ram_duration(self(), infinity), BQS2 = BQ:set_ram_duration_target(DesiredDuration, BQS1), - rabbit_event:if_enabled(StatsTimer, fun () -> emit_stats(State) end), + rabbit_event:if_enabled(StatsTimer, + fun () -> + emit_stats(State, + [{idle_since, erlang:now()}]) + end), State1 = State#q{stats_timer = rabbit_event:stop_stats_timer(StatsTimer), backing_queue_state = BQS2}, {hibernate, stop_rate_timer(State1)}. diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 19613a57..36ccd6b5 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -267,9 +267,11 @@ handle_info({'DOWN', _MRef, process, QPid, _Reason}, State) -> handle_pre_hibernate(State = #ch{stats_timer = StatsTimer}) -> ok = clear_permission_cache(), - rabbit_event:if_enabled(StatsTimer, fun () -> - internal_emit_stats(State) - end), + rabbit_event:if_enabled(StatsTimer, + fun () -> + internal_emit_stats( + State, [{idle_since, erlang:now()}]) + end), {hibernate, State#ch{stats_timer = rabbit_event:stop_stats_timer(StatsTimer)}}. @@ -1201,11 +1203,14 @@ update_measures(Type, QX, Inc, Measure) -> put({Type, QX}, orddict:store(Measure, Cur + Inc, Measures)). -internal_emit_stats(State = #ch{stats_timer = StatsTimer}) -> +internal_emit_stats(State) -> + internal_emit_stats(State, []). + +internal_emit_stats(State = #ch{stats_timer = StatsTimer}, Extra) -> CoarseStats = infos(?STATISTICS_KEYS, State), case rabbit_event:stats_level(StatsTimer) of coarse -> - rabbit_event:notify(channel_stats, CoarseStats); + rabbit_event:notify(channel_stats, Extra ++ CoarseStats); fine -> FineStats = [{channel_queue_stats, @@ -1215,7 +1220,8 @@ internal_emit_stats(State = #ch{stats_timer = StatsTimer}) -> {channel_queue_exchange_stats, [{QX, Stats} || {{queue_exchange_stats, QX}, Stats} <- get()]}], - rabbit_event:notify(channel_stats, CoarseStats ++ FineStats) + rabbit_event:notify(channel_stats, + Extra ++ CoarseStats ++ FineStats) end. erase_queue_stats(QPid) -> -- cgit v1.2.1 From b49c9993afa69c9cc1399717f2e02e1143ab8d5f Mon Sep 17 00:00:00 2001 From: Matthew Sackman Date: Tue, 16 Nov 2010 16:17:42 +0000 Subject: Added queue_ttl upgrade --- src/rabbit_queue_index.erl | 94 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/src/rabbit_queue_index.erl b/src/rabbit_queue_index.erl index bde9b3d3..d366ed36 100644 --- a/src/rabbit_queue_index.erl +++ b/src/rabbit_queue_index.erl @@ -36,6 +36,8 @@ publish/5, deliver/2, ack/2, sync/2, flush/1, read/3, next_segment_boundary/1, bounds/1, recover/1]). +-export([add_queue_ttl/0]). + -define(CLEAN_FILENAME, "clean.dot"). %%---------------------------------------------------------------------------- @@ -180,6 +182,8 @@ %%---------------------------------------------------------------------------- +-rabbit_upgrade({add_queue_ttl, []}). + -ifdef(use_specs). -type(hdl() :: ('undefined' | any())). @@ -226,6 +230,8 @@ -spec(recover/1 :: ([rabbit_amqqueue:name()]) -> {[[any()]], startup_fun_state()}). +-spec(add_queue_ttl/0 :: () -> 'ok'). + -endif. @@ -972,3 +978,91 @@ journal_minus_segment1({no_pub, del, ack}, {?PUB, del, no_ack}) -> {{no_pub, no_del, ack}, 0}; journal_minus_segment1({no_pub, del, ack}, {?PUB, del, ack}) -> {undefined, -1}. + +%%---------------------------------------------------------------------------- +%% upgrade +%%---------------------------------------------------------------------------- + +add_queue_ttl() -> + foreach_queue_index({fun add_queue_ttl_journal/1, + fun add_queue_ttl_segment/1}). + +add_queue_ttl_journal(<>) -> + {<>, Rest}; +add_queue_ttl_journal(<>) -> + {<>, Rest}; +add_queue_ttl_journal(<>) -> + {[<>, Guid, + expiry_to_binary(undefined)], Rest}; +add_queue_ttl_journal(_) -> + stop. + +add_queue_ttl_segment(<>) -> + {[<>, Guid, expiry_to_binary(undefined)], Rest}; +add_queue_ttl_segment(<>) -> + {<>, + Rest}; +add_queue_ttl_segment(_) -> + stop. + +%%---------------------------------------------------------------------------- + +foreach_queue_index(Funs) -> + QueuesDir = queues_dir(), + case file:list_dir(QueuesDir) of + {error, enoent} -> + ok; + {ok, Entries} -> + Queues = [ Dir || Entry <- Entries, + Dir <- [filename:join(QueuesDir, Entry)], + filelib:is_dir(Dir) ], + {ok, Gatherer} = gatherer:start_link(), + [begin + ok = gatherer:fork(Gatherer), + ok = worker_pool:submit_async( + fun () -> transform_queue(QueueDir, Gatherer, Funs) end) + end || QueueDir <- Queues], + empty = gatherer:out(Gatherer), + ok = gatherer:stop(Gatherer), + ok = rabbit_misc:unlink_and_capture_exit(Gatherer) + end. + +transform_queue(Dir, Gatherer, {JournalFun, SegmentFun}) -> + ok = transform_file(filename:join(Dir, ?JOURNAL_FILENAME), JournalFun), + [ok = transform_file(filename:join(Dir, Seg), SegmentFun) + || Seg <- filelib:wildcard("*" ++ ?SEGMENT_EXTENSION, Dir)], + ok = gatherer:finish(Gatherer). + +transform_file(Path, Fun) -> + PathTmp = Path ++ ".upgrade", + Size = filelib:file_size(Path), + + {ok, PathTmpHdl} = + file_handle_cache:open(PathTmp, [exclusive | ?WRITE_MODE], + [{write_buffer, infinity}]), + + {ok, PathHdl} = + file_handle_cache:open(Path, [{read_ahead, Size} | ?READ_MODE], []), + {ok, Content} = file_handle_cache:read(PathHdl, Size), + ok = file_handle_cache:close(PathHdl), + + ok = drive_transform_fun(Fun, PathTmpHdl, Content), + + ok = file_handle_cache:close(PathTmpHdl), + ok = file:rename(PathTmp, Path). + +drive_transform_fun(Fun, Hdl, Contents) -> + case Fun(Contents) of + stop -> + ok; + {Output, Contents1} -> + ok = file_handle_cache:append(Hdl, Output), + drive_transform_fun(Fun, Hdl, Contents1) + end. -- cgit v1.2.1 From 5d1217b70eb0d3450a17896d97cb449109681700 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 16 Nov 2010 17:26:29 +0000 Subject: Matthew points out that erlang:now() can be rewritten as now(). --- src/rabbit_amqqueue_process.erl | 3 +-- src/rabbit_channel.erl | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 183570a6..a999fe58 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1058,8 +1058,7 @@ handle_pre_hibernate(State = #q{backing_queue = BQ, BQS2 = BQ:set_ram_duration_target(DesiredDuration, BQS1), rabbit_event:if_enabled(StatsTimer, fun () -> - emit_stats(State, - [{idle_since, erlang:now()}]) + emit_stats(State, [{idle_since, now()}]) end), State1 = State#q{stats_timer = rabbit_event:stop_stats_timer(StatsTimer), backing_queue_state = BQS2}, diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 36ccd6b5..800cc237 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -270,7 +270,7 @@ handle_pre_hibernate(State = #ch{stats_timer = StatsTimer}) -> rabbit_event:if_enabled(StatsTimer, fun () -> internal_emit_stats( - State, [{idle_since, erlang:now()}]) + State, [{idle_since, now()}]) end), {hibernate, State#ch{stats_timer = rabbit_event:stop_stats_timer(StatsTimer)}}. -- cgit v1.2.1 From 430a86f27351e4578472fdefcaf62e4164bdb089 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 16 Nov 2010 17:56:40 +0000 Subject: Throw rather than return errors --- src/rabbit_mnesia.erl | 4 +--- src/rabbit_upgrade.erl | 29 ++++++++++------------------- 2 files changed, 11 insertions(+), 22 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 3b41237a..cb3251c7 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -380,9 +380,7 @@ init_db(ClusterNodes, Force) -> ok -> ensure_schema_ok(); version_not_available -> - schema_ok_or_move(); - {error, Reason} -> - throw({error, {upgrade_failed, Reason}}) + schema_ok_or_move() end; {[], true, _} -> %% "Master" (i.e. without config) disc node in cluster, diff --git a/src/rabbit_upgrade.erl b/src/rabbit_upgrade.erl index 9e95bb98..27a94f6f 100644 --- a/src/rabbit_upgrade.erl +++ b/src/rabbit_upgrade.erl @@ -35,19 +35,10 @@ -type(step() :: atom()). -type(version() :: [step()]). --type(upgrade_definition_error() :: - {'duplicate_upgrade_step', step()} | - {'dependency_on_unknown_upgrade_step', step()} | - {'cycle_in_upgrade_steps', [step()]}). - --spec(maybe_upgrade/0 :: () -> 'ok' | 'version_not_available' | - rabbit_types:error( - upgrade_definition_error() | - {'future_upgrades_founds', [step()]})). +-spec(maybe_upgrade/0 :: () -> 'ok' | 'version_not_available'). -spec(read_version/0 :: () -> rabbit_types:ok_or_error2(version(), any())). -spec(write_version/0 :: () -> 'ok'). --spec(desired_version/0 :: () -> rabbit_types:ok_or_error2( - version(), upgrade_definition_error())). +-spec(desired_version/0 :: () -> version()). -endif. @@ -66,7 +57,8 @@ maybe_upgrade() -> [] -> ok; Upgrades -> apply_upgrades(Upgrades) end; - Unknown -> {error, {future_upgrades_found, Unknown}} + Unknown -> throw({error, + {future_upgrades_found, Unknown}}) end end); {error, enoent} -> @@ -84,8 +76,7 @@ write_version() -> ok. desired_version() -> - {ok, Version} = with_upgrade_graph(fun (G) -> {ok, heads(G)} end), - Version. + with_upgrade_graph(fun (G) -> heads(G) end). %% ------------------------------------------------------------------- @@ -99,11 +90,11 @@ with_upgrade_graph(Fun) -> true = digraph:delete(G) end; {error, {vertex, duplicate, StepName}} -> - {error, {duplicate_upgrade_step, StepName}}; + throw({error, {duplicate_upgrade_step, StepName}}); {error, {edge, {bad_vertex, StepName}, _From, _To}} -> - {error, {dependency_on_unknown_upgrade_step, StepName}}; + throw({error, {dependency_on_unknown_upgrade_step, StepName}}); {error, {edge, {bad_edge, StepNames}, _From, _To}} -> - {error, {cycle_in_upgrade_steps, StepNames}} + throw({error, {cycle_in_upgrade_steps, StepNames}}) end. vertices(Module, Steps) -> @@ -145,9 +136,9 @@ apply_upgrades(Upgrades) -> ok = write_version(), ok = file:delete(LockFile); {error, eexist} -> - {error, previous_upgrade_failed}; + throw({error, previous_upgrade_failed}); {error, _} = Error -> - Error + throw(Error) end. apply_upgrade({M, F}) -> -- cgit v1.2.1 From 2c566713c29f406d90d336861c4056470f2ca97c Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 17 Nov 2010 13:55:50 +0000 Subject: cosmetic: remove superfluous imports --- src/rabbit_reader.erl | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 12730ccf..44d2ec7c 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -45,10 +45,6 @@ -export([emit_stats/1]). --import(gen_tcp). --import(inet). --import(prim_inet). - -define(HANDSHAKE_TIMEOUT, 10). -define(NORMAL_TIMEOUT, 3). -define(CLOSING_TIMEOUT, 1). -- cgit v1.2.1 From 4d22499e0ef689b2bc7a1d7ec65b802c9a3ca337 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Wed, 17 Nov 2010 14:35:45 +0000 Subject: fix confirm after basic.reject{requeue=false} --- src/rabbit_variable_queue.erl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 9b3cc58a..abad3adb 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -624,8 +624,9 @@ ack(AckTags, State) -> {Guids, State1} = ack(fun msg_store_remove/3, fun ({_IsPersistent, Guid, _MsgProps}, State1) -> - remove_confirms(gb_sets:singleton(Guid), State1), - State1 + remove_confirms(gb_sets:singleton(Guid), State1); + (#msg_status{msg = #basic_message{guid = Guid}}, State1) -> + remove_confirms(gb_sets:singleton(Guid), State1) end, AckTags, State), {Guids, a(State1)}. -- cgit v1.2.1 From 37d931f6c39a9b9b633930e45b315b5779d42417 Mon Sep 17 00:00:00 2001 From: Matthew Sackman Date: Wed, 17 Nov 2010 14:37:01 +0000 Subject: refactoring --- src/rabbit_queue_index.erl | 61 ++++++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 32 deletions(-) diff --git a/src/rabbit_queue_index.erl b/src/rabbit_queue_index.erl index d366ed36..248c1fbc 100644 --- a/src/rabbit_queue_index.erl +++ b/src/rabbit_queue_index.erl @@ -351,35 +351,36 @@ recover(DurableQueues) -> DurableDict = dict:from_list([ {queue_name_to_dir_name(Queue), Queue} || Queue <- DurableQueues ]), QueuesDir = queues_dir(), - Directories = case file:list_dir(QueuesDir) of - {ok, Entries} -> [ Entry || Entry <- Entries, - filelib:is_dir( - filename:join( - QueuesDir, Entry)) ]; - {error, enoent} -> [] - end, + QueueDirNames = all_queue_directory_names(QueuesDir), DurableDirectories = sets:from_list(dict:fetch_keys(DurableDict)), {DurableQueueNames, DurableTerms} = lists:foldl( - fun (QueueDir, {DurableAcc, TermsAcc}) -> - case sets:is_element(QueueDir, DurableDirectories) of + fun (QueueDirName, {DurableAcc, TermsAcc}) -> + QueueDirPath = filename:join(QueuesDir, QueueDirName), + case sets:is_element(QueueDirName, DurableDirectories) of true -> TermsAcc1 = - case read_shutdown_terms( - filename:join(QueuesDir, QueueDir)) of + case read_shutdown_terms(QueueDirPath) of {error, _} -> TermsAcc; {ok, Terms} -> [Terms | TermsAcc] end, - {[dict:fetch(QueueDir, DurableDict) | DurableAcc], + {[dict:fetch(QueueDirName, DurableDict) | DurableAcc], TermsAcc1}; false -> - Dir = filename:join(queues_dir(), QueueDir), - ok = rabbit_misc:recursive_delete([Dir]), + ok = rabbit_misc:recursive_delete([QueueDirPath]), {DurableAcc, TermsAcc} end - end, {[], []}, Directories), + end, {[], []}, QueueDirNames), {DurableTerms, {fun queue_index_walker/1, {start, DurableQueueNames}}}. +all_queue_directory_names(Dir) -> + case file:list_dir(Dir) of + {ok, Entries} -> [ Entry || Entry <- Entries, + filelib:is_dir( + filename:join(Dir, Entry)) ]; + {error, enoent} -> [] + end. + %%---------------------------------------------------------------------------- %% startup and shutdown %%---------------------------------------------------------------------------- @@ -1016,23 +1017,19 @@ add_queue_ttl_segment(_) -> foreach_queue_index(Funs) -> QueuesDir = queues_dir(), - case file:list_dir(QueuesDir) of - {error, enoent} -> - ok; - {ok, Entries} -> - Queues = [ Dir || Entry <- Entries, - Dir <- [filename:join(QueuesDir, Entry)], - filelib:is_dir(Dir) ], - {ok, Gatherer} = gatherer:start_link(), - [begin - ok = gatherer:fork(Gatherer), - ok = worker_pool:submit_async( - fun () -> transform_queue(QueueDir, Gatherer, Funs) end) - end || QueueDir <- Queues], - empty = gatherer:out(Gatherer), - ok = gatherer:stop(Gatherer), - ok = rabbit_misc:unlink_and_capture_exit(Gatherer) - end. + QueueDirNames = all_queue_directory_names(QueuesDir), + {ok, Gatherer} = gatherer:start_link(), + [begin + ok = gatherer:fork(Gatherer), + ok = worker_pool:submit_async( + fun () -> + transform_queue(filename:join(QueuesDir, QueueDirName), + Gatherer, Funs) + end) + end || QueueDirName <- QueueDirNames], + empty = gatherer:out(Gatherer), + ok = gatherer:stop(Gatherer), + ok = rabbit_misc:unlink_and_capture_exit(Gatherer). transform_queue(Dir, Gatherer, {JournalFun, SegmentFun}) -> ok = transform_file(filename:join(Dir, ?JOURNAL_FILENAME), JournalFun), -- cgit v1.2.1 From af1f6be9950a71dd5682dc604e87f9db881fffea Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 17 Nov 2010 16:30:37 +0000 Subject: some routing optimisation - handle the very common "message goes through single exchange" case specially - use a gb_set in all other cases - it performs much better than a set for smaller sets, and not too badly for very large sets either. --- src/rabbit_exchange.erl | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index 61a24388..4c0f341f 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -228,7 +228,7 @@ info_all(VHostPath, Items) -> map(VHostPath, fun (X) -> info(X, Items) end). publish(X = #exchange{name = XName}, Delivery) -> rabbit_router:deliver( - route(Delivery, {queue:from_list([X]), sets:from_list([XName]), []}), + route(Delivery, {queue:from_list([X]), XName, []}), Delivery). route(Delivery, {WorkList, SeenXs, QNames}) -> @@ -251,14 +251,23 @@ process_alternate(#exchange{name = XName, arguments = Args}, []) -> process_alternate(_X, Results) -> Results. +process_route(#resource{kind = exchange} = XName, + {_WorkList, XName, _QNames} = Acc) -> + 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}; process_route(#resource{kind = exchange} = XName, {WorkList, SeenXs, QNames} = Acc) -> - case sets:is_element(XName, SeenXs) of + 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, sets:add_element(XName, SeenXs), QNames} + end, gb_sets:add_element(XName, SeenXs), QNames} end; process_route(#resource{kind = queue} = QName, {WorkList, SeenXs, QNames}) -> -- cgit v1.2.1 From 908e2ec1c57d6af9c0db409048e8cde96f0037be Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 17 Nov 2010 22:59:33 +0000 Subject: remove some cruft in reader, and some minor tweaks --- src/rabbit_reader.erl | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 44d2ec7c..e68fbca8 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -318,7 +318,6 @@ start_connection(Parent, ChannelSupSupPid, Collector, StartHeartbeatFun, Deb, done. mainloop(Deb, State = #v1{parent = Parent, sock= Sock, recv_ref = Ref}) -> - %%?LOGDEBUG("Reader mainloop: ~p bytes available, need ~p~n", [HaveBytes, WaitUntilNBytes]), receive {inet_async, Sock, Ref, {ok, Data}} -> {State1, Callback1, Length1} = @@ -564,7 +563,6 @@ handle_frame(Type, Channel, Payload, error -> throw({unknown_frame, Channel, Type, Payload}); heartbeat -> throw({unexpected_heartbeat_frame, Channel}); AnalyzedFrame -> - %%?LOGDEBUG("Ch ~p Frame ~p~n", [Channel, AnalyzedFrame]), case get({channel, Channel}) of {ch_fr_pid, ChFrPid} -> ok = rabbit_framing_channel:process(ChFrPid, AnalyzedFrame), @@ -628,18 +626,16 @@ analyze_frame(_Type, _Body, _Protocol) -> error. handle_input(frame_header, <>, State) -> - %%?LOGDEBUG("Got frame header: ~p/~p/~p~n", [Type, Channel, PayloadSize]), {ensure_stats_timer(State), {frame_payload, Type, Channel, PayloadSize}, PayloadSize + 1}; -handle_input({frame_payload, Type, Channel, PayloadSize}, PayloadAndMarker, State) -> +handle_input({frame_payload, Type, Channel, PayloadSize}, + PayloadAndMarker, State) -> case PayloadAndMarker of <> -> - %%?LOGDEBUG("Frame completed: ~p/~p/~p~n", [Type, Channel, Payload]), - NewState = handle_frame(Type, Channel, Payload, State), - {NewState, frame_header, 7}; + {handle_frame(Type, Channel, Payload, State), frame_header, 7}; _ -> - throw({bad_payload, PayloadAndMarker}) + throw({bad_payload, Type, Channel, PayloadSize, PayloadAndMarker}) end; %% The two rules pertaining to version negotiation: -- cgit v1.2.1 From 4b82f5cf92c92a585eb820b2f432ca38cb414ce9 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 17 Nov 2010 23:17:24 +0000 Subject: minor optimisation: ask for next piece of data before processing This reduces the likelihood of the reader process stalling, i.e. ending up in 'receive' when there is nothing in the mailbox. --- src/rabbit_reader.erl | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index e68fbca8..d93a23fe 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -320,10 +320,8 @@ start_connection(Parent, ChannelSupSupPid, Collector, StartHeartbeatFun, Deb, mainloop(Deb, State = #v1{parent = Parent, sock= Sock, recv_ref = Ref}) -> receive {inet_async, Sock, Ref, {ok, Data}} -> - {State1, Callback1, Length1} = - handle_input(State#v1.callback, Data, - State#v1{recv_ref = none}), - mainloop(Deb, switch_callback(State1, Callback1, Length1)); + mainloop(Deb, handle_input(State#v1.callback, Data, + State#v1{recv_ref = none})); {inet_async, Sock, Ref, {error, closed}} -> if State#v1.connection_state =:= closed -> State; @@ -626,14 +624,16 @@ analyze_frame(_Type, _Body, _Protocol) -> error. handle_input(frame_header, <>, State) -> - {ensure_stats_timer(State), {frame_payload, Type, Channel, PayloadSize}, - PayloadSize + 1}; + ensure_stats_timer( + switch_callback(State, {frame_payload, Type, Channel, PayloadSize}, + PayloadSize + 1}; handle_input({frame_payload, Type, Channel, PayloadSize}, PayloadAndMarker, State) -> case PayloadAndMarker of <> -> - {handle_frame(Type, Channel, Payload, State), frame_header, 7}; + handle_frame(Type, Channel, Payload, + switch_callback(State, frame_header, 7)); _ -> throw({bad_payload, Type, Channel, PayloadSize, PayloadAndMarker}) end; @@ -686,11 +686,11 @@ start_connection({ProtocolMajor, ProtocolMinor, _ProtocolRevision}, mechanisms = <<"PLAIN AMQPLAIN">>, locales = <<"en_US">> }, ok = send_on_channel0(Sock, Start, Protocol), - {State#v1{connection = Connection#connection{ - timeout_sec = ?NORMAL_TIMEOUT, - protocol = Protocol}, - connection_state = starting}, - frame_header, 7}. + switch_callback(State#v1{connection = Connection#connection{ + timeout_sec = ?NORMAL_TIMEOUT, + protocol = Protocol}, + connection_state = starting}, + frame_header, 7). refuse_connection(Sock, Exception) -> ok = inet_op(fun () -> rabbit_net:send(Sock, <<"AMQP",0,0,9,1>>) end), -- cgit v1.2.1 From f0d3cb40839b47b8ea9321fa0ce57b23b6d991ad Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 17 Nov 2010 23:24:47 +0000 Subject: oops --- 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 d93a23fe..71115a73 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -626,7 +626,7 @@ analyze_frame(_Type, _Body, _Protocol) -> handle_input(frame_header, <>, State) -> ensure_stats_timer( switch_callback(State, {frame_payload, Type, Channel, PayloadSize}, - PayloadSize + 1}; + PayloadSize + 1)); handle_input({frame_payload, Type, Channel, PayloadSize}, PayloadAndMarker, State) -> -- cgit v1.2.1 From ef3a945414a2df5779e183f4c7d599e708ff107f Mon Sep 17 00:00:00 2001 From: Matthew Sackman Date: Thu, 18 Nov 2010 13:21:26 +0000 Subject: Minor refactorings, and a couple of fixes --- src/rabbit_tests.erl | 10 ++--- src/rabbit_variable_queue.erl | 96 ++++++++++++++++++++++--------------------- 2 files changed, 54 insertions(+), 52 deletions(-) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 3130bca3..27e4d925 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -1870,7 +1870,7 @@ test_variable_queue() -> passed. test_variable_queue_ack_limiting(VQ0) -> - %% start by sending in a bunch of messages < + %% start by sending in a bunch of messages Len = 1024, VQ1 = variable_queue_publish(false, Len, VQ0), @@ -1884,11 +1884,11 @@ test_variable_queue_ack_limiting(VQ0) -> %% fetch half the messages {VQ4, _AckTags} = variable_queue_fetch(Len div 2, false, false, Len, VQ3), - VQ5 = check_variable_queue_status(VQ4, [{len , Len div 2}, - {ram_ack_count , Len div 2}, - {ram_msg_count , Len div 2}]), + VQ5 = check_variable_queue_status(VQ4, [{len , Len div 2}, + {ram_ack_count, Len div 2}, + {ram_msg_count, Len div 2}]), - %% quarter the allowed duration + %% ensure all acks go to disk on 0 duration target VQ6 = check_variable_queue_status( rabbit_variable_queue:set_ram_duration_target(0, VQ5), [{len, Len div 2}, diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 48f621e1..e9910a56 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -171,19 +171,18 @@ %% segments. %% %% Pending acks are recorded in memory either as the tuple {SeqId, -%% Guid, MsgProps} (tuple form) or as the message itself (message -%% form). Acks for persistent messages are always stored in the tuple -%% form. Acks for transient messages are also stored in tuple form if -%% the message has been forgotten to disk as part of the memory -%% reduction process. For transient messages that haven't already been -%% written to disk, acks are stored in message form to avoid the -%% overhead of writing to disk. +%% Guid, MsgProps} (tuple-form) or as the message itself (message- +%% form). Acks for persistent messages are always stored in the tuple- +%% form. Acks for transient messages are also stored in tuple-form if +%% the message has been sent to disk as part of the memory reduction +%% process. For transient messages that haven't already been written +%% to disk, acks are stored in message-form. %% -%% During memory reduction, acks stored in message form are converted -%% to tuple form, and the corresponding messages are pushed out to +%% During memory reduction, acks stored in message-form are converted +%% to tuple-form, and the corresponding messages are pushed out to %% disk. %% -%% The order in which alphas are pushed to betas and message form acks +%% The order in which alphas are pushed to betas and message-form acks %% are pushed to disk is determined dynamically. We always prefer to %% push messages for the source (alphas or acks) that is growing the %% fastest (with growth measured as avg. ingress - avg. egress). In @@ -710,14 +709,15 @@ is_empty(State) -> 0 == len(State). set_ram_duration_target(DurationTarget, State = #vqstate { - rates = #rates { avg_egress = AvgEgressRate, - avg_ingress = AvgIngressRate }, + rates = + #rates { avg_egress = AvgEgressRate, + avg_ingress = AvgIngressRate }, ack_rates = #rates { avg_egress = AvgAckEgressRate, avg_ingress = AvgAckIngressRate }, target_ram_item_count = TargetRamItemCount }) -> - Rate = AvgEgressRate + AvgIngressRate + AvgAckEgressRate - + AvgAckIngressRate, + Rate = + AvgEgressRate + AvgIngressRate + AvgAckEgressRate + AvgAckIngressRate, TargetRamItemCount1 = case DurationTarget of infinity -> infinity; @@ -732,10 +732,11 @@ set_ram_duration_target(DurationTarget, end). ram_duration(State = #vqstate { - rates = #rates { egress = Egress, - ingress = Ingress, - timestamp = Timestamp } = Rates, - ack_rates = #rates { egress = AckEgress, + rates = #rates { timestamp = Timestamp, + egress = Egress, + ingress = Ingress } = Rates, + ack_rates = #rates { timestamp = AckTimestamp, + egress = AckEgress, ingress = AckIngress } = ARates, in_counter = InCount, out_counter = OutCount, @@ -749,10 +750,10 @@ ram_duration(State = #vqstate { {AvgEgressRate, Egress1} = update_rate(Now, Timestamp, OutCount, Egress), {AvgIngressRate, Ingress1} = update_rate(Now, Timestamp, InCount, Ingress), - {AvgAckEgressRate, AckEgress1} = - update_rate(Now, Timestamp, AckOutCount, AckEgress), + {AvgAckEgressRate, AckEgress1} = + update_rate(Now, AckTimestamp, AckOutCount, AckEgress), {AvgAckIngressRate, AckIngress1} = - update_rate(Now, Timestamp, AckInCount, AckIngress), + update_rate(Now, AckTimestamp, AckInCount, AckIngress), RamAckCount = gb_trees:size(RamAckIndex), @@ -762,7 +763,7 @@ ram_duration(State = #vqstate { true -> infinity; false -> (RamMsgCountPrev + RamMsgCount + RamAckCount + RamAckCountPrev) / - (2 * (AvgEgressRate + AvgIngressRate + + (4 * (AvgEgressRate + AvgIngressRate + AvgAckEgressRate + AvgAckIngressRate)) end, @@ -774,12 +775,15 @@ ram_duration(State = #vqstate { avg_ingress = AvgIngressRate, timestamp = Now }, ack_rates = ARates #rates { - egress = AckEgress1, - ingress = AckIngress1, - avg_egress = AvgAckEgressRate, - avg_ingress = AvgAckIngressRate }, + egress = AckEgress1, + ingress = AckIngress1, + avg_egress = AvgAckEgressRate, + avg_ingress = AvgAckIngressRate, + timestamp = Now }, in_counter = 0, out_counter = 0, + ack_in_counter = 0, + ack_out_counter = 0, ram_msg_count_prev = RamMsgCount, ram_ack_count_prev = RamAckCount }}. @@ -1047,18 +1051,17 @@ init(IsDurable, IndexState, DeltaCount, Terms, in_counter = 0, ack_out_counter = 0, ack_in_counter = 0, - rates = #rates { egress = {Now, 0}, - ingress = {Now, DeltaCount1}, - avg_egress = 0.0, - avg_ingress = 0.0, - timestamp = Now }, - ack_rates = #rates { egress = {Now, 0}, - ingress = {Now, 0}, - avg_egress = 0.0, - avg_ingress = 0.0, - timestamp = undefined } }, + rates = blank_rate(Now, DeltaCount1), + ack_rates = blank_rate(Now, 0) }, a(maybe_deltas_to_betas(State)). +blank_rate(Timestamp, IngressLength) -> + #rates { egress = {Timestamp, 0}, + ingress = {Timestamp, IngressLength}, + avg_egress = 0.0, + avg_ingress = 0.0, + timestamp = Timestamp }. + msg_store_callback(PersistentGuids, Pubs, AckTags, Fun, MsgPropsFun) -> Self = self(), F = fun () -> rabbit_amqqueue:maybe_run_queue_via_backing_queue( @@ -1315,7 +1318,7 @@ ack(MsgStoreFun, Fun, AckTags, State) -> ack_out_counter = AckOutCount }} = lists:foldl( fun (SeqId, {Acc, State2 = #vqstate { pending_ack = PA, - ram_ack_index = RAI}}) -> + ram_ack_index = RAI }}) -> AckEntry = dict:fetch(SeqId, PA), {accumulate_ack(SeqId, AckEntry, Acc), Fun(AckEntry, State2 #vqstate { @@ -1403,28 +1406,27 @@ reduce_memory_use(AlphaBetaFun, BetaGammaFun, BetaDeltaFun, AckFun, %% acks, push queue messages first. [AlphaBetaFun, AckFun] end, - {_, StateOut} = + {_, State2} = %% Both reduce functions get a chance to reduce %% memory. The second may very well get a quota of %% 0 if the first function managed to push out the %% maximum number of messages. lists:foldl( - fun(ReduceFun, {QuotaN, StateN}) -> + fun (ReduceFun, {QuotaN, StateN}) -> ReduceFun(QuotaN, StateN) end, {S1, State}, ReduceFuns), - {true, StateOut} + {true, State2} end, case State1 #vqstate.target_ram_item_count of - 0 -> {Reduce, BetaDeltaFun(State1)}; - _ -> case chunk_size(State1 #vqstate.ram_index_count, - permitted_ram_index_count(State1)) of - ?IO_BATCH_SIZE = S2 -> {true, BetaGammaFun(S2, State1)}; - _ -> {Reduce, State1} - end + 0 -> {Reduce, BetaDeltaFun(State1)}; + _ -> case chunk_size(State1 #vqstate.ram_index_count, + permitted_ram_index_count(State1)) of + ?IO_BATCH_SIZE = S2 -> {true, BetaGammaFun(S2, State1)}; + _ -> {Reduce, State1} + end end. - limit_ram_acks(0, State) -> {0, State}; limit_ram_acks(Quota, State = #vqstate { pending_ack = PA, -- cgit v1.2.1 From 09a05a6c8a202ba81b26678d8477e2fb6aac9e6c Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 18 Nov 2010 13:25:36 +0000 Subject: Take a backup before upgrading. --- src/rabbit_misc.erl | 32 +++++++++++++++++++++++++++++++- src/rabbit_upgrade.erl | 25 ++++++++++++++++++++++--- 2 files changed, 53 insertions(+), 4 deletions(-) diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 9290508f..44fbe4ad 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -61,7 +61,7 @@ -export([sort_field_table/1]). -export([pid_to_string/1, string_to_pid/1]). -export([version_compare/2, version_compare/3]). --export([recursive_delete/1, dict_cons/3, orddict_cons/3, +-export([recursive_delete/1, recursive_copy/2, dict_cons/3, orddict_cons/3, unlink_and_capture_exit/1]). -export([get_options/2]). -export([all_module_attributes/1, build_acyclic_graph/4]). @@ -184,6 +184,9 @@ -spec(recursive_delete/1 :: ([file:filename()]) -> rabbit_types:ok_or_error({file:filename(), any()})). +-spec(recursive_copy/2 :: + (file:filename(), file:filename()) + -> rabbit_types:ok_or_error({file:filename(), file:filename(), any()})). -spec(dict_cons/3 :: (any(), any(), dict()) -> dict()). -spec(orddict_cons/3 :: (any(), any(), orddict:orddict()) -> orddict:orddict()). -spec(unlink_and_capture_exit/1 :: (pid()) -> 'ok'). @@ -684,6 +687,33 @@ recursive_delete1(Path) -> end end. +recursive_copy(Src, Dest) -> + case filelib:is_dir(Src) of + false -> case file:copy(Src, Dest) of + {ok, _Bytes} -> ok; + {error, enoent} -> ok; %% Path doesn't exist anyway + {error, Err} -> {error, {Src, Dest, Err}} + end; + true -> case file:list_dir(Src) of + {ok, FileNames} -> + case file:make_dir(Dest) of + ok -> + lists:foldl( + fun (FileName, ok) -> + recursive_copy( + filename:join(Src, FileName), + filename:join(Dest, FileName)); + (_FileName, Error) -> + Error + end, ok, FileNames); + {error, Err} -> + {error, {Src, Dest, Err}} + end; + {error, Err} -> + {error, {Src, Dest, Err}} + end + end. + dict_cons(Key, Value, Dict) -> dict:update(Key, fun (List) -> [Value | List] end, [Value], Dict). diff --git a/src/rabbit_upgrade.erl b/src/rabbit_upgrade.erl index 9522227e..4b7b4342 100644 --- a/src/rabbit_upgrade.erl +++ b/src/rabbit_upgrade.erl @@ -120,14 +120,33 @@ heads(G) -> %% ------------------------------------------------------------------- apply_upgrades(Upgrades) -> - LockFile = lock_filename(), + LockFile = lock_filename(dir()), case file:open(LockFile, [write, exclusive]) of {ok, Lock} -> ok = file:close(Lock), + BackupDir = dir() ++ "-upgrade-backup", info("Upgrades: ~w to apply~n", [length(Upgrades)]), + case rabbit_misc:recursive_copy(dir(), BackupDir) of + ok -> + %% We need to make the backup after creating the lock file + %% so that it protects us from trying to overwrite the + %% backup. Unfortunately this means the lock file exists in + %% the backup too, which is not intuitive. Remove it. + ok = file:delete(lock_filename(BackupDir)), + ok; + {error, E} -> + %% If we can't backup, the upgrade hasn't started hence we + %% don't need the lockfile since the real mnesia dir is the + %% good one. + ok = file:delete(LockFile), + exit({could_not_back_up_mnesia_dir, E}) + end, + info("Upgrades: Mnesia dir backed up to ~p~n", [BackupDir]), [apply_upgrade(Upgrade) || Upgrade <- Upgrades], - info("Upgrades: All applied~n", []), + info("Upgrades: All upgrades applied successfully~n", []), ok = write_version(), + ok = rabbit_misc:recursive_delete([BackupDir]), + info("Upgrades: Mnesia backup removed~n", []), ok = file:delete(LockFile); {error, eexist} -> exit(previous_upgrade_failed); @@ -145,7 +164,7 @@ dir() -> rabbit_mnesia:dir(). schema_filename() -> filename:join(dir(), ?VERSION_FILENAME). -lock_filename() -> filename:join(dir(), ?LOCK_FILENAME). +lock_filename(Dir) -> filename:join(Dir, ?LOCK_FILENAME). %% NB: we cannot use rabbit_log here since it may not have been %% started yet -- cgit v1.2.1 From e7a7de3479960cc68f8549a7d517203d6f46f5d4 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 18 Nov 2010 16:22:59 +0000 Subject: Stop mnesia while taking the backup --- src/rabbit_mnesia.erl | 3 ++- src/rabbit_upgrade.erl | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index da81f884..e8547041 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -34,7 +34,7 @@ -export([ensure_mnesia_dir/0, dir/0, status/0, init/0, is_db_empty/0, cluster/1, force_cluster/1, reset/0, force_reset/0, - is_clustered/0, empty_ram_only_tables/0]). + is_clustered/0, empty_ram_only_tables/0, wait_for_tables/0]). -export([table_names/0]). @@ -65,6 +65,7 @@ -spec(is_clustered/0 :: () -> boolean()). -spec(empty_ram_only_tables/0 :: () -> 'ok'). -spec(create_tables/0 :: () -> 'ok'). +-spec(wait_for_tables/0 :: () -> 'ok'). -endif. diff --git a/src/rabbit_upgrade.erl b/src/rabbit_upgrade.erl index 4b7b4342..83ab2595 100644 --- a/src/rabbit_upgrade.erl +++ b/src/rabbit_upgrade.erl @@ -126,6 +126,7 @@ apply_upgrades(Upgrades) -> ok = file:close(Lock), BackupDir = dir() ++ "-upgrade-backup", info("Upgrades: ~w to apply~n", [length(Upgrades)]), + mnesia:stop(), case rabbit_misc:recursive_copy(dir(), BackupDir) of ok -> %% We need to make the backup after creating the lock file @@ -141,6 +142,8 @@ apply_upgrades(Upgrades) -> ok = file:delete(LockFile), exit({could_not_back_up_mnesia_dir, E}) end, + rabbit_misc:ensure_ok(mnesia:start(), cannot_start_mnesia), + rabbit_mnesia:wait_for_tables(), info("Upgrades: Mnesia dir backed up to ~p~n", [BackupDir]), [apply_upgrade(Upgrade) || Upgrade <- Upgrades], info("Upgrades: All upgrades applied successfully~n", []), -- cgit v1.2.1 From 7d4d318c48c827318e929fa79c1ce68614a1bd64 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 18 Nov 2010 17:04:53 +0000 Subject: Remove rabbitmq-asroot-script-wrapper --- packaging/RPMS/Fedora/rabbitmq-server.spec | 9 ++--- packaging/common/rabbitmq-asroot-script-wrapper | 45 ------------------------- packaging/macports/Makefile | 4 +-- 3 files changed, 4 insertions(+), 54 deletions(-) delete mode 100644 packaging/common/rabbitmq-asroot-script-wrapper diff --git a/packaging/RPMS/Fedora/rabbitmq-server.spec b/packaging/RPMS/Fedora/rabbitmq-server.spec index 209a90ee..5032f471 100644 --- a/packaging/RPMS/Fedora/rabbitmq-server.spec +++ b/packaging/RPMS/Fedora/rabbitmq-server.spec @@ -9,8 +9,7 @@ Source: http://www.rabbitmq.com/releases/rabbitmq-server/v%{version}/%{name}-%{v Source1: rabbitmq-server.init Source2: rabbitmq-script-wrapper Source3: rabbitmq-server.logrotate -Source4: rabbitmq-asroot-script-wrapper -Source5: rabbitmq-server.ocf +Source4: rabbitmq-server.ocf URL: http://www.rabbitmq.com/ BuildArch: noarch BuildRequires: erlang >= R12B-3, python-simplejson, xmlto, libxslt @@ -29,8 +28,7 @@ scalable implementation of an AMQP broker. %define _rabbit_libdir %{_exec_prefix}/lib/rabbitmq %define _rabbit_erllibdir %{_rabbit_libdir}/lib/rabbitmq_server-%{version} %define _rabbit_wrapper %{_builddir}/`basename %{S:2}` -%define _rabbit_asroot_wrapper %{_builddir}/`basename %{S:4}` -%define _rabbit_server_ocf %{_builddir}/`basename %{S:5}` +%define _rabbit_server_ocf %{_builddir}/`basename %{S:4}` %define _plugins_state_dir %{_localstatedir}/lib/rabbitmq/plugins %define _maindir %{buildroot}%{_rabbit_erllibdir} @@ -40,8 +38,7 @@ scalable implementation of an AMQP broker. %build cp %{S:2} %{_rabbit_wrapper} -cp %{S:4} %{_rabbit_asroot_wrapper} -cp %{S:5} %{_rabbit_server_ocf} +cp %{S:4} %{_rabbit_server_ocf} make %{?_smp_mflags} %install diff --git a/packaging/common/rabbitmq-asroot-script-wrapper b/packaging/common/rabbitmq-asroot-script-wrapper deleted file mode 100644 index 693a6f0b..00000000 --- a/packaging/common/rabbitmq-asroot-script-wrapper +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/sh -## The contents of this file are subject to the Mozilla Public License -## Version 1.1 (the "License"); you may not use this file except in -## compliance with the License. You may obtain a copy of the License at -## http://www.mozilla.org/MPL/ -## -## Software distributed under the License is distributed on an "AS IS" -## basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the -## License for the specific language governing rights and limitations -## under the License. -## -## The Original Code is RabbitMQ. -## -## The Initial Developers of the Original Code are LShift Ltd, -## Cohesive Financial Technologies LLC, and Rabbit Technologies Ltd. -## -## Portions created before 22-Nov-2008 00:00:00 GMT by LShift Ltd, -## Cohesive Financial Technologies LLC, or Rabbit Technologies Ltd -## are Copyright (C) 2007-2008 LShift Ltd, Cohesive Financial -## Technologies LLC, and Rabbit Technologies Ltd. -## -## Portions created by LShift Ltd are Copyright (C) 2007-2010 LShift -## Ltd. Portions created by Cohesive Financial Technologies LLC are -## Copyright (C) 2007-2010 Cohesive Financial Technologies -## LLC. Portions created by Rabbit Technologies Ltd are Copyright -## (C) 2007-2010 Rabbit Technologies Ltd. -## -## All Rights Reserved. -## -## Contributor(s): ______________________________________. -## - -cd /var/lib/rabbitmq - -SCRIPT=`basename $0` - -if [ `id -u` = 0 ] ; then - /usr/lib/rabbitmq/bin/${SCRIPT} "$@" -else - echo - echo "Only root should run ${SCRIPT}" - echo - exit 1 -fi - diff --git a/packaging/macports/Makefile b/packaging/macports/Makefile index ee79c95a..47da02dc 100644 --- a/packaging/macports/Makefile +++ b/packaging/macports/Makefile @@ -38,9 +38,7 @@ $(DEST)/Portfile: Portfile.in # needs vars such as HOME to be set. So we have to set them # explicitly. macports: dirs $(DEST)/Portfile - for f in rabbitmq-asroot-script-wrapper rabbitmq-script-wrapper ; do \ - cp $(COMMON_DIR)/$$f $(DEST)/files ; \ - done + cp $(COMMON_DIR)/rabbitmq-script-wrapper $(DEST)/files sed -i -e 's|@SU_RABBITMQ_SH_C@|SHELL=/bin/sh HOME=/var/lib/rabbitmq USER=rabbitmq LOGNAME=rabbitmq PATH="$$(eval `PATH=MACPORTS_PREFIX/bin /usr/libexec/path_helper -s`; echo $$PATH)" su -m rabbitmq -c|' \ $(DEST)/files/rabbitmq-script-wrapper cp patch-org.macports.rabbitmq-server.plist.diff $(DEST)/files -- cgit v1.2.1 From 43f00ae3c8bbff8c0ae9770e09d258bfd0d742f8 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Tue, 23 Nov 2010 16:07:54 +0000 Subject: Use internal_send_command, since we're sending to a 'raw' socket --- src/rabbit_reader.erl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 71115a73..28876838 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -590,8 +590,9 @@ handle_frame(Type, Channel, Payload, %% We're already closing this channel, so %% there's no cleanup to do (notify %% queues, etc.) - ok = rabbit_writer:send_command(State#v1.sock, - #'channel.close_ok'{}); + ok = rabbit_writer:internal_send_command( + State#v1.sock, Channel, #'channel.close_ok'{}, + Protocol); _ -> ok end, State; -- cgit v1.2.1 From afe4c0db445cbb8117f94bf1e5e4b4d7343341ca Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 23 Nov 2010 17:22:18 +0000 Subject: cosmetic --- src/rabbit_reader.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 28876838..4dd150a2 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -591,8 +591,8 @@ handle_frame(Type, Channel, Payload, %% there's no cleanup to do (notify %% queues, etc.) ok = rabbit_writer:internal_send_command( - State#v1.sock, Channel, #'channel.close_ok'{}, - Protocol); + State#v1.sock, Channel, + #'channel.close_ok'{}, Protocol); _ -> ok end, State; -- cgit v1.2.1 From 400f210f9a4c8bc92b5f1953ac3b4f637dca0671 Mon Sep 17 00:00:00 2001 From: Matthew Sackman Date: Tue, 23 Nov 2010 17:40:09 +0000 Subject: It's rather crucial to call reduce_memory_use at the end of publish_delivered if we've added to the acks. Also reorder items in backing_queue_status so that they fit better on my 160-char wide xterms. It's also a bit more logical --- src/rabbit_variable_queue.erl | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index e9910a56..5ac042a2 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -521,10 +521,11 @@ publish_delivered(true, Msg = #basic_message { is_persistent = IsPersistent }, {MsgStatus1, State1} = maybe_write_to_disk(false, false, MsgStatus, State), State2 = record_pending_ack(m(MsgStatus1), State1), PCount1 = PCount + one_if(IsPersistent1), - {SeqId, a(State2 #vqstate { next_seq_id = SeqId + 1, - out_counter = OutCount + 1, - in_counter = InCount + 1, - persistent_count = PCount1 })}. + {SeqId, a(reduce_memory_use( + State2 #vqstate { next_seq_id = SeqId + 1, + out_counter = OutCount + 1, + in_counter = InCount + 1, + persistent_count = PCount1 }))}. dropwhile(Pred, State) -> {_OkOrEmpty, State1} = dropwhile1(Pred, State), @@ -825,17 +826,17 @@ status(#vqstate { q1 = Q1, q2 = Q2, delta = Delta, q3 = Q3, q4 = Q4, {q4 , queue:len(Q4)}, {len , Len}, {pending_acks , dict:size(PA)}, - {ram_ack_count , gb_trees:size(RAI)}, {outstanding_txns , length(From)}, {target_ram_item_count , TargetRamItemCount}, {ram_msg_count , RamMsgCount}, + {ram_ack_count , gb_trees:size(RAI)}, {ram_index_count , RamIndexCount}, {next_seq_id , NextSeqId}, {persistent_count , PersistentCount}, - {avg_egress_rate , AvgEgressRate}, {avg_ingress_rate , AvgIngressRate}, - {avg_ack_egress_rate , AvgAckEgressRate}, - {avg_ack_ingress_rate , AvgAckIngressRate}]. + {avg_egress_rate , AvgEgressRate}, + {avg_ack_ingress_rate , AvgAckIngressRate}, + {avg_ack_egress_rate , AvgAckEgressRate} ]. %%---------------------------------------------------------------------------- %% Minor helpers -- cgit v1.2.1 From f7cb7d6518fbd94cac33d78637d06ebdaf3eddaf Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Wed, 24 Nov 2010 10:38:56 +0000 Subject: remove redundant record field --- include/rabbit.hrl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/rabbit.hrl b/include/rabbit.hrl index 1b877214..fccfad97 100644 --- a/include/rabbit.hrl +++ b/include/rabbit.hrl @@ -70,8 +70,8 @@ -record(ssl_socket, {tcp, ssl}). -record(delivery, {mandatory, immediate, txn, sender, message, - origin, msg_seq_no}). --record(amqp_error, {name, explanation, method = none}). + msg_seq_no}). +-record(amqp_error, {name, explanation = "", method = none}). -record(event, {type, props, timestamp}). -- cgit v1.2.1 From 215f40f5fa6ea7564d2ab2d5b4d24a19622544d9 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Wed, 24 Nov 2010 11:58:08 +0000 Subject: cosmetic + handle missing case --- src/rabbit_amqqueue_process.erl | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 1d7c62c3..0a3713e3 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -445,14 +445,16 @@ confirm_message(#basic_message{guid = Guid}, State) -> record_confirm_message(#delivery{msg_seq_no = undefined}, State) -> State; -record_confirm_message(#delivery{message = #basic_message{ - is_persistent = false}}, State) -> - State; -record_confirm_message(#delivery{msg_seq_no = MsgSeqNo, +record_confirm_message(#delivery{message = #basic_message { + is_persistent = true, + guid = Guid}, sender = ChPid, - message = #basic_message{guid = Guid}}, - State = #q{guid_to_channel = GTC}) -> - State#q{guid_to_channel = dict:store(Guid, {ChPid, MsgSeqNo}, GTC)}. + msg_seq_no = MsgSeqNo}, + State = #q{q = #amqqueue{durable = true}, + guid_to_channel = GTC}) -> + State#q{guid_to_channel = dict:store(Guid, {ChPid, MsgSeqNo}, GTC)}; +record_confirm_message(_Delivery, State) -> + State. ack_by_acktags(AckTags, State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> @@ -486,11 +488,12 @@ attempt_delivery(#delivery{txn = none, %% not being enqueued, so we use an empty %% message_properties. {AckTag, BQS1} = - BQ:publish_delivered(AckRequired, Message, - ?BASE_MESSAGE_PROPERTIES - #message_properties { - needs_confirming = NeedsConfirming }, - BQS), + BQ:publish_delivered( + AckRequired, Message, + ?BASE_MESSAGE_PROPERTIES + #message_properties{ + needs_confirming = NeedsConfirming}, + BQS), {{Message, false, AckTag}, true, State1#q{backing_queue_state = BQS1}} end, @@ -512,8 +515,8 @@ deliver_or_enqueue(Delivery, State) -> {false, State1 = #q{backing_queue = BQ, backing_queue_state = BQS}} -> #delivery{message = Message, msg_seq_no = MsgSeqNo} = Delivery, BQS1 = BQ:publish(Message, - (message_properties(State)) #message_properties { - needs_confirming = (MsgSeqNo =/= undefined) }, + (message_properties(State)) #message_properties{ + needs_confirming = (MsgSeqNo =/= undefined)}, BQS), {false, ensure_ttl_timer(State1#q{backing_queue_state = BQS1})} end. -- cgit v1.2.1 From f32f7994cdca2ca32165dddb15c346fdfc6fd7ea Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Wed, 24 Nov 2010 12:37:54 +0000 Subject: cosmetic --- src/rabbit_channel.erl | 69 +++++++++++++++++++++++--------------------------- 1 file changed, 31 insertions(+), 38 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 4695d619..8f11faef 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -104,7 +104,7 @@ -spec(info_all/1 :: (rabbit_types:info_keys()) -> [rabbit_types:infos()]). -spec(emit_stats/1 :: (pid()) -> 'ok'). -spec(flush_multiple_acks/1 :: (pid()) -> 'ok'). --spec(confirm/2 ::(pid(), integer()) -> 'ok'). +-spec(confirm/2 ::(pid(), non_neg_integer()) -> 'ok'). -endif. @@ -279,13 +279,8 @@ handle_cast(emit_stats, State = #ch{stats_timer = StatsTimer}) -> State#ch{stats_timer = rabbit_event:reset_stats_timer(StatsTimer)}, hibernate}; -handle_cast(flush_multiple_acks, - State = #ch{writer_pid = WriterPid, - held_confirms = As, - unconfirmed = UC}) -> - flush_multiple(WriterPid, As, UC), - {noreply, State#ch{held_confirms = gb_sets:new(), - confirm_tref = undefined}}; +handle_cast(flush_multiple_acks, State) -> + {noreply, flush_multiple(State)}; handle_cast({confirm, MsgSeqNo, From}, State) -> {noreply, send_or_enqueue_ack(MsgSeqNo, From, State)}. @@ -304,19 +299,14 @@ handle_info({'DOWN', _MRef, process, QPid, _Reason}, erase_queue_stats(QPid), {noreply, queue_blocked(QPid, State1), hibernate}. -handle_pre_hibernate(State = #ch{writer_pid = WriterPid, - held_confirms = As, - stats_timer = StatsTimer, - unconfirmed = UC}) -> +handle_pre_hibernate(State = #ch{stats_timer = StatsTimer}) -> ok = clear_permission_cache(), - flush_multiple(WriterPid, As, UC), + State1 = flush_multiple(State), rabbit_event:if_enabled(StatsTimer, fun() -> - internal_emit_stats(State) + internal_emit_stats(State1) end), StatsTimer1 = rabbit_event:stop_stats_timer(StatsTimer), - {hibernate, State#ch{held_confirms = gb_sets:new(), - stats_timer = StatsTimer1, - confirm_tref = undefined}}. + {hibernate, State1#ch{stats_timer = StatsTimer1}}. terminate(_Reason, State = #ch{state = terminating}) -> terminate(State); @@ -477,7 +467,7 @@ send_or_enqueue_ack(MsgSeqNo, QPid, State = #ch{confirm_multiple = true}) -> do_if_unconfirmed( MsgSeqNo, QPid, fun(MSN, State1 = #ch{held_confirms = As}) -> - start_ack_timer(State1#ch{held_confirms = gb_sets:add(MSN, As)}) + start_confirm_timer(State1#ch{held_confirms = gb_sets:add(MSN, As)}) end, State). do_if_unconfirmed(MsgSeqNo, QPid, ConfirmFun, @@ -486,11 +476,11 @@ do_if_unconfirmed(MsgSeqNo, QPid, ConfirmFun, %% clears references to MsgSeqNo and does ConfirmFun case gb_sets:is_element(MsgSeqNo, UC) of true -> + Unconfirmed1 = gb_sets:delete(MsgSeqNo, UC), case QPid of undefined -> ConfirmFun(MsgSeqNo, - State#ch{unconfirmed = - gb_sets:delete(MsgSeqNo, UC)}); + State#ch{unconfirmed = Unconfirmed1}); _ -> {ok, Qs} = dict:find(MsgSeqNo, QFM), Qs1 = sets:del_element(QPid, Qs), @@ -499,8 +489,7 @@ do_if_unconfirmed(MsgSeqNo, QPid, ConfirmFun, State#ch{ queues_for_msg = dict:erase(MsgSeqNo, QFM), - unconfirmed = - gb_sets:delete(MsgSeqNo, UC)}); + unconfirmed = Unconfirmed1}); _ -> State#ch{queues_for_msg = dict:store(MsgSeqNo, Qs1, QFM)} end @@ -557,7 +546,8 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, {RoutingRes, DeliveredQPids} = rabbit_exchange:publish( Exchange, - rabbit_basic:delivery(Mandatory, Immediate, TxnKey, Message, MsgSeqNo)), + rabbit_basic:delivery(Mandatory, Immediate, TxnKey, Message, + MsgSeqNo)), State2 = process_routing_result(RoutingRes, DeliveredQPids, MsgSeqNo, Message, State1), maybe_incr_stats([{ExchangeName, 1} | @@ -1280,7 +1270,7 @@ internal_deliver(WriterPid, Notify, ConsumerTag, DeliveryTag, end. terminate(State) -> - stop_ack_timer(State), + stop_confirm_timer(State), pg_local:leave(rabbit_channels, self()), rabbit_event:notify(channel_closed, [{pid, self()}]). @@ -1363,35 +1353,38 @@ erase_queue_stats(QPid) -> [erase({queue_exchange_stats, QX}) || {{queue_exchange_stats, QX = {QPid0, _}}, _} <- get(), QPid =:= QPid0]. -start_ack_timer(State = #ch{confirm_tref = undefined}) -> +start_confirm_timer(State = #ch{confirm_tref = undefined}) -> {ok, TRef} = timer:apply_after(?FLUSH_MULTIPLE_ACKS_INTERVAL, ?MODULE, flush_multiple_acks, [self()]), State#ch{confirm_tref = TRef}; -start_ack_timer(State) -> +start_confirm_timer(State) -> State. -stop_ack_timer(State = #ch{confirm_tref = undefined}) -> +stop_confirm_timer(State = #ch{confirm_tref = undefined}) -> State; -stop_ack_timer(State = #ch{confirm_tref = TRef}) -> +stop_confirm_timer(State = #ch{confirm_tref = TRef}) -> {ok, cancel} = timer:cancel(TRef), State#ch{confirm_tref = undefined}. -flush_multiple(WriterPid, As, NA) -> - case gb_sets:is_empty(As) of - true -> ok; - false -> [First | Rest] = gb_sets:to_list(As), +flush_multiple(State = #ch{writer_pid = WriterPid, + held_confirms = Cs, + unconfirmed = UC}) -> + case gb_sets:is_empty(Cs) of + true -> State; + false -> [First | Rest] = gb_sets:to_list(Cs), [rabbit_writer:send_command(WriterPid, - #'basic.ack'{delivery_tag = A}) || - A <- case Rest of + #'basic.ack'{delivery_tag = T}) || + T <- case Rest of [] -> [First]; _ -> flush_multiple( First, Rest, WriterPid, - case gb_sets:is_empty(NA) of - false -> gb_sets:smallest(NA); - true -> gb_sets:largest(As) + 1 + case gb_sets:is_empty(UC) of + false -> gb_sets:smallest(UC); + true -> gb_sets:largest(Cs) + 1 end) end], - ok + State#ch{held_confirms = gb_sets:new(), + confirm_tref = undefined} end. flush_multiple(Prev, [Cur | Rest], WriterPid, SNA) -> -- cgit v1.2.1 From 864455892ac2e007ecb989515710c45a539fc761 Mon Sep 17 00:00:00 2001 From: Matthew Sackman Date: Wed, 24 Nov 2010 12:38:35 +0000 Subject: gratuitous cosmetic --- src/rabbit_mnesia.erl | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index 431f9a21..6c74d4dd 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -378,10 +378,8 @@ init_db(ClusterNodes, Force) -> %% True single disc node, attempt upgrade wait_for_tables(), case rabbit_upgrade:maybe_upgrade() of - ok -> - ensure_schema_ok(); - version_not_available -> - schema_ok_or_move() + ok -> ensure_schema_ok(); + version_not_available -> schema_ok_or_move() end; {[], true, _} -> %% "Master" (i.e. without config) disc node in cluster, -- cgit v1.2.1 From 0abc8207c7299a292caaf3d8c4279c25cde8c57b Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Wed, 24 Nov 2010 12:51:04 +0000 Subject: cosmetic --- src/rabbit_msg_store.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rabbit_msg_store.erl b/src/rabbit_msg_store.erl index 40e184f1..96d67ad4 100644 --- a/src/rabbit_msg_store.erl +++ b/src/rabbit_msg_store.erl @@ -150,7 +150,8 @@ (atom(), file:filename(), [binary()] | 'undefined', startup_fun_state()) -> rabbit_types:ok_pid_or_error()). -spec(successfully_recovered_state/1 :: (server()) -> boolean()). --spec(client_init/3 :: (server(), client_ref(), guid_fun()) -> client_msstate()). +-spec(client_init/3 :: (server(), client_ref(), guid_fun()) -> + client_msstate()). -spec(client_terminate/1 :: (client_msstate()) -> 'ok'). -spec(client_delete_and_terminate/1 :: (client_msstate()) -> 'ok'). -spec(client_ref/1 :: (client_msstate()) -> client_ref()). -- cgit v1.2.1 From 89e810694694c470eeb49ae30ad96d1063677db5 Mon Sep 17 00:00:00 2001 From: Matthew Sackman Date: Wed, 24 Nov 2010 13:15:05 +0000 Subject: Refactoring --- src/rabbit_mnesia.erl | 18 ++++++++++++++---- src/rabbit_upgrade.erl | 22 +++++++++------------- 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index e8547041..8c7e30f9 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -34,7 +34,7 @@ -export([ensure_mnesia_dir/0, dir/0, status/0, init/0, is_db_empty/0, cluster/1, force_cluster/1, reset/0, force_reset/0, - is_clustered/0, empty_ram_only_tables/0, wait_for_tables/0]). + is_clustered/0, empty_ram_only_tables/0, copy_db/1]). -export([table_names/0]). @@ -65,7 +65,7 @@ -spec(is_clustered/0 :: () -> boolean()). -spec(empty_ram_only_tables/0 :: () -> 'ok'). -spec(create_tables/0 :: () -> 'ok'). --spec(wait_for_tables/0 :: () -> 'ok'). +-spec(copy_db/1 :: (file:filename()) -> rabbit_types:ok_or_error(any())). -endif. @@ -376,7 +376,7 @@ init_db(ClusterNodes, Force) -> mnesia:system_info(db_nodes)} of {[], true, [_]} -> %% True single disc node, attempt upgrade - wait_for_tables(), + ok = wait_for_tables(), case rabbit_upgrade:maybe_upgrade() of ok -> schema_ok_or_exit(); @@ -386,7 +386,7 @@ init_db(ClusterNodes, Force) -> {[], true, _} -> %% "Master" (i.e. without config) disc node in cluster, %% verify schema - wait_for_tables(), + ok = wait_for_tables(), version_ok_or_exit(rabbit_upgrade:read_version()), schema_ok_or_exit(); {[], false, _} -> @@ -480,6 +480,16 @@ move_db() -> rabbit_misc:ensure_ok(mnesia:start(), cannot_start_mnesia), ok. +copy_db(Destination) -> + mnesia:stop(), + case rabbit_misc:recursive_copy(dir(), Destination) of + ok -> + rabbit_misc:ensure_ok(mnesia:start(), cannot_start_mnesia), + ok = wait_for_tables(); + {error, E} -> + {error, E} + end. + create_tables() -> lists:foreach(fun ({Tab, TabDef}) -> TabDef1 = proplists:delete(match, TabDef), diff --git a/src/rabbit_upgrade.erl b/src/rabbit_upgrade.erl index 83ab2595..27f18588 100644 --- a/src/rabbit_upgrade.erl +++ b/src/rabbit_upgrade.erl @@ -126,31 +126,27 @@ apply_upgrades(Upgrades) -> ok = file:close(Lock), BackupDir = dir() ++ "-upgrade-backup", info("Upgrades: ~w to apply~n", [length(Upgrades)]), - mnesia:stop(), - case rabbit_misc:recursive_copy(dir(), BackupDir) of + case rabbit_mnesia:copy_db(BackupDir) of ok -> %% We need to make the backup after creating the lock file %% so that it protects us from trying to overwrite the %% backup. Unfortunately this means the lock file exists in %% the backup too, which is not intuitive. Remove it. ok = file:delete(lock_filename(BackupDir)), - ok; + info("Upgrades: Mnesia dir backed up to ~p~n", [BackupDir]), + [apply_upgrade(Upgrade) || Upgrade <- Upgrades], + info("Upgrades: All upgrades applied successfully~n", []), + ok = write_version(), + ok = rabbit_misc:recursive_delete([BackupDir]), + info("Upgrades: Mnesia backup removed~n", []), + ok = file:delete(LockFile); {error, E} -> %% If we can't backup, the upgrade hasn't started hence we %% don't need the lockfile since the real mnesia dir is the %% good one. ok = file:delete(LockFile), exit({could_not_back_up_mnesia_dir, E}) - end, - rabbit_misc:ensure_ok(mnesia:start(), cannot_start_mnesia), - rabbit_mnesia:wait_for_tables(), - info("Upgrades: Mnesia dir backed up to ~p~n", [BackupDir]), - [apply_upgrade(Upgrade) || Upgrade <- Upgrades], - info("Upgrades: All upgrades applied successfully~n", []), - ok = write_version(), - ok = rabbit_misc:recursive_delete([BackupDir]), - info("Upgrades: Mnesia backup removed~n", []), - ok = file:delete(LockFile); + end; {error, eexist} -> exit(previous_upgrade_failed); {error, _} = Error -> -- cgit v1.2.1 From 2e61008fe9ea9166d45b4c44ebc427a79e8e9397 Mon Sep 17 00:00:00 2001 From: Matthew Sackman Date: Wed, 24 Nov 2010 13:20:54 +0000 Subject: Application of M-Q --- src/rabbit_upgrade.erl | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/rabbit_upgrade.erl b/src/rabbit_upgrade.erl index 27f18588..246fbef7 100644 --- a/src/rabbit_upgrade.erl +++ b/src/rabbit_upgrade.erl @@ -128,10 +128,11 @@ apply_upgrades(Upgrades) -> info("Upgrades: ~w to apply~n", [length(Upgrades)]), case rabbit_mnesia:copy_db(BackupDir) of ok -> - %% We need to make the backup after creating the lock file - %% so that it protects us from trying to overwrite the - %% backup. Unfortunately this means the lock file exists in - %% the backup too, which is not intuitive. Remove it. + %% We need to make the backup after creating the + %% lock file so that it protects us from trying to + %% overwrite the backup. Unfortunately this means + %% the lock file exists in the backup too, which + %% is not intuitive. Remove it. ok = file:delete(lock_filename(BackupDir)), info("Upgrades: Mnesia dir backed up to ~p~n", [BackupDir]), [apply_upgrade(Upgrade) || Upgrade <- Upgrades], @@ -141,9 +142,9 @@ apply_upgrades(Upgrades) -> info("Upgrades: Mnesia backup removed~n", []), ok = file:delete(LockFile); {error, E} -> - %% If we can't backup, the upgrade hasn't started hence we - %% don't need the lockfile since the real mnesia dir is the - %% good one. + %% If we can't backup, the upgrade hasn't started + %% hence we don't need the lockfile since the real + %% mnesia dir is the good one. ok = file:delete(LockFile), exit({could_not_back_up_mnesia_dir, E}) end; -- cgit v1.2.1 From 51a2041df7647cc91ffc6ae8a84eb063279de4d9 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Wed, 24 Nov 2010 13:43:15 +0000 Subject: cosmetic --- src/rabbit_amqqueue_process.erl | 14 +++++++------- src/rabbit_router.erl | 9 +++++---- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 0a3713e3..bb98fe31 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -445,13 +445,13 @@ confirm_message(#basic_message{guid = Guid}, State) -> record_confirm_message(#delivery{msg_seq_no = undefined}, State) -> State; -record_confirm_message(#delivery{message = #basic_message { - is_persistent = true, - guid = Guid}, - sender = ChPid, - msg_seq_no = MsgSeqNo}, - State = #q{q = #amqqueue{durable = true}, - guid_to_channel = GTC}) -> +record_confirm_message(#delivery{sender = ChPid, + msg_seq_no = MsgSeqNo, + message = #basic_message { + is_persistent = true, + guid = Guid}}, + State = #q{guid_to_channel = GTC, + q = #amqqueue{durable = true}}) -> State#q{guid_to_channel = dict:store(Guid, {ChPid, MsgSeqNo}, GTC)}; record_confirm_message(_Delivery, State) -> State. diff --git a/src/rabbit_router.erl b/src/rabbit_router.erl index 4a1a08e4..e36feb76 100644 --- a/src/rabbit_router.erl +++ b/src/rabbit_router.erl @@ -75,10 +75,11 @@ deliver(QNames, Delivery = #delivery{mandatory = false, deliver(QNames, Delivery = #delivery{mandatory = Mandatory, immediate = Immediate}) -> QPids = lookup_qpids(QNames), - {Success, _} = delegate:invoke( - QPids, fun (Pid) -> - rabbit_amqqueue:deliver(Pid, Delivery) - end), + {Success, _} = + delegate:invoke(QPids, + fun (Pid) -> + rabbit_amqqueue:deliver(Pid, Delivery) + end), {Routed, Handled} = lists:foldl(fun fold_deliveries/2, {false, []}, Success), case check_delivery(Mandatory, Immediate, {Routed, Handled}) of -- cgit v1.2.1 From 817ea4c665772214eb3acfbcca49be709825649c Mon Sep 17 00:00:00 2001 From: Matthew Sackman Date: Wed, 24 Nov 2010 13:43:35 +0000 Subject: exit -> throw seeing as bug 23535 is invalid --- src/rabbit_upgrade.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_upgrade.erl b/src/rabbit_upgrade.erl index 40af654a..64d0c251 100644 --- a/src/rabbit_upgrade.erl +++ b/src/rabbit_upgrade.erl @@ -152,7 +152,7 @@ apply_upgrades(Upgrades) -> %% hence we don't need the lockfile since the real %% mnesia dir is the good one. ok = file:delete(LockFile), - exit({could_not_back_up_mnesia_dir, E}) + throw({could_not_back_up_mnesia_dir, E}) end; {error, eexist} -> throw({error, previous_upgrade_failed}); -- cgit v1.2.1 From d5f9803e4389abb922a211e5577c1b728bef1a19 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Wed, 24 Nov 2010 14:02:54 +0000 Subject: change order of tuple elements --- src/rabbit_amqqueue.erl | 4 ++-- src/rabbit_amqqueue_process.erl | 2 +- src/rabbit_variable_queue.erl | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 0872a048..88ad5f06 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -157,9 +157,9 @@ (name()) -> rabbit_types:ok_or_error('not_found') | rabbit_types:connection_exit()). -spec(maybe_run_queue_via_backing_queue/2 :: - (pid(), (fun ((A) -> A))) -> 'ok'). + (pid(), (fun ((A) -> A | {any(), A}))) -> 'ok'). -spec(maybe_run_queue_via_backing_queue_async/2 :: - (pid(), (fun ((A) -> A))) -> 'ok'). + (pid(), (fun ((A) -> A | {any(), A}))) -> 'ok'). -spec(update_ram_duration/1 :: (pid()) -> 'ok'). -spec(set_ram_duration_target/2 :: (pid(), number() | 'infinity') -> 'ok'). -spec(set_maximum_since_use/2 :: (pid(), non_neg_integer()) -> 'ok'). diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index bb98fe31..4672e339 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -623,7 +623,7 @@ qname(#q{q = #amqqueue{name = QName}}) -> QName. maybe_run_queue_via_backing_queue(Fun, State = #q{backing_queue_state = BQS}) -> {BQS2, State1} = case Fun(BQS) of - {BQS1, {confirm, Guids}} -> {BQS1, confirm_messages(Guids, State)}; + {{confirm, Guids}, BQS1} -> {BQS1, confirm_messages(Guids, State)}; BQS1 -> {BQS1, State} end, run_message_queue(State1#q{backing_queue_state = BQS2}). diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 47a894a8..0e5675f3 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -1308,7 +1308,7 @@ remove_confirms(GuidSet, State = #vqstate { msgs_on_disk = MOD, unconfirmed = gb_sets:difference(UC, GuidSet) }. msgs_confirmed(GuidSet, State) -> - {remove_confirms(GuidSet, State), {confirm, gb_sets:to_list(GuidSet)}}. + {{confirm, gb_sets:to_list(GuidSet)}, remove_confirms(GuidSet, State)}. msgs_written_to_disk(QPid, GuidSet) -> rabbit_amqqueue:maybe_run_queue_via_backing_queue_async( -- cgit v1.2.1 From ed6477ab5915428188c2b0fbd5b334c1cddce5de Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Wed, 24 Nov 2010 17:08:59 +0000 Subject: let frame interleaving happen but still optimise the delivery of short messages --- src/rabbit_writer.erl | 41 +++++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/src/rabbit_writer.erl b/src/rabbit_writer.erl index 50bca390..9c32c272 100644 --- a/src/rabbit_writer.erl +++ b/src/rabbit_writer.erl @@ -170,7 +170,7 @@ call(Pid, Msg) -> %--------------------------------------------------------------------------- -assemble_frames(Channel, MethodRecord, Protocol) -> +assemble_frame(Channel, MethodRecord, Protocol) -> ?LOGMESSAGE(out, Channel, MethodRecord, none), rabbit_binary_generator:build_simple_method_frame( Channel, MethodRecord, Protocol). @@ -185,17 +185,34 @@ assemble_frames(Channel, MethodRecord, Content, FrameMax, Protocol) -> Channel, Content, FrameMax, Protocol), [MethodFrame | ContentFrames]. +%% We optimise delivery of small messages. Content-bearing methods +%% require at least three frames. Small messages always fit into +%% that. We hand their frames to the Erlang network functions in one +%% go, which leads to more efficient processing in the runtime and a +%% greater chance of coalescing into fewer TCP packets. +%% +%% By contrast, for larger messages, split across many frames, we want +%% to allow interleaving of frames on different channels. Hence we +%% hand them to the Erlang network functions one frame at a time. +send_frames(Fun, Sock, Frames) when length(Frames) =< 3 -> + Fun(Sock, Frames); +send_frames(Fun, Sock, Frames) -> + lists:foldl(fun (Frame, ok) -> Fun(Sock, Frame); + (_Frame, Other) -> Other + end, ok, Frames). + tcp_send(Sock, Data) -> rabbit_misc:throw_on_error(inet_error, fun () -> rabbit_net:send(Sock, Data) end). internal_send_command(Sock, Channel, MethodRecord, Protocol) -> - ok = tcp_send(Sock, assemble_frames(Channel, MethodRecord, Protocol)). + ok = tcp_send(Sock, assemble_frame(Channel, MethodRecord, Protocol)). internal_send_command(Sock, Channel, MethodRecord, Content, FrameMax, Protocol) -> - ok = tcp_send(Sock, assemble_frames(Channel, MethodRecord, - Content, FrameMax, Protocol)). + ok = send_frames(fun tcp_send/2, Sock, + assemble_frames(Channel, MethodRecord, + Content, FrameMax, Protocol)). %% gen_tcp:send/2 does a selective receive of {inet_reply, Sock, %% Status} to obtain the result. That is bad when it is called from @@ -219,19 +236,19 @@ internal_send_command_async(MethodRecord, #wstate{sock = Sock, channel = Channel, protocol = Protocol}) -> - true = port_cmd(Sock, assemble_frames(Channel, MethodRecord, Protocol)), - ok. + ok = port_cmd(Sock, assemble_frame(Channel, MethodRecord, Protocol)). internal_send_command_async(MethodRecord, Content, #wstate{sock = Sock, channel = Channel, frame_max = FrameMax, protocol = Protocol}) -> - true = port_cmd(Sock, assemble_frames(Channel, MethodRecord, - Content, FrameMax, Protocol)), - ok. + ok = send_frames(fun port_cmd/2, Sock, + assemble_frames(Channel, MethodRecord, + Content, FrameMax, Protocol)). port_cmd(Sock, Data) -> - try rabbit_net:port_command(Sock, Data) - catch error:Error -> exit({writer, send_failed, Error}) - end. + true = try rabbit_net:port_command(Sock, Data) + catch error:Error -> exit({writer, send_failed, Error}) + end, + ok. -- cgit v1.2.1 From 8492fa65414c296220f913cd5ff28db8f30d3044 Mon Sep 17 00:00:00 2001 From: Marek Majkowski Date: Thu, 25 Nov 2010 11:52:54 +0000 Subject: Updated portfile as suggested by macports guru --- packaging/macports/Portfile.in | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/packaging/macports/Portfile.in b/packaging/macports/Portfile.in index ce6b1e34..f8417b83 100644 --- a/packaging/macports/Portfile.in +++ b/packaging/macports/Portfile.in @@ -7,6 +7,8 @@ version @VERSION@ categories net maintainers paperplanes.de:meyer rabbitmq.com:tonyg openmaintainer platforms darwin +supported_archs noarch + description The RabbitMQ AMQP Server long_description \ RabbitMQ is an implementation of AMQP, the emerging standard for \ @@ -31,17 +33,13 @@ checksums \ depends_lib port:erlang depends_build port:libxslt -platform darwin 7 { - depends_build-append port:py25-simplejson - build.args PYTHON=${prefix}/bin/python2.5 -} platform darwin 8 { - depends_build-append port:py25-simplejson - build.args PYTHON=${prefix}/bin/python2.5 + depends_build-append port:py26-simplejson + build.args PYTHON=${prefix}/bin/python2.6 } platform darwin 9 { - depends_build-append port:py25-simplejson - build.args PYTHON=${prefix}/bin/python2.5 + depends_build-append port:py26-simplejson + build.args PYTHON=${prefix}/bin/python2.6 } # no need for simplejson on Snow Leopard or higher -- cgit v1.2.1 From a3bddd01bfe6e9b0c17dbb6e43b034cf97101311 Mon Sep 17 00:00:00 2001 From: Matthew Sackman Date: Thu, 25 Nov 2010 14:17:32 +0000 Subject: Make sure we get the stacktrace out of a boot failure --- src/rabbit.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 61a3a53d..a619ef36 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -294,7 +294,8 @@ run_boot_step({StepName, Attributes}) -> [try apply(M,F,A) catch - _:Reason -> boot_error("FAILED~nReason: ~p~n", [Reason]) + _:Reason -> boot_error("FAILED~nReason: ~p~nStacktrace: ~p~n", + [Reason, erlang:get_stacktrace()]) end || {M,F,A} <- MFAs], io:format("done~n"), ok -- cgit v1.2.1 From b273afc13a115a56bde8224f29b2f15aa8c6d66b Mon Sep 17 00:00:00 2001 From: Matthew Sackman Date: Thu, 25 Nov 2010 14:50:14 +0000 Subject: Abstract out file locking, and revert to not using the R14-ism exclusive flag --- src/rabbit_misc.erl | 13 +++++++++++++ src/rabbit_upgrade.erl | 9 +++------ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 0067a410..f549dfe0 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -66,6 +66,7 @@ -export([get_options/2]). -export([all_module_attributes/1, build_acyclic_graph/3]). -export([now_ms/0]). +-export([lock_file/1]). -import(mnesia). -import(lists). @@ -200,6 +201,7 @@ {bad_edge, [digraph:vertex()]}), digraph:vertex(), digraph:vertex()})). -spec(now_ms/0 :: () -> non_neg_integer()). +-spec(lock_file/1 :: (file:filename()) -> rabbit_types:ok_or_error('eexist')). -endif. @@ -811,3 +813,14 @@ build_acyclic_graph(VertexFun, EdgeFun, Graph) -> true = digraph:delete(G), {error, Reason} end. + +%% TODO: When we stop supporting Erlang prior to R14, this should be +%% replaced with file:open [write, exclusive] +lock_file(Path) -> + case filelib:is_file(Path) of + true -> + {error, eexist}; + false -> + {ok, Lock} = file:open(Path, [write]), + ok = file:close(Lock) + end. diff --git a/src/rabbit_upgrade.erl b/src/rabbit_upgrade.erl index 64d0c251..97a07514 100644 --- a/src/rabbit_upgrade.erl +++ b/src/rabbit_upgrade.erl @@ -127,9 +127,8 @@ heads(G) -> apply_upgrades(Upgrades) -> LockFile = lock_filename(dir()), - case file:open(LockFile, [write, exclusive]) of - {ok, Lock} -> - ok = file:close(Lock), + case rabbit_misc:lock_file(LockFile) of + ok -> BackupDir = dir() ++ "-upgrade-backup", info("Upgrades: ~w to apply~n", [length(Upgrades)]), case rabbit_mnesia:copy_db(BackupDir) of @@ -155,9 +154,7 @@ apply_upgrades(Upgrades) -> throw({could_not_back_up_mnesia_dir, E}) end; {error, eexist} -> - throw({error, previous_upgrade_failed}); - {error, _} = Error -> - throw(Error) + throw({error, previous_upgrade_failed}) end. apply_upgrade({M, F}) -> -- cgit v1.2.1 From 28e2faaeff629beca778fb979b4a780f34e47075 Mon Sep 17 00:00:00 2001 From: Matthew Sackman Date: Thu, 25 Nov 2010 14:52:18 +0000 Subject: No need to open these files at all exclusive. Which is an R14ism anyway. --- src/rabbit_queue_index.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rabbit_queue_index.erl b/src/rabbit_queue_index.erl index 248c1fbc..62ff725a 100644 --- a/src/rabbit_queue_index.erl +++ b/src/rabbit_queue_index.erl @@ -1042,7 +1042,7 @@ transform_file(Path, Fun) -> Size = filelib:file_size(Path), {ok, PathTmpHdl} = - file_handle_cache:open(PathTmp, [exclusive | ?WRITE_MODE], + file_handle_cache:open(PathTmp, ?WRITE_MODE, [{write_buffer, infinity}]), {ok, PathHdl} = @@ -1063,3 +1063,4 @@ drive_transform_fun(Fun, Hdl, Contents) -> ok = file_handle_cache:append(Hdl, Output), drive_transform_fun(Fun, Hdl, Contents1) end. +rr -- cgit v1.2.1 From b69f323741a72d28dba7d859bfa48c0fc7860fcd Mon Sep 17 00:00:00 2001 From: Rob Harrop Date: Thu, 25 Nov 2010 15:00:06 +0000 Subject: cosmetic changes --- src/rabbit_amqqueue_process.erl | 10 ++++++---- src/rabbit_channel.erl | 3 ++- src/rabbit_msg_store.erl | 3 ++- src/rabbit_reader.erl | 5 +++-- src/rabbit_variable_queue.erl | 12 +++++++----- 5 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index d7066e2f..3dbd2b22 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -450,8 +450,9 @@ record_confirm_message(#delivery{sender = ChPid, message = #basic_message { is_persistent = true, guid = Guid}}, - State = #q{guid_to_channel = GTC, - q = #amqqueue{durable = true}}) -> + State = + #q{guid_to_channel = GTC, + q = #amqqueue{durable = true}}) -> State#q{guid_to_channel = dict:store(Guid, {ChPid, MsgSeqNo}, GTC)}; record_confirm_message(_Delivery, State) -> State. @@ -859,8 +860,9 @@ handle_call({basic_get, ChPid, NoAck}, _From, case AckRequired of true -> C = #cr{acktags = ChAckTags} = ch_record(ChPid), true = maybe_store_ch_record( - C#cr{acktags = sets:add_element(AckTag, - ChAckTags)}), + C#cr{acktags = + sets:add_element(AckTag, + ChAckTags)}), State2; false -> confirm_message(Message, State2) end, diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 0b6e10e5..b2e6658b 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -471,7 +471,8 @@ send_or_enqueue_ack(MsgSeqNo, QPid, State = #ch{confirm_multiple = true}) -> do_if_unconfirmed( MsgSeqNo, QPid, fun(MSN, State1 = #ch{held_confirms = As}) -> - start_confirm_timer(State1#ch{held_confirms = gb_sets:add(MSN, As)}) + start_confirm_timer( + State1#ch{held_confirms = gb_sets:add(MSN, As)}) end, State). do_if_unconfirmed(MsgSeqNo, QPid, ConfirmFun, diff --git a/src/rabbit_msg_store.erl b/src/rabbit_msg_store.erl index 96d67ad4..fea7d4a8 100644 --- a/src/rabbit_msg_store.erl +++ b/src/rabbit_msg_store.erl @@ -727,7 +727,8 @@ handle_cast({write, CRef, Guid}, ok = index_update_ref_count(Guid, RefCount + 1, State1), CTG2 = case {dict:find(CRef, CODC), File} of {{ok, _}, CurFile} -> CTG1; - {{ok, Fun}, _} -> Fun(gb_sets:singleton(Guid)), CTG; + {{ok, Fun}, _} -> Fun(gb_sets:singleton(Guid)), + CTG; _ -> CTG1 end, noreply(State #msstate { cref_to_guids = CTG2 }) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 71115a73..c903665f 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -590,8 +590,9 @@ handle_frame(Type, Channel, Payload, %% We're already closing this channel, so %% there's no cleanup to do (notify %% queues, etc.) - ok = rabbit_writer:send_command(State#v1.sock, - #'channel.close_ok'{}); + ok = rabbit_writer:send_command( + State#v1.sock, + #'channel.close_ok'{}); _ -> ok end, State; diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 5dbfa62b..27e482d5 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -523,7 +523,8 @@ publish_delivered(false, _Msg, _MsgProps, State = #vqstate { len = 0 }) -> {blank_ack, a(State)}; publish_delivered(true, Msg = #basic_message { is_persistent = IsPersistent, guid = Guid }, - MsgProps = #message_properties { needs_confirming = NeedsConfirming }, + MsgProps = #message_properties { + needs_confirming = NeedsConfirming }, State = #vqstate { len = 0, next_seq_id = SeqId, out_counter = OutCount, @@ -1366,10 +1367,11 @@ ack(MsgStoreFun, Fun, AckTags, State) -> end, {{[], orddict:new()}, State}, AckTags), IndexState1 = rabbit_queue_index:ack(SeqIds, IndexState), AckdGuids = lists:concat( - orddict:fold(fun (IsPersistent, Guids, Gs) -> - MsgStoreFun(MSCState, IsPersistent, Guids), - [Guids | Gs] - end, [], GuidsByStore)), + orddict:fold( + fun (IsPersistent, Guids, Gs) -> + MsgStoreFun(MSCState, IsPersistent, Guids), + [Guids | Gs] + end, [], GuidsByStore)), PCount1 = PCount - find_persistent_count(sum_guids_by_store_to_len( orddict:new(), GuidsByStore)), {AckdGuids, State1 #vqstate { index_state = IndexState1, -- cgit v1.2.1 From bf7ceac3b544c004bf4a98417397f1a1e44d135d Mon Sep 17 00:00:00 2001 From: Matthew Sackman Date: Thu, 25 Nov 2010 15:00:53 +0000 Subject: Cope with files not existing (or just being empty) --- src/rabbit_queue_index.erl | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/rabbit_queue_index.erl b/src/rabbit_queue_index.erl index 62ff725a..59dab0d4 100644 --- a/src/rabbit_queue_index.erl +++ b/src/rabbit_queue_index.erl @@ -1039,21 +1039,24 @@ transform_queue(Dir, Gatherer, {JournalFun, SegmentFun}) -> transform_file(Path, Fun) -> PathTmp = Path ++ ".upgrade", - Size = filelib:file_size(Path), - - {ok, PathTmpHdl} = - file_handle_cache:open(PathTmp, ?WRITE_MODE, - [{write_buffer, infinity}]), + case filelib:file_size(Path) of + 0 -> + ok; + Size -> + {ok, PathTmpHdl} = + file_handle_cache:open(PathTmp, ?WRITE_MODE, + [{write_buffer, infinity}]), - {ok, PathHdl} = - file_handle_cache:open(Path, [{read_ahead, Size} | ?READ_MODE], []), - {ok, Content} = file_handle_cache:read(PathHdl, Size), - ok = file_handle_cache:close(PathHdl), + {ok, PathHdl} = file_handle_cache:open( + Path, [{read_ahead, Size} | ?READ_MODE], []), + {ok, Content} = file_handle_cache:read(PathHdl, Size), + ok = file_handle_cache:close(PathHdl), - ok = drive_transform_fun(Fun, PathTmpHdl, Content), + ok = drive_transform_fun(Fun, PathTmpHdl, Content), - ok = file_handle_cache:close(PathTmpHdl), - ok = file:rename(PathTmp, Path). + ok = file_handle_cache:close(PathTmpHdl), + ok = file:rename(PathTmp, Path) + end. drive_transform_fun(Fun, Hdl, Contents) -> case Fun(Contents) of -- cgit v1.2.1 From 5fc03dbdf3ad574496a838c1255afa9b9a8dcb0f Mon Sep 17 00:00:00 2001 From: Matthew Sackman Date: Thu, 25 Nov 2010 15:02:15 +0000 Subject: Opps, they shouldn't have got there... --- src/rabbit_queue_index.erl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/rabbit_queue_index.erl b/src/rabbit_queue_index.erl index 59dab0d4..43dbf9d4 100644 --- a/src/rabbit_queue_index.erl +++ b/src/rabbit_queue_index.erl @@ -1066,4 +1066,3 @@ drive_transform_fun(Fun, Hdl, Contents) -> ok = file_handle_cache:append(Hdl, Output), drive_transform_fun(Fun, Hdl, Contents1) end. -rr -- cgit v1.2.1 From 3a32ef5eef1eaa56efb8aefcc6df1936a15cf639 Mon Sep 17 00:00:00 2001 From: Matthew Sackman Date: Thu, 25 Nov 2010 15:02:54 +0000 Subject: Opps, they shouldn't have got there... --- src/rabbit_queue_index.erl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/rabbit_queue_index.erl b/src/rabbit_queue_index.erl index 62ff725a..a312e8a8 100644 --- a/src/rabbit_queue_index.erl +++ b/src/rabbit_queue_index.erl @@ -1063,4 +1063,3 @@ drive_transform_fun(Fun, Hdl, Contents) -> ok = file_handle_cache:append(Hdl, Output), drive_transform_fun(Fun, Hdl, Contents1) end. -rr -- cgit v1.2.1 From eacba0373a6702803146fc5ed90e627890b45569 Mon Sep 17 00:00:00 2001 From: Matthew Sackman Date: Thu, 25 Nov 2010 15:26:34 +0000 Subject: cosmetic --- src/rabbit_misc.erl | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index f549dfe0..1a05b729 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -818,9 +818,7 @@ build_acyclic_graph(VertexFun, EdgeFun, Graph) -> %% replaced with file:open [write, exclusive] lock_file(Path) -> case filelib:is_file(Path) of - true -> - {error, eexist}; - false -> - {ok, Lock} = file:open(Path, [write]), - ok = file:close(Lock) + true -> {error, eexist}; + false -> {ok, Lock} = file:open(Path, [write]), + ok = file:close(Lock) end. -- cgit v1.2.1 From 9029a0ad67fd1c9a486b6468f8dffe4812f54454 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 25 Nov 2010 15:37:12 +0000 Subject: cosmetic --- src/rabbit_queue_index.erl | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/src/rabbit_queue_index.erl b/src/rabbit_queue_index.erl index 43dbf9d4..2857eac4 100644 --- a/src/rabbit_queue_index.erl +++ b/src/rabbit_queue_index.erl @@ -1040,29 +1040,25 @@ transform_queue(Dir, Gatherer, {JournalFun, SegmentFun}) -> transform_file(Path, Fun) -> PathTmp = Path ++ ".upgrade", case filelib:file_size(Path) of - 0 -> - ok; - Size -> - {ok, PathTmpHdl} = - file_handle_cache:open(PathTmp, ?WRITE_MODE, - [{write_buffer, infinity}]), + 0 -> ok; + Size -> {ok, PathTmpHdl} = + file_handle_cache:open(PathTmp, ?WRITE_MODE, + [{write_buffer, infinity}]), - {ok, PathHdl} = file_handle_cache:open( - Path, [{read_ahead, Size} | ?READ_MODE], []), - {ok, Content} = file_handle_cache:read(PathHdl, Size), - ok = file_handle_cache:close(PathHdl), + {ok, PathHdl} = file_handle_cache:open( + Path, [{read_ahead, Size} | ?READ_MODE], []), + {ok, Content} = file_handle_cache:read(PathHdl, Size), + ok = file_handle_cache:close(PathHdl), - ok = drive_transform_fun(Fun, PathTmpHdl, Content), + ok = drive_transform_fun(Fun, PathTmpHdl, Content), - ok = file_handle_cache:close(PathTmpHdl), - ok = file:rename(PathTmp, Path) + ok = file_handle_cache:close(PathTmpHdl), + ok = file:rename(PathTmp, Path) end. drive_transform_fun(Fun, Hdl, Contents) -> case Fun(Contents) of - stop -> - ok; - {Output, Contents1} -> - ok = file_handle_cache:append(Hdl, Output), - drive_transform_fun(Fun, Hdl, Contents1) + stop -> ok; + {Output, Contents1} -> ok = file_handle_cache:append(Hdl, Output), + drive_transform_fun(Fun, Hdl, Contents1) end. -- cgit v1.2.1 From 84c7a67e489d8291bd52980bfe9bb5254417f841 Mon Sep 17 00:00:00 2001 From: Rob Harrop Date: Thu, 25 Nov 2010 15:44:56 +0000 Subject: cosmetic --- 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 c903665f..82cc6288 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -591,8 +591,7 @@ handle_frame(Type, Channel, Payload, %% there's no cleanup to do (notify %% queues, etc.) ok = rabbit_writer:send_command( - State#v1.sock, - #'channel.close_ok'{}); + State#v1.sock, #'channel.close_ok'{}); _ -> ok end, State; -- cgit v1.2.1 From 8fa783acc1f1380907ef6ff86098844b1340b92d Mon Sep 17 00:00:00 2001 From: Rob Harrop Date: Thu, 25 Nov 2010 16:27:10 +0000 Subject: Cosmetic --- src/rabbit_router.erl | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/rabbit_router.erl b/src/rabbit_router.erl index e36feb76..d49c072c 100644 --- a/src/rabbit_router.erl +++ b/src/rabbit_router.erl @@ -82,10 +82,8 @@ deliver(QNames, Delivery = #delivery{mandatory = Mandatory, end), {Routed, Handled} = lists:foldl(fun fold_deliveries/2, {false, []}, Success), - case check_delivery(Mandatory, Immediate, {Routed, Handled}) of - {routed, Qs} -> {routed, Qs}; - O -> O - end. + check_delivery(Mandatory, Immediate, {Routed, Handled}). + %% TODO: Maybe this should be handled by a cursor instead. %% TODO: This causes a full scan for each entry with the same source -- cgit v1.2.1 From 846b6a3f9efd003cdb066764bf7855207f4b0260 Mon Sep 17 00:00:00 2001 From: Rob Harrop Date: Thu, 25 Nov 2010 16:41:35 +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 27e482d5..7c676164 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -1239,7 +1239,7 @@ publish(Msg = #basic_message { is_persistent = IsPersistent, guid = Guid }, persistent_count = PCount, durable = IsDurable, ram_msg_count = RamMsgCount, - unconfirmed = Unconfirmed}) -> + unconfirmed = Unconfirmed }) -> IsPersistent1 = IsDurable andalso IsPersistent, MsgStatus = (msg_status(IsPersistent1, SeqId, Msg, MsgProps)) #msg_status { is_delivered = IsDelivered, msg_on_disk = MsgOnDisk}, -- cgit v1.2.1 From 963d834477df9a4bedaca26b13430b6ee8ef2858 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sun, 28 Nov 2010 10:56:09 +0000 Subject: flesh out writer API for completeness --- src/rabbit_writer.erl | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/rabbit_writer.erl b/src/rabbit_writer.erl index 50bca390..1b4710c6 100644 --- a/src/rabbit_writer.erl +++ b/src/rabbit_writer.erl @@ -34,8 +34,9 @@ -include("rabbit_framing.hrl"). -export([start/5, start_link/5, mainloop/2, mainloop1/2]). --export([send_command/2, send_command/3, send_command_sync/2, - send_command_sync/3, send_command_and_notify/5]). +-export([send_command/2, send_command/3, + send_command_sync/2, send_command_sync/3, + send_command_and_notify/4, send_command_and_notify/5]). -export([internal_send_command/4, internal_send_command/6]). -import(gen_tcp). @@ -66,6 +67,9 @@ -spec(send_command_sync/3 :: (pid(), rabbit_framing:amqp_method_record(), rabbit_types:content()) -> 'ok'). +-spec(send_command_and_notify/4 :: + (pid(), pid(), pid(), rabbit_framing:amqp_method_record()) + -> 'ok'). -spec(send_command_and_notify/5 :: (pid(), pid(), pid(), rabbit_framing:amqp_method_record(), rabbit_types:content()) @@ -130,6 +134,10 @@ handle_message({'$gen_call', From, {send_command_sync, MethodRecord, Content}}, ok = internal_send_command_async(MethodRecord, Content, State), gen_server:reply(From, ok), State; +handle_message({send_command_and_notify, QPid, ChPid, MethodRecord}, State) -> + ok = internal_send_command_async(MethodRecord, State), + rabbit_amqqueue:notify_sent(QPid, ChPid), + State; handle_message({send_command_and_notify, QPid, ChPid, MethodRecord, Content}, State) -> ok = internal_send_command_async(MethodRecord, Content, State), @@ -158,6 +166,10 @@ send_command_sync(W, MethodRecord) -> send_command_sync(W, MethodRecord, Content) -> call(W, {send_command_sync, MethodRecord, Content}). +send_command_and_notify(W, Q, ChPid, MethodRecord) -> + W ! {send_command_and_notify, Q, ChPid, MethodRecord}, + ok. + send_command_and_notify(W, Q, ChPid, MethodRecord, Content) -> W ! {send_command_and_notify, Q, ChPid, MethodRecord, Content}, ok. -- cgit v1.2.1 From 8b9a8422b5d6d0571b09c4c8f8f19257c2231e3b Mon Sep 17 00:00:00 2001 From: Rob Harrop Date: Tue, 30 Nov 2010 10:48:30 +0000 Subject: Changelogs for the 2.2.0 release --- packaging/RPMS/Fedora/rabbitmq-server.spec | 3 +++ packaging/debs/Debian/debian/changelog | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/packaging/RPMS/Fedora/rabbitmq-server.spec b/packaging/RPMS/Fedora/rabbitmq-server.spec index 5032f471..b37f7ab1 100644 --- a/packaging/RPMS/Fedora/rabbitmq-server.spec +++ b/packaging/RPMS/Fedora/rabbitmq-server.spec @@ -124,6 +124,9 @@ done rm -rf %{buildroot} %changelog +* Mon Nov 29 2010 rob@rabbitmq.com 2.2.0-1 +- New Upstream Release + * Tue Oct 19 2010 vlad@rabbitmq.com 2.1.1-1 - New Upstream Release diff --git a/packaging/debs/Debian/debian/changelog b/packaging/debs/Debian/debian/changelog index e81fda24..a60e691d 100644 --- a/packaging/debs/Debian/debian/changelog +++ b/packaging/debs/Debian/debian/changelog @@ -1,3 +1,9 @@ +rabbitmq-server (2.2.0-1) lucid; urgency=low + + * New Upstream Release + + -- Rob Harrop Mon, 29 Nov 2010 12:24:48 +0000 + rabbitmq-server (2.1.1-1) lucid; urgency=low * New Upstream Release -- cgit v1.2.1 -- cgit v1.2.1 From 0b3bade550d12574a398e4c84615e1e92a44cc70 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 30 Nov 2010 12:50:15 +0000 Subject: cosmetic: put rabbit_net exports, specs, and funs in sensible order and consistent too --- src/rabbit_net.erl | 85 +++++++++++++++++++++++------------------------------- 1 file changed, 36 insertions(+), 49 deletions(-) diff --git a/src/rabbit_net.erl b/src/rabbit_net.erl index 0940dce2..89954b06 100644 --- a/src/rabbit_net.erl +++ b/src/rabbit_net.erl @@ -32,9 +32,9 @@ -module(rabbit_net). -include("rabbit.hrl"). --export([async_recv/3, close/1, controlling_process/2, - getstat/2, peername/1, peercert/1, port_command/2, - send/2, sockname/1, is_ssl/1]). +-export([is_ssl/1, controlling_process/2, getstat/2, + async_recv/3, port_command/2, send/2, close/1, + sockname/1, peername/1, peercert/1]). %%--------------------------------------------------------------------------- @@ -49,26 +49,25 @@ -type(ok_or_any_error() :: rabbit_types:ok_or_error(any())). -type(socket() :: port() | #ssl_socket{}). +-spec(is_ssl/1 :: (socket()) -> boolean()). +-spec(controlling_process/2 :: (socket(), pid()) -> ok_or_any_error()). +-spec(getstat/2 :: + (socket(), [stat_option()]) + -> ok_val_or_error([{stat_option(), integer()}])). -spec(async_recv/3 :: (socket(), integer(), timeout()) -> rabbit_types:ok(any())). --spec(close/1 :: (socket()) -> ok_or_any_error()). --spec(controlling_process/2 :: (socket(), pid()) -> ok_or_any_error()). -spec(port_command/2 :: (socket(), iolist()) -> 'true'). --spec(send/2 :: - (socket(), binary() | iolist()) -> ok_or_any_error()). +-spec(send/2 :: (socket(), binary() | iolist()) -> ok_or_any_error()). +-spec(close/1 :: (socket()) -> ok_or_any_error()). +-spec(sockname/1 :: + (socket()) + -> ok_val_or_error({inet:ip_address(), rabbit_networking:ip_port()})). -spec(peername/1 :: (socket()) -> ok_val_or_error({inet:ip_address(), rabbit_networking:ip_port()})). -spec(peercert/1 :: (socket()) -> 'nossl' | ok_val_or_error(rabbit_ssl:certificate())). --spec(sockname/1 :: - (socket()) - -> ok_val_or_error({inet:ip_address(), rabbit_networking:ip_port()})). --spec(is_ssl/1 :: (socket()) -> boolean()). --spec(getstat/2 :: - (socket(), [stat_option()]) - -> ok_val_or_error([{stat_option(), integer()}])). -endif. @@ -76,6 +75,18 @@ -define(IS_SSL(Sock), is_record(Sock, ssl_socket)). +is_ssl(Sock) -> ?IS_SSL(Sock). + +controlling_process(Sock, Pid) when ?IS_SSL(Sock) -> + ssl:controlling_process(Sock#ssl_socket.ssl, Pid); +controlling_process(Sock, Pid) when is_port(Sock) -> + gen_tcp:controlling_process(Sock, Pid). + +getstat(Sock, Stats) when ?IS_SSL(Sock) -> + inet:getstat(Sock#ssl_socket.tcp, Stats); +getstat(Sock, Stats) when is_port(Sock) -> + inet:getstat(Sock, Stats). + async_recv(Sock, Length, Timeout) when ?IS_SSL(Sock) -> Pid = self(), Ref = make_ref(), @@ -90,31 +101,6 @@ async_recv(Sock, Length, infinity) when is_port(Sock) -> async_recv(Sock, Length, Timeout) when is_port(Sock) -> prim_inet:async_recv(Sock, Length, Timeout). -close(Sock) when ?IS_SSL(Sock) -> - ssl:close(Sock#ssl_socket.ssl); -close(Sock) when is_port(Sock) -> - gen_tcp:close(Sock). - -controlling_process(Sock, Pid) when ?IS_SSL(Sock) -> - ssl:controlling_process(Sock#ssl_socket.ssl, Pid); -controlling_process(Sock, Pid) when is_port(Sock) -> - gen_tcp:controlling_process(Sock, Pid). - -getstat(Sock, Stats) when ?IS_SSL(Sock) -> - inet:getstat(Sock#ssl_socket.tcp, Stats); -getstat(Sock, Stats) when is_port(Sock) -> - inet:getstat(Sock, Stats). - -peername(Sock) when ?IS_SSL(Sock) -> - ssl:peername(Sock#ssl_socket.ssl); -peername(Sock) when is_port(Sock) -> - inet:peername(Sock). - -peercert(Sock) when ?IS_SSL(Sock) -> - ssl:peercert(Sock#ssl_socket.ssl); -peercert(Sock) when is_port(Sock) -> - nossl. - port_command(Sock, Data) when ?IS_SSL(Sock) -> case ssl:send(Sock#ssl_socket.ssl, Data) of ok -> self() ! {inet_reply, Sock, ok}, @@ -124,16 +110,17 @@ port_command(Sock, Data) when ?IS_SSL(Sock) -> port_command(Sock, Data) when is_port(Sock) -> erlang:port_command(Sock, Data). -send(Sock, Data) when ?IS_SSL(Sock) -> - ssl:send(Sock#ssl_socket.ssl, Data); -send(Sock, Data) when is_port(Sock) -> - gen_tcp:send(Sock, Data). +send(Sock, Data) when ?IS_SSL(Sock) -> ssl:send(Sock#ssl_socket.ssl, Data); +send(Sock, Data) when is_port(Sock) -> gen_tcp:send(Sock, Data). + +close(Sock) when ?IS_SSL(Sock) -> ssl:close(Sock#ssl_socket.ssl); +close(Sock) when is_port(Sock) -> gen_tcp:close(Sock). +sockname(Sock) when ?IS_SSL(Sock) -> ssl:sockname(Sock#ssl_socket.ssl); +sockname(Sock) when is_port(Sock) -> inet:sockname(Sock). -sockname(Sock) when ?IS_SSL(Sock) -> - ssl:sockname(Sock#ssl_socket.ssl); -sockname(Sock) when is_port(Sock) -> - inet:sockname(Sock). +peername(Sock) when ?IS_SSL(Sock) -> ssl:peername(Sock#ssl_socket.ssl); +peername(Sock) when is_port(Sock) -> inet:peername(Sock). -is_ssl(Sock) -> - ?IS_SSL(Sock). +peercert(Sock) when ?IS_SSL(Sock) -> ssl:peercert(Sock#ssl_socket.ssl); +peercert(Sock) when is_port(Sock) -> nossl. -- cgit v1.2.1 From cd644f51606a08324376335fe8d9b8dc54259821 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Tue, 30 Nov 2010 15:57:26 +0000 Subject: Check if nodename is available before startup --- scripts/rabbitmq-server | 4 ++-- scripts/rabbitmq-server.bat | 5 +++-- src/rabbit_plugin_activator.erl | 11 +++++++++-- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/scripts/rabbitmq-server b/scripts/rabbitmq-server index c5d883c3..66ce4384 100755 --- a/scripts/rabbitmq-server +++ b/scripts/rabbitmq-server @@ -91,8 +91,8 @@ if [ "x" = "x$RABBITMQ_NODE_ONLY" ]; then -pa "$RABBITMQ_EBIN_ROOT" \ -noinput \ -hidden \ - -s rabbit_plugin_activator \ - -extra "$RABBITMQ_PLUGINS_DIR" "${RABBITMQ_PLUGINS_EXPAND_DIR}" + -s rabbit_prelaunch \ + -extra "$RABBITMQ_PLUGINS_DIR" "${RABBITMQ_PLUGINS_EXPAND_DIR}" "${RABBITMQ_NODENAME}" then RABBITMQ_BOOT_FILE="${RABBITMQ_PLUGINS_EXPAND_DIR}/rabbit" RABBITMQ_EBIN_PATH="" diff --git a/scripts/rabbitmq-server.bat b/scripts/rabbitmq-server.bat index 94180de9..f972a4aa 100644 --- a/scripts/rabbitmq-server.bat +++ b/scripts/rabbitmq-server.bat @@ -117,9 +117,10 @@ set RABBITMQ_EBIN_ROOT=!TDP0!..\ebin "!ERLANG_HOME!\bin\erl.exe" ^ -pa "!RABBITMQ_EBIN_ROOT!" ^ -noinput -hidden ^ --s rabbit_plugin_activator ^ +-s rabbit_prelaunch ^ -extra "!RABBITMQ_PLUGINS_DIR:\=/!" ^ - "!RABBITMQ_PLUGINS_EXPAND_DIR:\=/!" + "!RABBITMQ_PLUGINS_EXPAND_DIR:\=/!" ^ + "!RABBITMQ_NODENAME!" set RABBITMQ_BOOT_FILE=!RABBITMQ_PLUGINS_EXPAND_DIR!\rabbit if not exist "!RABBITMQ_BOOT_FILE!.boot" ( diff --git a/src/rabbit_plugin_activator.erl b/src/rabbit_plugin_activator.erl index 072f297e..59d8efd4 100644 --- a/src/rabbit_plugin_activator.erl +++ b/src/rabbit_plugin_activator.erl @@ -29,7 +29,7 @@ %% Contributor(s): ______________________________________. %% --module(rabbit_plugin_activator). +-module(rabbit_prelaunch). -export([start/0, stop/0]). @@ -52,7 +52,7 @@ start() -> io:format("Activating RabbitMQ plugins ...~n"), %% Determine our various directories - [PluginDir, UnpackedPluginDir] = init:get_plain_arguments(), + [PluginDir, UnpackedPluginDir, NodeName] = init:get_plain_arguments(), RootName = UnpackedPluginDir ++ "/rabbit", %% Unpack any .ez plugins @@ -130,6 +130,13 @@ start() -> [io:format("* ~s-~s~n", [App, proplists:get_value(App, AppVersions)]) || App <- PluginApps], io:nl(), + case net_kernel:start([list_to_atom(NodeName), shortnames]) of + {ok, _Pid} -> ok = net_kernel:stop(); + {error, Result} -> + terminate("starting node with name ~p failed - " + "an instance with the same name may already be running~n~p~n", + [NodeName, Result]) + end, halt(), ok. -- cgit v1.2.1 From bd06292f54f8cd0475005236968b688aab537687 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Tue, 30 Nov 2010 15:58:14 +0000 Subject: Rename plugin_activator to prelaunch --- src/rabbit_plugin_activator.erl | 263 ---------------------------------------- src/rabbit_prelaunch.erl | 263 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 263 insertions(+), 263 deletions(-) delete mode 100644 src/rabbit_plugin_activator.erl create mode 100644 src/rabbit_prelaunch.erl diff --git a/src/rabbit_plugin_activator.erl b/src/rabbit_plugin_activator.erl deleted file mode 100644 index 59d8efd4..00000000 --- a/src/rabbit_plugin_activator.erl +++ /dev/null @@ -1,263 +0,0 @@ -%% The contents of this file are subject to the Mozilla Public License -%% Version 1.1 (the "License"); you may not use this file except in -%% compliance with the License. You may obtain a copy of the License at -%% http://www.mozilla.org/MPL/ -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the -%% License for the specific language governing rights and limitations -%% under the License. -%% -%% The Original Code is RabbitMQ. -%% -%% The Initial Developers of the Original Code are LShift Ltd, -%% Cohesive Financial Technologies LLC, and Rabbit Technologies Ltd. -%% -%% Portions created before 22-Nov-2008 00:00:00 GMT by LShift Ltd, -%% Cohesive Financial Technologies LLC, or Rabbit Technologies Ltd -%% are Copyright (C) 2007-2008 LShift Ltd, Cohesive Financial -%% Technologies LLC, and Rabbit Technologies Ltd. -%% -%% Portions created by LShift Ltd are Copyright (C) 2007-2010 LShift -%% Ltd. Portions created by Cohesive Financial Technologies LLC are -%% Copyright (C) 2007-2010 Cohesive Financial Technologies -%% LLC. Portions created by Rabbit Technologies Ltd are Copyright -%% (C) 2007-2010 Rabbit Technologies Ltd. -%% -%% All Rights Reserved. -%% -%% Contributor(s): ______________________________________. -%% - --module(rabbit_prelaunch). - --export([start/0, stop/0]). - --define(BaseApps, [rabbit]). - -%%---------------------------------------------------------------------------- -%% Specs -%%---------------------------------------------------------------------------- - --ifdef(use_specs). - --spec(start/0 :: () -> no_return()). --spec(stop/0 :: () -> 'ok'). - --endif. - -%%---------------------------------------------------------------------------- - -start() -> - io:format("Activating RabbitMQ plugins ...~n"), - - %% Determine our various directories - [PluginDir, UnpackedPluginDir, NodeName] = init:get_plain_arguments(), - RootName = UnpackedPluginDir ++ "/rabbit", - - %% Unpack any .ez plugins - unpack_ez_plugins(PluginDir, UnpackedPluginDir), - - %% Build a list of required apps based on the fixed set, and any plugins - PluginApps = find_plugins(PluginDir) ++ find_plugins(UnpackedPluginDir), - RequiredApps = ?BaseApps ++ PluginApps, - - %% Build the entire set of dependencies - this will load the - %% applications along the way - AllApps = case catch sets:to_list(expand_dependencies(RequiredApps)) of - {failed_to_load_app, App, Err} -> - terminate("failed to load application ~s:~n~p", - [App, Err]); - AppList -> - AppList - end, - AppVersions = [determine_version(App) || App <- AllApps], - RabbitVersion = proplists:get_value(rabbit, AppVersions), - - %% Build the overall release descriptor - RDesc = {release, - {"rabbit", RabbitVersion}, - {erts, erlang:system_info(version)}, - AppVersions}, - - %% Write it out to $RABBITMQ_PLUGINS_EXPAND_DIR/rabbit.rel - file:write_file(RootName ++ ".rel", io_lib:format("~p.~n", [RDesc])), - - %% Compile the script - ScriptFile = RootName ++ ".script", - case systools:make_script(RootName, [local, silent, exref]) of - {ok, Module, Warnings} -> - %% This gets lots of spurious no-source warnings when we - %% have .ez files, so we want to supress them to prevent - %% hiding real issues. On Ubuntu, we also get warnings - %% about kernel/stdlib sources being out of date, which we - %% also ignore for the same reason. - WarningStr = Module:format_warning( - [W || W <- Warnings, - case W of - {warning, {source_not_found, _}} -> false; - {warning, {obj_out_of_date, {_,_,WApp,_,_}}} - when WApp == mnesia; - WApp == stdlib; - WApp == kernel; - WApp == sasl; - WApp == crypto; - WApp == os_mon -> false; - _ -> true - end]), - case length(WarningStr) of - 0 -> ok; - _ -> io:format("~s", [WarningStr]) - end, - ok; - {error, Module, Error} -> - terminate("generation of boot script file ~s failed:~n~s", - [ScriptFile, Module:format_error(Error)]) - end, - - case post_process_script(ScriptFile) of - ok -> ok; - {error, Reason} -> - terminate("post processing of boot script file ~s failed:~n~w", - [ScriptFile, Reason]) - end, - case systools:script2boot(RootName) of - ok -> ok; - error -> terminate("failed to compile boot script file ~s", - [ScriptFile]) - end, - io:format("~w plugins activated:~n", [length(PluginApps)]), - [io:format("* ~s-~s~n", [App, proplists:get_value(App, AppVersions)]) - || App <- PluginApps], - io:nl(), - case net_kernel:start([list_to_atom(NodeName), shortnames]) of - {ok, _Pid} -> ok = net_kernel:stop(); - {error, Result} -> - terminate("starting node with name ~p failed - " - "an instance with the same name may already be running~n~p~n", - [NodeName, Result]) - end, - halt(), - ok. - -stop() -> - ok. - -determine_version(App) -> - application:load(App), - {ok, Vsn} = application:get_key(App, vsn), - {App, Vsn}. - -delete_recursively(Fn) -> - case filelib:is_dir(Fn) and not(is_symlink(Fn)) of - true -> - case file:list_dir(Fn) of - {ok, Files} -> - case lists:foldl(fun ( Fn1, ok) -> delete_recursively( - Fn ++ "/" ++ Fn1); - (_Fn1, Err) -> Err - end, ok, Files) of - ok -> case file:del_dir(Fn) of - ok -> ok; - {error, E} -> {error, - {cannot_delete, Fn, E}} - end; - Err -> Err - end; - {error, E} -> - {error, {cannot_list_files, Fn, E}} - end; - false -> - case filelib:is_file(Fn) of - true -> case file:delete(Fn) of - ok -> ok; - {error, E} -> {error, {cannot_delete, Fn, E}} - end; - false -> ok - end - end. - -is_symlink(Name) -> - case file:read_link(Name) of - {ok, _} -> true; - _ -> false - end. - -unpack_ez_plugins(SrcDir, DestDir) -> - %% Eliminate the contents of the destination directory - case delete_recursively(DestDir) of - ok -> ok; - {error, E} -> terminate("Could not delete dir ~s (~p)", [DestDir, E]) - end, - case filelib:ensure_dir(DestDir ++ "/") of - ok -> ok; - {error, E2} -> terminate("Could not create dir ~s (~p)", [DestDir, E2]) - end, - [unpack_ez_plugin(PluginName, DestDir) || - PluginName <- filelib:wildcard(SrcDir ++ "/*.ez")]. - -unpack_ez_plugin(PluginFn, PluginDestDir) -> - zip:unzip(PluginFn, [{cwd, PluginDestDir}]), - ok. - -find_plugins(PluginDir) -> - [prepare_dir_plugin(PluginName) || - PluginName <- filelib:wildcard(PluginDir ++ "/*/ebin/*.app")]. - -prepare_dir_plugin(PluginAppDescFn) -> - %% Add the plugin ebin directory to the load path - PluginEBinDirN = filename:dirname(PluginAppDescFn), - code:add_path(PluginEBinDirN), - - %% We want the second-last token - NameTokens = string:tokens(PluginAppDescFn,"/."), - PluginNameString = lists:nth(length(NameTokens) - 1, NameTokens), - list_to_atom(PluginNameString). - -expand_dependencies(Pending) -> - expand_dependencies(sets:new(), Pending). -expand_dependencies(Current, []) -> - Current; -expand_dependencies(Current, [Next|Rest]) -> - case sets:is_element(Next, Current) of - true -> - expand_dependencies(Current, Rest); - false -> - case application:load(Next) of - ok -> - ok; - {error, {already_loaded, _}} -> - ok; - {error, Reason} -> - throw({failed_to_load_app, Next, Reason}) - end, - {ok, Required} = application:get_key(Next, applications), - Unique = [A || A <- Required, not(sets:is_element(A, Current))], - expand_dependencies(sets:add_element(Next, Current), Rest ++ Unique) - end. - -post_process_script(ScriptFile) -> - case file:consult(ScriptFile) of - {ok, [{script, Name, Entries}]} -> - NewEntries = lists:flatmap(fun process_entry/1, Entries), - case file:open(ScriptFile, [write]) of - {ok, Fd} -> - io:format(Fd, "%% script generated at ~w ~w~n~p.~n", - [date(), time(), {script, Name, NewEntries}]), - file:close(Fd), - ok; - {error, OReason} -> - {error, {failed_to_open_script_file_for_writing, OReason}} - end; - {error, Reason} -> - {error, {failed_to_load_script, Reason}} - end. - -process_entry(Entry = {apply,{application,start_boot,[rabbit,permanent]}}) -> - [{apply,{rabbit,prepare,[]}}, Entry]; -process_entry(Entry) -> - [Entry]. - -terminate(Fmt, Args) -> - io:format("ERROR: " ++ Fmt ++ "~n", Args), - halt(1). diff --git a/src/rabbit_prelaunch.erl b/src/rabbit_prelaunch.erl new file mode 100644 index 00000000..59d8efd4 --- /dev/null +++ b/src/rabbit_prelaunch.erl @@ -0,0 +1,263 @@ +%% The contents of this file are subject to the Mozilla Public License +%% Version 1.1 (the "License"); you may not use this file except in +%% compliance with the License. You may obtain a copy of the License at +%% http://www.mozilla.org/MPL/ +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +%% License for the specific language governing rights and limitations +%% under the License. +%% +%% The Original Code is RabbitMQ. +%% +%% The Initial Developers of the Original Code are LShift Ltd, +%% Cohesive Financial Technologies LLC, and Rabbit Technologies Ltd. +%% +%% Portions created before 22-Nov-2008 00:00:00 GMT by LShift Ltd, +%% Cohesive Financial Technologies LLC, or Rabbit Technologies Ltd +%% are Copyright (C) 2007-2008 LShift Ltd, Cohesive Financial +%% Technologies LLC, and Rabbit Technologies Ltd. +%% +%% Portions created by LShift Ltd are Copyright (C) 2007-2010 LShift +%% Ltd. Portions created by Cohesive Financial Technologies LLC are +%% Copyright (C) 2007-2010 Cohesive Financial Technologies +%% LLC. Portions created by Rabbit Technologies Ltd are Copyright +%% (C) 2007-2010 Rabbit Technologies Ltd. +%% +%% All Rights Reserved. +%% +%% Contributor(s): ______________________________________. +%% + +-module(rabbit_prelaunch). + +-export([start/0, stop/0]). + +-define(BaseApps, [rabbit]). + +%%---------------------------------------------------------------------------- +%% Specs +%%---------------------------------------------------------------------------- + +-ifdef(use_specs). + +-spec(start/0 :: () -> no_return()). +-spec(stop/0 :: () -> 'ok'). + +-endif. + +%%---------------------------------------------------------------------------- + +start() -> + io:format("Activating RabbitMQ plugins ...~n"), + + %% Determine our various directories + [PluginDir, UnpackedPluginDir, NodeName] = init:get_plain_arguments(), + RootName = UnpackedPluginDir ++ "/rabbit", + + %% Unpack any .ez plugins + unpack_ez_plugins(PluginDir, UnpackedPluginDir), + + %% Build a list of required apps based on the fixed set, and any plugins + PluginApps = find_plugins(PluginDir) ++ find_plugins(UnpackedPluginDir), + RequiredApps = ?BaseApps ++ PluginApps, + + %% Build the entire set of dependencies - this will load the + %% applications along the way + AllApps = case catch sets:to_list(expand_dependencies(RequiredApps)) of + {failed_to_load_app, App, Err} -> + terminate("failed to load application ~s:~n~p", + [App, Err]); + AppList -> + AppList + end, + AppVersions = [determine_version(App) || App <- AllApps], + RabbitVersion = proplists:get_value(rabbit, AppVersions), + + %% Build the overall release descriptor + RDesc = {release, + {"rabbit", RabbitVersion}, + {erts, erlang:system_info(version)}, + AppVersions}, + + %% Write it out to $RABBITMQ_PLUGINS_EXPAND_DIR/rabbit.rel + file:write_file(RootName ++ ".rel", io_lib:format("~p.~n", [RDesc])), + + %% Compile the script + ScriptFile = RootName ++ ".script", + case systools:make_script(RootName, [local, silent, exref]) of + {ok, Module, Warnings} -> + %% This gets lots of spurious no-source warnings when we + %% have .ez files, so we want to supress them to prevent + %% hiding real issues. On Ubuntu, we also get warnings + %% about kernel/stdlib sources being out of date, which we + %% also ignore for the same reason. + WarningStr = Module:format_warning( + [W || W <- Warnings, + case W of + {warning, {source_not_found, _}} -> false; + {warning, {obj_out_of_date, {_,_,WApp,_,_}}} + when WApp == mnesia; + WApp == stdlib; + WApp == kernel; + WApp == sasl; + WApp == crypto; + WApp == os_mon -> false; + _ -> true + end]), + case length(WarningStr) of + 0 -> ok; + _ -> io:format("~s", [WarningStr]) + end, + ok; + {error, Module, Error} -> + terminate("generation of boot script file ~s failed:~n~s", + [ScriptFile, Module:format_error(Error)]) + end, + + case post_process_script(ScriptFile) of + ok -> ok; + {error, Reason} -> + terminate("post processing of boot script file ~s failed:~n~w", + [ScriptFile, Reason]) + end, + case systools:script2boot(RootName) of + ok -> ok; + error -> terminate("failed to compile boot script file ~s", + [ScriptFile]) + end, + io:format("~w plugins activated:~n", [length(PluginApps)]), + [io:format("* ~s-~s~n", [App, proplists:get_value(App, AppVersions)]) + || App <- PluginApps], + io:nl(), + case net_kernel:start([list_to_atom(NodeName), shortnames]) of + {ok, _Pid} -> ok = net_kernel:stop(); + {error, Result} -> + terminate("starting node with name ~p failed - " + "an instance with the same name may already be running~n~p~n", + [NodeName, Result]) + end, + halt(), + ok. + +stop() -> + ok. + +determine_version(App) -> + application:load(App), + {ok, Vsn} = application:get_key(App, vsn), + {App, Vsn}. + +delete_recursively(Fn) -> + case filelib:is_dir(Fn) and not(is_symlink(Fn)) of + true -> + case file:list_dir(Fn) of + {ok, Files} -> + case lists:foldl(fun ( Fn1, ok) -> delete_recursively( + Fn ++ "/" ++ Fn1); + (_Fn1, Err) -> Err + end, ok, Files) of + ok -> case file:del_dir(Fn) of + ok -> ok; + {error, E} -> {error, + {cannot_delete, Fn, E}} + end; + Err -> Err + end; + {error, E} -> + {error, {cannot_list_files, Fn, E}} + end; + false -> + case filelib:is_file(Fn) of + true -> case file:delete(Fn) of + ok -> ok; + {error, E} -> {error, {cannot_delete, Fn, E}} + end; + false -> ok + end + end. + +is_symlink(Name) -> + case file:read_link(Name) of + {ok, _} -> true; + _ -> false + end. + +unpack_ez_plugins(SrcDir, DestDir) -> + %% Eliminate the contents of the destination directory + case delete_recursively(DestDir) of + ok -> ok; + {error, E} -> terminate("Could not delete dir ~s (~p)", [DestDir, E]) + end, + case filelib:ensure_dir(DestDir ++ "/") of + ok -> ok; + {error, E2} -> terminate("Could not create dir ~s (~p)", [DestDir, E2]) + end, + [unpack_ez_plugin(PluginName, DestDir) || + PluginName <- filelib:wildcard(SrcDir ++ "/*.ez")]. + +unpack_ez_plugin(PluginFn, PluginDestDir) -> + zip:unzip(PluginFn, [{cwd, PluginDestDir}]), + ok. + +find_plugins(PluginDir) -> + [prepare_dir_plugin(PluginName) || + PluginName <- filelib:wildcard(PluginDir ++ "/*/ebin/*.app")]. + +prepare_dir_plugin(PluginAppDescFn) -> + %% Add the plugin ebin directory to the load path + PluginEBinDirN = filename:dirname(PluginAppDescFn), + code:add_path(PluginEBinDirN), + + %% We want the second-last token + NameTokens = string:tokens(PluginAppDescFn,"/."), + PluginNameString = lists:nth(length(NameTokens) - 1, NameTokens), + list_to_atom(PluginNameString). + +expand_dependencies(Pending) -> + expand_dependencies(sets:new(), Pending). +expand_dependencies(Current, []) -> + Current; +expand_dependencies(Current, [Next|Rest]) -> + case sets:is_element(Next, Current) of + true -> + expand_dependencies(Current, Rest); + false -> + case application:load(Next) of + ok -> + ok; + {error, {already_loaded, _}} -> + ok; + {error, Reason} -> + throw({failed_to_load_app, Next, Reason}) + end, + {ok, Required} = application:get_key(Next, applications), + Unique = [A || A <- Required, not(sets:is_element(A, Current))], + expand_dependencies(sets:add_element(Next, Current), Rest ++ Unique) + end. + +post_process_script(ScriptFile) -> + case file:consult(ScriptFile) of + {ok, [{script, Name, Entries}]} -> + NewEntries = lists:flatmap(fun process_entry/1, Entries), + case file:open(ScriptFile, [write]) of + {ok, Fd} -> + io:format(Fd, "%% script generated at ~w ~w~n~p.~n", + [date(), time(), {script, Name, NewEntries}]), + file:close(Fd), + ok; + {error, OReason} -> + {error, {failed_to_open_script_file_for_writing, OReason}} + end; + {error, Reason} -> + {error, {failed_to_load_script, Reason}} + end. + +process_entry(Entry = {apply,{application,start_boot,[rabbit,permanent]}}) -> + [{apply,{rabbit,prepare,[]}}, Entry]; +process_entry(Entry) -> + [Entry]. + +terminate(Fmt, Args) -> + io:format("ERROR: " ++ Fmt ++ "~n", Args), + halt(1). -- cgit v1.2.1 From 0296a452b916f8ec93e5893fda5dd66e8b4930d3 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Wed, 1 Dec 2010 12:32:52 +0000 Subject: Diagnostics on node start failure --- src/rabbit_control.erl | 41 +++++++++++++++++++++++------------------ src/rabbit_prelaunch.erl | 11 ++++++++--- 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/src/rabbit_control.erl b/src/rabbit_control.erl index 72b77b1f..360217a2 100644 --- a/src/rabbit_control.erl +++ b/src/rabbit_control.erl @@ -32,7 +32,7 @@ -module(rabbit_control). -include("rabbit.hrl"). --export([start/0, stop/0, action/5]). +-export([start/0, stop/0, action/5, diagnostics/1]). -define(RPC_TIMEOUT, infinity). @@ -50,6 +50,7 @@ (atom(), node(), [string()], [{string(), any()}], fun ((string(), [any()]) -> 'ok')) -> 'ok'). +-spec(diagnostics/1 :: (node()) -> [{string(), [any()]}]). -spec(usage/0 :: () -> no_return()). -endif. @@ -116,24 +117,28 @@ fmt_stderr(Format, Args) -> rabbit_misc:format_stderr(Format ++ "~n", Args). print_error(Format, Args) -> fmt_stderr("Error: " ++ Format, Args). print_badrpc_diagnostics(Node) -> - fmt_stderr("diagnostics:", []), + [fmt_stderr(Fmt, Args) || {Fmt, Args} <- diagnostics(Node)]. + +diagnostics(Node) -> {_NodeName, NodeHost} = rabbit_misc:nodeparts(Node), - case net_adm:names(NodeHost) of - {error, EpmdReason} -> - fmt_stderr("- unable to connect to epmd on ~s: ~w", - [NodeHost, EpmdReason]); - {ok, NamePorts} -> - fmt_stderr("- nodes and their ports on ~s: ~p", - [NodeHost, [{list_to_atom(Name), Port} || - {Name, Port} <- NamePorts]]) - end, - fmt_stderr("- current node: ~w", [node()]), - case init:get_argument(home) of - {ok, [[Home]]} -> fmt_stderr("- current node home dir: ~s", [Home]); - Other -> fmt_stderr("- no current node home dir: ~p", [Other]) - end, - fmt_stderr("- current node cookie hash: ~s", [rabbit_misc:cookie_hash()]), - ok. + [ + {"diagnostics:", []}, + case net_adm:names(NodeHost) of + {error, EpmdReason} -> + {"- unable to connect to epmd on ~s: ~w", + [NodeHost, EpmdReason]}; + {ok, NamePorts} -> + {"- nodes and their ports on ~s: ~p", + [NodeHost, [{list_to_atom(Name), Port} || + {Name, Port} <- NamePorts]]} + end, + {"- current node: ~w", [node()]}, + case init:get_argument(home) of + {ok, [[Home]]} -> {"- current node home dir: ~s", [Home]}; + Other -> {"- no current node home dir: ~p", [Other]} + end, + {"- current node cookie hash: ~s", [rabbit_misc:cookie_hash()]} + ]. stop() -> ok. diff --git a/src/rabbit_prelaunch.erl b/src/rabbit_prelaunch.erl index 59d8efd4..b37573dd 100644 --- a/src/rabbit_prelaunch.erl +++ b/src/rabbit_prelaunch.erl @@ -130,12 +130,17 @@ start() -> [io:format("* ~s-~s~n", [App, proplists:get_value(App, AppVersions)]) || App <- PluginApps], io:nl(), + + % check whether it is possible to start a node with the requested nodename case net_kernel:start([list_to_atom(NodeName), shortnames]) of {ok, _Pid} -> ok = net_kernel:stop(); {error, Result} -> - terminate("starting node with name ~p failed - " - "an instance with the same name may already be running~n~p~n", - [NodeName, Result]) + io:format("starting node with name ~p failed. " + "(is RabbitMQ already running?)~n~p~n", + [NodeName, Result]), + [io:format(Fmt ++ "~n", Args) || + {Fmt, Args} <- rabbit_control:diagnostics(NodeName)], + halt(1) end, halt(), ok. -- cgit v1.2.1 From ae7b7d4cae6805c5b1c1a7d1ec0d67abf95d9fe1 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Thu, 2 Dec 2010 09:03:04 +0000 Subject: simplify multiple confirm code --- src/rabbit_channel.erl | 44 ++++++++++++++++++-------------------------- 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index b2e6658b..f7b875a0 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -1359,35 +1359,27 @@ stop_confirm_timer(State = #ch{confirm_tref = TRef}) -> State#ch{confirm_tref = undefined}. flush_multiple(State = #ch{writer_pid = WriterPid, - held_confirms = Cs, - unconfirmed = UC}) -> + held_confirms = Cs}) -> case gb_sets:is_empty(Cs) of - true -> State; + true -> State#ch{confirm_tref = undefined}; false -> [First | Rest] = gb_sets:to_list(Cs), - [rabbit_writer:send_command(WriterPid, - #'basic.ack'{delivery_tag = T}) || - T <- case Rest of - [] -> [First]; - _ -> flush_multiple( - First, Rest, WriterPid, - case gb_sets:is_empty(UC) of - false -> gb_sets:smallest(UC); - true -> gb_sets:largest(Cs) + 1 - end) - end], + {Mult, Inds} = find_consecutive_sequence(First, Rest), + ok = rabbit_writer:send_command( + WriterPid, + #'basic.ack'{delivery_tag = Mult, multiple = true}), + ok = lists:foldl( + fun(T, ok) -> rabbit_writer:send_command( + WriterPid, + #'basic.ack'{delivery_tag = T}) + end, ok, Inds), State#ch{held_confirms = gb_sets:new(), confirm_tref = undefined} end. -flush_multiple(Prev, [Cur | Rest], WriterPid, SNA) -> - ExpNext = Prev + 1, - case {SNA >= Cur, Cur} of - {true, ExpNext} -> flush_multiple(Cur, Rest, WriterPid, SNA); - _ -> flush_multiple(Prev, [], WriterPid, SNA), - [Cur | Rest] - end; -flush_multiple(Prev, [], WriterPid, _) -> - ok = rabbit_writer:send_command(WriterPid, - #'basic.ack'{delivery_tag = Prev, - multiple = true}), - []. +%% Find longest sequence of consecutive numbers at the beginning. +find_consecutive_sequence(Last, []) -> + {Last, []}; +find_consecutive_sequence(Last, [N | Ns]) when N == (Last + 1) -> + find_consecutive_sequence(N, Ns); +find_consecutive_sequence(Last, Ns) -> + {Last, Ns}. -- cgit v1.2.1 From 563a024b9bad2560b7bb53a565e2f9f593fbdbc3 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Thu, 2 Dec 2010 10:05:29 +0000 Subject: fix to compile and run under R12B5 --- 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 3dbd2b22..f518c561 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -488,12 +488,12 @@ attempt_delivery(#delivery{txn = none, %% we don't need an expiry here because messages are %% not being enqueued, so we use an empty %% message_properties. + BP = ?BASE_MESSAGE_PROPERTIES, {AckTag, BQS1} = BQ:publish_delivered( AckRequired, Message, - ?BASE_MESSAGE_PROPERTIES - #message_properties{ - needs_confirming = NeedsConfirming}, + BP#message_properties{ + needs_confirming = NeedsConfirming}, BQS), {{Message, false, AckTag}, true, State1#q{backing_queue_state = BQS1}} -- cgit v1.2.1 From 6402594d24c71b3849347f445a8cd7a0cc25ecfb Mon Sep 17 00:00:00 2001 From: Rob Harrop Date: Thu, 2 Dec 2010 12:02:29 +0000 Subject: Added cosmetic change suggested in the bug report --- src/rabbit_amqqueue_process.erl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index f518c561..25859c22 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -488,11 +488,10 @@ attempt_delivery(#delivery{txn = none, %% we don't need an expiry here because messages are %% not being enqueued, so we use an empty %% message_properties. - BP = ?BASE_MESSAGE_PROPERTIES, {AckTag, BQS1} = BQ:publish_delivered( AckRequired, Message, - BP#message_properties{ + (?BASE_MESSAGE_PROPERTIES)#message_properties{ needs_confirming = NeedsConfirming}, BQS), {{Message, false, AckTag}, true, -- cgit v1.2.1 From 7d68ac0e63c3be4571591ef8dfdf6a15da15801d Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 2 Dec 2010 12:39:32 +0000 Subject: Tone down the claims in the optimisation comment. --- src/rabbit_writer.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_writer.erl b/src/rabbit_writer.erl index 9c32c272..12b34bcd 100644 --- a/src/rabbit_writer.erl +++ b/src/rabbit_writer.erl @@ -188,8 +188,8 @@ assemble_frames(Channel, MethodRecord, Content, FrameMax, Protocol) -> %% We optimise delivery of small messages. Content-bearing methods %% require at least three frames. Small messages always fit into %% that. We hand their frames to the Erlang network functions in one -%% go, which leads to more efficient processing in the runtime and a -%% greater chance of coalescing into fewer TCP packets. +%% go, which may lead to somewhat more efficient processing in the +%% runtime and a greater chance of coalescing into fewer TCP packets. %% %% By contrast, for larger messages, split across many frames, we want %% to allow interleaving of frames on different channels. Hence we -- cgit v1.2.1 From 710e4bc6ee31229f555ea816cbfc167704f857ab Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 2 Dec 2010 21:00:30 +0000 Subject: cosmetic --- src/rabbit_channel.erl | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index f7b875a0..3d120046 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -460,20 +460,19 @@ send_or_enqueue_ack(undefined, _QPid, State) -> send_or_enqueue_ack(_MsgSeqNo, _QPid, State = #ch{confirm_enabled = false}) -> State; send_or_enqueue_ack(MsgSeqNo, QPid, State = #ch{confirm_multiple = false}) -> - do_if_unconfirmed( - MsgSeqNo, QPid, - fun(MSN, State1 = #ch{writer_pid = WriterPid}) -> - ok = rabbit_writer:send_command( - WriterPid, #'basic.ack'{delivery_tag = MSN}), - State1 - end, State); + do_if_unconfirmed(MsgSeqNo, QPid, + fun(MSN, State1 = #ch{writer_pid = WriterPid}) -> + ok = rabbit_writer:send_command( + WriterPid, #'basic.ack'{ + delivery_tag = MSN}), + State1 + end, State); send_or_enqueue_ack(MsgSeqNo, QPid, State = #ch{confirm_multiple = true}) -> - do_if_unconfirmed( - MsgSeqNo, QPid, - fun(MSN, State1 = #ch{held_confirms = As}) -> - start_confirm_timer( - State1#ch{held_confirms = gb_sets:add(MSN, As)}) - end, State). + do_if_unconfirmed(MsgSeqNo, QPid, + fun(MSN, State1 = #ch{held_confirms = As}) -> + start_confirm_timer( + State1#ch{held_confirms = gb_sets:add(MSN, As)}) + end, State). do_if_unconfirmed(MsgSeqNo, QPid, ConfirmFun, State = #ch{unconfirmed = UC, @@ -484,9 +483,8 @@ do_if_unconfirmed(MsgSeqNo, QPid, ConfirmFun, Unconfirmed1 = gb_sets:delete(MsgSeqNo, UC), case QPid of undefined -> - ConfirmFun(MsgSeqNo, - State#ch{unconfirmed = Unconfirmed1}); - _ -> + ConfirmFun(MsgSeqNo, State#ch{unconfirmed = Unconfirmed1}); + _ -> {ok, Qs} = dict:find(MsgSeqNo, QFM), Qs1 = sets:del_element(QPid, Qs), case sets:size(Qs1) of @@ -499,7 +497,8 @@ do_if_unconfirmed(MsgSeqNo, QPid, ConfirmFun, dict:store(MsgSeqNo, Qs1, QFM)} end end; - false -> State + false -> + State end. handle_method(#'channel.open'{}, _, State = #ch{state = starting}) -> -- cgit v1.2.1 From 1646f9716e4daa5f8f679ed04d8c86d48fe6ec75 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 2 Dec 2010 21:05:52 +0000 Subject: cosmetic: rename field/var --- src/rabbit_channel.erl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 3d120046..0c8ad00a 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -49,7 +49,7 @@ uncommitted_ack_q, unacked_message_q, username, virtual_host, most_recently_declared_queue, consumer_mapping, blocking, queue_collector_pid, stats_timer, - confirm_enabled, published_count, confirm_multiple, confirm_tref, + confirm_enabled, publish_seqno, confirm_multiple, confirm_tref, held_confirms, unconfirmed, queues_for_msg}). -define(MAX_PERMISSION_CACHE_SIZE, 12). @@ -191,7 +191,7 @@ init([Channel, ReaderPid, WriterPid, Username, VHost, CollectorPid, queue_collector_pid = CollectorPid, stats_timer = StatsTimer, confirm_enabled = false, - published_count = 0, + publish_seqno = 0, confirm_multiple = false, held_confirms = gb_sets:new(), unconfirmed = gb_sets:new(), @@ -536,11 +536,11 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, {MsgSeqNo, State1} = case ConfirmEnabled of false -> {undefined, State}; - true -> Count = State#ch.published_count, - {Count, - State#ch{published_count = Count + 1, + true -> SeqNo = State#ch.publish_seqno, + {SeqNo, + State#ch{publish_seqno = SeqNo + 1, unconfirmed = - gb_sets:add(Count, State#ch.unconfirmed)}} + gb_sets:add(SeqNo, State#ch.unconfirmed)}} end, Message = #basic_message{exchange_name = ExchangeName, routing_key = RoutingKey, -- cgit v1.2.1 From 63c4c3994f96993ea29f83588030568a8143f944 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Fri, 3 Dec 2010 11:13:14 +0000 Subject: Consistent prelaunch checks on windows --- scripts/rabbitmq-server.bat | 3 +-- src/rabbit_prelaunch.erl | 21 ++++++++++++++++++--- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/scripts/rabbitmq-server.bat b/scripts/rabbitmq-server.bat index f972a4aa..872c87e3 100644 --- a/scripts/rabbitmq-server.bat +++ b/scripts/rabbitmq-server.bat @@ -123,8 +123,7 @@ set RABBITMQ_EBIN_ROOT=!TDP0!..\ebin "!RABBITMQ_NODENAME!" set RABBITMQ_BOOT_FILE=!RABBITMQ_PLUGINS_EXPAND_DIR!\rabbit -if not exist "!RABBITMQ_BOOT_FILE!.boot" ( - echo Custom Boot File "!RABBITMQ_BOOT_FILE!.boot" is missing. +if ERRORLEVEL 1 ( exit /B 1 ) diff --git a/src/rabbit_prelaunch.erl b/src/rabbit_prelaunch.erl index b37573dd..35cc79d4 100644 --- a/src/rabbit_prelaunch.erl +++ b/src/rabbit_prelaunch.erl @@ -34,6 +34,7 @@ -export([start/0, stop/0]). -define(BaseApps, [rabbit]). +-define(ERROR_CODE, 1). %%---------------------------------------------------------------------------- %% Specs @@ -135,14 +136,16 @@ start() -> case net_kernel:start([list_to_atom(NodeName), shortnames]) of {ok, _Pid} -> ok = net_kernel:stop(); {error, Result} -> + % consistent layout by flushing error_log mailbox + gen_event:which_handlers(error_logger), io:format("starting node with name ~p failed. " "(is RabbitMQ already running?)~n~p~n", [NodeName, Result]), [io:format(Fmt ++ "~n", Args) || {Fmt, Args} <- rabbit_control:diagnostics(NodeName)], - halt(1) + terminate(?ERROR_CODE) end, - halt(), + terminate(0), ok. stop() -> @@ -265,4 +268,16 @@ process_entry(Entry) -> terminate(Fmt, Args) -> io:format("ERROR: " ++ Fmt ++ "~n", Args), - halt(1). + terminate(?ERROR_CODE). + +terminate(Status) -> + case os:type() of + {unix, _} -> + halt(Status); + {win32, _} -> + init:stop(Status), + receive + after infinity -> ok + end + end. + -- cgit v1.2.1 From 4551f44b18a848e1dc3d543e5ccc3ca1c9dc2932 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sun, 5 Dec 2010 18:57:19 +0000 Subject: cosmetic --- src/rabbit_variable_queue.erl | 41 ++++++++++++++++------------------------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index 7c676164..ce80bc92 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -1471,31 +1471,22 @@ reduce_memory_use(AlphaBetaFun, BetaGammaFun, BetaDeltaFun, AckFun, {Reduce, State1} = case chunk_size(RamMsgCount + gb_trees:size(RamAckIndex), TargetRamItemCount) of - 0 -> - {false, State}; - S1 -> - ReduceFuns = - case (AvgAckIngress - AvgAckEgress) > - (AvgIngress - AvgEgress) of - true -> - %% ACKs are growing faster than the queue, - %% push messages from there first. - [AckFun, AlphaBetaFun]; - false -> - %% The queue is growing faster than the - %% acks, push queue messages first. - [AlphaBetaFun, AckFun] - end, - {_, State2} = - %% Both reduce functions get a chance to reduce - %% memory. The second may very well get a quota of - %% 0 if the first function managed to push out the - %% maximum number of messages. - lists:foldl( - fun (ReduceFun, {QuotaN, StateN}) -> - ReduceFun(QuotaN, StateN) - end, {S1, State}, ReduceFuns), - {true, State2} + 0 -> {false, State}; + %% Reduce memory of pending acks and alphas. The order is + %% determined based on which is growing faster. Whichever + %% comes second may very well get a quota of 0 if the + %% first manages to push out the max number of messages. + S1 -> {_, State2} = + lists:foldl(fun (ReduceFun, {QuotaN, StateN}) -> + ReduceFun(QuotaN, StateN) + end, + {S1, State}, + case (AvgAckIngress - AvgAckEgress) > + (AvgIngress - AvgEgress) of + true -> [AckFun, AlphaBetaFun]; + false -> [AlphaBetaFun, AckFun] + end), + {true, State2} end, case State1 #vqstate.target_ram_item_count of -- cgit v1.2.1 From 37ee768e978377a87bc5d17d072d51adc97547d7 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sun, 5 Dec 2010 19:31:22 +0000 Subject: cosmetic --- src/rabbit_tests.erl | 2 +- src/rabbit_variable_queue.erl | 194 +++++++++++++++++++++--------------------- 2 files changed, 96 insertions(+), 100 deletions(-) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 8b58b822..b9edad9a 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -1898,7 +1898,7 @@ test_variable_queue_ack_limiting(VQ0) -> VQ6 = check_variable_queue_status( rabbit_variable_queue:set_ram_duration_target(0, VQ5), [{len, Len div 2}, - {target_ram_item_count, 0}, + {target_ram_count, 0}, {ram_msg_count, 0}, {ram_ack_count, 0}]), diff --git a/src/rabbit_variable_queue.erl b/src/rabbit_variable_queue.erl index ce80bc92..225ded3c 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -158,7 +158,7 @@ %% The conversion from alphas to betas is also chunked, but only to %% ensure no more than ?IO_BATCH_SIZE alphas are converted to betas at %% any one time. This further smooths the effects of changes to the -%% target_ram_item_count and ensures the queue remains responsive +%% target_ram_count and ensures the queue remains responsive %% even when there is a large amount of IO work to do. The %% idle_timeout callback is utilised to ensure that conversions are %% done as promptly as possible whilst ensuring the queue remains @@ -256,7 +256,7 @@ len, persistent_count, - target_ram_item_count, + target_ram_count, ram_msg_count, ram_msg_count_prev, ram_ack_count_prev, @@ -351,7 +351,7 @@ persistent_count :: non_neg_integer(), transient_threshold :: non_neg_integer(), - target_ram_item_count :: non_neg_integer() | 'infinity', + target_ram_count :: non_neg_integer() | 'infinity', ram_msg_count :: non_neg_integer(), ram_msg_count_prev :: non_neg_integer(), ram_index_count :: non_neg_integer(), @@ -734,26 +734,24 @@ len(#vqstate { len = Len }) -> Len. is_empty(State) -> 0 == len(State). -set_ram_duration_target(DurationTarget, - State = #vqstate { - rates = - #rates { avg_egress = AvgEgressRate, - avg_ingress = AvgIngressRate }, - ack_rates = - #rates { avg_egress = AvgAckEgressRate, - avg_ingress = AvgAckIngressRate }, - target_ram_item_count = TargetRamItemCount }) -> +set_ram_duration_target( + DurationTarget, State = #vqstate { + rates = #rates { avg_egress = AvgEgressRate, + avg_ingress = AvgIngressRate }, + ack_rates = #rates { avg_egress = AvgAckEgressRate, + avg_ingress = AvgAckIngressRate }, + target_ram_count = TargetRamCount }) -> Rate = AvgEgressRate + AvgIngressRate + AvgAckEgressRate + AvgAckIngressRate, - TargetRamItemCount1 = + TargetRamCount1 = case DurationTarget of infinity -> infinity; _ -> trunc(DurationTarget * Rate) %% msgs = sec * msgs/sec end, - State1 = State #vqstate { target_ram_item_count = TargetRamItemCount1 }, - a(case TargetRamItemCount1 == infinity orelse - (TargetRamItemCount =/= infinity andalso - TargetRamItemCount1 >= TargetRamItemCount) of + State1 = State #vqstate { target_ram_count = TargetRamCount1 }, + a(case TargetRamCount1 == infinity orelse + (TargetRamCount =/= infinity andalso + TargetRamCount1 >= TargetRamCount) of true -> State1; false -> reduce_memory_use(State1) end). @@ -829,40 +827,39 @@ idle_timeout(State) -> a(reduce_memory_use(tx_commit_index(State))). handle_pre_hibernate(State = #vqstate { index_state = IndexState }) -> State #vqstate { index_state = rabbit_queue_index:flush(IndexState) }. -status(#vqstate { q1 = Q1, q2 = Q2, delta = Delta, q3 = Q3, q4 = Q4, - len = Len, - pending_ack = PA, - ram_ack_index = RAI, - on_sync = #sync { funs = From }, - target_ram_item_count = TargetRamItemCount, - ram_msg_count = RamMsgCount, - ram_index_count = RamIndexCount, - next_seq_id = NextSeqId, - persistent_count = PersistentCount, - rates = #rates { - avg_egress = AvgEgressRate, - avg_ingress = AvgIngressRate }, - ack_rates = #rates { - avg_egress = AvgAckEgressRate, - avg_ingress = AvgAckIngressRate } }) -> - [ {q1 , queue:len(Q1)}, - {q2 , bpqueue:len(Q2)}, - {delta , Delta}, - {q3 , bpqueue:len(Q3)}, - {q4 , queue:len(Q4)}, - {len , Len}, - {pending_acks , dict:size(PA)}, - {outstanding_txns , length(From)}, - {target_ram_item_count , TargetRamItemCount}, - {ram_msg_count , RamMsgCount}, - {ram_ack_count , gb_trees:size(RAI)}, - {ram_index_count , RamIndexCount}, - {next_seq_id , NextSeqId}, - {persistent_count , PersistentCount}, - {avg_ingress_rate , AvgIngressRate}, - {avg_egress_rate , AvgEgressRate}, - {avg_ack_ingress_rate , AvgAckIngressRate}, - {avg_ack_egress_rate , AvgAckEgressRate} ]. +status(#vqstate { + q1 = Q1, q2 = Q2, delta = Delta, q3 = Q3, q4 = Q4, + len = Len, + pending_ack = PA, + ram_ack_index = RAI, + on_sync = #sync { funs = From }, + target_ram_count = TargetRamCount, + ram_msg_count = RamMsgCount, + ram_index_count = RamIndexCount, + next_seq_id = NextSeqId, + persistent_count = PersistentCount, + rates = #rates { avg_egress = AvgEgressRate, + avg_ingress = AvgIngressRate }, + ack_rates = #rates { avg_egress = AvgAckEgressRate, + avg_ingress = AvgAckIngressRate } }) -> + [ {q1 , queue:len(Q1)}, + {q2 , bpqueue:len(Q2)}, + {delta , Delta}, + {q3 , bpqueue:len(Q3)}, + {q4 , queue:len(Q4)}, + {len , Len}, + {pending_acks , dict:size(PA)}, + {outstanding_txns , length(From)}, + {target_ram_count , TargetRamCount}, + {ram_msg_count , RamMsgCount}, + {ram_ack_count , gb_trees:size(RAI)}, + {ram_index_count , RamIndexCount}, + {next_seq_id , NextSeqId}, + {persistent_count , PersistentCount}, + {avg_ingress_rate , AvgIngressRate}, + {avg_egress_rate , AvgEgressRate}, + {avg_ack_ingress_rate, AvgAckIngressRate}, + {avg_ack_egress_rate , AvgAckEgressRate} ]. %%---------------------------------------------------------------------------- %% Minor helpers @@ -1056,37 +1053,37 @@ init(IsDurable, IndexState, DeltaCount, Terms, end, Now = now(), State = #vqstate { - q1 = queue:new(), - q2 = bpqueue:new(), - delta = Delta, - q3 = bpqueue:new(), - q4 = queue:new(), - next_seq_id = NextSeqId, - pending_ack = dict:new(), - ram_ack_index = gb_trees:empty(), - index_state = IndexState1, - msg_store_clients = {PersistentClient, TransientClient}, - on_sync = ?BLANK_SYNC, - durable = IsDurable, - transient_threshold = NextSeqId, - - len = DeltaCount1, - persistent_count = DeltaCount1, - - target_ram_item_count = infinity, - ram_msg_count = 0, - ram_msg_count_prev = 0, - ram_ack_count_prev = 0, - ram_index_count = 0, - out_counter = 0, - in_counter = 0, - msgs_on_disk = gb_sets:new(), - msg_indices_on_disk = gb_sets:new(), - unconfirmed = gb_sets:new(), - ack_out_counter = 0, - ack_in_counter = 0, - rates = blank_rate(Now, DeltaCount1), - ack_rates = blank_rate(Now, 0) }, + q1 = queue:new(), + q2 = bpqueue:new(), + delta = Delta, + q3 = bpqueue:new(), + q4 = queue:new(), + next_seq_id = NextSeqId, + pending_ack = dict:new(), + ram_ack_index = gb_trees:empty(), + index_state = IndexState1, + msg_store_clients = {PersistentClient, TransientClient}, + on_sync = ?BLANK_SYNC, + durable = IsDurable, + transient_threshold = NextSeqId, + + len = DeltaCount1, + persistent_count = DeltaCount1, + + target_ram_count = infinity, + ram_msg_count = 0, + ram_msg_count_prev = 0, + ram_ack_count_prev = 0, + ram_index_count = 0, + out_counter = 0, + in_counter = 0, + rates = blank_rate(Now, DeltaCount1), + msgs_on_disk = gb_sets:new(), + msg_indices_on_disk = gb_sets:new(), + unconfirmed = gb_sets:new(), + ack_out_counter = 0, + ack_in_counter = 0, + ack_rates = blank_rate(Now, 0) }, a(maybe_deltas_to_betas(State)). blank_rate(Timestamp, IngressLength) -> @@ -1443,7 +1440,7 @@ msg_indices_written_to_disk(QPid, GuidSet) -> %% though the conversion function for that is called as necessary. The %% reason is twofold. Firstly, this is safe because the conversion is %% only ever necessary just after a transition to a -%% target_ram_item_count of zero or after an incremental alpha->beta +%% target_ram_count of zero or after an incremental alpha->beta %% conversion. In the former case the conversion is performed straight %% away (i.e. any betas present at the time are converted to deltas), %% and in the latter case the need for a conversion is flagged up @@ -1454,23 +1451,22 @@ msg_indices_written_to_disk(QPid, GuidSet) -> %% 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, _BetaGammaFun, _BetaDeltaFun, _AckFun, - State = #vqstate {target_ram_item_count = infinity}) -> + State = #vqstate {target_ram_count = infinity}) -> {false, State}; reduce_memory_use(AlphaBetaFun, BetaGammaFun, BetaDeltaFun, AckFun, State = #vqstate { - ram_ack_index = RamAckIndex, - ram_msg_count = RamMsgCount, - target_ram_item_count = TargetRamItemCount, - rates = #rates { - avg_ingress = AvgIngress, - avg_egress = AvgEgress }, - ack_rates = #rates { - avg_ingress = AvgAckIngress, - avg_egress = AvgAckEgress } }) -> + ram_ack_index = RamAckIndex, + ram_msg_count = RamMsgCount, + target_ram_count = TargetRamCount, + rates = #rates { avg_ingress = AvgIngress, + avg_egress = AvgEgress }, + ack_rates = #rates { avg_ingress = AvgAckIngress, + avg_egress = AvgAckEgress } + }) -> {Reduce, State1} = case chunk_size(RamMsgCount + gb_trees:size(RamAckIndex), - TargetRamItemCount) of + TargetRamCount) of 0 -> {false, State}; %% Reduce memory of pending acks and alphas. The order is %% determined based on which is growing faster. Whichever @@ -1489,7 +1485,7 @@ reduce_memory_use(AlphaBetaFun, BetaGammaFun, BetaDeltaFun, AckFun, {true, State2} end, - case State1 #vqstate.target_ram_item_count of + case State1 #vqstate.target_ram_count of 0 -> {Reduce, BetaDeltaFun(State1)}; _ -> case chunk_size(State1 #vqstate.ram_index_count, permitted_ram_index_count(State1)) of @@ -1685,11 +1681,11 @@ maybe_push_q4_to_betas(Quota, State = #vqstate { q4 = Q4 }) -> maybe_push_alphas_to_betas(_Generator, _Consumer, Quota, _Q, State = #vqstate { - ram_msg_count = RamMsgCount, - target_ram_item_count = TargetRamItemCount }) + ram_msg_count = RamMsgCount, + target_ram_count = TargetRamCount }) when Quota =:= 0 orelse - TargetRamItemCount =:= infinity orelse - TargetRamItemCount >= RamMsgCount -> + TargetRamCount =:= infinity orelse + TargetRamCount >= RamMsgCount -> {Quota, State}; maybe_push_alphas_to_betas(Generator, Consumer, Quota, Q, State) -> case Generator(Q) of -- cgit v1.2.1 From bb3beff8d8ad3ed6381e2e39ed4ed3ffeac81e91 Mon Sep 17 00:00:00 2001 From: Matthew Sackman Date: Mon, 6 Dec 2010 12:06:01 +0000 Subject: Minor correction --- 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 225ded3c..0db51165 100644 --- a/src/rabbit_variable_queue.erl +++ b/src/rabbit_variable_queue.erl @@ -31,7 +31,7 @@ -module(rabbit_variable_queue). --export([init/5, init/3, terminate/1, delete_and_terminate/1, +-export([init/3, terminate/1, delete_and_terminate/1, purge/1, publish/3, publish_delivered/4, fetch/2, ack/2, tx_publish/4, tx_ack/3, tx_rollback/2, tx_commit/4, requeue/3, len/1, is_empty/1, dropwhile/2, @@ -42,7 +42,7 @@ -export([start/1, stop/0]). %% exported for testing only --export([start_msg_store/2, stop_msg_store/0]). +-export([start_msg_store/2, stop_msg_store/0, init/5]). %%---------------------------------------------------------------------------- %% Definitions: -- cgit v1.2.1 From 491142304b11035c82436ee14a36ba97005ef454 Mon Sep 17 00:00:00 2001 From: Matthew Sackman Date: Mon, 6 Dec 2010 12:39:53 +0000 Subject: Correction of specs. --- src/rabbit_msg_store.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_msg_store.erl b/src/rabbit_msg_store.erl index fea7d4a8..e8b4e8e2 100644 --- a/src/rabbit_msg_store.erl +++ b/src/rabbit_msg_store.erl @@ -144,13 +144,13 @@ -type(startup_fun_state() :: {(fun ((A) -> 'finished' | {rabbit_guid:guid(), non_neg_integer(), A})), A}). --type(guid_fun() :: fun ((gb_set()) -> any())). +-type(maybe_guid_fun() :: 'undefined' | fun ((gb_set()) -> any())). -spec(start_link/4 :: (atom(), file:filename(), [binary()] | 'undefined', startup_fun_state()) -> rabbit_types:ok_pid_or_error()). -spec(successfully_recovered_state/1 :: (server()) -> boolean()). --spec(client_init/3 :: (server(), client_ref(), guid_fun()) -> +-spec(client_init/3 :: (server(), client_ref(), maybe_guid_fun()) -> client_msstate()). -spec(client_terminate/1 :: (client_msstate()) -> 'ok'). -spec(client_delete_and_terminate/1 :: (client_msstate()) -> 'ok'). -- cgit v1.2.1 From 5d3b291386560f1d9629be9293f2d624779ef322 Mon Sep 17 00:00:00 2001 From: Matthew Sackman Date: Mon, 6 Dec 2010 13:04:06 +0000 Subject: Rip out old persister as it no longer works given the arrival of pub acks --- Makefile | 3 - ebin/rabbit_app.in | 1 - src/rabbit_invariable_queue.erl | 314 ------------------------- src/rabbit_persister.erl | 496 ---------------------------------------- 4 files changed, 814 deletions(-) delete mode 100644 src/rabbit_invariable_queue.erl delete mode 100644 src/rabbit_persister.erl diff --git a/Makefile b/Makefile index d3f052f6..e0d5744c 100644 --- a/Makefile +++ b/Makefile @@ -178,9 +178,6 @@ start-rabbit-on-node: all stop-rabbit-on-node: all echo "rabbit:stop()." | $(ERL_CALL) -force-snapshot: all - echo "rabbit_persister:force_snapshot()." | $(ERL_CALL) - set-memory-alarm: all echo "alarm_handler:set_alarm({vm_memory_high_watermark, []})." | \ $(ERL_CALL) diff --git a/ebin/rabbit_app.in b/ebin/rabbit_app.in index 17d05a99..6c33ef8b 100644 --- a/ebin/rabbit_app.in +++ b/ebin/rabbit_app.in @@ -6,7 +6,6 @@ {registered, [rabbit_amqqueue_sup, rabbit_log, rabbit_node_monitor, - rabbit_persister, rabbit_router, rabbit_sup, rabbit_tcp_client_sup]}, diff --git a/src/rabbit_invariable_queue.erl b/src/rabbit_invariable_queue.erl deleted file mode 100644 index 5a0532ea..00000000 --- a/src/rabbit_invariable_queue.erl +++ /dev/null @@ -1,314 +0,0 @@ -%% The contents of this file are subject to the Mozilla Public License -%% Version 1.1 (the "License"); you may not use this file except in -%% compliance with the License. You may obtain a copy of the License at -%% http://www.mozilla.org/MPL/ -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the -%% License for the specific language governing rights and limitations -%% under the License. -%% -%% The Original Code is RabbitMQ. -%% -%% The Initial Developers of the Original Code are LShift Ltd, -%% Cohesive Financial Technologies LLC, and Rabbit Technologies Ltd. -%% -%% Portions created before 22-Nov-2008 00:00:00 GMT by LShift Ltd, -%% Cohesive Financial Technologies LLC, or Rabbit Technologies Ltd -%% are Copyright (C) 2007-2008 LShift Ltd, Cohesive Financial -%% Technologies LLC, and Rabbit Technologies Ltd. -%% -%% Portions created by LShift Ltd are Copyright (C) 2007-2010 LShift -%% Ltd. Portions created by Cohesive Financial Technologies LLC are -%% Copyright (C) 2007-2010 Cohesive Financial Technologies -%% LLC. Portions created by Rabbit Technologies Ltd are Copyright -%% (C) 2007-2010 Rabbit Technologies Ltd. -%% -%% All Rights Reserved. -%% -%% Contributor(s): ______________________________________. -%% - --module(rabbit_invariable_queue). - --export([init/3, terminate/1, delete_and_terminate/1, purge/1, publish/3, - publish_delivered/4, fetch/2, ack/2, tx_publish/4, tx_ack/3, - dropwhile/2, tx_rollback/2, tx_commit/4, requeue/3, len/1, is_empty/1, - set_ram_duration_target/2, ram_duration/1, needs_idle_timeout/1, - idle_timeout/1, handle_pre_hibernate/1, status/1]). - --export([start/1, stop/0]). - --behaviour(rabbit_backing_queue). - --include("rabbit.hrl"). - --record(iv_state, { queue, qname, durable, len, pending_ack }). --record(tx, { pending_messages, pending_acks, is_persistent }). - --ifdef(use_specs). - --type(ack() :: rabbit_guid:guid() | 'blank_ack'). --type(state() :: #iv_state { queue :: queue(), - qname :: rabbit_amqqueue:name(), - len :: non_neg_integer(), - pending_ack :: dict() - }). --include("rabbit_backing_queue_spec.hrl"). - --endif. - -start(DurableQueues) -> - ok = rabbit_sup:start_child(rabbit_persister, [DurableQueues]). - -stop() -> - ok = rabbit_sup:stop_child(rabbit_persister). - -init(QName, IsDurable, Recover) -> - Q = queue:from_list(case IsDurable andalso Recover of - true -> rabbit_persister:queue_content(QName); - false -> [] - end), - #iv_state { queue = Q, - qname = QName, - durable = IsDurable, - len = queue:len(Q), - pending_ack = dict:new() }. - -terminate(State) -> - State #iv_state { queue = queue:new(), len = 0, pending_ack = dict:new() }. - -delete_and_terminate(State = #iv_state { qname = QName, durable = IsDurable, - pending_ack = PA }) -> - ok = persist_acks(QName, IsDurable, none, dict:fetch_keys(PA), PA), - {_PLen, State1} = purge(State), - terminate(State1). - -purge(State = #iv_state { queue = Q, qname = QName, durable = IsDurable, - len = Len }) -> - %% We do not purge messages pending acks. - {AckTags, PA} = - rabbit_misc:queue_fold( - fun ({#basic_message { is_persistent = false }, - _MsgProps, _IsDelivered}, Acc) -> - Acc; - ({Msg = #basic_message { guid = Guid }, MsgProps, IsDelivered}, - {AckTagsN, PAN}) -> - ok = persist_delivery(QName, IsDurable, IsDelivered, Msg), - {[Guid | AckTagsN], store_ack(Msg, MsgProps, PAN)} - end, {[], dict:new()}, Q), - ok = persist_acks(QName, IsDurable, none, AckTags, PA), - {Len, State #iv_state { len = 0, queue = queue:new() }}. - -publish(Msg, MsgProps, State = #iv_state { queue = Q, - qname = QName, - durable = IsDurable, - len = Len }) -> - ok = persist_message(QName, IsDurable, none, Msg, MsgProps), - State #iv_state { queue = enqueue(Msg, MsgProps, false, Q), len = Len + 1 }. - -publish_delivered(false, _Msg, _MsgProps, State) -> - {blank_ack, State}; -publish_delivered(true, Msg = #basic_message { guid = Guid }, - MsgProps, - State = #iv_state { qname = QName, durable = IsDurable, - len = 0, pending_ack = PA }) -> - ok = persist_message(QName, IsDurable, none, Msg, MsgProps), - ok = persist_delivery(QName, IsDurable, false, Msg), - {Guid, State #iv_state { pending_ack = store_ack(Msg, MsgProps, PA) }}. - -dropwhile(_Pred, State = #iv_state { len = 0 }) -> - State; -dropwhile(Pred, State = #iv_state { queue = Q }) -> - {{value, {Msg, MsgProps, IsDelivered}}, Q1} = queue:out(Q), - case Pred(MsgProps) of - true -> {_, State1} = fetch_internal(false, Q1, Msg, MsgProps, - IsDelivered, State), - dropwhile(Pred, State1); - false -> State - end. - -fetch(_AckRequired, State = #iv_state { len = 0 }) -> - {empty, State}; -fetch(AckRequired, State = #iv_state { queue = Q }) -> - {{value, {Msg, MsgProps, IsDelivered}}, Q1} = queue:out(Q), - fetch_internal(AckRequired, Q1, Msg, MsgProps, IsDelivered, State). - -fetch_internal(AckRequired, Q1, - Msg = #basic_message { guid = Guid }, - MsgProps, IsDelivered, - State = #iv_state { len = Len, - qname = QName, - durable = IsDurable, - pending_ack = PA }) -> - Len1 = Len - 1, - ok = persist_delivery(QName, IsDurable, IsDelivered, Msg), - PA1 = store_ack(Msg, MsgProps, PA), - {AckTag, PA2} = case AckRequired of - true -> {Guid, PA1}; - false -> ok = persist_acks(QName, IsDurable, none, - [Guid], PA1), - {blank_ack, PA} - end, - {{Msg, IsDelivered, AckTag, Len1}, - State #iv_state { queue = Q1, len = Len1, pending_ack = PA2 }}. - -ack(AckTags, State = #iv_state { qname = QName, durable = IsDurable, - pending_ack = PA }) -> - ok = persist_acks(QName, IsDurable, none, AckTags, PA), - PA1 = remove_acks(AckTags, PA), - State #iv_state { pending_ack = PA1 }. - -tx_publish(Txn, Msg, MsgProps, State = #iv_state { qname = QName, - durable = IsDurable }) -> - Tx = #tx { pending_messages = Pubs } = lookup_tx(Txn), - store_tx(Txn, Tx #tx { pending_messages = [{Msg, MsgProps} | Pubs] }), - ok = persist_message(QName, IsDurable, Txn, Msg, MsgProps), - State. - -tx_ack(Txn, AckTags, State = #iv_state { qname = QName, durable = IsDurable, - pending_ack = PA }) -> - Tx = #tx { pending_acks = Acks } = lookup_tx(Txn), - store_tx(Txn, Tx #tx { pending_acks = [AckTags | Acks] }), - ok = persist_acks(QName, IsDurable, Txn, AckTags, PA), - State. - -tx_rollback(Txn, State = #iv_state { qname = QName }) -> - #tx { pending_acks = AckTags } = lookup_tx(Txn), - ok = do_if_persistent(fun rabbit_persister:rollback_transaction/1, - Txn, QName), - erase_tx(Txn), - {lists:flatten(AckTags), State}. - -tx_commit(Txn, Fun, MsgPropsFun, State = #iv_state { qname = QName, - pending_ack = PA, - queue = Q, - len = Len }) -> - #tx { pending_acks = AckTags, pending_messages = PubsRev } = lookup_tx(Txn), - ok = do_if_persistent(fun rabbit_persister:commit_transaction/1, - Txn, QName), - erase_tx(Txn), - Fun(), - AckTags1 = lists:flatten(AckTags), - PA1 = remove_acks(AckTags1, PA), - {Q1, Len1} = lists:foldr(fun ({Msg, MsgProps}, {QN, LenN}) -> - {enqueue(Msg, MsgPropsFun(MsgProps), - false, QN), - LenN + 1} - end, {Q, Len}, PubsRev), - {AckTags1, State #iv_state { pending_ack = PA1, queue = Q1, len = Len1 }}. - -requeue(AckTags, MsgPropsFun, State = #iv_state { pending_ack = PA, - queue = Q, - len = Len }) -> - %% We don't need to touch the persister here - the persister will - %% already have these messages published and delivered as - %% necessary. The complication is that the persister's seq_id will - %% now be wrong, given the position of these messages in our queue - %% here. However, the persister's seq_id is only used for sorting - %% on startup, and requeue is silent as to where the requeued - %% messages should appear, thus the persister is permitted to sort - %% based on seq_id, even though it'll likely give a different - %% order to the last known state of our queue, prior to shutdown. - {Q1, Len1} = lists:foldl( - fun (Guid, {QN, LenN}) -> - {Msg = #basic_message {}, MsgProps} - = dict:fetch(Guid, PA), - {enqueue(Msg, MsgPropsFun(MsgProps), true, QN), - LenN + 1} - end, {Q, Len}, AckTags), - PA1 = remove_acks(AckTags, PA), - State #iv_state { pending_ack = PA1, queue = Q1, len = Len1 }. - -enqueue(Msg, MsgProps, IsDelivered, Q) -> - queue:in({Msg, MsgProps, IsDelivered}, Q). - -len(#iv_state { len = Len }) -> Len. - -is_empty(State) -> 0 == len(State). - -set_ram_duration_target(_DurationTarget, State) -> State. - -ram_duration(State) -> {0, State}. - -needs_idle_timeout(_State) -> false. - -idle_timeout(State) -> State. - -handle_pre_hibernate(State) -> State. - -status(_State) -> []. - -%%---------------------------------------------------------------------------- - -remove_acks(AckTags, PA) -> lists:foldl(fun dict:erase/2, PA, AckTags). - -store_ack(Msg = #basic_message { guid = Guid }, MsgProps, PA) -> - dict:store(Guid, {Msg, MsgProps}, PA). - -%%---------------------------------------------------------------------------- - -lookup_tx(Txn) -> - case get({txn, Txn}) of - undefined -> #tx { pending_messages = [], - pending_acks = [], - is_persistent = false }; - V -> V - end. - -store_tx(Txn, Tx) -> - put({txn, Txn}, Tx). - -erase_tx(Txn) -> - erase({txn, Txn}). - -mark_tx_persistent(Txn) -> - store_tx(Txn, (lookup_tx(Txn)) #tx { is_persistent = true }). - -is_tx_persistent(Txn) -> - (lookup_tx(Txn)) #tx.is_persistent. - -do_if_persistent(F, Txn, QName) -> - ok = case is_tx_persistent(Txn) of - false -> ok; - true -> F({Txn, QName}) - end. - -%%---------------------------------------------------------------------------- - -persist_message(QName, true, Txn, Msg = #basic_message { - is_persistent = true }, MsgProps) -> - Msg1 = Msg #basic_message { - %% don't persist any recoverable decoded properties - content = rabbit_binary_parser:clear_decoded_content( - Msg #basic_message.content)}, - persist_work(Txn, QName, - [{publish, Msg1, MsgProps, - {QName, Msg1 #basic_message.guid}}]); -persist_message(_QName, _IsDurable, _Txn, _Msg, _MsgProps) -> - ok. - -persist_delivery(QName, true, false, #basic_message { is_persistent = true, - guid = Guid }) -> - persist_work(none, QName, [{deliver, {QName, Guid}}]); -persist_delivery(_QName, _IsDurable, _IsDelivered, _Msg) -> - ok. - -persist_acks(QName, true, Txn, AckTags, PA) -> - persist_work(Txn, QName, - [{ack, {QName, Guid}} || Guid <- AckTags, - begin - {Msg, _MsgProps} - = dict:fetch(Guid, PA), - Msg #basic_message.is_persistent - end]); -persist_acks(_QName, _IsDurable, _Txn, _AckTags, _PA) -> - ok. - -persist_work(_Txn,_QName, []) -> - ok; -persist_work(none, _QName, WorkList) -> - rabbit_persister:dirty_work(WorkList); -persist_work(Txn, QName, WorkList) -> - mark_tx_persistent(Txn), - rabbit_persister:extend_transaction({Txn, QName}, WorkList). diff --git a/src/rabbit_persister.erl b/src/rabbit_persister.erl deleted file mode 100644 index 11056c8e..00000000 --- a/src/rabbit_persister.erl +++ /dev/null @@ -1,496 +0,0 @@ -%% The contents of this file are subject to the Mozilla Public License -%% Version 1.1 (the "License"); you may not use this file except in -%% compliance with the License. You may obtain a copy of the License at -%% http://www.mozilla.org/MPL/ -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the -%% License for the specific language governing rights and limitations -%% under the License. -%% -%% The Original Code is RabbitMQ. -%% -%% The Initial Developers of the Original Code are LShift Ltd, -%% Cohesive Financial Technologies LLC, and Rabbit Technologies Ltd. -%% -%% Portions created before 22-Nov-2008 00:00:00 GMT by LShift Ltd, -%% Cohesive Financial Technologies LLC, or Rabbit Technologies Ltd -%% are Copyright (C) 2007-2008 LShift Ltd, Cohesive Financial -%% Technologies LLC, and Rabbit Technologies Ltd. -%% -%% Portions created by LShift Ltd are Copyright (C) 2007-2010 LShift -%% Ltd. Portions created by Cohesive Financial Technologies LLC are -%% Copyright (C) 2007-2010 Cohesive Financial Technologies -%% LLC. Portions created by Rabbit Technologies Ltd are Copyright -%% (C) 2007-2010 Rabbit Technologies Ltd. -%% -%% All Rights Reserved. -%% -%% Contributor(s): ______________________________________. -%% - --module(rabbit_persister). - --behaviour(gen_server). - --export([start_link/1]). - --export([init/1, handle_call/3, handle_cast/2, handle_info/2, - terminate/2, code_change/3]). - --export([transaction/1, extend_transaction/2, dirty_work/1, - commit_transaction/1, rollback_transaction/1, - force_snapshot/0, queue_content/1]). - --include("rabbit.hrl"). - --define(SERVER, ?MODULE). - --define(LOG_BUNDLE_DELAY, 5). --define(COMPLETE_BUNDLE_DELAY, 2). - --define(PERSISTER_LOG_FORMAT_VERSION, {2, 6}). - --record(pstate, {log_handle, entry_count, deadline, - pending_logs, pending_replies, snapshot}). - -%% two tables for efficient persistency -%% one maps a key to a message -%% the other maps a key to one or more queues. -%% The aim is to reduce the overload of storing a message multiple times -%% when it appears in several queues. --record(psnapshot, {transactions, messages, queues, next_seq_id}). - -%%---------------------------------------------------------------------------- - --ifdef(use_specs). - --type(pkey() :: rabbit_guid:guid()). --type(pmsg() :: {rabbit_amqqueue:name(), pkey()}). - --type(work_item() :: - {publish, - rabbit_types:message(), rabbit_types:message_properties(), pmsg()} | - {deliver, pmsg()} | - {ack, pmsg()}). - --spec(start_link/1 :: ([rabbit_amqqueue:name()]) -> - rabbit_types:ok_pid_or_error()). --spec(transaction/1 :: ([work_item()]) -> 'ok'). --spec(extend_transaction/2 :: - ({rabbit_types:txn(), rabbit_amqqueue:name()}, [work_item()]) - -> 'ok'). --spec(dirty_work/1 :: ([work_item()]) -> 'ok'). --spec(commit_transaction/1 :: - ({rabbit_types:txn(), rabbit_amqqueue:name()}) -> 'ok'). --spec(rollback_transaction/1 :: - ({rabbit_types:txn(), rabbit_amqqueue:name()}) -> 'ok'). --spec(force_snapshot/0 :: () -> 'ok'). --spec(queue_content/1 :: - (rabbit_amqqueue:name()) -> [{rabbit_types:message(), boolean()}]). - --endif. - -%%---------------------------------------------------------------------------- - -start_link(DurableQueues) -> - gen_server:start_link({local, ?SERVER}, ?MODULE, [DurableQueues], []). - -transaction(MessageList) -> - ?LOGDEBUG("transaction ~p~n", [MessageList]), - TxnKey = rabbit_guid:guid(), - gen_server:call(?SERVER, {transaction, TxnKey, MessageList}, infinity). - -extend_transaction(TxnKey, MessageList) -> - ?LOGDEBUG("extend_transaction ~p ~p~n", [TxnKey, MessageList]), - gen_server:cast(?SERVER, {extend_transaction, TxnKey, MessageList}). - -dirty_work(MessageList) -> - ?LOGDEBUG("dirty_work ~p~n", [MessageList]), - gen_server:cast(?SERVER, {dirty_work, MessageList}). - -commit_transaction(TxnKey) -> - ?LOGDEBUG("commit_transaction ~p~n", [TxnKey]), - gen_server:call(?SERVER, {commit_transaction, TxnKey}, infinity). - -rollback_transaction(TxnKey) -> - ?LOGDEBUG("rollback_transaction ~p~n", [TxnKey]), - gen_server:cast(?SERVER, {rollback_transaction, TxnKey}). - -force_snapshot() -> - gen_server:call(?SERVER, force_snapshot, infinity). - -queue_content(QName) -> - gen_server:call(?SERVER, {queue_content, QName}, infinity). - -%%-------------------------------------------------------------------- - -init([DurableQueues]) -> - process_flag(trap_exit, true), - FileName = base_filename(), - ok = filelib:ensure_dir(FileName), - Snapshot = #psnapshot{transactions = dict:new(), - messages = ets:new(messages, []), - queues = ets:new(queues, [ordered_set]), - next_seq_id = 0}, - LogHandle = - case disk_log:open([{name, rabbit_persister}, - {head, current_snapshot(Snapshot)}, - {file, FileName}]) of - {ok, LH} -> LH; - {repaired, LH, {recovered, Recovered}, {badbytes, Bad}} -> - WarningFun = if - Bad > 0 -> fun rabbit_log:warning/2; - true -> fun rabbit_log:info/2 - end, - WarningFun("Repaired persister log - ~p recovered, ~p bad~n", - [Recovered, Bad]), - LH - end, - {Res, NewSnapshot} = - internal_load_snapshot(LogHandle, DurableQueues, Snapshot), - case Res of - ok -> - ok = take_snapshot(LogHandle, NewSnapshot); - {error, Reason} -> - rabbit_log:error("Failed to load persister log: ~p~n", [Reason]), - ok = take_snapshot_and_save_old(LogHandle, NewSnapshot) - end, - State = #pstate{log_handle = LogHandle, - entry_count = 0, - deadline = infinity, - pending_logs = [], - pending_replies = [], - snapshot = NewSnapshot}, - {ok, State}. - -handle_call({transaction, Key, MessageList}, From, State) -> - NewState = internal_extend(Key, MessageList, State), - do_noreply(internal_commit(From, Key, NewState)); -handle_call({commit_transaction, TxnKey}, From, State) -> - do_noreply(internal_commit(From, TxnKey, State)); -handle_call(force_snapshot, _From, State) -> - do_reply(ok, flush(true, State)); -handle_call({queue_content, QName}, _From, - State = #pstate{snapshot = #psnapshot{messages = Messages, - queues = Queues}}) -> - MatchSpec= [{{{QName,'$1'}, '$2', '$3', '$4'}, [], - [{{'$4', '$1', '$2', '$3'}}]}], - do_reply([{ets:lookup_element(Messages, K, 2), MP, D} || - {_, K, D, MP} <- lists:sort(ets:select(Queues, MatchSpec))], - State); -handle_call(_Request, _From, State) -> - {noreply, State}. - -handle_cast({rollback_transaction, TxnKey}, State) -> - do_noreply(internal_rollback(TxnKey, State)); -handle_cast({dirty_work, MessageList}, State) -> - do_noreply(internal_dirty_work(MessageList, State)); -handle_cast({extend_transaction, TxnKey, MessageList}, State) -> - do_noreply(internal_extend(TxnKey, MessageList, State)); -handle_cast(_Msg, State) -> - {noreply, State}. - -handle_info(timeout, State = #pstate{deadline = infinity}) -> - State1 = flush(true, State), - {noreply, State1, hibernate}; -handle_info(timeout, State) -> - do_noreply(flush(State)); -handle_info(_Info, State) -> - {noreply, State}. - -terminate(_Reason, State = #pstate{log_handle = LogHandle}) -> - flush(State), - disk_log:close(LogHandle), - ok. - -code_change(_OldVsn, State, _Extra) -> - {ok, flush(State)}. - -%%-------------------------------------------------------------------- - -internal_extend(Key, MessageList, State) -> - log_work(fun (ML) -> {extend_transaction, Key, ML} end, - MessageList, State). - -internal_dirty_work(MessageList, State) -> - log_work(fun (ML) -> {dirty_work, ML} end, - MessageList, State). - -internal_commit(From, Key, State = #pstate{snapshot = Snapshot}) -> - Unit = {commit_transaction, Key}, - NewSnapshot = internal_integrate1(Unit, Snapshot), - complete(From, Unit, State#pstate{snapshot = NewSnapshot}). - -internal_rollback(Key, State = #pstate{snapshot = Snapshot}) -> - Unit = {rollback_transaction, Key}, - NewSnapshot = internal_integrate1(Unit, Snapshot), - log(State#pstate{snapshot = NewSnapshot}, Unit). - -complete(From, Item, State = #pstate{deadline = ExistingDeadline, - pending_logs = Logs, - pending_replies = Waiting}) -> - State#pstate{deadline = compute_deadline( - ?COMPLETE_BUNDLE_DELAY, ExistingDeadline), - pending_logs = [Item | Logs], - pending_replies = [From | Waiting]}. - -%% This is made to limit disk usage by writing messages only once onto -%% disk. We keep a table associating pkeys to messages, and provided -%% the list of messages to output is left to right, we can guarantee -%% that pkeys will be a backreference to a message in memory when a -%% "tied" is met. -log_work(CreateWorkUnit, MessageList, - State = #pstate{ - snapshot = Snapshot = #psnapshot{messages = Messages}}) -> - Unit = CreateWorkUnit( - rabbit_misc:map_in_order( - fun (M = {publish, Message, MsgProps, QK = {_QName, PKey}}) -> - case ets:lookup(Messages, PKey) of - [_] -> {tied, MsgProps, QK}; - [] -> ets:insert(Messages, {PKey, Message}), - M - end; - (M) -> M - end, - MessageList)), - NewSnapshot = internal_integrate1(Unit, Snapshot), - log(State#pstate{snapshot = NewSnapshot}, Unit). - -log(State = #pstate{deadline = ExistingDeadline, pending_logs = Logs}, - Message) -> - State#pstate{deadline = compute_deadline(?LOG_BUNDLE_DELAY, - ExistingDeadline), - pending_logs = [Message | Logs]}. - -base_filename() -> - rabbit_mnesia:dir() ++ "/rabbit_persister.LOG". - -take_snapshot(LogHandle, OldFileName, Snapshot) -> - ok = disk_log:sync(LogHandle), - %% current_snapshot is the Head (ie. first thing logged) - ok = disk_log:reopen(LogHandle, OldFileName, current_snapshot(Snapshot)). - -take_snapshot(LogHandle, Snapshot) -> - OldFileName = lists:flatten(base_filename() ++ ".previous"), - file:delete(OldFileName), - rabbit_log:info("Rolling persister log to ~p~n", [OldFileName]), - ok = take_snapshot(LogHandle, OldFileName, Snapshot). - -take_snapshot_and_save_old(LogHandle, Snapshot) -> - {MegaSecs, Secs, MicroSecs} = erlang:now(), - Timestamp = MegaSecs * 1000000 + Secs * 1000 + MicroSecs, - OldFileName = lists:flatten(io_lib:format("~s.saved.~p", - [base_filename(), Timestamp])), - rabbit_log:info("Saving persister log in ~p~n", [OldFileName]), - ok = take_snapshot(LogHandle, OldFileName, Snapshot). - -maybe_take_snapshot(Force, State = #pstate{entry_count = EntryCount, - log_handle = LH, - snapshot = Snapshot}) -> - {ok, MaxWrapEntries} = application:get_env(persister_max_wrap_entries), - if - Force orelse EntryCount >= MaxWrapEntries -> - ok = take_snapshot(LH, Snapshot), - State#pstate{entry_count = 0}; - true -> - State - end. - -later_ms(DeltaMilliSec) -> - {MegaSec, Sec, MicroSec} = now(), - %% Note: not normalised. Unimportant for this application. - {MegaSec, Sec, MicroSec + (DeltaMilliSec * 1000)}. - -%% Result = B - A, more or less -time_diff({B1, B2, B3}, {A1, A2, A3}) -> - (B1 - A1) * 1000000 + (B2 - A2) + (B3 - A3) / 1000000.0 . - -compute_deadline(TimerDelay, infinity) -> - later_ms(TimerDelay); -compute_deadline(_TimerDelay, ExistingDeadline) -> - ExistingDeadline. - -compute_timeout(infinity) -> - {ok, HibernateAfter} = application:get_env(persister_hibernate_after), - HibernateAfter; -compute_timeout(Deadline) -> - DeltaMilliSec = time_diff(Deadline, now()) * 1000.0, - if - DeltaMilliSec =< 1 -> - 0; - true -> - round(DeltaMilliSec) - end. - -do_noreply(State = #pstate{deadline = Deadline}) -> - {noreply, State, compute_timeout(Deadline)}. - -do_reply(Reply, State = #pstate{deadline = Deadline}) -> - {reply, Reply, State, compute_timeout(Deadline)}. - -flush(State) -> flush(false, State). - -flush(ForceSnapshot, State = #pstate{pending_logs = PendingLogs, - pending_replies = Waiting, - log_handle = LogHandle}) -> - State1 = if PendingLogs /= [] -> - disk_log:alog(LogHandle, lists:reverse(PendingLogs)), - State#pstate{entry_count = State#pstate.entry_count + 1}; - true -> - State - end, - State2 = maybe_take_snapshot(ForceSnapshot, State1), - if Waiting /= [] -> - ok = disk_log:sync(LogHandle), - lists:foreach(fun (From) -> gen_server:reply(From, ok) end, - Waiting); - true -> - ok - end, - State2#pstate{deadline = infinity, - pending_logs = [], - pending_replies = []}. - -current_snapshot(_Snapshot = #psnapshot{transactions = Ts, - messages = Messages, - queues = Queues, - next_seq_id = NextSeqId}) -> - %% Avoid infinite growth of the table by removing messages not - %% bound to a queue anymore - PKeys = ets:foldl(fun ({{_QName, PKey}, _Delivered, - _MsgProps, _SeqId}, S) -> - sets:add_element(PKey, S) - end, sets:new(), Queues), - prune_table(Messages, fun (Key) -> sets:is_element(Key, PKeys) end), - InnerSnapshot = {{txns, Ts}, - {messages, ets:tab2list(Messages)}, - {queues, ets:tab2list(Queues)}, - {next_seq_id, NextSeqId}}, - ?LOGDEBUG("Inner snapshot: ~p~n", [InnerSnapshot]), - {persist_snapshot, {vsn, ?PERSISTER_LOG_FORMAT_VERSION}, - term_to_binary(InnerSnapshot)}. - -prune_table(Tab, Pred) -> - true = ets:safe_fixtable(Tab, true), - ok = prune_table(Tab, Pred, ets:first(Tab)), - true = ets:safe_fixtable(Tab, false). - -prune_table(_Tab, _Pred, '$end_of_table') -> ok; -prune_table(Tab, Pred, Key) -> - case Pred(Key) of - true -> ok; - false -> ets:delete(Tab, Key) - end, - prune_table(Tab, Pred, ets:next(Tab, Key)). - -internal_load_snapshot(LogHandle, - DurableQueues, - Snapshot = #psnapshot{messages = Messages, - queues = Queues}) -> - {K, [Loaded_Snapshot | Items]} = disk_log:chunk(LogHandle, start), - case check_version(Loaded_Snapshot) of - {ok, StateBin} -> - {{txns, Ts}, {messages, Ms}, {queues, Qs}, - {next_seq_id, NextSeqId}} = binary_to_term(StateBin), - true = ets:insert(Messages, Ms), - true = ets:insert(Queues, Qs), - Snapshot1 = replay(Items, LogHandle, K, - Snapshot#psnapshot{ - transactions = Ts, - next_seq_id = NextSeqId}), - %% Remove all entries for queues that no longer exist. - %% Note that the 'messages' table is pruned when the next - %% snapshot is taken. - DurableQueuesSet = sets:from_list(DurableQueues), - prune_table(Snapshot1#psnapshot.queues, - fun ({QName, _PKey}) -> - sets:is_element(QName, DurableQueuesSet) - end), - %% uncompleted transactions are discarded - this is TRTTD - %% since we only get into this code on node restart, so - %% any uncompleted transactions will have been aborted. - {ok, Snapshot1#psnapshot{transactions = dict:new()}}; - {error, Reason} -> {{error, Reason}, Snapshot} - end. - -check_version({persist_snapshot, {vsn, ?PERSISTER_LOG_FORMAT_VERSION}, - StateBin}) -> - {ok, StateBin}; -check_version({persist_snapshot, {vsn, Vsn}, _StateBin}) -> - {error, {unsupported_persister_log_format, Vsn}}; -check_version(_Other) -> - {error, unrecognised_persister_log_format}. - -replay([], LogHandle, K, Snapshot) -> - case disk_log:chunk(LogHandle, K) of - {K1, Items} -> - replay(Items, LogHandle, K1, Snapshot); - {K1, Items, Badbytes} -> - rabbit_log:warning("~p bad bytes recovering persister log~n", - [Badbytes]), - replay(Items, LogHandle, K1, Snapshot); - eof -> Snapshot - end; -replay([Item | Items], LogHandle, K, Snapshot) -> - NewSnapshot = internal_integrate_messages(Item, Snapshot), - replay(Items, LogHandle, K, NewSnapshot). - -internal_integrate_messages(Items, Snapshot) -> - lists:foldl(fun (Item, Snap) -> internal_integrate1(Item, Snap) end, - Snapshot, Items). - -internal_integrate1({extend_transaction, Key, MessageList}, - Snapshot = #psnapshot {transactions = Transactions}) -> - Snapshot#psnapshot{transactions = rabbit_misc:dict_cons(Key, MessageList, - Transactions)}; -internal_integrate1({rollback_transaction, Key}, - Snapshot = #psnapshot{transactions = Transactions}) -> - Snapshot#psnapshot{transactions = dict:erase(Key, Transactions)}; -internal_integrate1({commit_transaction, Key}, - Snapshot = #psnapshot{transactions = Transactions, - messages = Messages, - queues = Queues, - next_seq_id = SeqId}) -> - case dict:find(Key, Transactions) of - {ok, MessageLists} -> - ?LOGDEBUG("persist committing txn ~p~n", [Key]), - NextSeqId = - lists:foldr( - fun (ML, SeqIdN) -> - perform_work(ML, Messages, Queues, SeqIdN) end, - SeqId, MessageLists), - Snapshot#psnapshot{transactions = dict:erase(Key, Transactions), - next_seq_id = NextSeqId}; - error -> - Snapshot - end; -internal_integrate1({dirty_work, MessageList}, - Snapshot = #psnapshot{messages = Messages, - queues = Queues, - next_seq_id = SeqId}) -> - Snapshot#psnapshot{next_seq_id = perform_work(MessageList, Messages, - Queues, SeqId)}. - -perform_work(MessageList, Messages, Queues, SeqId) -> - lists:foldl(fun (Item, NextSeqId) -> - perform_work_item(Item, Messages, Queues, NextSeqId) - end, SeqId, MessageList). - -perform_work_item({publish, Message, MsgProps, QK = {_QName, PKey}}, - Messages, Queues, NextSeqId) -> - true = ets:insert(Messages, {PKey, Message}), - true = ets:insert(Queues, {QK, false, MsgProps, NextSeqId}), - NextSeqId + 1; - -perform_work_item({tied, MsgProps, QK}, _Messages, Queues, NextSeqId) -> - true = ets:insert(Queues, {QK, false, MsgProps, NextSeqId}), - NextSeqId + 1; - -perform_work_item({deliver, QK}, _Messages, Queues, NextSeqId) -> - true = ets:update_element(Queues, QK, {2, true}), - NextSeqId; - -perform_work_item({ack, QK}, _Messages, Queues, NextSeqId) -> - true = ets:delete(Queues, QK), - NextSeqId. -- cgit v1.2.1 From a500710672ea861eddd08bb09ae9e803e6767a7e Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Tue, 7 Dec 2010 12:29:59 +0000 Subject: Cope with absence of epmd while performing pre-launch checks --- scripts/rabbitmq-service.bat | 8 ++++---- src/rabbit_prelaunch.erl | 35 ++++++++++++++++++++++------------- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/scripts/rabbitmq-service.bat b/scripts/rabbitmq-service.bat index 2c96b6fd..d2592931 100644 --- a/scripts/rabbitmq-service.bat +++ b/scripts/rabbitmq-service.bat @@ -186,13 +186,13 @@ set RABBITMQ_EBIN_ROOT=!TDP0!..\ebin "!ERLANG_HOME!\bin\erl.exe" ^ -pa "!RABBITMQ_EBIN_ROOT!" ^ -noinput -hidden ^ --s rabbit_plugin_activator ^ +-s rabbit_prelaunch ^ -extra "!RABBITMQ_PLUGINS_DIR:\=/!" ^ - "!RABBITMQ_PLUGINS_EXPAND_DIR:\=/!" + "!RABBITMQ_PLUGINS_EXPAND_DIR:\=/!" ^ + "" set RABBITMQ_BOOT_FILE=!RABBITMQ_PLUGINS_EXPAND_DIR!\rabbit -if not exist "!RABBITMQ_BOOT_FILE!.boot" ( - echo Custom Boot File "!RABBITMQ_BOOT_FILE!.boot" is missing. +if ERRORLEVEL 1 ( exit /B 1 ) diff --git a/src/rabbit_prelaunch.erl b/src/rabbit_prelaunch.erl index 35cc79d4..79634352 100644 --- a/src/rabbit_prelaunch.erl +++ b/src/rabbit_prelaunch.erl @@ -53,7 +53,7 @@ start() -> io:format("Activating RabbitMQ plugins ...~n"), %% Determine our various directories - [PluginDir, UnpackedPluginDir, NodeName] = init:get_plain_arguments(), + [PluginDir, UnpackedPluginDir, Node] = init:get_plain_arguments(), RootName = UnpackedPluginDir ++ "/rabbit", %% Unpack any .ez plugins @@ -132,19 +132,28 @@ start() -> || App <- PluginApps], io:nl(), - % check whether it is possible to start a node with the requested nodename - case net_kernel:start([list_to_atom(NodeName), shortnames]) of - {ok, _Pid} -> ok = net_kernel:stop(); - {error, Result} -> - % consistent layout by flushing error_log mailbox - gen_event:which_handlers(error_logger), - io:format("starting node with name ~p failed. " - "(is RabbitMQ already running?)~n~p~n", - [NodeName, Result]), - [io:format(Fmt ++ "~n", Args) || - {Fmt, Args} <- rabbit_control:diagnostics(NodeName)], - terminate(?ERROR_CODE) + % check whether a node with the same name is already running + case Node of + [] -> ok; + _ -> {NodeName, NodeHost} = rabbit_misc:nodeparts(Node), + case net_adm:names(NodeHost) of + {ok, NamePorts} -> + case proplists:is_defined(NodeName, NamePorts) of + true -> io:format("node with name ~p " + "already running on ~p~n", + [NodeName, NodeHost]), + [io:format(Fmt ++ "~n", Args) || + {Fmt, Args} <- + rabbit_control:diagnostics(Node)], + terminate(?ERROR_CODE); + false -> ok + end; + {error, address} -> ok; + {error, EpmdReason} -> terminate("unexpected epmd error:~p~n", + [EpmdReason]) + end end, + terminate(0), ok. -- cgit v1.2.1 From e279f44c444dc59e75f51b89ce140d0eb2dd2f6b Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Tue, 7 Dec 2010 12:48:58 +0000 Subject: Perform duplicate node check in a function --- src/rabbit_prelaunch.erl | 48 +++++++++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/src/rabbit_prelaunch.erl b/src/rabbit_prelaunch.erl index 79634352..0314cde5 100644 --- a/src/rabbit_prelaunch.erl +++ b/src/rabbit_prelaunch.erl @@ -132,27 +132,7 @@ start() -> || App <- PluginApps], io:nl(), - % check whether a node with the same name is already running - case Node of - [] -> ok; - _ -> {NodeName, NodeHost} = rabbit_misc:nodeparts(Node), - case net_adm:names(NodeHost) of - {ok, NamePorts} -> - case proplists:is_defined(NodeName, NamePorts) of - true -> io:format("node with name ~p " - "already running on ~p~n", - [NodeName, NodeHost]), - [io:format(Fmt ++ "~n", Args) || - {Fmt, Args} <- - rabbit_control:diagnostics(Node)], - terminate(?ERROR_CODE); - false -> ok - end; - {error, address} -> ok; - {error, EpmdReason} -> terminate("unexpected epmd error:~p~n", - [EpmdReason]) - end - end, + ok = duplicate_node_check(Node), terminate(0), ok. @@ -275,6 +255,32 @@ process_entry(Entry = {apply,{application,start_boot,[rabbit,permanent]}}) -> process_entry(Entry) -> [Entry]. +%% Check whether a node with the same name is already running +duplicate_node_check([]) -> + %% Ignore running node while installing windows service + ok; +duplicate_node_check(Node) -> + case Node of + [] -> ok; + _ -> {NodeName, NodeHost} = rabbit_misc:nodeparts(Node), + case net_adm:names(NodeHost) of + {ok, NamePorts} -> + case proplists:is_defined(NodeName, NamePorts) of + true -> io:format("node with name ~p " + "already running on ~p~n", + [NodeName, NodeHost]), + [io:format(Fmt ++ "~n", Args) || + {Fmt, Args} <- + rabbit_control:diagnostics(Node)], + terminate(?ERROR_CODE); + false -> ok + end; + {error, address} -> ok; + {error, EpmdReason} -> terminate("unexpected epmd error:~p~n", + [EpmdReason]) + end + end. + terminate(Fmt, Args) -> io:format("ERROR: " ++ Fmt ++ "~n", Args), terminate(?ERROR_CODE). -- cgit v1.2.1 From ecc3f04426cd3e496e03bf279c5521e32d02adde Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Tue, 7 Dec 2010 12:57:36 +0000 Subject: Remove redundant code --- src/rabbit_prelaunch.erl | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/src/rabbit_prelaunch.erl b/src/rabbit_prelaunch.erl index 0314cde5..a9c86e50 100644 --- a/src/rabbit_prelaunch.erl +++ b/src/rabbit_prelaunch.erl @@ -260,25 +260,21 @@ duplicate_node_check([]) -> %% Ignore running node while installing windows service ok; duplicate_node_check(Node) -> - case Node of - [] -> ok; - _ -> {NodeName, NodeHost} = rabbit_misc:nodeparts(Node), - case net_adm:names(NodeHost) of - {ok, NamePorts} -> - case proplists:is_defined(NodeName, NamePorts) of - true -> io:format("node with name ~p " - "already running on ~p~n", - [NodeName, NodeHost]), - [io:format(Fmt ++ "~n", Args) || - {Fmt, Args} <- - rabbit_control:diagnostics(Node)], - terminate(?ERROR_CODE); - false -> ok - end; - {error, address} -> ok; - {error, EpmdReason} -> terminate("unexpected epmd error:~p~n", - [EpmdReason]) - end + {NodeName, NodeHost} = rabbit_misc:nodeparts(Node), + case net_adm:names(NodeHost) of + {ok, NamePorts} -> + case proplists:is_defined(NodeName, NamePorts) of + true -> io:format("node with name ~p " + "already running on ~p~n", + [NodeName, NodeHost]), + [io:format(Fmt ++ "~n", Args) || + {Fmt, Args} <- rabbit_control:diagnostics(Node)], + terminate(?ERROR_CODE); + false -> ok + end; + {error, address} -> ok; + {error, EpmdReason} -> terminate("unexpected epmd error:~p~n", + [EpmdReason]) end. terminate(Fmt, Args) -> -- cgit v1.2.1 From 3349ff2911de8bd2a5bfbbfa3a310fc73dc04b8e Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Tue, 7 Dec 2010 13:09:15 +0000 Subject: Error message readability --- src/rabbit_prelaunch.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_prelaunch.erl b/src/rabbit_prelaunch.erl index a9c86e50..867ecb12 100644 --- a/src/rabbit_prelaunch.erl +++ b/src/rabbit_prelaunch.erl @@ -273,7 +273,7 @@ duplicate_node_check(Node) -> false -> ok end; {error, address} -> ok; - {error, EpmdReason} -> terminate("unexpected epmd error:~p~n", + {error, EpmdReason} -> terminate("unexpected epmd error: ~p~n", [EpmdReason]) end. -- cgit v1.2.1 From c558f642972b0a7ec8759b29b066bdc99a80ed8c Mon Sep 17 00:00:00 2001 From: Marek Majkowski Date: Wed, 8 Dec 2010 12:35:28 +0000 Subject: Get rid of -import() statments. --- src/rabbit.erl | 6 ------ src/rabbit_amqqueue.erl | 5 ----- src/rabbit_amqqueue_process.erl | 4 ---- src/rabbit_binary_generator.erl | 2 -- src/rabbit_binary_parser.erl | 2 -- src/rabbit_log.erl | 3 --- src/rabbit_misc.erl | 5 ----- src/rabbit_tests.erl | 2 -- src/rabbit_writer.erl | 2 -- 9 files changed, 31 deletions(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index a619ef36..ace8f286 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -170,12 +170,6 @@ %%--------------------------------------------------------------------------- --import(application). --import(mnesia). --import(lists). --import(inet). --import(gen_tcp). - -include("rabbit_framing.hrl"). -include("rabbit.hrl"). diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 70d8f2dd..775c631d 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -49,11 +49,6 @@ -export([commit_all/3, rollback_all/3, notify_down_all/2, limit_all/3]). -export([on_node_down/1]). --import(mnesia). --import(gen_server2). --import(lists). --import(queue). - -include("rabbit.hrl"). -include_lib("stdlib/include/qlc.hrl"). diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 25859c22..78bb6835 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -48,10 +48,6 @@ handle_info/2, handle_pre_hibernate/1, prioritise_call/3, prioritise_cast/2, prioritise_info/2]). --import(queue). --import(erlang). --import(lists). - % Queue's state -record(q, {q, exclusive_consumer, diff --git a/src/rabbit_binary_generator.erl b/src/rabbit_binary_generator.erl index b2997ae2..a5297a70 100644 --- a/src/rabbit_binary_generator.erl +++ b/src/rabbit_binary_generator.erl @@ -49,8 +49,6 @@ -export([ensure_content_encoded/2, clear_encoded_content/1]). -export([map_exception/3]). --import(lists). - %%---------------------------------------------------------------------------- -ifdef(use_specs). diff --git a/src/rabbit_binary_parser.erl b/src/rabbit_binary_parser.erl index ebf063f0..4b4358b4 100644 --- a/src/rabbit_binary_parser.erl +++ b/src/rabbit_binary_parser.erl @@ -36,8 +36,6 @@ -export([parse_table/1, parse_properties/2]). -export([ensure_content_decoded/1, clear_decoded_content/1]). --import(lists). - %%---------------------------------------------------------------------------- -ifdef(use_specs). diff --git a/src/rabbit_log.erl b/src/rabbit_log.erl index 863f77e7..a1a8364c 100644 --- a/src/rabbit_log.erl +++ b/src/rabbit_log.erl @@ -41,9 +41,6 @@ -export([debug/1, debug/2, message/4, info/1, info/2, warning/1, warning/2, error/1, error/2]). --import(io). --import(error_logger). - -define(SERVER, ?MODULE). %%---------------------------------------------------------------------------- diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 1a05b729..52d76ac4 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -68,11 +68,6 @@ -export([now_ms/0]). -export([lock_file/1]). --import(mnesia). --import(lists). --import(cover). --import(disk_log). - %%---------------------------------------------------------------------------- -ifdef(use_specs). diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index b9edad9a..adf968cb 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -35,8 +35,6 @@ -export([all_tests/0, test_parsing/0]). --import(lists). - -include("rabbit.hrl"). -include("rabbit_framing.hrl"). -include_lib("kernel/include/file.hrl"). diff --git a/src/rabbit_writer.erl b/src/rabbit_writer.erl index 0159609d..068ac186 100644 --- a/src/rabbit_writer.erl +++ b/src/rabbit_writer.erl @@ -39,8 +39,6 @@ send_command_and_notify/4, send_command_and_notify/5]). -export([internal_send_command/4, internal_send_command/6]). --import(gen_tcp). - -record(wstate, {sock, channel, frame_max, protocol}). -define(HIBERNATE_AFTER, 5000). -- cgit v1.2.1 From 130f0c264e7618de6cd2853c7fca6510d319e0ac Mon Sep 17 00:00:00 2001 From: Marek Majkowski Date: Wed, 8 Dec 2010 12:45:31 +0000 Subject: cosmetic: Simplify channel:internal_deliver, notify is always true --- src/rabbit_channel.erl | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 0c8ad00a..27dbe611 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -266,7 +266,7 @@ handle_cast({deliver, ConsumerTag, AckRequired, Msg}, State1 = lock_message(AckRequired, ack_record(DeliveryTag, ConsumerTag, Msg), State), - ok = internal_deliver(WriterPid, true, ConsumerTag, DeliveryTag, Msg), + ok = internal_deliver(WriterPid, ConsumerTag, DeliveryTag, Msg), {_QName, QPid, _MsgId, _Redelivered, _Msg} = Msg, maybe_incr_stats([{QPid, 1}], case AckRequired of @@ -1240,7 +1240,7 @@ lock_message(true, MsgStruct, State = #ch{unacked_message_q = UAMQ}) -> lock_message(false, _MsgStruct, State) -> State. -internal_deliver(WriterPid, Notify, ConsumerTag, DeliveryTag, +internal_deliver(WriterPid, ConsumerTag, DeliveryTag, {_QName, QPid, _MsgId, Redelivered, #basic_message{exchange_name = ExchangeName, routing_key = RoutingKey, @@ -1250,11 +1250,8 @@ internal_deliver(WriterPid, Notify, ConsumerTag, DeliveryTag, redelivered = Redelivered, exchange = ExchangeName#resource.name, routing_key = RoutingKey}, - ok = case Notify of - true -> rabbit_writer:send_command_and_notify( - WriterPid, QPid, self(), M, Content); - false -> rabbit_writer:send_command(WriterPid, M, Content) - end. + rabbit_writer:send_command_and_notify(WriterPid, QPid, self(), M, Content), + ok. terminate(State) -> stop_confirm_timer(State), -- cgit v1.2.1 From a782032665224f63bbc64ceb155cd6cc5fe2a759 Mon Sep 17 00:00:00 2001 From: Marek Majkowski Date: Wed, 8 Dec 2010 13:27:17 +0000 Subject: cosmetic: get rid of channel:internal_deliver --- src/rabbit_channel.erl | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 27dbe611..36db5a94 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -266,7 +266,19 @@ handle_cast({deliver, ConsumerTag, AckRequired, Msg}, State1 = lock_message(AckRequired, ack_record(DeliveryTag, ConsumerTag, Msg), State), - ok = internal_deliver(WriterPid, ConsumerTag, DeliveryTag, Msg), + + {_QName, QPid, _MsgId, Redelivered, + #basic_message{exchange_name = ExchangeName, + routing_key = RoutingKey, + content = Content}} = Msg, + + M = #'basic.deliver'{consumer_tag = ConsumerTag, + delivery_tag = DeliveryTag, + redelivered = Redelivered, + exchange = ExchangeName#resource.name, + routing_key = RoutingKey}, + rabbit_writer:send_command_and_notify(WriterPid, QPid, self(), M, Content), + {_QName, QPid, _MsgId, _Redelivered, _Msg} = Msg, maybe_incr_stats([{QPid, 1}], case AckRequired of @@ -1240,19 +1252,6 @@ lock_message(true, MsgStruct, State = #ch{unacked_message_q = UAMQ}) -> lock_message(false, _MsgStruct, State) -> State. -internal_deliver(WriterPid, ConsumerTag, DeliveryTag, - {_QName, QPid, _MsgId, Redelivered, - #basic_message{exchange_name = ExchangeName, - routing_key = RoutingKey, - content = Content}}) -> - M = #'basic.deliver'{consumer_tag = ConsumerTag, - delivery_tag = DeliveryTag, - redelivered = Redelivered, - exchange = ExchangeName#resource.name, - routing_key = RoutingKey}, - rabbit_writer:send_command_and_notify(WriterPid, QPid, self(), M, Content), - ok. - terminate(State) -> stop_confirm_timer(State), pg_local:leave(rabbit_channels, self()), -- cgit v1.2.1 From 79a6cc8f2b0adc5956fe4ff4df62aff6f81e1451 Mon Sep 17 00:00:00 2001 From: Marek Majkowski Date: Wed, 8 Dec 2010 13:36:11 +0000 Subject: cosmetic: move unpacking around in channel:deliver --- src/rabbit_channel.erl | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 36db5a94..a1db2ccf 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -260,18 +260,17 @@ handle_cast({command, Msg}, State = #ch{writer_pid = WriterPid}) -> ok = rabbit_writer:send_command(WriterPid, Msg), noreply(State); -handle_cast({deliver, ConsumerTag, AckRequired, Msg}, +handle_cast({deliver, ConsumerTag, AckRequired, + Msg = {_QName, QPid, _MsgId, Redelivered, + #basic_message{exchange_name = ExchangeName, + routing_key = RoutingKey, + content = Content}}}, State = #ch{writer_pid = WriterPid, next_tag = DeliveryTag}) -> State1 = lock_message(AckRequired, ack_record(DeliveryTag, ConsumerTag, Msg), State), - {_QName, QPid, _MsgId, Redelivered, - #basic_message{exchange_name = ExchangeName, - routing_key = RoutingKey, - content = Content}} = Msg, - M = #'basic.deliver'{consumer_tag = ConsumerTag, delivery_tag = DeliveryTag, redelivered = Redelivered, @@ -279,7 +278,6 @@ handle_cast({deliver, ConsumerTag, AckRequired, Msg}, routing_key = RoutingKey}, rabbit_writer:send_command_and_notify(WriterPid, QPid, self(), M, Content), - {_QName, QPid, _MsgId, _Redelivered, _Msg} = Msg, maybe_incr_stats([{QPid, 1}], case AckRequired of true -> deliver; -- cgit v1.2.1