From 7c260b5ca20fdef51083c88494169f2d1f5b15eb Mon Sep 17 00:00:00 2001 From: Tony Garnock-Jones Date: Mon, 13 Apr 2009 10:56:26 -0700 Subject: Initial 0-9-1 port. - exchange auto-delete no longer supported - access.request no longer supported - connection redirection no longer supported - trace frames no longer supported --- Makefile | 2 +- include/rabbit.hrl | 3 +-- src/rabbit_access_control.erl | 2 +- src/rabbit_channel.erl | 10 +++------ src/rabbit_error_logger.erl | 2 +- src/rabbit_exchange.erl | 36 ++++++-------------------------- src/rabbit_reader.erl | 48 ++++++------------------------------------- 7 files changed, 19 insertions(+), 84 deletions(-) diff --git a/Makefile b/Makefile index 47aa586c..bec10a2b 100644 --- a/Makefile +++ b/Makefile @@ -31,7 +31,7 @@ TARGET_SRC_DIR=dist/$(TARBALL_NAME) SIBLING_CODEGEN_DIR=../rabbitmq-codegen/ AMQP_CODEGEN_DIR=$(shell [ -d $(SIBLING_CODEGEN_DIR) ] && echo $(SIBLING_CODEGEN_DIR) || echo codegen) -AMQP_SPEC_JSON_PATH=$(AMQP_CODEGEN_DIR)/amqp-0.8.json +AMQP_SPEC_JSON_PATH=$(AMQP_CODEGEN_DIR)/amqp-0.9.1.json ERL_CALL=erl_call -sname $(RABBITMQ_NODENAME) -e diff --git a/include/rabbit.hrl b/include/rabbit.hrl index c707112f..7515e714 100644 --- a/include/rabbit.hrl +++ b/include/rabbit.hrl @@ -49,7 +49,7 @@ -record(resource, {virtual_host, kind, name}). --record(exchange, {name, type, durable, auto_delete, arguments}). +-record(exchange, {name, type, durable, arguments}). -record(amqqueue, {name, durable, auto_delete, arguments, pid}). @@ -105,7 +105,6 @@ #exchange{name :: exchange_name(), type :: exchange_type(), durable :: bool(), - auto_delete :: bool(), arguments :: amqp_table()}). -type(binding() :: #binding{exchange_name :: exchange_name(), diff --git a/src/rabbit_access_control.erl b/src/rabbit_access_control.erl index 54348d9a..fed2fd5b 100644 --- a/src/rabbit_access_control.erl +++ b/src/rabbit_access_control.erl @@ -234,7 +234,7 @@ add_vhost(VHostPath) -> write), [rabbit_exchange:declare( rabbit_misc:r(VHostPath, exchange, Name), - Type, true, false, []) || + Type, true, []) || {Name,Type} <- [{<<"">>, direct}, {<<"amq.direct">>, direct}, diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 7574cd67..80dde26b 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -299,9 +299,6 @@ handle_method(#'channel.close'{}, _, State = #ch{writer_pid = WriterPid}) -> ok = rabbit_writer:send_command(WriterPid, #'channel.close_ok'{}), stop; -handle_method(#'access.request'{},_, State) -> - {reply, #'access.request_ok'{ticket = 1}, State}; - handle_method(#'basic.publish'{exchange = ExchangeNameBin, routing_key = RoutingKey, mandatory = Mandatory, @@ -372,7 +369,7 @@ handle_method(#'basic.get'{queue = QueueNameBin, Content), {noreply, State1#ch{next_tag = DeliveryTag + 1}}; empty -> - {reply, #'basic.get_empty'{cluster_id = <<>>}, State} + {reply, #'basic.get_empty'{deprecated_cluster_id = <<>>}, State} end; handle_method(#'basic.consume'{queue = QueueNameBin, @@ -539,8 +536,8 @@ handle_method(#'exchange.declare'{exchange = ExchangeNameBin, type = TypeNameBin, passive = false, durable = Durable, - auto_delete = AutoDelete, - internal = false, + deprecated_auto_delete = false, %% 0-9-1: true not supported + deprecated_internal = false, %% 0-9-1: true not supported nowait = NoWait, arguments = Args}, _, State = #ch{ virtual_host = VHostPath }) -> @@ -554,7 +551,6 @@ handle_method(#'exchange.declare'{exchange = ExchangeNameBin, rabbit_exchange:declare(ExchangeName, CheckedType, Durable, - AutoDelete, Args) end, ok = rabbit_exchange:assert_type(X, CheckedType), diff --git a/src/rabbit_error_logger.erl b/src/rabbit_error_logger.erl index dc5824f1..625bea90 100644 --- a/src/rabbit_error_logger.erl +++ b/src/rabbit_error_logger.erl @@ -41,7 +41,7 @@ init([DefaultVHost]) -> #exchange{} = rabbit_exchange:declare( rabbit_misc:r(DefaultVHost, exchange, ?LOG_EXCH_NAME), - topic, true, false, []), + topic, true, []), {ok, #resource{virtual_host = DefaultVHost, kind = exchange, name = ?LOG_EXCH_NAME}}. diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index a57e8076..7ce6949d 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -34,7 +34,7 @@ -include("rabbit.hrl"). -include("rabbit_framing.hrl"). --export([recover/0, declare/5, lookup/1, lookup_or_die/1, +-export([recover/0, declare/4, lookup/1, lookup_or_die/1, list/1, info/1, info/2, info_all/1, info_all/2, simple_publish/6, simple_publish/3, route/3]). @@ -62,8 +62,7 @@ -type(bind_res() :: 'ok' | {'error', 'queue_not_found' | 'exchange_not_found'}). -spec(recover/0 :: () -> 'ok'). --spec(declare/5 :: (exchange_name(), exchange_type(), bool(), bool(), - amqp_table()) -> exchange()). +-spec(declare/4 :: (exchange_name(), exchange_type(), bool(), amqp_table()) -> exchange()). -spec(check_type/1 :: (binary()) -> atom()). -spec(assert_type/2 :: (exchange(), atom()) -> 'ok'). -spec(lookup/1 :: (exchange_name()) -> {'ok', exchange()} | not_found()). @@ -100,7 +99,7 @@ %%---------------------------------------------------------------------------- --define(INFO_KEYS, [name, type, durable, auto_delete, arguments]. +-define(INFO_KEYS, [name, type, durable, arguments]. recover() -> ok = rabbit_misc:table_foreach( @@ -115,11 +114,10 @@ recover() -> ReverseRoute, write) end, rabbit_durable_route). -declare(ExchangeName, Type, Durable, AutoDelete, Args) -> +declare(ExchangeName, Type, Durable, Args) -> Exchange = #exchange{name = ExchangeName, type = Type, durable = Durable, - auto_delete = AutoDelete, arguments = Args}, rabbit_misc:execute_mnesia_transaction( fun () -> @@ -181,7 +179,6 @@ infos(Items, X) -> [{Item, i(Item, X)} || Item <- Items]. i(name, #exchange{name = Name}) -> Name; i(type, #exchange{type = Type}) -> Type; i(durable, #exchange{durable = Durable}) -> Durable; -i(auto_delete, #exchange{auto_delete = AutoDelete}) -> AutoDelete; i(arguments, #exchange{arguments = Arguments}) -> Arguments; i(Item, _) -> throw({bad_argument, Item}). @@ -306,7 +303,6 @@ delete_bindings_for_exchange(ExchangeName) -> ok. delete_bindings_for_queue(QueueName) -> - Exchanges = exchanges_for_queue(QueueName), [begin ok = delete_forward_routes(reverse_route(Route)), ok = mnesia:delete_object(rabbit_reverse_route, Route, write) @@ -316,25 +312,12 @@ delete_bindings_for_queue(QueueName) -> #route{binding = #binding{queue_name = QueueName, _ = '_'}}), write)], - [begin - [X] = mnesia:read({rabbit_exchange, ExchangeName}), - ok = maybe_auto_delete(X) - end || ExchangeName <- Exchanges], ok. delete_forward_routes(Route) -> ok = mnesia:delete_object(rabbit_route, Route, write), ok = mnesia:delete_object(rabbit_durable_route, Route, write). -exchanges_for_queue(QueueName) -> - MatchHead = reverse_route( - #route{binding = #binding{exchange_name = '$1', - queue_name = QueueName, - _ = '_'}}), - sets:to_list( - sets:from_list( - mnesia:select(rabbit_reverse_route, [{MatchHead, [], ['$1']}]))). - has_bindings(ExchangeName) -> MatchHead = #route{binding = #binding{exchange_name = ExchangeName, _ = '_'}}, @@ -386,11 +369,10 @@ add_binding(ExchangeName, QueueName, RoutingKey, Arguments) -> delete_binding(ExchangeName, QueueName, RoutingKey, Arguments) -> call_with_exchange_and_queue( ExchangeName, QueueName, - fun (X, Q) -> + fun (_X, Q) -> ok = sync_binding( ExchangeName, QueueName, RoutingKey, Arguments, - Q#amqqueue.durable, fun mnesia:delete_object/3), - maybe_auto_delete(X) + Q#amqqueue.durable, fun mnesia:delete_object/3) end). sync_binding(ExchangeName, QueueName, RoutingKey, Arguments, Durable, Fun) -> @@ -545,12 +527,6 @@ delete(ExchangeName, _IfUnused = true) -> delete(ExchangeName, _IfUnused = false) -> call_with_exchange(ExchangeName, fun unconditional_delete/1). -maybe_auto_delete(#exchange{auto_delete = false}) -> - ok; -maybe_auto_delete(Exchange = #exchange{auto_delete = true}) -> - conditional_delete(Exchange), - ok. - conditional_delete(Exchange = #exchange{name = ExchangeName}) -> case has_bindings(ExchangeName) of false -> unconditional_delete(Exchange); diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index ef8038e7..3760f5d6 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -453,7 +453,6 @@ handle_frame(Type, 0, Payload, State) -> case analyze_frame(Type, Payload) of error -> throw({unknown_frame, Type, Payload}); heartbeat -> State; - trace -> State; {method, MethodName, FieldsBin} -> handle_method0(MethodName, FieldsBin, State); Other -> throw({unexpected_frame_on_channel0, Other}) @@ -462,7 +461,6 @@ handle_frame(Type, Channel, Payload, State) -> case analyze_frame(Type, Payload) of error -> throw({unknown_frame, Type, Payload}); heartbeat -> throw({unexpected_heartbeat_frame, Channel}); - trace -> throw({unexpected_trace_frame, Channel}); AnalyzedFrame -> %%?LOGDEBUG("Ch ~p Frame ~p~n", [Channel, AnalyzedFrame]), case get({channel, Channel}) of @@ -487,8 +485,6 @@ analyze_frame(?FRAME_HEADER, < {content_body, Body}; -analyze_frame(?FRAME_TRACE, _Body) -> - trace; analyze_frame(?FRAME_HEARTBEAT, <<>>) -> heartbeat; analyze_frame(_Type, _Body) -> @@ -606,35 +602,18 @@ handle_method0(#'connection.tune_ok'{channel_max = _ChannelMax, connection = Connection#connection{ timeout_sec = ClientHeartbeat, frame_max = FrameMax}}; -handle_method0(#'connection.open'{virtual_host = VHostPath, - insist = Insist}, +handle_method0(#'connection.open'{virtual_host = VHostPath}, State = #v1{connection_state = opening, connection = Connection = #connection{ user = User}, sock = Sock}) -> ok = rabbit_access_control:check_vhost_access(User, VHostPath), NewConnection = Connection#connection{vhost = VHostPath}, - KnownHosts = format_listeners(rabbit_networking:active_listeners()), - Redirects = compute_redirects(Insist), - if Redirects == [] -> - ok = send_on_channel0( - Sock, - #'connection.open_ok'{known_hosts = KnownHosts}), - State#v1{connection_state = running, - connection = NewConnection}; - true -> - %% FIXME: 'host' is supposed to only contain one - %% address; but which one do we pick? This is - %% really a problem with the spec. - Host = format_listeners(Redirects), - rabbit_log:info("connection ~p redirecting to ~p~n", - [self(), Host]), - ok = send_on_channel0( - Sock, - #'connection.redirect'{host = Host, - known_hosts = KnownHosts}), - close_connection(State#v1{connection = NewConnection}) - end; + ok = send_on_channel0( + Sock, + #'connection.open_ok'{deprecated_known_hosts = <<>>}), + State#v1{connection_state = running, + connection = NewConnection}; handle_method0(#'connection.close'{}, State = #v1{connection_state = running}) -> lists:foreach(fun rabbit_framing_channel:shutdown/1, all_channels()), @@ -653,21 +632,6 @@ handle_method0(_Method, #v1{connection_state = S}) -> send_on_channel0(Sock, Method) -> ok = rabbit_writer:internal_send_command(Sock, 0, Method). -format_listeners(Listeners) -> - list_to_binary( - rabbit_misc:intersperse( - $,, - [io_lib:format("~s:~w", [Host, Port]) || - #listener{host = Host, port = Port} <- Listeners])). - -compute_redirects(true) -> []; -compute_redirects(false) -> - Node = node(), - LNode = rabbit_load:pick(), - if Node == LNode -> []; - true -> rabbit_networking:node_listeners(LNode) - end. - %%-------------------------------------------------------------------------- infos(Items, State) -> [{Item, i(Item, State)} || Item <- Items]. -- cgit v1.2.1 From 415632249c34de913f6a7d71791849c2e2910b6c Mon Sep 17 00:00:00 2001 From: Tony Garnock-Jones Date: Mon, 13 Apr 2009 13:47:02 -0700 Subject: Implement recover-async, rather than recover, while we figure out how recover should work --- src/rabbit_channel.erl | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 80dde26b..089550ba 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -488,7 +488,7 @@ handle_method(#'basic.qos'{prefetch_count = PrefetchCount}, ok = rabbit_limiter:limit(NewLimiterPid, PrefetchCount), {reply, #'basic.qos_ok'{}, State#ch{limiter_pid = NewLimiterPid}}; -handle_method(#'basic.recover'{requeue = true}, +handle_method(#'basic.recover_async'{requeue = true}, _, State = #ch{ transaction_id = none, unacked_message_q = UAMQ }) -> ok = fold_per_queue( @@ -500,10 +500,11 @@ handle_method(#'basic.recover'{requeue = true}, rabbit_amqqueue:requeue( QPid, lists:reverse(MsgIds), self()) end, ok, UAMQ), - %% No answer required, apparently! + %% No answer required - basic.recover is the newer, synchronous + %% variant of this method {noreply, State#ch{unacked_message_q = queue:new()}}; -handle_method(#'basic.recover'{requeue = false}, +handle_method(#'basic.recover_async'{requeue = false}, _, State = #ch{ transaction_id = none, writer_pid = WriterPid, unacked_message_q = UAMQ }) -> @@ -525,10 +526,11 @@ handle_method(#'basic.recover'{requeue = false}, WriterPid, false, ConsumerTag, DeliveryTag, {QName, QPid, MsgId, true, Message}) end, queue:to_list(UAMQ)), - %% No answer required, apparently! + %% No answer required - basic.recover is the newer, synchronous + %% variant of this method {noreply, State}; -handle_method(#'basic.recover'{}, _, _State) -> +handle_method(#'basic.recover_async'{}, _, _State) -> rabbit_misc:protocol_error( not_allowed, "attempt to recover a transactional channel",[]); -- cgit v1.2.1 From 10c4786851c34472c3e93b37701409285e380dd1 Mon Sep 17 00:00:00 2001 From: Tony Garnock-Jones Date: Mon, 13 Apr 2009 14:06:45 -0700 Subject: Use symbolic constants now available. --- src/rabbit_channel.erl | 15 ++++----------- src/rabbit_exchange.erl | 2 +- src/rabbit_router.erl | 4 ++-- 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 089550ba..078c1602 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -259,10 +259,7 @@ expand_routing_key_shortcut(_QueueNameBin, RoutingKey, _State) -> RoutingKey. die_precondition_failed(Fmt, Params) -> - %% FIXME: 406 should be replaced with precondition_failed when we - %% move to AMQP spec >=8.1 - rabbit_misc:protocol_error({false, 406, <<"PRECONDITION_FAILED">>}, - Fmt, Params). + rabbit_misc:protocol_error(precondition_failed, Fmt, Params). %% check that an exchange/queue name does not contain the reserved %% "amq." prefix. @@ -774,14 +771,10 @@ deliver(QPids, Mandatory, Immediate, Txn, Message, WriterPid) -> case rabbit_router:deliver(QPids, Mandatory, Immediate, Txn, Message) of {ok, DeliveredQPids} -> DeliveredQPids; {error, unroutable} -> - %% FIXME: 312 should be replaced by the ?NO_ROUTE - %% definition, when we move to >=0-9 - ok = basic_return(Message, WriterPid, 312, <<"unroutable">>), + ok = basic_return(Message, WriterPid, ?NO_ROUTE, <<"unroutable">>), []; - {error, not_delivered} -> - %% FIXME: 313 should be replaced by the ?NO_CONSUMERS - %% definition, when we move to >=0-9 - ok = basic_return(Message, WriterPid, 313, <<"not_delivered">>), + {error, no_consumers} -> + ok = basic_return(Message, WriterPid, ?NO_CONSUMERS, <<"no_consumers">>), [] end. diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index 7ce6949d..c523eda3 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -58,7 +58,7 @@ -ifdef(use_specs). -type(publish_res() :: {'ok', [pid()]} | - not_found() | {'error', 'unroutable' | 'not_delivered'}). + not_found() | {'error', 'unroutable' | 'no_consumers'}). -type(bind_res() :: 'ok' | {'error', 'queue_not_found' | 'exchange_not_found'}). -spec(recover/0 :: () -> 'ok'). diff --git a/src/rabbit_router.erl b/src/rabbit_router.erl index 0b06a063..afc534f9 100644 --- a/src/rabbit_router.erl +++ b/src/rabbit_router.erl @@ -51,7 +51,7 @@ -spec(start_link/0 :: () -> {'ok', pid()} | 'ignore' | {'error', any()}). -spec(deliver/5 :: ([pid()], bool(), bool(), maybe(txn()), message()) -> - {'ok', [pid()]} | {'error', 'unroutable' | 'not_delivered'}). + {'ok', [pid()]} | {'error', 'unroutable' | 'no_consumers'}). -endif. @@ -180,5 +180,5 @@ run_bindings(QPids, IsMandatory, IsImmediate, Txn, Message) -> %% check_delivery(Mandatory, Immediate, {WasRouted, QPids}) check_delivery(true, _ , {false, []}) -> {error, unroutable}; -check_delivery(_ , true, {_ , []}) -> {error, not_delivered}; +check_delivery(_ , true, {_ , []}) -> {error, no_consumers}; check_delivery(_ , _ , {_ , Qs}) -> {ok, Qs}. -- cgit v1.2.1 From 8184d3cdc5c495520b994cc9768ec36a41478375 Mon Sep 17 00:00:00 2001 From: Tony Garnock-Jones Date: Fri, 24 Apr 2009 17:10:18 +0100 Subject: Fix up merge damage --- src/rabbit_exchange.erl | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index 43c77c92..5aff3abf 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -312,7 +312,6 @@ delete_transient_queue_bindings(QueueName) -> delete_queue_bindings(QueueName, fun delete_transient_forward_routes/1). delete_queue_bindings(QueueName, FwdDeleteFun) -> - Exchanges = exchanges_for_queue(QueueName), [begin ok = FwdDeleteFun(reverse_route(Route)), ok = mnesia:delete_object(rabbit_reverse_route, Route, write) @@ -331,15 +330,6 @@ delete_forward_routes(Route) -> delete_transient_forward_routes(Route) -> ok = mnesia:delete_object(rabbit_route, Route, write). -exchanges_for_queue(QueueName) -> - MatchHead = reverse_route( - #route{binding = #binding{exchange_name = '$1', - queue_name = QueueName, - _ = '_'}}), - sets:to_list( - sets:from_list( - mnesia:select(rabbit_reverse_route, [{MatchHead, [], ['$1']}]))). - has_bindings(ExchangeName) -> MatchHead = #route{binding = #binding{exchange_name = ExchangeName, _ = '_'}}, -- cgit v1.2.1 From ee1a261d858e94a4b1d017bd58ce3b733c69837e Mon Sep 17 00:00:00 2001 From: Tony Garnock-Jones Date: Tue, 12 May 2009 16:34:03 +0100 Subject: Cope with both 0-8/0-9 style and 0-9-1 style protocol headers. --- codegen.py | 1 + src/rabbit_reader.erl | 33 ++++++++++++++++++++------------- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/codegen.py b/codegen.py index 84741ea2..997f3e32 100644 --- a/codegen.py +++ b/codegen.py @@ -308,6 +308,7 @@ def genHrl(spec): print "-define(PROTOCOL_VERSION_MAJOR, %d)." % (spec.major) print "-define(PROTOCOL_VERSION_MINOR, %d)." % (spec.minor) + print "-define(PROTOCOL_VERSION_REVISION, %d)." % (spec.revision) print "-define(PROTOCOL_PORT, %d)." % (spec.port) for (c,v,cls) in spec.constants: diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 3760f5d6..f0d9033d 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -504,8 +504,25 @@ handle_input({frame_payload, Type, Channel, PayloadSize}, PayloadAndMarker, Stat throw({bad_payload, PayloadAndMarker}) end; -handle_input(handshake, <<"AMQP",1,1,ProtocolMajor,ProtocolMinor>>, - State = #v1{sock = Sock, connection = Connection}) -> +handle_input(handshake, <<"AMQP",0,ProtocolMajor,ProtocolMinor,ProtocolRevision>>, State) -> + %% 0-9-1 style protocol header. + check_protocol_header(ProtocolMajor, ProtocolMinor, ProtocolRevision, State); +handle_input(handshake, <<"AMQP",1,1,ProtocolMajor,ProtocolMinor>>, State) -> + %% 0-8 and 0-9 style protocol header. + check_protocol_header(ProtocolMajor, ProtocolMinor, 0, State); + +handle_input(handshake, Other, #v1{sock = Sock}) -> + ok = inet_op(fun () -> gen_tcp:send( + Sock, <<"AMQP",1,1, + ?PROTOCOL_VERSION_MAJOR, + ?PROTOCOL_VERSION_MINOR>>) end), + throw({bad_header, Other}); + +handle_input(Callback, Data, _State) -> + throw({bad_input, Callback, Data}). + +check_protocol_header(ProtocolMajor, ProtocolMinor, _ProtocolRevision, + State = #v1{sock = Sock, connection = Connection}) -> case check_version({ProtocolMajor, ProtocolMinor}, {?PROTOCOL_VERSION_MAJOR, ?PROTOCOL_VERSION_MINOR}) of true -> @@ -532,17 +549,7 @@ handle_input(handshake, <<"AMQP",1,1,ProtocolMajor,ProtocolMinor>>, frame_header, 7}; false -> throw({bad_version, ProtocolMajor, ProtocolMinor}) - end; - -handle_input(handshake, Other, #v1{sock = Sock}) -> - ok = inet_op(fun () -> gen_tcp:send( - Sock, <<"AMQP",1,1, - ?PROTOCOL_VERSION_MAJOR, - ?PROTOCOL_VERSION_MINOR>>) end), - throw({bad_header, Other}); - -handle_input(Callback, Data, _State) -> - throw({bad_input, Callback, Data}). + end. %% the 0-8 spec, confusingly, defines the version as 8-0 adjust_version({8,0}) -> {0,8}; -- cgit v1.2.1 From 97f1ae552a09108d521f0f4ab50e8942bab34167 Mon Sep 17 00:00:00 2001 From: Ben Hood <0x6e6562@gmail.com> Date: Mon, 8 Jun 2009 16:03:56 +0100 Subject: Removed check for durable queues on transient exchanges --- src/rabbit_exchange.erl | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index 815b9e35..e9161e8a 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -367,12 +367,8 @@ call_with_exchange_and_queue(Exchange, Queue, Fun) -> add_binding(ExchangeName, QueueName, RoutingKey, Arguments) -> binding_action( ExchangeName, QueueName, RoutingKey, Arguments, - fun (X, Q, B) -> - if Q#amqqueue.durable and not(X#exchange.durable) -> - {error, durability_settings_incompatible}; - true -> ok = sync_binding(B, Q#amqqueue.durable, - fun mnesia:write/3) - end + fun (_X, Q, B) -> + ok = sync_binding(B, Q#amqqueue.durable, fun mnesia:write/3) end). delete_binding(ExchangeName, QueueName, RoutingKey, Arguments) -> -- cgit v1.2.1 From 3d4a3f10e9f9dde10cf50d5a80007208de44d3b6 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Mon, 5 Oct 2009 16:02:19 +0100 Subject: auto_delete is no longer a property of the exchange class (protocol spec section 1.6) --- src/rabbit_tests.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 5c5c55f1..62a4bd1d 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -656,7 +656,7 @@ test_server_status() -> %% list exchanges ok = info_action( list_exchanges, - [name, type, durable, auto_delete, arguments], + [name, type, durable, arguments], true), %% list bindings -- cgit v1.2.1 From 35670931c3464fc998d76aad671dfcd9593caf7a Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Tue, 6 Oct 2009 14:51:13 +0100 Subject: auto_delete is no longer a property of exchanges --- src/rabbit_control.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_control.erl b/src/rabbit_control.erl index a53ac289..2ad99aeb 100644 --- a/src/rabbit_control.erl +++ b/src/rabbit_control.erl @@ -182,8 +182,8 @@ arguments, node, messages_ready, messages_unacknowledged, messages_uncommitted, messages, acks_uncommitted, consumers, transactions, memory]. The default is to display name and (number of) messages. - must be a member of the list [name, type, durable, -auto_delete, arguments]. The default is to display name and type. + must be a member of the list [name, type, durable, +arguments]. The default is to display name and type. The output format for \"list_bindings\" is a list of rows containing exchange name, routing key, queue name and arguments, in that order. -- cgit v1.2.1 From 539e7a4f82fef0c7143e48a54fd35cf41bec21df Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Tue, 13 Oct 2009 13:19:48 +0100 Subject: Bug 20768: Accept a protocol header with 0-9 or 0-9-1 only, and disconnect otherwise --- src/rabbit_reader.erl | 92 ++++++++++++++++++++++++--------------------------- 1 file changed, 44 insertions(+), 48 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 5816ba10..496e6c1a 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -483,62 +483,58 @@ handle_input({frame_payload, Type, Channel, PayloadSize}, PayloadAndMarker, Stat throw({bad_payload, PayloadAndMarker}) end; -handle_input(handshake, <<"AMQP",0,ProtocolMajor,ProtocolMinor,ProtocolRevision>>, State) -> +%% The two rules pertaining to version negotiation: +%% +%% * If the server cannot support the protocol specified in the +%% protocol header, it MUST respond with a valid protocol header and +%% then close the socket connection. +%% +%% * The server MUST provide a protocol version that is lower than or +%% equal to that requested by the client in the protocol header. +%% +%% We support 0-9-1 and 0-9, so by the first rule, we must close the +%% connection if we're sent anything else. Then, we must send that +%% version in the Connection.start method. +handle_input(handshake, <<"AMQP",0,0,9,1>>, State) -> %% 0-9-1 style protocol header. - check_protocol_header(ProtocolMajor, ProtocolMinor, ProtocolRevision, State); -handle_input(handshake, <<"AMQP",1,1,ProtocolMajor,ProtocolMinor>>, State) -> - %% 0-8 and 0-9 style protocol header. - check_protocol_header(ProtocolMajor, ProtocolMinor, 0, State); + protocol_negotiate(0, 9, 1, State); +handle_input(handshake, <<"AMQP",1,1,0,9>>, State) -> + %% 0-8 and 0-9 style protocol header; we support only 0-9 + protocol_negotiate(0, 9, 0, State); handle_input(handshake, Other, #v1{sock = Sock}) -> ok = inet_op(fun () -> rabbit_net:send( - Sock, <<"AMQP",1,1, - ?PROTOCOL_VERSION_MAJOR, - ?PROTOCOL_VERSION_MINOR>>) end), + Sock, <<"AMQP",0,0,9,1>>) end), throw({bad_header, Other}); handle_input(Callback, Data, _State) -> throw({bad_input, Callback, Data}). -check_protocol_header(ProtocolMajor, ProtocolMinor, _ProtocolRevision, - State = #v1{sock = Sock, connection = Connection}) -> - case check_version({ProtocolMajor, ProtocolMinor}, - {?PROTOCOL_VERSION_MAJOR, ?PROTOCOL_VERSION_MINOR}) of - true -> - {ok, Product} = application:get_key(id), - {ok, Version} = application:get_key(vsn), - ok = send_on_channel0( - Sock, - #'connection.start'{ - version_major = ?PROTOCOL_VERSION_MAJOR, - version_minor = ?PROTOCOL_VERSION_MINOR, - server_properties = - [{list_to_binary(K), longstr, list_to_binary(V)} || - {K, V} <- - [{"product", Product}, - {"version", Version}, - {"platform", "Erlang/OTP"}, - {"copyright", ?COPYRIGHT_MESSAGE}, - {"information", ?INFORMATION_MESSAGE}]], - mechanisms = <<"PLAIN AMQPLAIN">>, - locales = <<"en_US">> }), - {State#v1{connection = Connection#connection{ - timeout_sec = ?NORMAL_TIMEOUT}, - connection_state = starting}, - frame_header, 7}; - false -> - throw({bad_version, ProtocolMajor, ProtocolMinor}) - end. - -%% the 0-8 spec, confusingly, defines the version as 8-0 -adjust_version({8,0}) -> {0,8}; -adjust_version(Version) -> Version. -check_version(ClientVersion, ServerVersion) -> - {ClientMajor, ClientMinor} = adjust_version(ClientVersion), - {ServerMajor, ServerMinor} = adjust_version(ServerVersion), - ClientMajor > ServerMajor - orelse - (ClientMajor == ServerMajor andalso - ClientMinor >= ServerMinor). +%% Offer a protocol version to the client.. Connection.start only +%% includes a major and minor version number, Luckily 0-9 and 0-9-1 +%% are similar enough that clients will be happy with either. +protocol_negotiate(ProtocolMajor, ProtocolMinor, _ProtocolRevision, + State = #v1{sock = Sock, connection = Connection}) -> + {ok, Product} = application:get_key(id), + {ok, Version} = application:get_key(vsn), + ok = send_on_channel0( + Sock, + #'connection.start'{ + version_major = ProtocolMajor, + version_minor = ProtocolMinor, + server_properties = + [{list_to_binary(K), longstr, list_to_binary(V)} || + {K, V} <- + [{"product", Product}, + {"version", Version}, + {"platform", "Erlang/OTP"}, + {"copyright", ?COPYRIGHT_MESSAGE}, + {"information", ?INFORMATION_MESSAGE}]], + mechanisms = <<"PLAIN AMQPLAIN">>, + locales = <<"en_US">> }), + {State#v1{connection = Connection#connection{ + timeout_sec = ?NORMAL_TIMEOUT}, + connection_state = starting}, + frame_header, 7}. %%-------------------------------------------------------------------------- -- cgit v1.2.1 From 0825267a5cc589f2f54a28ec935b5ce9edcf912f Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Mon, 19 Oct 2009 13:09:31 +0100 Subject: Include protocol revision in banner --- src/rabbit.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rabbit.erl b/src/rabbit.erl index 18fd1b17..acc8f84a 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -247,9 +247,10 @@ print_banner() -> "| ~s +---+ |~n" "| |~n" "+-------------------+~n" - "AMQP ~p-~p~n~s~n~s~n~n", + "AMQP ~p-~p-~p~n~s~n~s~n~n", [Product, string:right([$v|Version], ProductLen), ?PROTOCOL_VERSION_MAJOR, ?PROTOCOL_VERSION_MINOR, + ?PROTOCOL_VERSION_REVISION, ?COPYRIGHT_MESSAGE, ?INFORMATION_MESSAGE]), Settings = [{"node", node()}, {"app descriptor", app_location()}, -- cgit v1.2.1 From c44103847c904f85fc0070fd3f1d93e966286e78 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Thu, 22 Oct 2009 17:17:05 +0100 Subject: - keep track of the owner in the amqqueue record. We need this so we can check equivalence on queue.declare - speaking of which, implement the "equivalent" rule of queue.declare (bug 21832) - queues are exclusive on creation, and don't change owner, so get rid of claim_queue - also check exclusive in the passive version of queue.declare; this is explicitly mandated by the spec (queue.declare, exclusive) - bug 21385: exclusive queues are deleted when their owners go away, as per the spec. This didn't actually change implementation -- it was wrong for 0-8 --- include/rabbit.hrl | 13 ++++---- src/rabbit_amqqueue.erl | 12 +++---- src/rabbit_amqqueue_process.erl | 55 ++++++++----------------------- src/rabbit_channel.erl | 72 ++++++++++++++++++++++++++--------------- 4 files changed, 70 insertions(+), 82 deletions(-) diff --git a/include/rabbit.hrl b/include/rabbit.hrl index c94965f9..f8ff4778 100644 --- a/include/rabbit.hrl +++ b/include/rabbit.hrl @@ -51,7 +51,7 @@ -record(exchange, {name, type, durable, arguments}). --record(amqqueue, {name, durable, auto_delete, arguments, pid}). +-record(amqqueue, {name, durable, auto_delete, exclusive_owner = none, arguments, pid}). %% mnesia doesn't like unary records, so we add a dummy 'value' field -record(route, {binding, value = const}). @@ -102,11 +102,12 @@ write :: regexp(), read :: regexp()}). -type(amqqueue() :: - #amqqueue{name :: queue_name(), - durable :: boolean(), - auto_delete :: boolean(), - arguments :: amqp_table(), - pid :: maybe(pid())}). + #amqqueue{name :: queue_name(), + durable :: boolean(), + auto_delete :: boolean(), + exclusive_owner :: maybe(pid()), + arguments :: amqp_table(), + pid :: maybe(pid())}). -type(exchange() :: #exchange{name :: exchange_name(), type :: exchange_type(), diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 1a5e82d7..52c54754 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -31,13 +31,12 @@ -module(rabbit_amqqueue). --export([start/0, recover/0, declare/4, delete/3, purge/1]). +-export([start/0, recover/0, declare/5, delete/3, purge/1]). -export([internal_declare/2, internal_delete/1]). -export([pseudo_queue/2]). -export([lookup/1, with/2, with_or_die/2, stat/1, stat_all/0, deliver/2, redeliver/2, requeue/3, ack/4]). -export([list/1, info/1, info/2, info_all/1, info_all/2]). --export([claim_queue/2]). -export([basic_get/3, basic_consume/8, basic_cancel/4]). -export([notify_sent/2, unblock/2]). -export([commit_all/2, rollback_all/2, notify_down_all/2, limit_all/3]). @@ -63,7 +62,7 @@ -spec(start/0 :: () -> 'ok'). -spec(recover/0 :: () -> 'ok'). --spec(declare/4 :: (queue_name(), boolean(), boolean(), amqp_table()) -> +-spec(declare/5 :: (queue_name(), boolean(), boolean(), amqp_table(), maybe(pid())) -> amqqueue()). -spec(lookup/1 :: (queue_name()) -> {'ok', amqqueue()} | not_found()). -spec(with/2 :: (queue_name(), qfun(A)) -> A | not_found()). @@ -91,7 +90,6 @@ -spec(rollback_all/2 :: ([pid()], txn()) -> ok_or_errors()). -spec(notify_down_all/2 :: ([pid()], pid()) -> ok_or_errors()). -spec(limit_all/3 :: ([pid()], pid(), pid() | 'undefined') -> ok_or_errors()). --spec(claim_queue/2 :: (amqqueue(), pid()) -> 'ok' | 'locked'). -spec(basic_get/3 :: (amqqueue(), pid(), boolean()) -> {'ok', non_neg_integer(), msg()} | 'empty'). -spec(basic_consume/8 :: @@ -151,11 +149,12 @@ recover_durable_queues() -> end)), ok. -declare(QueueName, Durable, AutoDelete, Args) -> +declare(QueueName, Durable, AutoDelete, Args, Owner) -> Q = start_queue_process(#amqqueue{name = QueueName, durable = Durable, auto_delete = AutoDelete, arguments = Args, + exclusive_owner = Owner, pid = none}), internal_declare(Q, true). @@ -286,9 +285,6 @@ limit_all(QPids, ChPid, LimiterPid) -> fun (QPid) -> gen_server2:cast(QPid, {limit, ChPid, LimiterPid}) end, QPids). -claim_queue(#amqqueue{pid = QPid}, ReaderPid) -> - gen_server2:call(QPid, {claim_queue, ReaderPid}, infinity). - basic_get(#amqqueue{pid = QPid}, ChPid, NoAck) -> gen_server2:call(QPid, {basic_get, ChPid, NoAck}, infinity). diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index fe2e8509..3a867f86 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -49,7 +49,6 @@ % Queue's state -record(q, {q, - owner, exclusive_consumer, has_had_consumers, next_msg_id, @@ -95,8 +94,11 @@ start_link(Q) -> init(Q) -> ?LOGDEBUG("Queue starting - ~p~n", [Q]), + case Q#amqqueue.exclusive_owner of + none -> ok; + ReaderPid -> erlang:monitor(process, ReaderPid) + end, {ok, #q{q = Q, - owner = none, exclusive_consumer = none, has_had_consumers = false, next_msg_id = 1, @@ -331,9 +333,9 @@ cancel_holder(ChPid, ConsumerTag, {ChPid, ConsumerTag}) -> cancel_holder(_ChPid, _ConsumerTag, Holder) -> Holder. -check_queue_owner(none, _) -> ok; -check_queue_owner({ReaderPid, _}, ReaderPid) -> ok; -check_queue_owner({_, _}, _) -> mismatch. +check_queue_owner(none, _) -> ok; +check_queue_owner(ReaderPid, ReaderPid) -> ok; +check_queue_owner(_, _) -> mismatch. check_exclusive_access({_ChPid, _ConsumerTag}, _ExclusiveConsume, _State) -> in_use; @@ -607,7 +609,7 @@ handle_call({basic_get, ChPid, NoAck}, _From, handle_call({basic_consume, NoAck, ReaderPid, ChPid, LimiterPid, ConsumerTag, ExclusiveConsume, OkMsg}, - _From, State = #q{owner = Owner, + _From, State = #q{q = #amqqueue{exclusive_owner = Owner}, exclusive_consumer = ExistingHolder}) -> case check_queue_owner(Owner, ReaderPid) of mismatch -> @@ -705,29 +707,7 @@ handle_call({delete, IfUnused, IfEmpty}, _From, handle_call(purge, _From, State = #q{message_buffer = MessageBuffer}) -> ok = purge_message_buffer(qname(State), MessageBuffer), reply({ok, queue:len(MessageBuffer)}, - State#q{message_buffer = queue:new()}); - -handle_call({claim_queue, ReaderPid}, _From, State = #q{owner = Owner, - exclusive_consumer = Holder}) -> - case Owner of - none -> - case check_exclusive_access(Holder, true, State) of - in_use -> - %% FIXME: Is this really the right answer? What if - %% an active consumer's reader is actually the - %% claiming pid? Should that be allowed? In order - %% to check, we'd need to hold not just the ch - %% pid for each consumer, but also its reader - %% pid... - reply(locked, State); - ok -> - reply(ok, State#q{owner = {ReaderPid, erlang:monitor(process, ReaderPid)}}) - end; - {ReaderPid, _MonitorRef} -> - reply(ok, State); - _ -> - reply(locked, State) - end. + State#q{message_buffer = queue:new()}). handle_cast({deliver, Txn, Message, ChPid}, State) -> %% Asynchronous, non-"mandatory", non-"immediate" deliver mode. @@ -799,19 +779,10 @@ handle_cast({limit, ChPid, LimiterPid}, State) -> C#cr{limiter_pid = LimiterPid, is_limit_active = NewLimited} end)). -handle_info({'DOWN', MonitorRef, process, DownPid, _Reason}, - State = #q{owner = {DownPid, MonitorRef}}) -> - %% We know here that there are no consumers on this queue that are - %% owned by other pids than the one that just went down, so since - %% exclusive in some sense implies autodelete, we delete the queue - %% here. The other way of implementing the "exclusive implies - %% autodelete" feature is to actually set autodelete when an - %% exclusive declaration is seen, but this has the problem that - %% the python tests rely on the queue not going away after a - %% basic.cancel when the queue was declared exclusive and - %% nonautodelete. - NewState = State#q{owner = none}, - {stop, normal, NewState}; +handle_info({'DOWN', _MonitorRef, process, DownPid, _Reason}, + State = #q{q= #amqqueue{ exclusive_owner = DownPid}}) -> + %% Exclusively owned queues must disappear with their owner. + {stop, normal, State}; handle_info({'DOWN', _MonitorRef, process, DownPid, _Reason}, State) -> handle_ch_down(DownPid, State); diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 26db0777..e54ba4ed 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -617,25 +617,37 @@ handle_method(#'queue.declare'{queue = QueueNameBin, arguments = Args}, _, State = #ch { virtual_host = VHostPath, reader_pid = ReaderPid }) -> - %% FIXME: atomic create&claim - Finish = - fun (Q) -> - if ExclusiveDeclare -> - case rabbit_amqqueue:claim_queue(Q, ReaderPid) of - locked -> - %% AMQP 0-8 doesn't say which - %% exception to use, so we mimic QPid - %% here. - rabbit_misc:protocol_error( - resource_locked, - "cannot obtain exclusive access to locked ~s", - [rabbit_misc:rs(Q#amqqueue.name)]); - ok -> ok - end; - true -> - ok - end, - Q + Owner = case ExclusiveDeclare of + true -> ReaderPid; + false -> none + end, + %% We use this in both branches, because queue_declare may yet return an + %% existing queue. + Finish = + fun(Q) -> + case Q of + %% "equivalent" rule. NB: we don't pay attention to + %% anything in the arguments table, so for the sake of the + %% "equivalent" rule, all tables of arguments are + %% semantically equivalant. + Matched = #amqqueue{name = QueueName, + durable = Durable, %% i.e., as supplied + exclusive_owner = Owner, + auto_delete = AutoDelete %% i.e,. as supplied + } -> + check_configure_permitted(QueueName, State), + Matched; + %% exclusivity trumps non-equivalence arbitrarily + #amqqueue{name = QueueName, exclusive_owner = ExclusiveOwner} + when ExclusiveOwner =/= Owner -> + rabbit_misc:protocol_error(resource_locked, + "cannot obtain exclusive access to locked ~s", + [rabbit_misc:rs(QueueName)]); + #amqqueue{name = QueueName} -> + rabbit_misc:protocol_error(channel_error, + "parameters for ~s not equivalent", + [rabbit_misc:rs(QueueName)]) + end end, Q = case rabbit_amqqueue:with( rabbit_misc:r(VHostPath, queue, QueueNameBin), @@ -648,21 +660,29 @@ handle_method(#'queue.declare'{queue = QueueNameBin, end, QueueName = rabbit_misc:r(VHostPath, queue, ActualNameBin), check_configure_permitted(QueueName, State), - Finish(rabbit_amqqueue:declare(QueueName, - Durable, AutoDelete, Args)); - Other = #amqqueue{name = QueueName} -> - check_configure_permitted(QueueName, State), - Other + Finish(rabbit_amqqueue:declare(QueueName, Durable, AutoDelete, Args, Owner)); + Found -> Found end, return_queue_declare_ok(State, NoWait, Q); handle_method(#'queue.declare'{queue = QueueNameBin, passive = true, nowait = NoWait}, - _, State = #ch{ virtual_host = VHostPath }) -> + _, State = #ch{ virtual_host = VHostPath, + reader_pid = ReaderPid }) -> QueueName = rabbit_misc:r(VHostPath, queue, QueueNameBin), check_configure_permitted(QueueName, State), - Q = rabbit_amqqueue:with_or_die(QueueName, fun (Q) -> Q end), + CheckExclusive = + fun(Q) -> + case Q of + #amqqueue{ exclusive_owner = none} -> Q; + #amqqueue{ exclusive_owner = ReaderPid } -> Q; + _ -> rabbit_misc:protocol_error(resource_locked, + "cannot obtain exclusive access to locked ~s", + [rabbit_misc:rs(Q#amqqueue.name)]) + end + end, + Q = rabbit_amqqueue:with_or_die(QueueName, CheckExclusive), return_queue_declare_ok(State, NoWait, Q); handle_method(#'queue.delete'{queue = QueueNameBin, -- cgit v1.2.1 From bf6620a80d0e82048daa2ddf9999638859280a7c Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Tue, 3 Nov 2009 15:41:26 +0000 Subject: bug 21385: Move exclusive queue checks to rabbit_channel, and add them in to the queue operations mentioned in the 0-9-1 spec's "exclusive" rule (except for queue.bind which is a bit harder). --- src/rabbit_amqqueue_process.erl | 81 ++++++++++++++++++----------------------- src/rabbit_channel.erl | 45 +++++++++++------------ 2 files changed, 58 insertions(+), 68 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 3a867f86..cf08e85a 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -333,10 +333,6 @@ cancel_holder(ChPid, ConsumerTag, {ChPid, ConsumerTag}) -> cancel_holder(_ChPid, _ConsumerTag, Holder) -> Holder. -check_queue_owner(none, _) -> ok; -check_queue_owner(ReaderPid, ReaderPid) -> ok; -check_queue_owner(_, _) -> mismatch. - check_exclusive_access({_ChPid, _ConsumerTag}, _ExclusiveConsume, _State) -> in_use; check_exclusive_access(none, false, _State) -> @@ -611,48 +607,43 @@ handle_call({basic_consume, NoAck, ReaderPid, ChPid, LimiterPid, ConsumerTag, ExclusiveConsume, OkMsg}, _From, State = #q{q = #amqqueue{exclusive_owner = Owner}, exclusive_consumer = ExistingHolder}) -> - case check_queue_owner(Owner, ReaderPid) of - mismatch -> - reply({error, queue_owned_by_another_connection}, State); + case check_exclusive_access(ExistingHolder, ExclusiveConsume, + State) of + in_use -> + reply({error, exclusive_consume_unavailable}, State); ok -> - case check_exclusive_access(ExistingHolder, ExclusiveConsume, - State) of - in_use -> - reply({error, exclusive_consume_unavailable}, State); - ok -> - C = #cr{consumer_count = ConsumerCount} = ch_record(ChPid), - Consumer = #consumer{tag = ConsumerTag, - ack_required = not(NoAck)}, - store_ch_record(C#cr{consumer_count = ConsumerCount +1, - limiter_pid = LimiterPid}), - if ConsumerCount == 0 -> - ok = rabbit_limiter:register(LimiterPid, self()); - true -> - ok - end, - ExclusiveConsumer = - if ExclusiveConsume -> {ChPid, ConsumerTag}; - true -> ExistingHolder - end, - State1 = State#q{has_had_consumers = true, - exclusive_consumer = ExclusiveConsumer}, - ok = maybe_send_reply(ChPid, OkMsg), - State2 = - case is_ch_blocked(C) of - true -> State1#q{ - blocked_consumers = - add_consumer( - ChPid, Consumer, - State1#q.blocked_consumers)}; - false -> run_poke_burst( - State1#q{ - active_consumers = - add_consumer( - ChPid, Consumer, - State1#q.active_consumers)}) - end, - reply(ok, State2) - end + C = #cr{consumer_count = ConsumerCount} = ch_record(ChPid), + Consumer = #consumer{tag = ConsumerTag, + ack_required = not(NoAck)}, + store_ch_record(C#cr{consumer_count = ConsumerCount +1, + limiter_pid = LimiterPid}), + if ConsumerCount == 0 -> + ok = rabbit_limiter:register(LimiterPid, self()); + true -> + ok + end, + ExclusiveConsumer = + if ExclusiveConsume -> {ChPid, ConsumerTag}; + true -> ExistingHolder + end, + State1 = State#q{has_had_consumers = true, + exclusive_consumer = ExclusiveConsumer}, + ok = maybe_send_reply(ChPid, OkMsg), + State2 = + case is_ch_blocked(C) of + true -> State1#q{ + blocked_consumers = + add_consumer( + ChPid, Consumer, + State1#q.blocked_consumers)}; + false -> run_poke_burst( + State1#q{ + active_consumers = + add_consumer( + ChPid, Consumer, + State1#q.active_consumers)}) + end, + reply(ok, State2) end; handle_call({basic_cancel, ChPid, ConsumerTag, OkMsg}, _From, diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index e54ba4ed..7403095e 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -241,6 +241,15 @@ check_write_permitted(Resource, #ch{ username = Username}) -> check_read_permitted(Resource, #ch{ username = Username}) -> check_resource_access(Username, Resource, read). +exclusive_access_or_locked(ReaderPid, Q) -> + case Q of + #amqqueue{ exclusive_owner = none} -> Q; + #amqqueue{ exclusive_owner = ReaderPid } -> Q; + _ -> rabbit_misc:protocol_error(resource_locked, + "cannot obtain exclusive access to locked ~s", + [rabbit_misc:rs(Q#amqqueue.name)]) + end. + expand_queue_name_shortcut(<<>>, #ch{ most_recently_declared_queue = <<>> }) -> rabbit_misc:protocol_error( not_allowed, "no previously declared queue", []); @@ -414,7 +423,8 @@ handle_method(#'basic.consume'{queue = QueueNameBin, QueueName, fun (Q) -> rabbit_amqqueue:basic_consume( - Q, NoAck, ReaderPid, self(), LimiterPid, + exclusive_access_or_locked(ReaderPid, Q), + NoAck, ReaderPid, self(), LimiterPid, ActualConsumerTag, ExclusiveConsume, ok_msg(NoWait, #'basic.consume_ok'{ consumer_tag = ActualConsumerTag})) @@ -424,14 +434,6 @@ handle_method(#'basic.consume'{queue = QueueNameBin, dict:store(ActualConsumerTag, QueueName, ConsumerMapping)}}; - {error, queue_owned_by_another_connection} -> - %% The spec is silent on which exception to use - %% here. This seems reasonable? - %% FIXME: check this - - rabbit_misc:protocol_error( - resource_locked, "~s owned by another connection", - [rabbit_misc:rs(QueueName)]); {error, exclusive_consume_unavailable} -> rabbit_misc:protocol_error( access_refused, "~s in exclusive use", @@ -672,16 +674,7 @@ handle_method(#'queue.declare'{queue = QueueNameBin, reader_pid = ReaderPid }) -> QueueName = rabbit_misc:r(VHostPath, queue, QueueNameBin), check_configure_permitted(QueueName, State), - CheckExclusive = - fun(Q) -> - case Q of - #amqqueue{ exclusive_owner = none} -> Q; - #amqqueue{ exclusive_owner = ReaderPid } -> Q; - _ -> rabbit_misc:protocol_error(resource_locked, - "cannot obtain exclusive access to locked ~s", - [rabbit_misc:rs(Q#amqqueue.name)]) - end - end, + CheckExclusive = fun(Q) -> exclusive_access_or_locked(ReaderPid, Q) end, Q = rabbit_amqqueue:with_or_die(QueueName, CheckExclusive), return_queue_declare_ok(State, NoWait, Q); @@ -690,12 +683,15 @@ handle_method(#'queue.delete'{queue = QueueNameBin, if_empty = IfEmpty, nowait = NoWait }, - _, State) -> + _, State = #ch{ reader_pid = ReaderPid }) -> QueueName = expand_queue_name_shortcut(QueueNameBin, State), check_configure_permitted(QueueName, State), case rabbit_amqqueue:with_or_die( QueueName, - fun (Q) -> rabbit_amqqueue:delete(Q, IfUnused, IfEmpty) end) of + fun (Q) -> + rabbit_amqqueue:delete(exclusive_access_or_locked(ReaderPid, Q), + IfUnused, IfEmpty) + end) of {error, in_use} -> rabbit_misc:protocol_error( precondition_failed, "~s in use", [rabbit_misc:rs(QueueName)]); @@ -727,12 +723,15 @@ handle_method(#'queue.unbind'{queue = QueueNameBin, handle_method(#'queue.purge'{queue = QueueNameBin, nowait = NoWait}, - _, State) -> + _, State = #ch{ reader_pid = ReaderPid }) -> QueueName = expand_queue_name_shortcut(QueueNameBin, State), check_read_permitted(QueueName, State), {ok, PurgedMessageCount} = rabbit_amqqueue:with_or_die( QueueName, - fun (Q) -> rabbit_amqqueue:purge(Q) end), + fun (Q) -> + exclusive_access_or_locked(ReaderPid, Q), + rabbit_amqqueue:purge(Q) + end), return_ok(State, NoWait, #'queue.purge_ok'{message_count = PurgedMessageCount}); -- cgit v1.2.1 From 71ba7e1b7ae726d93e7e44518faf1135a85c99aa Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Tue, 3 Nov 2009 17:13:10 +0000 Subject: bug 21385: check exclusivity for queue.bind as well. Because I need to do the check inside the transaction, I had to change rabbit_exchange:add_binding/4 (and delete_binding/4) to take another parameter for the check. I also made the exclusivity check more obviously run for its side-effect. --- src/rabbit_amqqueue.erl | 2 +- src/rabbit_channel.erl | 25 ++++++++++++++----------- src/rabbit_exchange.erl | 24 ++++++++++++++---------- 3 files changed, 29 insertions(+), 22 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 52c54754..b958f306 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -191,7 +191,7 @@ start_queue_process(Q) -> add_default_binding(#amqqueue{name = QueueName}) -> Exchange = rabbit_misc:r(QueueName, exchange, <<>>), RoutingKey = QueueName#resource.name, - rabbit_exchange:add_binding(Exchange, QueueName, RoutingKey, []), + rabbit_exchange:add_binding(Exchange, QueueName, RoutingKey, [], fun (_X, _Q) -> ok end), ok. lookup(Name) -> diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 7403095e..baa39752 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -241,7 +241,7 @@ check_write_permitted(Resource, #ch{ username = Username}) -> check_read_permitted(Resource, #ch{ username = Username}) -> check_resource_access(Username, Resource, read). -exclusive_access_or_locked(ReaderPid, Q) -> +check_queue_exclusivity(ReaderPid, Q) -> case Q of #amqqueue{ exclusive_owner = none} -> Q; #amqqueue{ exclusive_owner = ReaderPid } -> Q; @@ -422,9 +422,9 @@ handle_method(#'basic.consume'{queue = QueueNameBin, case rabbit_amqqueue:with_or_die( QueueName, fun (Q) -> + check_queue_exclusivity(ReaderPid, Q), rabbit_amqqueue:basic_consume( - exclusive_access_or_locked(ReaderPid, Q), - NoAck, ReaderPid, self(), LimiterPid, + Q, NoAck, ReaderPid, self(), LimiterPid, ActualConsumerTag, ExclusiveConsume, ok_msg(NoWait, #'basic.consume_ok'{ consumer_tag = ActualConsumerTag})) @@ -674,7 +674,7 @@ handle_method(#'queue.declare'{queue = QueueNameBin, reader_pid = ReaderPid }) -> QueueName = rabbit_misc:r(VHostPath, queue, QueueNameBin), check_configure_permitted(QueueName, State), - CheckExclusive = fun(Q) -> exclusive_access_or_locked(ReaderPid, Q) end, + CheckExclusive = fun(Q) -> check_queue_exclusivity(ReaderPid, Q) end, Q = rabbit_amqqueue:with_or_die(QueueName, CheckExclusive), return_queue_declare_ok(State, NoWait, Q); @@ -689,8 +689,8 @@ handle_method(#'queue.delete'{queue = QueueNameBin, case rabbit_amqqueue:with_or_die( QueueName, fun (Q) -> - rabbit_amqqueue:delete(exclusive_access_or_locked(ReaderPid, Q), - IfUnused, IfEmpty) + check_queue_exclusivity(ReaderPid, Q), + rabbit_amqqueue:delete(Q, IfUnused, IfEmpty) end) of {error, in_use} -> rabbit_misc:protocol_error( @@ -709,7 +709,7 @@ handle_method(#'queue.bind'{queue = QueueNameBin, routing_key = RoutingKey, nowait = NoWait, arguments = Arguments}, _, State) -> - binding_action(fun rabbit_exchange:add_binding/4, ExchangeNameBin, + binding_action(fun rabbit_exchange:add_binding/5, ExchangeNameBin, QueueNameBin, RoutingKey, Arguments, #'queue.bind_ok'{}, NoWait, State); @@ -717,7 +717,7 @@ handle_method(#'queue.unbind'{queue = QueueNameBin, exchange = ExchangeNameBin, routing_key = RoutingKey, arguments = Arguments}, _, State) -> - binding_action(fun rabbit_exchange:delete_binding/4, ExchangeNameBin, + binding_action(fun rabbit_exchange:delete_binding/5, ExchangeNameBin, QueueNameBin, RoutingKey, Arguments, #'queue.unbind_ok'{}, false, State); @@ -729,7 +729,7 @@ handle_method(#'queue.purge'{queue = QueueNameBin, {ok, PurgedMessageCount} = rabbit_amqqueue:with_or_die( QueueName, fun (Q) -> - exclusive_access_or_locked(ReaderPid, Q), + check_queue_exclusivity(ReaderPid, Q), rabbit_amqqueue:purge(Q) end), return_ok(State, NoWait, @@ -772,7 +772,9 @@ handle_method(_MethodRecord, _Content, _State) -> %%---------------------------------------------------------------------------- binding_action(Fun, ExchangeNameBin, QueueNameBin, RoutingKey, Arguments, - ReturnMethod, NoWait, State = #ch{virtual_host = VHostPath}) -> + ReturnMethod, NoWait, + State = #ch{ virtual_host = VHostPath, + reader_pid = ReaderPid }) -> %% FIXME: connection exception (!) on failure?? %% (see rule named "failure" in spec-XML) %% FIXME: don't allow binding to internal exchanges - @@ -783,7 +785,8 @@ binding_action(Fun, ExchangeNameBin, QueueNameBin, RoutingKey, Arguments, State), ExchangeName = rabbit_misc:r(VHostPath, exchange, ExchangeNameBin), check_read_permitted(ExchangeName, State), - case Fun(ExchangeName, QueueName, ActualRoutingKey, Arguments) of + CheckExclusive = fun(_X, Q) -> check_queue_exclusivity(ReaderPid, Q) end, + case Fun(ExchangeName, QueueName, ActualRoutingKey, Arguments, CheckExclusive) of {error, exchange_not_found} -> rabbit_misc:not_found(ExchangeName); {error, queue_not_found} -> diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index 37a1357d..4b7a9ac5 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -37,7 +37,7 @@ -export([recover/0, declare/4, lookup/1, lookup_or_die/1, list/1, info/1, info/2, info_all/1, info_all/2, publish/2]). --export([add_binding/4, delete_binding/4, list_bindings/1]). +-export([add_binding/5, delete_binding/5, list_bindings/1]). -export([delete/2]). -export([delete_queue_bindings/1, delete_transient_queue_bindings/1]). -export([check_type/1, assert_type/2, topic_matches/2, headers_match/2]). @@ -60,6 +60,8 @@ 'queue_not_found' | 'exchange_not_found' | 'exchange_and_queue_not_found'}). +-type(inner_fun() :: fun((exchange(), queue()) -> any())). + -spec(recover/0 :: () -> 'ok'). -spec(declare/4 :: (exchange_name(), exchange_type(), boolean(), amqp_table()) -> exchange()). -spec(check_type/1 :: (binary()) -> atom()). @@ -72,11 +74,11 @@ -spec(info_all/1 :: (vhost()) -> [[info()]]). -spec(info_all/2 :: (vhost(), [info_key()]) -> [[info()]]). -spec(publish/2 :: (exchange(), delivery()) -> {routing_result(), [pid()]}). --spec(add_binding/4 :: - (exchange_name(), queue_name(), routing_key(), amqp_table()) -> +-spec(add_binding/5 :: + (exchange_name(), queue_name(), routing_key(), amqp_table(), inner_fun()) -> bind_res() | {'error', 'durability_settings_incompatible'}). --spec(delete_binding/4 :: - (exchange_name(), queue_name(), routing_key(), amqp_table()) -> +-spec(delete_binding/5 :: + (exchange_name(), queue_name(), routing_key(), amqp_table(), inner_fun()) -> bind_res() | {'error', 'binding_not_found'}). -spec(list_bindings/1 :: (vhost()) -> [{exchange_name(), queue_name(), routing_key(), amqp_table()}]). @@ -364,21 +366,23 @@ call_with_exchange_and_queue(Exchange, Queue, Fun) -> end end). -add_binding(ExchangeName, QueueName, RoutingKey, Arguments) -> +add_binding(ExchangeName, QueueName, RoutingKey, Arguments, InnerFun) -> binding_action( ExchangeName, QueueName, RoutingKey, Arguments, - fun (_X, Q, B) -> + fun (X, Q, B) -> + InnerFun(X, Q), ok = sync_binding(B, Q#amqqueue.durable, fun mnesia:write/3) end). -delete_binding(ExchangeName, QueueName, RoutingKey, Arguments) -> +delete_binding(ExchangeName, QueueName, RoutingKey, Arguments, InnerFun) -> binding_action( ExchangeName, QueueName, RoutingKey, Arguments, - fun (_X, Q, B) -> + fun (X, Q, B) -> case mnesia:match_object(rabbit_route, #route{binding = B}, write) of [] -> {error, binding_not_found}; - _ -> ok = sync_binding(B, Q#amqqueue.durable, + _ -> InnerFun(X, Q), + ok = sync_binding(B, Q#amqqueue.durable, fun mnesia:delete_object/3) end end). -- cgit v1.2.1 From 60654ae6901afb7ca4904b26f71ac397983cb33e Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Wed, 4 Nov 2009 11:22:38 +0000 Subject: bug 21385: Make basic.get respect exclusivity --- src/rabbit_channel.erl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index baa39752..759840aa 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -372,12 +372,16 @@ handle_method(#'basic.ack'{delivery_tag = DeliveryTag, handle_method(#'basic.get'{queue = QueueNameBin, no_ack = NoAck}, _, State = #ch{ writer_pid = WriterPid, + reader_pid = ReaderPid, next_tag = DeliveryTag }) -> QueueName = expand_queue_name_shortcut(QueueNameBin, State), check_read_permitted(QueueName, State), case rabbit_amqqueue:with_or_die( QueueName, - fun (Q) -> rabbit_amqqueue:basic_get(Q, self(), NoAck) end) of + fun (Q) -> + check_queue_exclusivity(ReaderPid, Q), + rabbit_amqqueue:basic_get(Q, self(), NoAck) + end) of {ok, MessageCount, Msg = {_QName, _QPid, _MsgId, Redelivered, #basic_message{exchange_name = ExchangeName, -- cgit v1.2.1 From 53a9bc4934715ccd8cf66f634c802ea250b393de Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Wed, 4 Nov 2009 11:44:36 +0000 Subject: Update test to rabbit_amqqueue:declare/5 --- src/rabbit_tests.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 62a4bd1d..52139ba7 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -643,7 +643,7 @@ test_server_status() -> %% create a queue so we have something to list Q = #amqqueue{} = rabbit_amqqueue:declare( rabbit_misc:r(<<"/">>, queue, <<"foo">>), - false, false, []), + false, false, [], none), %% list queues ok = info_action( -- cgit v1.2.1 From 6b3177ff16b00c5800377080805a1fb7d5ce0b5a Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Wed, 4 Nov 2009 16:50:02 +0000 Subject: bug 20578: Spring-clean queues on recovery by checking each to see if it is exclusive to a dead process (connection); and if so, deleting it. As things stand, a queue can't be owned by a connection on another node (so the aliveness check is redundant), but this will account for such scenarios in the event of connection- or queue-mobility. --- src/rabbit_amqqueue.erl | 57 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 20 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index b958f306..77895c2a 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -120,24 +120,40 @@ recover() -> ok = recover_durable_queues(), ok. +shared_or_live_owner(none) -> + true; +shared_or_live_owner(Owner) when is_pid(Owner) -> + rpc:call(node(Owner), erlang, is_process_alive, [Owner]). + recover_durable_queues() -> Node = node(), lists:foreach( - fun (RecoveredQ) -> - Q = start_queue_process(RecoveredQ), - %% We need to catch the case where a client connected to - %% another node has deleted the queue (and possibly - %% re-created it). - case rabbit_misc:execute_mnesia_transaction( - fun () -> case mnesia:match_object( - rabbit_durable_queue, RecoveredQ, read) of - [_] -> ok = store_queue(Q), - true; - [] -> false - end - end) of - true -> ok; - false -> exit(Q#amqqueue.pid, shutdown) + fun (RecoveredQ = #amqqueue{ exclusive_owner = Owner }) -> + case shared_or_live_owner(Owner) of + true -> + Q = start_queue_process(RecoveredQ), + %% We need to catch the case where a client connected to + %% another node has deleted the queue (and possibly + %% re-created it). + case rabbit_misc:execute_mnesia_transaction( + fun () -> case mnesia:match_object( + rabbit_durable_queue, RecoveredQ, read) of + [_] -> ok = store_queue(Q), + true; + [] -> false + end + end) of + true -> ok; + false -> exit(Q#amqqueue.pid, shutdown) + end; + false -> + rabbit_misc:execute_mnesia_transaction( + fun () -> case mnesia:match_object( + rabbit_durable_queue, RecoveredQ, read) of + [_] -> internal_delete2(RecoveredQ#amqqueue.name); + [] -> ok + end + end) end end, %% TODO: use dirty ops instead @@ -304,16 +320,17 @@ notify_sent(QPid, ChPid) -> unblock(QPid, ChPid) -> gen_server2:pcast(QPid, 8, {unblock, ChPid}). +internal_delete2(QueueName) -> + ok = rabbit_exchange:delete_queue_bindings(QueueName), + ok = mnesia:delete({rabbit_queue, QueueName}), + ok = mnesia:delete({rabbit_durable_queue, QueueName}). + internal_delete(QueueName) -> rabbit_misc:execute_mnesia_transaction( fun () -> case mnesia:wread({rabbit_queue, QueueName}) of [] -> {error, not_found}; - [_] -> - ok = rabbit_exchange:delete_queue_bindings(QueueName), - ok = mnesia:delete({rabbit_queue, QueueName}), - ok = mnesia:delete({rabbit_durable_queue, QueueName}), - ok + [_] -> internal_delete2(QueueName) end end). -- cgit v1.2.1 From 0f0233099f4bf64e1cab1b9a867be59526f24d18 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Wed, 4 Nov 2009 17:11:23 +0000 Subject: bug 20578: factor out the check-if-the-queue-has-changed pattern of the two branches --- src/rabbit_amqqueue.erl | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 77895c2a..9ebec399 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -129,31 +129,30 @@ recover_durable_queues() -> Node = node(), lists:foreach( fun (RecoveredQ = #amqqueue{ exclusive_owner = Owner }) -> + %% We need to catch the case where a client connected to + %% another node has deleted the queue (and possibly + %% re-created it). + DoIfSameQueue = + fun (Action) -> + rabbit_misc:execute_mnesia_transaction( + fun () -> case mnesia:match_object( + rabbit_durable_queue, RecoveredQ, read) of + [_] -> ok = Action(), + true; + [] -> false + end + end) + end, case shared_or_live_owner(Owner) of true -> - Q = start_queue_process(RecoveredQ), - %% We need to catch the case where a client connected to - %% another node has deleted the queue (and possibly - %% re-created it). - case rabbit_misc:execute_mnesia_transaction( - fun () -> case mnesia:match_object( - rabbit_durable_queue, RecoveredQ, read) of - [_] -> ok = store_queue(Q), - true; - [] -> false - end - end) of + Q = start_queue_process(RecoveredQ), + case DoIfSameQueue(fun () -> store_queue(Q) end) of true -> ok; false -> exit(Q#amqqueue.pid, shutdown) end; false -> - rabbit_misc:execute_mnesia_transaction( - fun () -> case mnesia:match_object( - rabbit_durable_queue, RecoveredQ, read) of - [_] -> internal_delete2(RecoveredQ#amqqueue.name); - [] -> ok - end - end) + DoIfSameQueue( + fun () -> internal_delete2(RecoveredQ#amqqueue.name) end) end end, %% TODO: use dirty ops instead -- cgit v1.2.1 From 167499115701e02dc0d432a73f7de1a02b2ea61c Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Thu, 12 Nov 2009 16:56:47 +0000 Subject: Bug 21836: Test that the content framing code doesn't produce frames that break frame-max --- src/rabbit_tests.erl | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index c3280508..aff5f296 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -51,6 +51,7 @@ all_tests() -> passed = test_priority_queue(), passed = test_unfold(), passed = test_parsing(), + passed = test_content_framing(), passed = test_topic_matching(), passed = test_log_management(), passed = test_app_management(), @@ -249,6 +250,45 @@ test_content_properties() -> V -> exit({got_success_but_expected_failure, V}) end. +%% Test that content frames don't exceed frame-max +test_content_framing(FrameMax, Fragments) -> + [Header | Frames] = + rabbit_binary_generator:build_simple_content_frames( + 1, + #content{class_id = 0, properties_bin = <<>>, + payload_fragments_rev = Fragments}, + FrameMax), + % header is formatted correctly and the size is the total of the + % fragments + <<_FrameHeader:7/binary, _ClassAndWeight:4/binary, + BodySize:64/unsigned, _Rest/binary>> = list_to_binary(Header), + BodySize = size(list_to_binary(Fragments)), + false = lists:any( + fun (ContentFrame) -> + FrameBinary = list_to_binary(ContentFrame), + % assert + <<_TypeAndChannel:3/binary, + Size:32/unsigned, + _Payload:Size/binary, + 16#CE>> = FrameBinary, + size(FrameBinary) > FrameMax + end, + Frames), + passed. + +test_content_framing() -> + % no content + passed = test_content_framing(4096, []), + passed = test_content_framing(4096, [<<>>]), + % easily fit in one frame + passed = test_content_framing(4096, [<<"Easy">>]), + % exactly one frame (empty frame = 8 bytes) + passed = test_content_framing(11, [<<"One">>]), + % more than one frame + passed = test_content_framing(20, [<<"into more than one frame">>, + <<"This will have to go">>]), + passed. + test_topic_match(P, R) -> test_topic_match(P, R, true). -- cgit v1.2.1 From ebdf272770185490f258e5c1ea947c6261ff13ba Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Thu, 12 Nov 2009 18:59:20 +0000 Subject: Enforce frame-max negotiation --- src/rabbit_reader.erl | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 496e6c1a..97377efa 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -50,6 +50,9 @@ -define(NORMAL_TIMEOUT, 3). -define(CLOSING_TIMEOUT, 1). -define(CHANNEL_TERMINATION_TIMEOUT, 3). +%% set to zero once QPid fix their negotiation +-define(FRAME_MAX, 131072). +-define(CHANNEL_MAX, 0). %--------------------------------------------------------------------------- @@ -563,9 +566,8 @@ handle_method0(#'connection.start_ok'{mechanism = Mechanism, User = rabbit_access_control:check_login(Mechanism, Response), ok = send_on_channel0( Sock, - #'connection.tune'{channel_max = 0, - %% set to zero once QPid fix their negotiation - frame_max = 131072, + #'connection.tune'{channel_max = ?CHANNEL_MAX, + frame_max = ?FRAME_MAX, heartbeat = 0}), State#v1{connection_state = tuning, connection = Connection#connection{user = User}}; @@ -575,14 +577,23 @@ handle_method0(#'connection.tune_ok'{channel_max = _ChannelMax, State = #v1{connection_state = tuning, connection = Connection, sock = Sock}) -> - %% if we have a channel_max limit that the client wishes to + if (FrameMax =< ?FRAME_MIN_SIZE) or + (?FRAME_MAX /= 0) and (FrameMax > ?FRAME_MAX) -> + rabbit_misc:protocol_error( + mistuned, "peer sent tune_ok with invalid frame_max"); + %% If we have a channel_max limit that the client wishes to %% exceed, die as per spec. Not currently a problem, so we ignore %% the client's channel_max parameter. - rabbit_heartbeat:start_heartbeat(Sock, ClientHeartbeat), - State#v1{connection_state = opening, - connection = Connection#connection{ - timeout_sec = ClientHeartbeat, - frame_max = FrameMax}}; +%% (?CHANNEL_MAX /= 0) and (ChannelMax > ?CHANNEL_MAX) -> +%% rabbit_misc:protocol_error( +%% mistuned, "peer sent tune_ok with invalid channel_max"); + true -> + rabbit_heartbeat:start_heartbeat(Sock, ClientHeartbeat), + State#v1{connection_state = opening, + connection = Connection#connection{ + timeout_sec = ClientHeartbeat, + frame_max = FrameMax}} + end; handle_method0(#'connection.open'{virtual_host = VHostPath}, State = #v1{connection_state = opening, connection = Connection = #connection{ -- cgit v1.2.1 From 1725d2c88a9fa6c473dd7a49528afa9d6a66a6dd Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Tue, 17 Nov 2009 16:56:20 +0000 Subject: Bug 21986: Implement synchronous basic.recover --- src/rabbit_channel.erl | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 759840aa..d525f941 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -556,6 +556,14 @@ handle_method(#'basic.recover_async'{}, _, _State) -> rabbit_misc:protocol_error( not_allowed, "attempt to recover a transactional channel",[]); +handle_method(#'basic.recover'{requeue = Requeue}, Content, State) -> + {noreply, State2 = #ch{writer_pid = WriterPid}} = + handle_method(#'basic.recover_async'{requeue = Requeue}, + Content, + State), + ok = rabbit_writer:send_command(WriterPid, #'basic.recover_ok'{}), + {noreply, State2}; + handle_method(#'exchange.declare'{exchange = ExchangeNameBin, type = TypeNameBin, passive = false, -- cgit v1.2.1 From b335047ced8c0d920f014d3916d02fac4857f966 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Tue, 17 Nov 2009 18:31:43 +0000 Subject: Bug 21986: Use gen_server2:call instead of gen_server2:cast to make this synchronous. This means recover_async is now also synchronous, but it's deprecated, and anyway we do the same thing with no_wait elsewhere. This way we have just the one code path. --- src/rabbit_amqqueue.erl | 2 +- src/rabbit_amqqueue_process.erl | 29 +++++++++++++++-------------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 9ebec399..4a3a8a5b 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -269,7 +269,7 @@ redeliver(QPid, Messages) -> gen_server2:cast(QPid, {redeliver, Messages}). requeue(QPid, MsgIds, ChPid) -> - gen_server2:cast(QPid, {requeue, MsgIds, ChPid}). + gen_server2:call(QPid, {requeue, MsgIds, ChPid}). ack(QPid, Txn, MsgIds, ChPid) -> gen_server2:cast(QPid, {ack, Txn, MsgIds, ChPid}). diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 29ebc873..8b1d79c5 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -704,7 +704,21 @@ handle_call({delete, IfUnused, IfEmpty}, _From, handle_call(purge, _From, State = #q{message_buffer = MessageBuffer}) -> ok = purge_message_buffer(qname(State), MessageBuffer), reply({ok, queue:len(MessageBuffer)}, - State#q{message_buffer = queue:new()}). + State#q{message_buffer = queue:new()}); + +handle_call({requeue, MsgIds, ChPid}, _From, State) -> + case lookup_ch(ChPid) of + not_found -> + rabbit_log:warning("Ignoring requeue from unknown ch: ~p~n", + [ChPid]), + reply(ok, State); + C = #cr{unacked_messages = UAM} -> + {Messages, NewUAM} = collect_messages(MsgIds, UAM), + store_ch_record(C#cr{unacked_messages = NewUAM}), + reply(ok, deliver_or_enqueue_n( + [{Message, true} || Message <- Messages], State)) + end. + handle_cast({deliver, Txn, Message, ChPid}, State) -> %% Asynchronous, non-"mandatory", non-"immediate" deliver mode. @@ -735,19 +749,6 @@ handle_cast({rollback, Txn}, State) -> handle_cast({redeliver, Messages}, State) -> noreply(deliver_or_enqueue_n(Messages, State)); -handle_cast({requeue, MsgIds, ChPid}, State) -> - case lookup_ch(ChPid) of - not_found -> - rabbit_log:warning("Ignoring requeue from unknown ch: ~p~n", - [ChPid]), - noreply(State); - C = #cr{unacked_messages = UAM} -> - {Messages, NewUAM} = collect_messages(MsgIds, UAM), - store_ch_record(C#cr{unacked_messages = NewUAM}), - noreply(deliver_or_enqueue_n( - [{Message, true} || Message <- Messages], State)) - end; - handle_cast({unblock, ChPid}, State) -> noreply( possibly_unblock(State, ChPid, -- cgit v1.2.1 From d3f120bd13903fbbe596b823273bd9fbab0e8810 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Wed, 2 Dec 2009 13:04:56 +0000 Subject: Bug 21838: Don't check the type in a exchange.declare{passive=true}, as per the field doc in the spec. --- src/rabbit_channel.erl | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 73778b80..71a6b117 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -596,14 +596,12 @@ handle_method(#'exchange.declare'{exchange = ExchangeNameBin, return_ok(State, NoWait, #'exchange.declare_ok'{}); handle_method(#'exchange.declare'{exchange = ExchangeNameBin, - type = TypeNameBin, passive = true, nowait = NoWait}, _, State = #ch{ virtual_host = VHostPath }) -> ExchangeName = rabbit_misc:r(VHostPath, exchange, ExchangeNameBin), check_configure_permitted(ExchangeName, State), - X = rabbit_exchange:lookup_or_die(ExchangeName), - ok = rabbit_exchange:assert_type(X, rabbit_exchange:check_type(TypeNameBin)), + _ = rabbit_exchange:lookup_or_die(ExchangeName), return_ok(State, NoWait, #'exchange.declare_ok'{}); handle_method(#'exchange.delete'{exchange = ExchangeNameBin, -- cgit v1.2.1 From 841c0d6da2cd067354a393b0f5be2264137941e1 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Wed, 2 Dec 2009 17:42:46 +0000 Subject: Bug 21838: check equivalence on exchange.declare --- src/rabbit_channel.erl | 2 +- src/rabbit_exchange.erl | 32 ++++++++++++++++++++++++++++++-- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 71a6b117..84c34b04 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -592,7 +592,7 @@ handle_method(#'exchange.declare'{exchange = ExchangeNameBin, Durable, Args) end, - ok = rabbit_exchange:assert_type(X, CheckedType), + ok = rabbit_exchange:assert_equivalence(X, CheckedType, Durable, Args), return_ok(State, NoWait, #'exchange.declare_ok'{}); handle_method(#'exchange.declare'{exchange = ExchangeNameBin, diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index 899756e1..68a04811 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -39,7 +39,7 @@ -export([add_binding/5, delete_binding/5, list_bindings/1]). -export([delete/2]). -export([delete_queue_bindings/1, delete_transient_queue_bindings/1]). --export([check_type/1, assert_type/2]). +-export([check_type/1, assert_equivalence/4]). %% EXTENDED API -export([list_exchange_bindings/1]). @@ -63,7 +63,7 @@ -spec(recover/0 :: () -> 'ok'). -spec(declare/4 :: (exchange_name(), exchange_type(), boolean(), amqp_table()) -> exchange()). -spec(check_type/1 :: (binary()) -> atom()). --spec(assert_type/2 :: (exchange(), atom()) -> 'ok'). +-spec(assert_equivalence/4 :: (exchange(), atom(), boolean(), amqp_table()) -> 'ok'). -spec(lookup/1 :: (exchange_name()) -> {'ok', exchange()} | not_found()). -spec(lookup_or_die/1 :: (exchange_name()) -> exchange()). -spec(list/1 :: (vhost()) -> [exchange()]). @@ -160,6 +160,16 @@ check_type(T) -> Module end. +assert_equivalence(X = #exchange{ durable = ActualDurable }, + RequiredType, RequiredDurable, RequiredArgs) + when ActualDurable==RequiredDurable -> + ok = assert_type(X, RequiredType), + ok = assert_args_equivalence(X, RequiredArgs); +assert_equivalence(#exchange{ name = Name }, _Type, _Durable, _Args) -> + rabbit_misc:protocol_error( + not_allowed, "cannot redeclare ~s with different durable value", + [rabbit_misc:rs(Name)]). + assert_type(#exchange{ type = ActualType }, RequiredType) when ActualType == RequiredType -> ok; @@ -170,6 +180,24 @@ assert_type(#exchange{ name = Name, type = ActualType }, RequiredType) -> plugin_module_to_typename(ActualType), plugin_module_to_typename(RequiredType)]). +alternate_exchange_value(Args) -> + lists:keysearch(<<"alternate-exchange">>, 1, Args). + +assert_args_equivalence(#exchange{ name = Name, + arguments = Args }, + RequiredArgs) -> + %% The spec says "Arguments are compared for semantic + %% equivalence". The only arg we care about is + %% "alternate-exchange". + Ae1 = alternate_exchange_value(RequiredArgs), + Ae2 = alternate_exchange_value(Args), + if Ae1==Ae2 -> ok; + true -> rabbit_misc:protocol_error( + not_allowed, + "cannot redeclare ~s with inequivalent args", + [rabbit_misc:rs(Name)]) + end. + lookup(Name) -> rabbit_misc:dirty_read({rabbit_exchange, Name}). -- cgit v1.2.1 From af61f0ed480439be4f2930ced288c6d95f74042e Mon Sep 17 00:00:00 2001 From: "David R. MacIver" Date: Fri, 22 Jan 2010 17:31:52 +0000 Subject: fixing merges --- src/rabbit_exchange.erl | 51 ++++++++++++------------------------------------- 1 file changed, 12 insertions(+), 39 deletions(-) diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index 9cfcb4a6..c16396d3 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -342,41 +342,16 @@ delete_transient_queue_bindings(QueueName) -> delete_queue_bindings(QueueName, fun delete_transient_forward_routes/1). delete_queue_bindings(QueueName, FwdDeleteFun) -> - DeletedBindings = - [begin - FwdRoute = reverse_route(Route), - ok = FwdDeleteFun(FwdRoute), - ok = mnesia:delete_object(rabbit_reverse_route, Route, write), - FwdRoute#route.binding - end || Route <- mnesia:match_object( - rabbit_reverse_route, - reverse_route( - #route{binding = #binding{queue_name = QueueName, - _ = '_'}}), - write)], - %% We need the keysort to group the bindings by exchange name, so - %% that cleanup_deleted_queue_bindings can inform the exchange of - %% its vanished bindings. - ok = cleanup_deleted_queue_bindings(lists:keysort(#binding.exchange_name, DeletedBindings), - none, []). - -%% Requires that its input binding list is sorted in exchange-name -%% order, so that the grouping of bindings (for passing to -%% cleanup_deleted_queue_bindings1) works properly. -cleanup_deleted_queue_bindings([], ExchangeName, Bindings) -> - cleanup_deleted_queue_bindings1(ExchangeName, Bindings); -cleanup_deleted_queue_bindings([B = #binding{exchange_name = N} | Rest], ExchangeName, Bindings) - when N =:= ExchangeName -> - cleanup_deleted_queue_bindings(Rest, ExchangeName, [B | Bindings]); -cleanup_deleted_queue_bindings([B = #binding{exchange_name = N} | Rest], ExchangeName, Bindings) -> - cleanup_deleted_queue_bindings1(ExchangeName, Bindings), - cleanup_deleted_queue_bindings(Rest, N, [B]). - -cleanup_deleted_queue_bindings1(none, []) -> - ok; -cleanup_deleted_queue_bindings1(ExchangeName, Bindings) -> - [X = #exchange{type = Type}] = mnesia:read({rabbit_exchange, ExchangeName}), - [ok = Type:delete_binding(X, B) || B <- Bindings], + Exchanges = exchanges_for_queue(QueueName), + [begin + ok = FwdDeleteFun(reverse_route(Route)), + ok = mnesia:delete_object(rabbit_reverse_route, Route, write) + end || Route <- mnesia:match_object( + rabbit_reverse_route, + reverse_route( + #route{binding = #binding{queue_name = QueueName, + _ = '_'}}), + write)], ok. delete_forward_routes(Route) -> @@ -436,8 +411,7 @@ add_binding(ExchangeName, QueueName, RoutingKey, Arguments, InnerFun) -> fun (X = #exchange{type = Type}, Q, B) -> InnerFun(X, Q), ok = sync_binding(B, Q#amqqueue.durable, - fun mnesia:write/3), - ok = Type:add_binding(X, B) + fun mnesia:write/3) end). delete_binding(ExchangeName, QueueName, RoutingKey, Arguments, InnerFun) -> @@ -450,8 +424,7 @@ delete_binding(ExchangeName, QueueName, RoutingKey, Arguments, InnerFun) -> _ -> InnerFun(X, Q), ok = sync_binding(B, Q#amqqueue.durable, - fun mnesia:delete_object/3), - ok = Type:delete_binding(X, B) + fun mnesia:delete_object/3) end end). -- cgit v1.2.1 From 49ab8180083621226a201b96cb98f939e066c415 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Mon, 15 Feb 2010 13:56:17 +0000 Subject: Remove usused variables. That includes removing ReaderPid from the signature of rabbit_amqqueue:basic_consume/8 --- src/rabbit_amqqueue.erl | 10 +++++----- src/rabbit_amqqueue_process.erl | 2 +- src/rabbit_channel.erl | 2 +- src/rabbit_exchange.erl | 5 ++--- src/rabbit_tests.erl | 2 +- 5 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 2611d189..3c4d35fe 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -38,7 +38,7 @@ stat/1, stat_all/0, deliver/2, redeliver/2, requeue/3, ack/4]). -export([list/1, info_keys/0, info/1, info/2, info_all/1, info_all/2]). -export([consumers/1, consumers_all/1]). --export([basic_get/3, basic_consume/8, basic_cancel/4]). +-export([basic_get/3, basic_consume/7, basic_cancel/4]). -export([notify_sent/2, unblock/2]). -export([commit_all/2, rollback_all/2, notify_down_all/2, limit_all/3]). -export([on_node_down/1]). @@ -97,8 +97,8 @@ -spec(limit_all/3 :: ([pid()], pid(), pid() | 'undefined') -> ok_or_errors()). -spec(basic_get/3 :: (amqqueue(), pid(), boolean()) -> {'ok', non_neg_integer(), msg()} | 'empty'). --spec(basic_consume/8 :: - (amqqueue(), boolean(), pid(), pid(), pid() | 'undefined', ctag(), +-spec(basic_consume/7 :: + (amqqueue(), boolean(), pid(), pid() | 'undefined', ctag(), boolean(), any()) -> 'ok' | {'error', 'queue_owned_by_another_connection' | 'exclusive_consume_unavailable'}). @@ -329,9 +329,9 @@ limit_all(QPids, ChPid, LimiterPid) -> basic_get(#amqqueue{pid = QPid}, ChPid, NoAck) -> gen_server2:call(QPid, {basic_get, ChPid, NoAck}, infinity). -basic_consume(#amqqueue{pid = QPid}, NoAck, ReaderPid, ChPid, LimiterPid, +basic_consume(#amqqueue{pid = QPid}, NoAck, ChPid, LimiterPid, ConsumerTag, ExclusiveConsume, OkMsg) -> - gen_server2:call(QPid, {basic_consume, NoAck, ReaderPid, ChPid, + gen_server2:call(QPid, {basic_consume, NoAck, ChPid, LimiterPid, ConsumerTag, ExclusiveConsume, OkMsg}, infinity). diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 6c5f4757..94a6d7d8 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -633,7 +633,7 @@ handle_call({basic_get, ChPid, NoAck}, _From, reply(empty, State) end; -handle_call({basic_consume, NoAck, ReaderPid, ChPid, LimiterPid, +handle_call({basic_consume, NoAck, ChPid, LimiterPid, ConsumerTag, ExclusiveConsume, OkMsg}, _From, State = #q{exclusive_consumer = ExistingHolder}) -> case check_exclusive_access(ExistingHolder, ExclusiveConsume, diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index ddb941cf..6c095a58 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -479,7 +479,7 @@ handle_method(#'basic.consume'{queue = QueueNameBin, fun (Q) -> check_queue_exclusivity(ReaderPid, Q), rabbit_amqqueue:basic_consume( - Q, NoAck, ReaderPid, self(), LimiterPid, + Q, NoAck, self(), LimiterPid, ActualConsumerTag, ExclusiveConsume, ok_msg(NoWait, #'basic.consume_ok'{ consumer_tag = ActualConsumerTag})) diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index bcba7d0b..1d4e41dd 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -345,7 +345,6 @@ delete_transient_queue_bindings(QueueName) -> delete_queue_bindings(QueueName, fun delete_transient_forward_routes/1). delete_queue_bindings(QueueName, FwdDeleteFun) -> - Exchanges = exchanges_for_queue(QueueName), [begin ok = FwdDeleteFun(reverse_route(Route)), ok = mnesia:delete_object(rabbit_reverse_route, Route, write) @@ -411,7 +410,7 @@ call_with_exchange_and_queue(Exchange, Queue, Fun) -> add_binding(ExchangeName, QueueName, RoutingKey, Arguments, InnerFun) -> binding_action( ExchangeName, QueueName, RoutingKey, Arguments, - fun (X = #exchange{type = Type}, Q, B) -> + fun (X, Q, B) -> InnerFun(X, Q), ok = sync_binding(B, Q#amqqueue.durable, fun mnesia:write/3) @@ -420,7 +419,7 @@ add_binding(ExchangeName, QueueName, RoutingKey, Arguments, InnerFun) -> delete_binding(ExchangeName, QueueName, RoutingKey, Arguments, InnerFun) -> binding_action( ExchangeName, QueueName, RoutingKey, Arguments, - fun (X = #exchange{type = Type}, Q, B) -> + fun (X, Q, B) -> case mnesia:match_object(rabbit_route, #route{binding = B}, write) of [] -> {error, binding_not_found}; diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 8602a209..06dfbbd9 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -759,7 +759,7 @@ test_server_status() -> false, false, [], none) || Name <- [<<"foo">>, <<"bar">>]], - ok = rabbit_amqqueue:basic_consume(Q, true, self(), Ch, undefined, + ok = rabbit_amqqueue:basic_consume(Q, true, Ch, undefined, <<"ctag">>, true, undefined), %% list queues ok = info_action(list_queues, rabbit_amqqueue:info_keys(), true), -- cgit v1.2.1 From fca3940ad43e5c8cdc259675fa2fca2992d21808 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Mon, 15 Feb 2010 15:48:46 +0000 Subject: Fix up things that dialyze complains about --- src/rabbit_channel.erl | 2 +- src/rabbit_exchange.erl | 9 --------- src/rabbit_reader.erl | 2 +- 3 files changed, 2 insertions(+), 11 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 6c095a58..f5360796 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -600,7 +600,7 @@ handle_method(#'basic.recover_async'{requeue = false}, internal_deliver( WriterPid, false, ConsumerTag, DeliveryTag, {QName, QPid, MsgId, true, Message}) - end, queue:to_list(UAMQ)), + end, ok, UAMQ), %% No answer required - basic.recover is the newer, synchronous %% variant of this method {noreply, State}; diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index 1d4e41dd..9a88d72c 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -363,15 +363,6 @@ delete_forward_routes(Route) -> delete_transient_forward_routes(Route) -> ok = mnesia:delete_object(rabbit_route, Route, write). -exchanges_for_queue(QueueName) -> - MatchHead = reverse_route( - #route{binding = #binding{exchange_name = '$1', - queue_name = QueueName, - _ = '_'}}), - sets:to_list( - sets:from_list( - mnesia:select(rabbit_reverse_route, [{MatchHead, [], ['$1']}]))). - contains(Table, MatchHead) -> try continue(mnesia:select(Table, [{MatchHead, [], ['$_']}], 1, read)) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 738f7e3f..472173f0 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -601,7 +601,7 @@ handle_method0(#'connection.tune_ok'{channel_max = _ChannelMax, if (FrameMax =< ?FRAME_MIN_SIZE) or (?FRAME_MAX /= 0) and (FrameMax > ?FRAME_MAX) -> rabbit_misc:protocol_error( - mistuned, "peer sent tune_ok with invalid frame_max"); + mistuned, "peer sent tune_ok with invalid frame_max", []); %% If we have a channel_max limit that the client wishes to %% exceed, die as per spec. Not currently a problem, so we ignore %% the client's channel_max parameter. -- cgit v1.2.1 From 8a5c679c66dddaa111dc8bfb93cb744a3987a224 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 11 May 2010 13:37:41 +0100 Subject: Don't recover exclusive queues if the owner pid has gone away. --- src/rabbit_amqqueue.erl | 43 ------------------------------- src/rabbit_amqqueue_process.erl | 56 ++++++++++++++++++++++++++--------------- 2 files changed, 36 insertions(+), 63 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index dc37c835..b237be8a 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -145,49 +145,6 @@ find_durable_queues() -> recover_durable_queues(DurableQueues) -> Qs = [start_queue_process(Q) || Q <- DurableQueues], [Q || Q <- Qs, gen_server2:call(Q#amqqueue.pid, {init, true}) == Q]. - -%% This changed too radically to merge. We'll fix this later; see bug 22695 -%% recover_durable_queues(DurableQueues) -> -%% lists:foldl( -%% fun (RecoveredQ = #amqqueue{ exclusive_owner = Owner }, -%% Acc) -> -%% %% We need to catch the case where a client connected to -%% %% another node has deleted the queue (and possibly -%% %% re-created it). -%% DoIfSameQueue = -%% fun (Action) -> -%% rabbit_misc:execute_mnesia_transaction( -%% fun () -> case mnesia:match_object( -%% rabbit_durable_queue, RecoveredQ, read) of -%% [_] -> {true, Action()}; -%% [] -> false -%% end -%% end) -%% end, -%% case shared_or_live_owner(Owner) of -%% true -> -%% Q = start_queue_process(RecoveredQ), -%% case DoIfSameQueue(fun () -> store_queue(Q) end) of -%% {true, ok} -> [Q | Acc]; -%% false -> exit(Q#amqqueue.pid, shutdown), -%% Acc -%% end; -%% false -> -%% case DoIfSameQueue( -%% fun () -> -%% internal_delete2(RecoveredQ#amqqueue.name) -%% end) of -%% {true, Hook} -> Hook(); -%% false -> ok -%% end, -%% Acc -%% end -%% end, [], DurableQueues). - -%% shared_or_live_owner(none) -> -%% true; -%% shared_or_live_owner(Owner) when is_pid(Owner) -> -%% rpc:call(node(Owner), erlang, is_process_alive, [Owner]). declare(QueueName, Durable, AutoDelete, Args, Owner) -> Q = start_queue_process(#amqqueue{name = QueueName, diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 92c21fa6..0d9e53db 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -517,27 +517,43 @@ handle_call({init, Recover}, From, State = #q{q = Q = #amqqueue{name = QName, durable = IsDurable, exclusive_owner = ExclusiveOwner}, backing_queue = BQ, backing_queue_state = undefined}) -> + Declare = fun() -> + case rabbit_amqqueue:internal_declare(Q, Recover) of + not_found -> + {stop, normal, not_found, State}; + Q -> + gen_server2:reply(From, Q), + ok = file_handle_cache:register_callback( + rabbit_amqqueue, set_maximum_since_use, [self()]), + ok = rabbit_memory_monitor:register( + self(), + {rabbit_amqqueue, set_ram_duration_target, [self()]}), + noreply(State#q{backing_queue_state = + BQ:init(QName, IsDurable, Recover)}); + Q1 -> + {stop, normal, Q1, State} + end + end, + case ExclusiveOwner of - none -> ok; - ReaderPid -> erlang:monitor(process, ReaderPid) - end, - %% TODO: If we're exclusively owned && our owner isn't alive && - %% Recover then we should BQ:init and then {stop, normal, - %% not_found, State}, relying on terminate to delete the queue. - case rabbit_amqqueue:internal_declare(Q, Recover) of - not_found -> - {stop, normal, not_found, State}; - Q -> - gen_server2:reply(From, Q), - ok = file_handle_cache:register_callback( - rabbit_amqqueue, set_maximum_since_use, [self()]), - ok = rabbit_memory_monitor:register( - self(), - {rabbit_amqqueue, set_ram_duration_target, [self()]}), - noreply(State#q{backing_queue_state = - BQ:init(QName, IsDurable, Recover)}); - Q1 -> - {stop, normal, Q1, State} + none -> Declare(); + Owner -> + case rpc:call(node(Owner), erlang, is_process_alive, [Owner]) of + true -> + erlang:monitor(process, Owner), + Declare(); + _ -> + case Recover of + true -> ok; + _ -> rabbit_log:warning( + "Queue ~p exclusive owner went away~n", + [QName]) + end, + %% Rely on terminate to delete the queue. + {stop, normal, not_found, + State#q{backing_queue_state = + BQ:init(QName, IsDurable, Recover)}} + end end; handle_call(info, _From, State) -> -- cgit v1.2.1 From 423c55c4819bd322efca74eb9b1be34eda0335a9 Mon Sep 17 00:00:00 2001 From: Matthew Sackman Date: Wed, 12 May 2010 15:22:35 +0100 Subject: Correct formatting --- src/rabbit_amqqueue_process.erl | 46 ++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 0d9e53db..ff1b8f1b 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -517,26 +517,30 @@ handle_call({init, Recover}, From, State = #q{q = Q = #amqqueue{name = QName, durable = IsDurable, exclusive_owner = ExclusiveOwner}, backing_queue = BQ, backing_queue_state = undefined}) -> - Declare = fun() -> - case rabbit_amqqueue:internal_declare(Q, Recover) of - not_found -> - {stop, normal, not_found, State}; - Q -> - gen_server2:reply(From, Q), - ok = file_handle_cache:register_callback( - rabbit_amqqueue, set_maximum_since_use, [self()]), - ok = rabbit_memory_monitor:register( - self(), - {rabbit_amqqueue, set_ram_duration_target, [self()]}), - noreply(State#q{backing_queue_state = - BQ:init(QName, IsDurable, Recover)}); - Q1 -> - {stop, normal, Q1, State} - end - end, + Declare = + fun() -> + case rabbit_amqqueue:internal_declare(Q, Recover) of + not_found -> + {stop, normal, not_found, State}; + Q -> + gen_server2:reply(From, Q), + ok = file_handle_cache:register_callback( + rabbit_amqqueue, set_maximum_since_use, + [self()]), + ok = rabbit_memory_monitor:register( + self(), {rabbit_amqqueue, + set_ram_duration_target, [self()]}), + noreply( + State#q{backing_queue_state = + BQ:init(QName, IsDurable, Recover)}); + Q1 -> + {stop, normal, Q1, State} + end + end, case ExclusiveOwner of - none -> Declare(); + none -> + Declare(); Owner -> case rpc:call(node(Owner), erlang, is_process_alive, [Owner]) of true -> @@ -546,13 +550,13 @@ handle_call({init, Recover}, From, case Recover of true -> ok; _ -> rabbit_log:warning( - "Queue ~p exclusive owner went away~n", - [QName]) + "Queue ~p exclusive owner went away~n", + [QName]) end, %% Rely on terminate to delete the queue. {stop, normal, not_found, State#q{backing_queue_state = - BQ:init(QName, IsDurable, Recover)}} + BQ:init(QName, IsDurable, Recover)}} end end; -- cgit v1.2.1 From 17cc6a4240ccf9cadf5e5444a406c3f2dae26098 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 15 Jun 2010 18:20:37 +0100 Subject: Send unexpected_frame if we get the wrong frame type or mismatched class ids. --- src/rabbit_framing_channel.erl | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/src/rabbit_framing_channel.erl b/src/rabbit_framing_channel.erl index b7c6aa96..161dfd84 100644 --- a/src/rabbit_framing_channel.erl +++ b/src/rabbit_framing_channel.erl @@ -73,14 +73,23 @@ read_frame(ChannelPid) -> end. mainloop(ChannelPid) -> - {method, MethodName, FieldsBin} = read_frame(ChannelPid), - Method = rabbit_framing:decode_method_fields(MethodName, FieldsBin), - case rabbit_framing:method_has_content(MethodName) of - true -> rabbit_channel:do(ChannelPid, Method, - collect_content(ChannelPid, MethodName)); - false -> rabbit_channel:do(ChannelPid, Method) - end, - ?MODULE:mainloop(ChannelPid). + Decoded = read_frame(ChannelPid), + case Decoded of + {method, MethodName, FieldsBin} -> + Method = rabbit_framing:decode_method_fields(MethodName, FieldsBin), + case rabbit_framing:method_has_content(MethodName) of + true -> rabbit_channel:do(ChannelPid, Method, + collect_content(ChannelPid, + MethodName)); + false -> rabbit_channel:do(ChannelPid, Method) + end, + ?MODULE:mainloop(ChannelPid); + _ -> + rabbit_misc:protocol_error( + unexpected_frame, + "expected method frame, got ~p instead", + [Decoded]) + end. collect_content(ChannelPid, MethodName) -> {ClassId, _MethodId} = rabbit_framing:method_id(MethodName), @@ -94,14 +103,14 @@ collect_content(ChannelPid, MethodName) -> payload_fragments_rev = Payload}; true -> rabbit_misc:protocol_error( - command_invalid, + unexpected_frame, "expected content header for class ~w, " "got one for class ~w instead", [ClassId, HeaderClassId]) end; _ -> rabbit_misc:protocol_error( - command_invalid, + unexpected_frame, "expected content header for class ~w, " "got non content header frame instead", [ClassId]) @@ -117,7 +126,7 @@ collect_content_payload(ChannelPid, RemainingByteCount, Acc) -> [FragmentBin | Acc]); _ -> rabbit_misc:protocol_error( - command_invalid, + unexpected_frame, "expected content body, got non content body frame instead", []) end. -- cgit v1.2.1 From a17e067cedd0a925a82042bd9e69a7f148f64949 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 16 Jun 2010 17:52:42 +0100 Subject: Revert exchange auto-delete removal, and tidy up a couple of whitespace diffs from default. --- include/rabbit.hrl | 3 ++- src/rabbit_access_control.erl | 2 +- src/rabbit_channel.erl | 14 ++++++++------ src/rabbit_error_logger.erl | 2 +- src/rabbit_exchange.erl | 31 ++++++++++++++++++++----------- 5 files changed, 32 insertions(+), 20 deletions(-) diff --git a/include/rabbit.hrl b/include/rabbit.hrl index d4327980..0d75310b 100644 --- a/include/rabbit.hrl +++ b/include/rabbit.hrl @@ -49,7 +49,7 @@ -record(resource, {virtual_host, kind, name}). --record(exchange, {name, type, durable, arguments}). +-record(exchange, {name, type, durable, auto_delete, arguments}). -record(amqqueue, {name, durable, auto_delete, exclusive_owner = none, arguments, pid}). @@ -115,6 +115,7 @@ #exchange{name :: exchange_name(), type :: exchange_type(), durable :: boolean(), + auto_delete :: boolean(), arguments :: amqp_table()}). -type(binding() :: #binding{exchange_name :: exchange_name(), diff --git a/src/rabbit_access_control.erl b/src/rabbit_access_control.erl index 23b84afb..a445f441 100644 --- a/src/rabbit_access_control.erl +++ b/src/rabbit_access_control.erl @@ -240,7 +240,7 @@ add_vhost(VHostPath) -> write), [rabbit_exchange:declare( rabbit_misc:r(VHostPath, exchange, Name), - Type, true, []) || + Type, true, false, []) || {Name,Type} <- [{<<"">>, direct}, {<<"amq.direct">>, direct}, diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 3dfc026b..d92becfa 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -663,7 +663,7 @@ handle_method(#'exchange.declare'{exchange = ExchangeNameBin, type = TypeNameBin, passive = false, durable = Durable, - deprecated_auto_delete = false, %% 0-9-1: true not supported + deprecated_auto_delete = AutoDelete, % 0-9-1: deprecated but we still support it deprecated_internal = false, %% 0-9-1: true not supported nowait = NoWait, arguments = Args}, @@ -685,9 +685,11 @@ handle_method(#'exchange.declare'{exchange = ExchangeNameBin, rabbit_exchange:declare(ExchangeName, CheckedType, Durable, + AutoDelete, Args) end, - ok = rabbit_exchange:assert_equivalence(X, CheckedType, Durable, Args), + ok = rabbit_exchange:assert_equivalence(X, CheckedType, Durable, + AutoDelete, Args), return_ok(State, NoWait, #'exchange.declare_ok'{}); handle_method(#'exchange.declare'{exchange = ExchangeNameBin, @@ -731,9 +733,9 @@ handle_method(#'queue.declare'{queue = QueueNameBin, end, %% We use this in both branches, because queue_declare may yet return an %% existing queue. - Finish = fun (#amqqueue{name = QueueName, - durable = Durable1, - auto_delete = AutoDelete1} = Q) + Finish = fun (#amqqueue{name = QueueName, + durable = Durable1, + auto_delete = AutoDelete1} = Q) when Durable =:= Durable1, AutoDelete =:= AutoDelete1 -> check_exclusive_access(Q, Owner, strict), check_configure_permitted(QueueName, State), @@ -752,7 +754,7 @@ handle_method(#'queue.declare'{queue = QueueNameBin, channel_error, "parameters for ~s not equivalent", [rabbit_misc:rs(QueueName)]) - end, + end, Q = case rabbit_amqqueue:with( rabbit_misc:r(VHostPath, queue, QueueNameBin), Finish) of diff --git a/src/rabbit_error_logger.erl b/src/rabbit_error_logger.erl index face0a1a..e9baf2c4 100644 --- a/src/rabbit_error_logger.erl +++ b/src/rabbit_error_logger.erl @@ -48,7 +48,7 @@ boot() -> init([DefaultVHost]) -> #exchange{} = rabbit_exchange:declare( rabbit_misc:r(DefaultVHost, exchange, ?LOG_EXCH_NAME), - topic, true, []), + topic, true, false, []), {ok, #resource{virtual_host = DefaultVHost, kind = exchange, name = ?LOG_EXCH_NAME}}. diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index eb6f3e49..512f248c 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -33,13 +33,13 @@ -include("rabbit.hrl"). -include("rabbit_framing.hrl"). --export([recover/0, declare/4, lookup/1, lookup_or_die/1, +-export([recover/0, declare/5, lookup/1, lookup_or_die/1, list/1, info_keys/0, info/1, info/2, info_all/1, info_all/2, publish/2]). -export([add_binding/5, delete_binding/5, list_bindings/1]). -export([delete/2]). -export([delete_queue_bindings/1, delete_transient_queue_bindings/1]). --export([assert_equivalence/4]). +-export([assert_equivalence/5]). -export([assert_args_equivalence/2]). -export([check_type/1]). @@ -63,9 +63,11 @@ -type(inner_fun() :: fun((exchange(), queue()) -> any())). -spec(recover/0 :: () -> 'ok'). --spec(declare/4 :: (exchange_name(), exchange_type(), boolean(), amqp_table()) -> exchange()). +-spec(declare/5 :: (exchange_name(), exchange_type(), boolean(), boolean(), + amqp_table()) -> exchange()). -spec(check_type/1 :: (binary()) -> atom()). --spec(assert_equivalence/4 :: (exchange(), atom(), boolean(), amqp_table()) -> 'ok'). +-spec(assert_equivalence/5 :: (exchange(), atom(), boolean(), boolean(), + amqp_table()) -> 'ok'). -spec(assert_args_equivalence/2 :: (exchange(), amqp_table()) -> 'ok'). -spec(lookup/1 :: (exchange_name()) -> {'ok', exchange()} | not_found()). -spec(lookup_or_die/1 :: (exchange_name()) -> exchange()). @@ -98,7 +100,7 @@ %%---------------------------------------------------------------------------- --define(INFO_KEYS, [name, type, durable, arguments]. +-define(INFO_KEYS, [name, type, durable, auto_delete, arguments]. recover() -> Exs = rabbit_misc:table_fold( @@ -133,10 +135,11 @@ recover_with_bindings(Bs, [X = #exchange{type = Type} | Xs], Bindings) -> recover_with_bindings([], [], []) -> ok. -declare(ExchangeName, Type, Durable, Args) -> +declare(ExchangeName, Type, Durable, AutoDelete, Args) -> Exchange = #exchange{name = ExchangeName, type = Type, durable = Durable, + auto_delete = AutoDelete, arguments = Args}, %% We want to upset things if it isn't ok; this is different from %% the other hooks invocations, where we tend to ignore the return @@ -187,11 +190,12 @@ check_type(TypeBin) -> end. assert_equivalence(X = #exchange{ durable = Durable, + auto_delete = AutoDelete, type = Type}, - Type, Durable, + Type, Durable, AutoDelete, RequiredArgs) -> ok = (type_to_module(Type)):assert_args_equivalence(X, RequiredArgs); -assert_equivalence(#exchange{ name = Name }, _Type, _Durable, +assert_equivalence(#exchange{ name = Name }, _Type, _Durable, _AutoDelete, _Args) -> rabbit_misc:protocol_error( precondition_failed, @@ -242,6 +246,7 @@ infos(Items, X) -> [{Item, i(Item, X)} || Item <- Items]. i(name, #exchange{name = Name}) -> Name; i(type, #exchange{type = Type}) -> Type; i(durable, #exchange{durable = Durable}) -> Durable; +i(auto_delete, #exchange{auto_delete = AutoDelete}) -> AutoDelete; i(arguments, #exchange{arguments = Arguments}) -> Arguments; i(Item, _) -> throw({bad_argument, Item}). @@ -519,9 +524,13 @@ delete(ExchangeName, IfUnused) -> Error end. -%% TODO: remove this autodelete machinery altogether. -maybe_auto_delete(Exchange) -> - {no_delete, Exchange}. +maybe_auto_delete(Exchange = #exchange{auto_delete = false}) -> + {no_delete, Exchange}; +maybe_auto_delete(Exchange = #exchange{auto_delete = true}) -> + case conditional_delete(Exchange) of + {error, in_use} -> {no_delete, Exchange}; + {deleted, Exchange, []} -> {auto_deleted, Exchange} + end. conditional_delete(Exchange = #exchange{name = ExchangeName}) -> Match = #route{binding = #binding{exchange_name = ExchangeName, _ = '_'}}, -- cgit v1.2.1 From ac39790ebcddbb4598b7f0c62eef98edea1508ee Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 22 Jun 2010 15:00:42 +0100 Subject: Fix >80 columns --- src/rabbit_channel.erl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index d92becfa..e8d41131 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -663,8 +663,10 @@ handle_method(#'exchange.declare'{exchange = ExchangeNameBin, type = TypeNameBin, passive = false, durable = Durable, - deprecated_auto_delete = AutoDelete, % 0-9-1: deprecated but we still support it - deprecated_internal = false, %% 0-9-1: true not supported + %% 0-9-1: deprecated but we still support it + deprecated_auto_delete = AutoDelete, + %% 0-9-1: true not supported + deprecated_internal = false, nowait = NoWait, arguments = Args}, _, State = #ch{ virtual_host = VHostPath }) -> -- cgit v1.2.1 From 2298af4a20d1450e71abef84bba13505e1c9fba6 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 23 Jun 2010 16:15:11 +0100 Subject: Use the new merged JSON for codegen, stripping out redirect and trace frame support on the way. This results in a broker which doesn't quite speak 0-8 as it gets connection.close wrong. --- Makefile | 2 +- src/rabbit_channel.erl | 6 +++--- src/rabbit_reader.erl | 48 ++++++------------------------------------------ 3 files changed, 10 insertions(+), 46 deletions(-) diff --git a/Makefile b/Makefile index 36b045f7..d715db74 100644 --- a/Makefile +++ b/Makefile @@ -56,7 +56,7 @@ TARGET_SRC_DIR=dist/$(TARBALL_NAME) SIBLING_CODEGEN_DIR=../rabbitmq-codegen/ AMQP_CODEGEN_DIR=$(shell [ -d $(SIBLING_CODEGEN_DIR) ] && echo $(SIBLING_CODEGEN_DIR) || echo codegen) -AMQP_SPEC_JSON_FILES=$(AMQP_CODEGEN_DIR)/amqp-0.8.json $(AMQP_CODEGEN_DIR)/rabbitmq-0.8-extensions.json +AMQP_SPEC_JSON_FILES=$(AMQP_CODEGEN_DIR)/amqp-rabbitmq.json ERL_CALL=erl_call -sname $(RABBITMQ_NODENAME) -e diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index d337df29..7c0b94d4 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -499,7 +499,7 @@ handle_method(#'basic.get'{queue = QueueNameBin, Content), {noreply, State1#ch{next_tag = DeliveryTag + 1}}; empty -> - {reply, #'basic.get_empty'{cluster_id = <<>>}, State} + {reply, #'basic.get_empty'{deprecated_cluster_id = <<>>}, State} end; handle_method(#'basic.consume'{queue = QueueNameBin, @@ -656,8 +656,8 @@ handle_method(#'exchange.declare'{exchange = ExchangeNameBin, type = TypeNameBin, passive = false, durable = Durable, - auto_delete = AutoDelete, - internal = false, + deprecated_auto_delete = AutoDelete, + deprecated_internal = false, nowait = NoWait, arguments = Args}, _, State = #ch{ virtual_host = VHostPath }) -> diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 73a58f13..20aacf33 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -461,7 +461,6 @@ handle_frame(Type, 0, Payload, State) -> case analyze_frame(Type, Payload) of error -> throw({unknown_frame, 0, Type, Payload}); heartbeat -> State; - trace -> State; {method, MethodName, FieldsBin} -> handle_method0(MethodName, FieldsBin, State); Other -> throw({unexpected_frame_on_channel0, Other}) @@ -470,7 +469,6 @@ handle_frame(Type, Channel, Payload, State) -> case analyze_frame(Type, Payload) of error -> throw({unknown_frame, Channel, Type, Payload}); heartbeat -> throw({unexpected_heartbeat_frame, Channel}); - trace -> throw({unexpected_trace_frame, Channel}); AnalyzedFrame -> %%?LOGDEBUG("Ch ~p Frame ~p~n", [Channel, AnalyzedFrame]), case get({channel, Channel}) of @@ -509,8 +507,6 @@ analyze_frame(?FRAME_HEADER, < {content_body, Body}; -analyze_frame(?FRAME_TRACE, _Body) -> - trace; analyze_frame(?FRAME_HEARTBEAT, <<>>) -> heartbeat; analyze_frame(_Type, _Body) -> @@ -626,35 +622,18 @@ handle_method0(#'connection.tune_ok'{channel_max = _ChannelMax, connection = Connection#connection{ timeout_sec = ClientHeartbeat, frame_max = FrameMax}}; -handle_method0(#'connection.open'{virtual_host = VHostPath, - insist = Insist}, +handle_method0(#'connection.open'{virtual_host = VHostPath}, State = #v1{connection_state = opening, connection = Connection = #connection{ user = User}, sock = Sock}) -> ok = rabbit_access_control:check_vhost_access(User, VHostPath), NewConnection = Connection#connection{vhost = VHostPath}, - KnownHosts = format_listeners(rabbit_networking:active_listeners()), - Redirects = compute_redirects(Insist), - if Redirects == [] -> - ok = send_on_channel0( - Sock, - #'connection.open_ok'{known_hosts = KnownHosts}), - State#v1{connection_state = running, - connection = NewConnection}; - true -> - %% FIXME: 'host' is supposed to only contain one - %% address; but which one do we pick? This is - %% really a problem with the spec. - Host = format_listeners(Redirects), - rabbit_log:info("connection ~p redirecting to ~p~n", - [self(), Host]), - ok = send_on_channel0( - Sock, - #'connection.redirect'{host = Host, - known_hosts = KnownHosts}), - close_connection(State#v1{connection = NewConnection}) - end; + ok = send_on_channel0( + Sock, + #'connection.open_ok'{deprecated_known_hosts = <<>>}), + State#v1{connection_state = running, + connection = NewConnection}; handle_method0(#'connection.close'{}, State = #v1{connection_state = running}) -> lists:foreach(fun rabbit_framing_channel:shutdown/1, all_channels()), @@ -673,21 +652,6 @@ handle_method0(_Method, #v1{connection_state = S}) -> send_on_channel0(Sock, Method) -> ok = rabbit_writer:internal_send_command(Sock, 0, Method). -format_listeners(Listeners) -> - list_to_binary( - rabbit_misc:intersperse( - $,, - [io_lib:format("~s:~w", [Host, Port]) || - #listener{host = Host, port = Port} <- Listeners])). - -compute_redirects(true) -> []; -compute_redirects(false) -> - Node = node(), - LNode = rabbit_load:pick(), - if Node == LNode -> []; - true -> rabbit_networking:node_listeners(LNode) - end. - %%-------------------------------------------------------------------------- infos(Items, State) -> [{Item, i(Item, State)} || Item <- Items]. -- cgit v1.2.1 From 9a0f42f64b2ba9d4200a785e8a2e7ae594e7bc14 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 23 Jun 2010 17:09:50 +0100 Subject: Fix up connection.close/ok and basic consume. I think we're now talking real AMQP 0-8. --- src/rabbit_binary_generator.erl | 9 ++++++++- src/rabbit_framing_channel.erl | 11 ++++++++++- src/rabbit_reader.erl | 11 ++++++++++- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/rabbit_binary_generator.erl b/src/rabbit_binary_generator.erl index 81cf3cee..cd8b44d1 100644 --- a/src/rabbit_binary_generator.erl +++ b/src/rabbit_binary_generator.erl @@ -73,10 +73,17 @@ build_simple_method_frame(ChannelInt, MethodRecord) -> MethodFields = rabbit_framing:encode_method_fields(MethodRecord), - MethodName = rabbit_misc:method_record_type(MethodRecord), + MethodName = adjust_close(rabbit_misc:method_record_type(MethodRecord)), {ClassId, MethodId} = rabbit_framing:method_id(MethodName), create_frame(1, ChannelInt, [<>, MethodFields]). +adjust_close('connection.close') -> + 'connection.close08'; +adjust_close('connection.close_ok') -> + 'connection.close08_ok'; +adjust_close(MethodName) -> + MethodName. + build_simple_content_frames(ChannelInt, #content{class_id = ClassId, properties = ContentProperties, diff --git a/src/rabbit_framing_channel.erl b/src/rabbit_framing_channel.erl index b7c6aa96..8eabf42b 100644 --- a/src/rabbit_framing_channel.erl +++ b/src/rabbit_framing_channel.erl @@ -74,7 +74,7 @@ read_frame(ChannelPid) -> mainloop(ChannelPid) -> {method, MethodName, FieldsBin} = read_frame(ChannelPid), - Method = rabbit_framing:decode_method_fields(MethodName, FieldsBin), + Method = decode_method_fields(MethodName, FieldsBin), case rabbit_framing:method_has_content(MethodName) of true -> rabbit_channel:do(ChannelPid, Method, collect_content(ChannelPid, MethodName)); @@ -82,6 +82,15 @@ mainloop(ChannelPid) -> end, ?MODULE:mainloop(ChannelPid). +%% Handle 0-8 version of basic.consume, which doesn't have a table on the end +decode_method_fields('basic.consume', FieldsBin) -> + T = rabbit_binary_generator:generate_table([]), + TLen = size(T), + rabbit_framing:decode_method_fields( + 'basic.consume', <>); +decode_method_fields(MethodName, FieldsBin) -> + rabbit_framing:decode_method_fields(MethodName, FieldsBin). + collect_content(ChannelPid, MethodName) -> {ClassId, _MethodId} = rabbit_framing:method_id(MethodName), case read_frame(ChannelPid) of diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 20aacf33..f83f932d 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -502,7 +502,9 @@ handle_frame(Type, Channel, Payload, State) -> end. analyze_frame(?FRAME_METHOD, <>) -> - {method, rabbit_framing:lookup_method_name({ClassId, MethodId}), MethodFields}; + {method, adjust_close(rabbit_framing:lookup_method_name({ClassId, + MethodId})), + MethodFields}; analyze_frame(?FRAME_HEADER, <>) -> {content_header, ClassId, Weight, BodySize, Properties}; analyze_frame(?FRAME_BODY, Body) -> @@ -512,6 +514,13 @@ analyze_frame(?FRAME_HEARTBEAT, <<>>) -> analyze_frame(_Type, _Body) -> error. +adjust_close('connection.close08') -> + 'connection.close'; +adjust_close('connection.close08_ok') -> + 'connection.close_ok'; +adjust_close(MethodName) -> + MethodName. + handle_input(frame_header, <>, State) -> %%?LOGDEBUG("Got frame header: ~p/~p/~p~n", [Type, Channel, PayloadSize]), {State, {frame_payload, Type, Channel, PayloadSize}, PayloadSize + 1}; -- cgit v1.2.1 From e41b256da90d99a0acb92431dd0c18fc71be69e6 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 24 Jun 2010 11:24:00 +0100 Subject: Handle 0-8 channel.tune-ok. --- src/rabbit_framing_channel.erl | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/rabbit_framing_channel.erl b/src/rabbit_framing_channel.erl index 8eabf42b..49043583 100644 --- a/src/rabbit_framing_channel.erl +++ b/src/rabbit_framing_channel.erl @@ -82,7 +82,14 @@ mainloop(ChannelPid) -> end, ?MODULE:mainloop(ChannelPid). -%% Handle 0-8 version of basic.consume, which doesn't have a table on the end +%% Handle 0-8 version of channel.tune-ok. In 0-9-1 it gained a longstr +%% "deprecated_channel_id". +decode_method_fields('channel.tune_ok', FieldsBin) -> + Len = 0, + rabbit_framing:decode_method_fields( + 'channel.tune_ok', <>); +%% Handle 0-8 version of basic.consume. In 0-9-1 it gained a table +%% "filter". decode_method_fields('basic.consume', FieldsBin) -> T = rabbit_binary_generator:generate_table([]), TLen = size(T), -- cgit v1.2.1 From 325c7df2da27599672356d88836e2b79b4d08c6d Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 24 Jun 2010 12:55:50 +0100 Subject: Add protocol to reader and writer state, and pass it around in enough places to let us switch based on protocol. Remove a couple of unused exports while we're there. --- include/rabbit.hrl | 4 +- src/rabbit_binary_generator.erl | 17 ++++---- src/rabbit_framing_channel.erl | 20 ++++----- src/rabbit_reader.erl | 92 +++++++++++++++++++++++++---------------- src/rabbit_writer.erl | 80 ++++++++++++++++++----------------- 5 files changed, 120 insertions(+), 93 deletions(-) diff --git a/include/rabbit.hrl b/include/rabbit.hrl index 06297c69..cff17e08 100644 --- a/include/rabbit.hrl +++ b/include/rabbit.hrl @@ -36,7 +36,8 @@ -record(vhost, {virtual_host, dummy}). --record(connection, {user, timeout_sec, frame_max, vhost, client_properties}). +-record(connection, {user, timeout_sec, frame_max, vhost, client_properties, + protocol}). -record(content, {class_id, @@ -173,6 +174,7 @@ explanation :: string(), method :: atom()}). +-type(protocol() :: atom()). -endif. %%---------------------------------------------------------------------------- diff --git a/src/rabbit_binary_generator.erl b/src/rabbit_binary_generator.erl index cd8b44d1..e29f9fcb 100644 --- a/src/rabbit_binary_generator.erl +++ b/src/rabbit_binary_generator.erl @@ -41,7 +41,7 @@ % See definition of check_empty_content_body_frame_size/0, an assertion called at startup. -define(EMPTY_CONTENT_BODY_FRAME_SIZE, 8). --export([build_simple_method_frame/2, +-export([build_simple_method_frame/3, build_simple_content_frames/3, build_heartbeat_frame/0]). -export([generate_table/1, encode_properties/2]). @@ -56,8 +56,8 @@ -type(frame() :: [binary()]). --spec(build_simple_method_frame/2 :: - (channel_number(), amqp_method_record()) -> frame()). +-spec(build_simple_method_frame/3 :: + (channel_number(), amqp_method_record(), protocol()) -> frame()). -spec(build_simple_content_frames/3 :: (channel_number(), content(), non_neg_integer()) -> [frame()]). -spec(build_heartbeat_frame/0 :: () -> frame()). @@ -71,17 +71,18 @@ %%---------------------------------------------------------------------------- -build_simple_method_frame(ChannelInt, MethodRecord) -> +build_simple_method_frame(ChannelInt, MethodRecord, Protocol) -> MethodFields = rabbit_framing:encode_method_fields(MethodRecord), - MethodName = adjust_close(rabbit_misc:method_record_type(MethodRecord)), + MethodName = adjust_close(rabbit_misc:method_record_type(MethodRecord), + Protocol), {ClassId, MethodId} = rabbit_framing:method_id(MethodName), create_frame(1, ChannelInt, [<>, MethodFields]). -adjust_close('connection.close') -> +adjust_close('connection.close', protocol_08) -> 'connection.close08'; -adjust_close('connection.close_ok') -> +adjust_close('connection.close_ok', protocol_08) -> 'connection.close08_ok'; -adjust_close(MethodName) -> +adjust_close(MethodName, _Protocol) -> MethodName. build_simple_content_frames(ChannelInt, diff --git a/src/rabbit_framing_channel.erl b/src/rabbit_framing_channel.erl index 49043583..90856cdf 100644 --- a/src/rabbit_framing_channel.erl +++ b/src/rabbit_framing_channel.erl @@ -32,20 +32,20 @@ -module(rabbit_framing_channel). -include("rabbit.hrl"). --export([start_link/2, process/2, shutdown/1]). +-export([start_link/3, process/2, shutdown/1]). %% internal --export([mainloop/1]). +-export([mainloop/2]). %%-------------------------------------------------------------------- -start_link(StartFun, StartArgs) -> +start_link(StartFun, StartArgs, Protocol) -> spawn_link( fun () -> %% we trap exits so that a normal termination of the %% channel or reader process terminates us too. process_flag(trap_exit, true), - mainloop(apply(StartFun, StartArgs)) + mainloop(apply(StartFun, StartArgs), Protocol) end). process(Pid, Frame) -> @@ -72,30 +72,30 @@ read_frame(ChannelPid) -> Msg -> exit({unexpected_message, Msg}) end. -mainloop(ChannelPid) -> +mainloop(ChannelPid, Protocol) -> {method, MethodName, FieldsBin} = read_frame(ChannelPid), - Method = decode_method_fields(MethodName, FieldsBin), + Method = decode_method_fields(MethodName, FieldsBin, Protocol), case rabbit_framing:method_has_content(MethodName) of true -> rabbit_channel:do(ChannelPid, Method, collect_content(ChannelPid, MethodName)); false -> rabbit_channel:do(ChannelPid, Method) end, - ?MODULE:mainloop(ChannelPid). + ?MODULE:mainloop(ChannelPid, Protocol). %% Handle 0-8 version of channel.tune-ok. In 0-9-1 it gained a longstr %% "deprecated_channel_id". -decode_method_fields('channel.tune_ok', FieldsBin) -> +decode_method_fields('channel.tune_ok', FieldsBin, protocol_08) -> Len = 0, rabbit_framing:decode_method_fields( 'channel.tune_ok', <>); %% Handle 0-8 version of basic.consume. In 0-9-1 it gained a table %% "filter". -decode_method_fields('basic.consume', FieldsBin) -> +decode_method_fields('basic.consume', FieldsBin, protocol_08) -> T = rabbit_binary_generator:generate_table([]), TLen = size(T), rabbit_framing:decode_method_fields( 'basic.consume', <>); -decode_method_fields(MethodName, FieldsBin) -> +decode_method_fields(MethodName, FieldsBin, _Protocol) -> rabbit_framing:decode_method_fields(MethodName, FieldsBin). collect_content(ChannelPid, MethodName) -> diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index f83f932d..e2d4880b 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -41,8 +41,6 @@ -export([server_properties/0]). --export([analyze_frame/2]). - -import(gen_tcp). -import(fprof). -import(inet). @@ -244,7 +242,8 @@ start_connection(Parent, Deb, Sock, SockTransform) -> timeout_sec = ?HANDSHAKE_TIMEOUT, frame_max = ?FRAME_MIN_SIZE, vhost = none, - client_properties = none}, + client_properties = none, + protocol = unknown}, callback = uninitialized_callback, recv_ref = none, connection_state = pre_init, @@ -432,7 +431,8 @@ wait_for_channel_termination(N, TimerRef) -> end. maybe_close(State = #v1{connection_state = closing, - queue_collector = Collector}) -> + queue_collector = Collector, + connection = #connection{protocol = Protocol}}) -> case all_channels() of [] -> %% Spec says "Exclusive queues may only be accessed by the current @@ -440,16 +440,19 @@ maybe_close(State = #v1{connection_state = closing, %% This does not strictly imply synchrony, but in practice it seems %% to be what people assume. rabbit_reader_queue_collector:delete_all(Collector), - ok = send_on_channel0(State#v1.sock, #'connection.close_ok'{}), + ok = send_on_channel0(State#v1.sock, #'connection.close_ok'{}, + Protocol), close_connection(State); _ -> State end; maybe_close(State) -> State. -handle_frame(Type, 0, Payload, State = #v1{connection_state = CS}) +handle_frame(Type, 0, Payload, + State = #v1{connection_state = CS, + connection = #connection{protocol = Protocol}}) when CS =:= closing; CS =:= closed -> - case analyze_frame(Type, Payload) of + case analyze_frame(Type, Payload, Protocol) of {method, MethodName, FieldsBin} -> handle_method0(MethodName, FieldsBin, State); _Other -> State @@ -457,16 +460,18 @@ handle_frame(Type, 0, Payload, State = #v1{connection_state = CS}) handle_frame(_Type, _Channel, _Payload, State = #v1{connection_state = CS}) when CS =:= closing; CS =:= closed -> State; -handle_frame(Type, 0, Payload, State) -> - case analyze_frame(Type, Payload) of +handle_frame(Type, 0, Payload, + State = #v1{connection = #connection{protocol = Protocol}}) -> + case analyze_frame(Type, Payload, Protocol) of error -> throw({unknown_frame, 0, Type, Payload}); heartbeat -> State; {method, MethodName, FieldsBin} -> handle_method0(MethodName, FieldsBin, State); Other -> throw({unexpected_frame_on_channel0, Other}) end; -handle_frame(Type, Channel, Payload, State) -> - case analyze_frame(Type, Payload) of +handle_frame(Type, Channel, Payload, + State = #v1{connection = #connection{protocol = Protocol}}) -> + case analyze_frame(Type, Payload, Protocol) of error -> throw({unknown_frame, Channel, Type, Payload}); heartbeat -> throw({unexpected_heartbeat_frame, Channel}); AnalyzedFrame -> @@ -501,24 +506,28 @@ handle_frame(Type, Channel, Payload, State) -> end end. -analyze_frame(?FRAME_METHOD, <>) -> - {method, adjust_close(rabbit_framing:lookup_method_name({ClassId, - MethodId})), +analyze_frame(?FRAME_METHOD, <>, + Protocol) -> + {method, adjust_close( + rabbit_framing:lookup_method_name({ClassId, MethodId}), + Protocol), MethodFields}; -analyze_frame(?FRAME_HEADER, <>) -> +analyze_frame(?FRAME_HEADER, + <>, + _Protocol) -> {content_header, ClassId, Weight, BodySize, Properties}; -analyze_frame(?FRAME_BODY, Body) -> +analyze_frame(?FRAME_BODY, Body, _Protocol) -> {content_body, Body}; -analyze_frame(?FRAME_HEARTBEAT, <<>>) -> +analyze_frame(?FRAME_HEARTBEAT, <<>>, _Protocol) -> heartbeat; -analyze_frame(_Type, _Body) -> +analyze_frame(_Type, _Body, _Protocol) -> error. -adjust_close('connection.close08') -> +adjust_close('connection.close08', protocol_08) -> 'connection.close'; -adjust_close('connection.close08_ok') -> +adjust_close('connection.close08_ok', protocol_08) -> 'connection.close_ok'; -adjust_close(MethodName) -> +adjust_close(MethodName, _Protocol) -> MethodName. handle_input(frame_header, <>, State) -> @@ -540,6 +549,7 @@ handle_input(handshake, <<"AMQP",1,1,ProtocolMajor,ProtocolMinor>>, case check_version({ProtocolMajor, ProtocolMinor}, {?PROTOCOL_VERSION_MAJOR, ?PROTOCOL_VERSION_MINOR}) of true -> + Protocol = protocol_08, ok = send_on_channel0( Sock, #'connection.start'{ @@ -547,9 +557,11 @@ handle_input(handshake, <<"AMQP",1,1,ProtocolMajor,ProtocolMinor>>, version_minor = ?PROTOCOL_VERSION_MINOR, server_properties = server_properties(), mechanisms = <<"PLAIN AMQPLAIN">>, - locales = <<"en_US">> }), + locales = <<"en_US">> }, + Protocol), {State#v1{connection = Connection#connection{ - timeout_sec = ?NORMAL_TIMEOUT}, + timeout_sec = ?NORMAL_TIMEOUT, + protocol = Protocol}, connection_state = starting}, frame_header, 7}; false -> @@ -604,7 +616,8 @@ handle_method0(#'connection.start_ok'{mechanism = Mechanism, response = Response, client_properties = ClientProperties}, State = #v1{connection_state = starting, - connection = Connection, + connection = Connection = + #connection{protocol = Protocol}, sock = Sock}) -> User = rabbit_access_control:check_login(Mechanism, Response), ok = send_on_channel0( @@ -612,7 +625,8 @@ handle_method0(#'connection.start_ok'{mechanism = Mechanism, #'connection.tune'{channel_max = 0, %% set to zero once QPid fix their negotiation frame_max = 131072, - heartbeat = 0}), + heartbeat = 0}, + Protocol), State#v1{connection_state = tuning, connection = Connection#connection{ user = User, @@ -634,13 +648,15 @@ handle_method0(#'connection.tune_ok'{channel_max = _ChannelMax, handle_method0(#'connection.open'{virtual_host = VHostPath}, State = #v1{connection_state = opening, connection = Connection = #connection{ - user = User}, + user = User, + protocol = Protocol}, sock = Sock}) -> ok = rabbit_access_control:check_vhost_access(User, VHostPath), NewConnection = Connection#connection{vhost = VHostPath}, ok = send_on_channel0( Sock, - #'connection.open_ok'{deprecated_known_hosts = <<>>}), + #'connection.open_ok'{deprecated_known_hosts = <<>>}, + Protocol), State#v1{connection_state = running, connection = NewConnection}; handle_method0(#'connection.close'{}, @@ -658,8 +674,8 @@ handle_method0(_Method, #v1{connection_state = S}) -> rabbit_misc:protocol_error( channel_error, "unexpected method in connection state ~w", [S]). -send_on_channel0(Sock, Method) -> - ok = rabbit_writer:internal_send_command(Sock, 0, Method). +send_on_channel0(Sock, Method, Protocol) -> + ok = rabbit_writer:internal_send_command(Sock, 0, Method, Protocol). %%-------------------------------------------------------------------------- @@ -711,16 +727,19 @@ i(Item, #v1{}) -> %%-------------------------------------------------------------------------- -send_to_new_channel(Channel, AnalyzedFrame, - State = #v1{queue_collector = Collector}) -> +send_to_new_channel(Channel, AnalyzedFrame, State = + #v1{queue_collector = Collector, + connection = #connection{protocol = Protocol}}) -> #v1{sock = Sock, connection = #connection{ frame_max = FrameMax, user = #user{username = Username}, - vhost = VHost}} = State, - WriterPid = rabbit_writer:start(Sock, Channel, FrameMax), + vhost = VHost, + protocol = Protocol}} = State, + WriterPid = rabbit_writer:start(Sock, Channel, FrameMax, Protocol), ChPid = rabbit_framing_channel:start_link( fun rabbit_channel:start_link/6, - [Channel, self(), WriterPid, Username, VHost, Collector]), + [Channel, self(), WriterPid, Username, VHost, Collector], + Protocol), put({channel, Channel}, {chpid, ChPid}), put({chpid, ChPid}, {channel, Channel}), ok = rabbit_framing_channel:process(ChPid, AnalyzedFrame). @@ -736,7 +755,8 @@ handle_exception(State = #v1{connection_state = CS}, Channel, Reason) -> log_channel_error(CS, Channel, Reason), send_exception(State, Channel, Reason). -send_exception(State, Channel, Reason) -> +send_exception(State = #v1{connection = #connection{protocol = Protocol}}, + Channel, Reason) -> {ShouldClose, CloseChannel, CloseMethod} = map_exception(Channel, Reason), NewState = case ShouldClose of true -> terminate_channels(), @@ -744,7 +764,7 @@ send_exception(State, Channel, Reason) -> false -> close_channel(Channel, State) end, ok = rabbit_writer:internal_send_command( - NewState#v1.sock, CloseChannel, CloseMethod), + NewState#v1.sock, CloseChannel, CloseMethod, Protocol), NewState. map_exception(Channel, Reason) -> diff --git a/src/rabbit_writer.erl b/src/rabbit_writer.erl index 3d10dc12..375e540e 100644 --- a/src/rabbit_writer.erl +++ b/src/rabbit_writer.erl @@ -33,14 +33,14 @@ -include("rabbit.hrl"). -include("rabbit_framing.hrl"). --export([start/3, start_link/3, shutdown/1, mainloop/1]). +-export([start/4, shutdown/1, mainloop/1]). -export([send_command/2, send_command/3, send_command_and_signal_back/3, send_command_and_signal_back/4, send_command_and_notify/5]). --export([internal_send_command/3, internal_send_command/5]). +-export([internal_send_command/4, internal_send_command/6]). -import(gen_tcp). --record(wstate, {sock, channel, frame_max}). +-record(wstate, {sock, channel, frame_max, protocol}). -define(HIBERNATE_AFTER, 5000). @@ -48,8 +48,8 @@ -ifdef(use_specs). --spec(start/3 :: (socket(), channel_number(), non_neg_integer()) -> pid()). --spec(start_link/3 :: (socket(), channel_number(), non_neg_integer()) -> pid()). +-spec(start/4 :: + (socket(), channel_number(), non_neg_integer(), protocol()) -> pid()). -spec(send_command/2 :: (pid(), amqp_method_record()) -> 'ok'). -spec(send_command/3 :: (pid(), amqp_method_record(), content()) -> 'ok'). -spec(send_command_and_signal_back/3 :: (pid(), amqp_method(), pid()) -> 'ok'). @@ -57,25 +57,21 @@ (pid(), amqp_method(), content(), pid()) -> 'ok'). -spec(send_command_and_notify/5 :: (pid(), pid(), pid(), amqp_method_record(), content()) -> 'ok'). --spec(internal_send_command/3 :: - (socket(), channel_number(), amqp_method_record()) -> 'ok'). --spec(internal_send_command/5 :: +-spec(internal_send_command/4 :: + (socket(), channel_number(), amqp_method_record(), protocol()) -> 'ok'). +-spec(internal_send_command/6 :: (socket(), channel_number(), amqp_method_record(), - content(), non_neg_integer()) -> 'ok'). + content(), non_neg_integer(), protocol()) -> 'ok'). -endif. %%---------------------------------------------------------------------------- -start(Sock, Channel, FrameMax) -> +start(Sock, Channel, FrameMax, Protocol) -> spawn(?MODULE, mainloop, [#wstate{sock = Sock, channel = Channel, - frame_max = FrameMax}]). - -start_link(Sock, Channel, FrameMax) -> - spawn_link(?MODULE, mainloop, [#wstate{sock = Sock, - channel = Channel, - frame_max = FrameMax}]). + frame_max = FrameMax, + protocol = Protocol}]). mainloop(State) -> receive @@ -85,35 +81,40 @@ mainloop(State) -> end. handle_message({send_command, MethodRecord}, - State = #wstate{sock = Sock, channel = Channel}) -> - ok = internal_send_command_async(Sock, Channel, MethodRecord), + State = #wstate{sock = Sock, channel = Channel, + protocol = Protocol}) -> + ok = internal_send_command_async(Sock, Channel, MethodRecord, Protocol), State; handle_message({send_command, MethodRecord, Content}, State = #wstate{sock = Sock, channel = Channel, - frame_max = FrameMax}) -> + frame_max = FrameMax, + protocol = Protocol}) -> ok = internal_send_command_async(Sock, Channel, MethodRecord, - Content, FrameMax), + Content, FrameMax, Protocol), State; handle_message({send_command_and_signal_back, MethodRecord, Parent}, - State = #wstate{sock = Sock, channel = Channel}) -> - ok = internal_send_command_async(Sock, Channel, MethodRecord), + State = #wstate{sock = Sock, channel = Channel, + protocol = Protocol}) -> + ok = internal_send_command_async(Sock, Channel, MethodRecord, Protocol), Parent ! rabbit_writer_send_command_signal, State; handle_message({send_command_and_signal_back, MethodRecord, Content, Parent}, State = #wstate{sock = Sock, channel = Channel, - frame_max = FrameMax}) -> + frame_max = FrameMax, + protocol = Protocol}) -> ok = internal_send_command_async(Sock, Channel, MethodRecord, - Content, FrameMax), + Content, FrameMax, Protocol), Parent ! rabbit_writer_send_command_signal, State; handle_message({send_command_and_notify, QPid, ChPid, MethodRecord, Content}, State = #wstate{sock = Sock, channel = Channel, - frame_max = FrameMax}) -> + frame_max = FrameMax, + protocol = Protocol}) -> ok = internal_send_command_async(Sock, Channel, MethodRecord, - Content, FrameMax), + Content, FrameMax, Protocol), rabbit_amqqueue:notify_sent(QPid, ChPid), State; handle_message({inet_reply, _, ok}, State) -> @@ -153,16 +154,17 @@ shutdown(W) -> %--------------------------------------------------------------------------- -assemble_frames(Channel, MethodRecord) -> +assemble_frames(Channel, MethodRecord, Protocol) -> ?LOGMESSAGE(out, Channel, MethodRecord, none), - rabbit_binary_generator:build_simple_method_frame(Channel, MethodRecord). + rabbit_binary_generator:build_simple_method_frame(Channel, MethodRecord, + Protocol). -assemble_frames(Channel, MethodRecord, Content, FrameMax) -> +assemble_frames(Channel, MethodRecord, Content, FrameMax, Protocol) -> ?LOGMESSAGE(out, Channel, MethodRecord, Content), MethodName = rabbit_misc:method_record_type(MethodRecord), true = rabbit_framing:method_has_content(MethodName), % assertion MethodFrame = rabbit_binary_generator:build_simple_method_frame( - Channel, MethodRecord), + Channel, MethodRecord, Protocol), ContentFrames = rabbit_binary_generator:build_simple_content_frames( Channel, Content, FrameMax), [MethodFrame | ContentFrames]. @@ -171,12 +173,13 @@ tcp_send(Sock, Data) -> rabbit_misc:throw_on_error(inet_error, fun () -> rabbit_net:send(Sock, Data) end). -internal_send_command(Sock, Channel, MethodRecord) -> - ok = tcp_send(Sock, assemble_frames(Channel, MethodRecord)). +internal_send_command(Sock, Channel, MethodRecord, Protocol) -> + ok = tcp_send(Sock, assemble_frames(Channel, MethodRecord, Protocol)). -internal_send_command(Sock, Channel, MethodRecord, Content, FrameMax) -> +internal_send_command(Sock, Channel, MethodRecord, Content, FrameMax, + Protocol) -> ok = tcp_send(Sock, assemble_frames(Channel, MethodRecord, - Content, FrameMax)). + 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 @@ -196,13 +199,14 @@ internal_send_command(Sock, Channel, MethodRecord, Content, FrameMax) -> %% Also note that the port has bounded buffers and port_command blocks %% when these are full. So the fact that we process the result %% asynchronously does not impact flow control. -internal_send_command_async(Sock, Channel, MethodRecord) -> - true = port_cmd(Sock, assemble_frames(Channel, MethodRecord)), +internal_send_command_async(Sock, Channel, MethodRecord, Protocol) -> + true = port_cmd(Sock, assemble_frames(Channel, MethodRecord, Protocol)), ok. -internal_send_command_async(Sock, Channel, MethodRecord, Content, FrameMax) -> +internal_send_command_async(Sock, Channel, MethodRecord, Content, FrameMax, + Protocol) -> true = port_cmd(Sock, assemble_frames(Channel, MethodRecord, - Content, FrameMax)), + Content, FrameMax, Protocol)), ok. port_cmd(Sock, Data) -> -- cgit v1.2.1 From d0e12bd0f6eeb2631637b7feb67cf8ef1bfa7c50 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 24 Jun 2010 13:01:40 +0100 Subject: This is clearly driving me insane. channel.tune-ok does not exist, even in 0-9. (And this was not caught by tests since our Java client seems happy with either version of open-ok, but we shouldn't count on that.) --- src/rabbit_framing_channel.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rabbit_framing_channel.erl b/src/rabbit_framing_channel.erl index 90856cdf..6c184c2c 100644 --- a/src/rabbit_framing_channel.erl +++ b/src/rabbit_framing_channel.erl @@ -82,12 +82,12 @@ mainloop(ChannelPid, Protocol) -> end, ?MODULE:mainloop(ChannelPid, Protocol). -%% Handle 0-8 version of channel.tune-ok. In 0-9-1 it gained a longstr +%% Handle 0-8 version of channel.open-ok. In 0-9-1 it gained a longstr %% "deprecated_channel_id". -decode_method_fields('channel.tune_ok', FieldsBin, protocol_08) -> +decode_method_fields('channel.open_ok', FieldsBin, protocol_08) -> Len = 0, rabbit_framing:decode_method_fields( - 'channel.tune_ok', <>); + 'channel.open_ok', <>); %% Handle 0-8 version of basic.consume. In 0-9-1 it gained a table %% "filter". decode_method_fields('basic.consume', FieldsBin, protocol_08) -> -- cgit v1.2.1 From 96a1984b44dbd71cfd0191d1e4dcca7f84d197a7 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 24 Jun 2010 13:17:53 +0100 Subject: Rename protocol constant. --- src/rabbit_binary_generator.erl | 4 ++-- src/rabbit_framing_channel.erl | 4 ++-- src/rabbit_reader.erl | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/rabbit_binary_generator.erl b/src/rabbit_binary_generator.erl index e29f9fcb..28f34e7c 100644 --- a/src/rabbit_binary_generator.erl +++ b/src/rabbit_binary_generator.erl @@ -78,9 +78,9 @@ build_simple_method_frame(ChannelInt, MethodRecord, Protocol) -> {ClassId, MethodId} = rabbit_framing:method_id(MethodName), create_frame(1, ChannelInt, [<>, MethodFields]). -adjust_close('connection.close', protocol_08) -> +adjust_close('connection.close', amqp_0_8) -> 'connection.close08'; -adjust_close('connection.close_ok', protocol_08) -> +adjust_close('connection.close_ok', amqp_0_8) -> 'connection.close08_ok'; adjust_close(MethodName, _Protocol) -> MethodName. diff --git a/src/rabbit_framing_channel.erl b/src/rabbit_framing_channel.erl index 6c184c2c..648a3fdd 100644 --- a/src/rabbit_framing_channel.erl +++ b/src/rabbit_framing_channel.erl @@ -84,13 +84,13 @@ mainloop(ChannelPid, Protocol) -> %% Handle 0-8 version of channel.open-ok. In 0-9-1 it gained a longstr %% "deprecated_channel_id". -decode_method_fields('channel.open_ok', FieldsBin, protocol_08) -> +decode_method_fields('channel.open_ok', FieldsBin, amqp_0_8) -> Len = 0, rabbit_framing:decode_method_fields( 'channel.open_ok', <>); %% Handle 0-8 version of basic.consume. In 0-9-1 it gained a table %% "filter". -decode_method_fields('basic.consume', FieldsBin, protocol_08) -> +decode_method_fields('basic.consume', FieldsBin, amqp_0_8) -> T = rabbit_binary_generator:generate_table([]), TLen = size(T), rabbit_framing:decode_method_fields( diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index e2d4880b..73884d99 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -523,9 +523,9 @@ analyze_frame(?FRAME_HEARTBEAT, <<>>, _Protocol) -> analyze_frame(_Type, _Body, _Protocol) -> error. -adjust_close('connection.close08', protocol_08) -> +adjust_close('connection.close08', amqp_0_8) -> 'connection.close'; -adjust_close('connection.close08_ok', protocol_08) -> +adjust_close('connection.close08_ok', amqp_0_8) -> 'connection.close_ok'; adjust_close(MethodName, _Protocol) -> MethodName. @@ -549,7 +549,7 @@ handle_input(handshake, <<"AMQP",1,1,ProtocolMajor,ProtocolMinor>>, case check_version({ProtocolMajor, ProtocolMinor}, {?PROTOCOL_VERSION_MAJOR, ?PROTOCOL_VERSION_MINOR}) of true -> - Protocol = protocol_08, + Protocol = amqp_0_8, ok = send_on_channel0( Sock, #'connection.start'{ -- cgit v1.2.1 From c66e8d1f66a0bdbba4a5584d62cd98eb6837b642 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 24 Jun 2010 13:36:42 +0100 Subject: Don't pull protocol version from the JSON. --- codegen.py | 2 -- include/rabbit.hrl | 1 + src/rabbit.erl | 4 ++-- src/rabbit_reader.erl | 10 ++++------ 4 files changed, 7 insertions(+), 10 deletions(-) diff --git a/codegen.py b/codegen.py index 0d6d9d56..467b0285 100644 --- a/codegen.py +++ b/codegen.py @@ -410,8 +410,6 @@ def genHrl(spec): methods = spec.allMethods() printFileHeader() - print "-define(PROTOCOL_VERSION_MAJOR, %d)." % (spec.major) - print "-define(PROTOCOL_VERSION_MINOR, %d)." % (spec.minor) print "-define(PROTOCOL_PORT, %d)." % (spec.port) for (c,v,cls) in spec.constants: diff --git a/include/rabbit.hrl b/include/rabbit.hrl index cff17e08..7e6ba8fc 100644 --- a/include/rabbit.hrl +++ b/include/rabbit.hrl @@ -181,6 +181,7 @@ -define(COPYRIGHT_MESSAGE, "Copyright (C) 2007-2010 LShift Ltd., Cohesive Financial Technologies LLC., and Rabbit Technologies Ltd."). -define(INFORMATION_MESSAGE, "Licensed under the MPL. See http://www.rabbitmq.com/"). +-define(PROTOCOL_VERSION, "AMQP 0-8"). -define(ERTS_MINIMUM, "5.6.3"). -define(MAX_WAIT, 16#ffffffff). diff --git a/src/rabbit.erl b/src/rabbit.erl index 6cf6d7d5..7df39987 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -424,9 +424,9 @@ print_banner() -> "| ~s +---+ |~n" "| |~n" "+-------------------+~n" - "AMQP ~p-~p~n~s~n~s~n~n", + "~s~n~s~n~s~n~n", [Product, string:right([$v|Version], ProductLen), - ?PROTOCOL_VERSION_MAJOR, ?PROTOCOL_VERSION_MINOR, + ?PROTOCOL_VERSION, ?COPYRIGHT_MESSAGE, ?INFORMATION_MESSAGE]), Settings = [{"node", node()}, {"app descriptor", app_location()}, diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 73884d99..98b4d647 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -547,14 +547,14 @@ handle_input({frame_payload, Type, Channel, PayloadSize}, PayloadAndMarker, Stat handle_input(handshake, <<"AMQP",1,1,ProtocolMajor,ProtocolMinor>>, State = #v1{sock = Sock, connection = Connection}) -> case check_version({ProtocolMajor, ProtocolMinor}, - {?PROTOCOL_VERSION_MAJOR, ?PROTOCOL_VERSION_MINOR}) of + {0, 8}) of true -> Protocol = amqp_0_8, ok = send_on_channel0( Sock, #'connection.start'{ - version_major = ?PROTOCOL_VERSION_MAJOR, - version_minor = ?PROTOCOL_VERSION_MINOR, + version_major = 0, + version_minor = 8, server_properties = server_properties(), mechanisms = <<"PLAIN AMQPLAIN">>, locales = <<"en_US">> }, @@ -570,9 +570,7 @@ handle_input(handshake, <<"AMQP",1,1,ProtocolMajor,ProtocolMinor>>, handle_input(handshake, Other, #v1{sock = Sock}) -> ok = inet_op(fun () -> rabbit_net:send( - Sock, <<"AMQP",1,1, - ?PROTOCOL_VERSION_MAJOR, - ?PROTOCOL_VERSION_MINOR>>) end), + Sock, <<"AMQP",1,1,0,8>>) end), throw({bad_header, Other}); handle_input(Callback, Data, _State) -> -- cgit v1.2.1 From 6ffed4950c8e99d33858b615dd4ea144fe6cdc61 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 24 Jun 2010 16:55:14 +0100 Subject: Codegen up two different versions of rabbit_framing and provide a dispatcher to choose. The codegen now builds header files that are the union of 0-8 and 0-9-1. --- .hgignore | 2 +- Makefile | 22 +++++---- codegen.py | 9 +++- src/rabbit_basic.erl | 8 +++- src/rabbit_binary_generator.erl | 14 ++---- src/rabbit_channel.erl | 6 +-- src/rabbit_framing.erl | 102 ++++++++++++++++++++++++++++++++++++++++ src/rabbit_framing_channel.erl | 22 ++------- src/rabbit_reader.erl | 26 ++++------ src/rabbit_tests.erl | 2 +- 10 files changed, 150 insertions(+), 63 deletions(-) create mode 100644 src/rabbit_framing.erl diff --git a/.hgignore b/.hgignore index 7b796b66..03b60914 100644 --- a/.hgignore +++ b/.hgignore @@ -11,7 +11,7 @@ syntax: regexp ^dist/ ^include/rabbit_framing\.hrl$ ^include/rabbit_framing_spec\.hrl$ -^src/rabbit_framing\.erl$ +^src/rabbit_framing_amqp.*\.erl$ ^src/.*\_usage.erl$ ^rabbit\.plt$ ^basic.plt$ diff --git a/Makefile b/Makefile index d715db74..102fe1fb 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ EBIN_DIR=ebin INCLUDE_DIR=include DOCS_DIR=docs INCLUDES=$(wildcard $(INCLUDE_DIR)/*.hrl) $(INCLUDE_DIR)/rabbit_framing.hrl $(INCLUDE_DIR)/rabbit_framing_spec.hrl -SOURCES=$(wildcard $(SOURCE_DIR)/*.erl) $(SOURCE_DIR)/rabbit_framing.erl $(USAGES_ERL) +SOURCES=$(wildcard $(SOURCE_DIR)/*.erl) $(SOURCE_DIR)/rabbit_framing_amqp_0_9_1.erl $(SOURCE_DIR)/rabbit_framing_amqp_0_8.erl $(USAGES_ERL) BEAM_TARGETS=$(patsubst $(SOURCE_DIR)/%.erl, $(EBIN_DIR)/%.beam, $(SOURCES)) TARGETS=$(EBIN_DIR)/rabbit.app $(INCLUDE_DIR)/rabbit_framing.hrl $(INCLUDE_DIR)/rabbit_framing_spec.hrl $(BEAM_TARGETS) WEB_URL=http://stage.rabbitmq.com/ @@ -56,7 +56,8 @@ TARGET_SRC_DIR=dist/$(TARBALL_NAME) SIBLING_CODEGEN_DIR=../rabbitmq-codegen/ AMQP_CODEGEN_DIR=$(shell [ -d $(SIBLING_CODEGEN_DIR) ] && echo $(SIBLING_CODEGEN_DIR) || echo codegen) -AMQP_SPEC_JSON_FILES=$(AMQP_CODEGEN_DIR)/amqp-rabbitmq.json +AMQP_SPEC_JSON_FILES_0_9_1=$(AMQP_CODEGEN_DIR)/amqp-rabbitmq.json +AMQP_SPEC_JSON_FILES_0_8=$(AMQP_CODEGEN_DIR)/amqp-0.8.json $(AMQP_CODEGEN_DIR)/rabbitmq-0.8-extensions.json ERL_CALL=erl_call -sname $(RABBITMQ_NODENAME) -e @@ -87,14 +88,17 @@ $(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) - $(PYTHON) codegen.py header $(AMQP_SPEC_JSON_FILES) $@ +$(INCLUDE_DIR)/rabbit_framing.hrl: codegen.py $(AMQP_CODEGEN_DIR)/amqp_codegen.py $(AMQP_SPEC_JSON_FILES_0_9_1) + $(PYTHON) codegen.py header $(AMQP_SPEC_JSON_FILES_0_9_1) $(AMQP_SPEC_JSON_FILES_0_8) $@ -$(INCLUDE_DIR)/rabbit_framing_spec.hrl: codegen.py $(AMQP_CODEGEN_DIR)/amqp_codegen.py $(AMQP_SPEC_JSON_FILES) - $(PYTHON) codegen.py spec $(AMQP_SPEC_JSON_FILES) $@ +$(INCLUDE_DIR)/rabbit_framing_spec.hrl: codegen.py $(AMQP_CODEGEN_DIR)/amqp_codegen.py $(AMQP_SPEC_JSON_FILES_0_9_1) + $(PYTHON) codegen.py spec $(AMQP_SPEC_JSON_FILES_0_9_1) $(AMQP_SPEC_JSON_FILES_0_8) $@ -$(SOURCE_DIR)/rabbit_framing.erl: codegen.py $(AMQP_CODEGEN_DIR)/amqp_codegen.py $(AMQP_SPEC_JSON_FILES) - $(PYTHON) codegen.py body $(AMQP_SPEC_JSON_FILES) $@ +$(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) + $(PYTHON) codegen.py body $(AMQP_SPEC_JSON_FILES_0_8) $@ dialyze: $(BEAM_TARGETS) $(BASIC_PLT) $(ERL_EBIN) -eval \ @@ -119,7 +123,7 @@ $(BASIC_PLT): $(BEAM_TARGETS) clean: rm -f $(EBIN_DIR)/*.beam rm -f $(EBIN_DIR)/rabbit.app $(EBIN_DIR)/rabbit.boot $(EBIN_DIR)/rabbit.script $(EBIN_DIR)/rabbit.rel - rm -f $(INCLUDE_DIR)/rabbit_framing.hrl $(INCLUDE_DIR)/rabbit_framing_spec.hrl $(SOURCE_DIR)/rabbit_framing.erl codegen.pyc + rm -f $(INCLUDE_DIR)/rabbit_framing*.hrl $(INCLUDE_DIR)/rabbit_framing_spec.hrl $(SOURCE_DIR)/rabbit_framing_amqp_*.erl codegen.pyc rm -f $(DOCS_DIR)/*.[0-9].gz $(DOCS_DIR)/*.man.xml $(DOCS_DIR)/*.erl $(USAGES_ERL) rm -f $(RABBIT_PLT) rm -f $(DEPS_FILE) diff --git a/codegen.py b/codegen.py index 467b0285..9596f5b1 100644 --- a/codegen.py +++ b/codegen.py @@ -315,8 +315,13 @@ def genErl(spec): methods = spec.allMethods() printFileHeader() - print """-module(rabbit_framing). --include("rabbit_framing.hrl"). + module = "rabbit_framing_amqp_%d_%d" % (spec.major, spec.minor) + if spec.revision != '0': + module = "%s_%d" % (module, spec.revision) + if module == "rabbit_framing_amqp_8_0": + module = "rabbit_framing_amqp_0_8" + print "-module(%s)." % module + print """-include("rabbit_framing.hrl"). -export([lookup_method_name/1]). diff --git a/src/rabbit_basic.erl b/src/rabbit_basic.erl index 4ab7a2a0..5e2d7d9a 100644 --- a/src/rabbit_basic.erl +++ b/src/rabbit_basic.erl @@ -80,7 +80,9 @@ delivery(Mandatory, Immediate, Txn, Message) -> sender = self(), message = Message}. build_content(Properties, BodyBin) -> - {ClassId, _MethodId} = rabbit_framing:method_id('basic.publish'), + %% TODO - is this ever used? If so remove hard coded amqp_0_9_1 + {ClassId, _MethodId} = + rabbit_framing:method_id('basic.publish', amqp_0_9_1), #content{class_id = ClassId, properties = Properties, properties_bin = none, @@ -91,7 +93,9 @@ from_content(Content) -> properties = Props, payload_fragments_rev = FragmentsRev} = rabbit_binary_parser:ensure_content_decoded(Content), - {ClassId, _MethodId} = rabbit_framing:method_id('basic.publish'), + %% TODO - is this ever used? If so remove hard coded amqp_0_9_1 + {ClassId, _MethodId} = + rabbit_framing:method_id('basic.publish', amqp_0_9_1), {Props, list_to_binary(lists:reverse(FragmentsRev))}. message(ExchangeName, RoutingKeyBin, RawProperties, BodyBin) -> diff --git a/src/rabbit_binary_generator.erl b/src/rabbit_binary_generator.erl index 28f34e7c..04251d11 100644 --- a/src/rabbit_binary_generator.erl +++ b/src/rabbit_binary_generator.erl @@ -72,19 +72,11 @@ %%---------------------------------------------------------------------------- build_simple_method_frame(ChannelInt, MethodRecord, Protocol) -> - MethodFields = rabbit_framing:encode_method_fields(MethodRecord), - MethodName = adjust_close(rabbit_misc:method_record_type(MethodRecord), - Protocol), - {ClassId, MethodId} = rabbit_framing:method_id(MethodName), + MethodFields = rabbit_framing:encode_method_fields(MethodRecord, Protocol), + MethodName = rabbit_misc:method_record_type(MethodRecord), + {ClassId, MethodId} = rabbit_framing:method_id(MethodName, Protocol), create_frame(1, ChannelInt, [<>, MethodFields]). -adjust_close('connection.close', amqp_0_8) -> - 'connection.close08'; -adjust_close('connection.close_ok', amqp_0_8) -> - 'connection.close08_ok'; -adjust_close(MethodName, _Protocol) -> - MethodName. - build_simple_content_frames(ChannelInt, #content{class_id = ClassId, properties = ContentProperties, diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 7c0b94d4..d337df29 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -499,7 +499,7 @@ handle_method(#'basic.get'{queue = QueueNameBin, Content), {noreply, State1#ch{next_tag = DeliveryTag + 1}}; empty -> - {reply, #'basic.get_empty'{deprecated_cluster_id = <<>>}, State} + {reply, #'basic.get_empty'{cluster_id = <<>>}, State} end; handle_method(#'basic.consume'{queue = QueueNameBin, @@ -656,8 +656,8 @@ handle_method(#'exchange.declare'{exchange = ExchangeNameBin, type = TypeNameBin, passive = false, durable = Durable, - deprecated_auto_delete = AutoDelete, - deprecated_internal = false, + auto_delete = AutoDelete, + internal = false, nowait = NoWait, arguments = Args}, _, State = #ch{ virtual_host = VHostPath }) -> diff --git a/src/rabbit_framing.erl b/src/rabbit_framing.erl new file mode 100644 index 00000000..2d4d1ce4 --- /dev/null +++ b/src/rabbit_framing.erl @@ -0,0 +1,102 @@ +%% 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_framing). +-include("rabbit.hrl"). +-include("rabbit_framing.hrl"). + + +-export([encode_method_fields/2]). +-export([decode_method_fields/3]). +-export([lookup_method_name/2]). +-export([method_id/2]). + +-export([method_has_content/1]). +-export([is_method_synchronous/1]). +-export([method_record/1]). +-export([method_fieldnames/1]). +-export([decode_properties/2]). +-export([encode_properties/1]). +-export([lookup_amqp_exception/1]). +-export([amqp_exception/1]). + +%% Method signatures +-ifdef(use_specs). +-spec(encode_method_fields/2 :: (amqp_method_record(), protocol()) -> binary()). +-spec(decode_method_fields/3 :: (amqp_method_name(), binary(), protocol()) -> + amqp_method_record()). +-spec(lookup_method_name/2 :: (amqp_method(), protocol()) -> + amqp_method_name()). +-spec(method_id/2 :: (amqp_method_name(), protocol()) -> amqp_method()). + +-spec(method_has_content/1 :: (amqp_method_name()) -> boolean()). +-spec(is_method_synchronous/1 :: (amqp_method_record()) -> boolean()). +-spec(method_record/1 :: (amqp_method_name()) -> amqp_method_record()). +-spec(method_fieldnames/1 :: (amqp_method_name()) -> + [amqp_method_field_name()]). +-spec(decode_properties/2 :: (non_neg_integer(), binary()) -> + amqp_property_record()). +-spec(encode_properties/1 :: (amqp_method_record()) -> binary()). +-spec(lookup_amqp_exception/1 :: + (amqp_exception()) -> {boolean(), amqp_exception_code(), binary()}). +-spec(amqp_exception/1 :: (amqp_exception_code()) -> amqp_exception()). +-endif. % use_specs + +encode_method_fields(MethodRecord, amqp_0_9_1) -> + rabbit_framing_amqp_0_9_1:encode_method_fields(MethodRecord); +encode_method_fields(MethodRecord, amqp_0_8) -> + rabbit_framing_amqp_0_8:encode_method_fields(MethodRecord). + +decode_method_fields(MethodName, FieldsBin, amqp_0_9_1) -> + rabbit_framing_amqp_0_9_1:decode_method_fields(MethodName, FieldsBin); +decode_method_fields(MethodName, FieldsBin, amqp_0_8) -> + rabbit_framing_amqp_0_8:decode_method_fields(MethodName, FieldsBin). + +lookup_method_name(ClassMethod, amqp_0_9_1) -> + rabbit_framing_amqp_0_9_1:lookup_method_name(ClassMethod); +lookup_method_name(ClassMethod, amqp_0_8) -> + rabbit_framing_amqp_0_8:lookup_method_name(ClassMethod). + +method_id(MethodName, amqp_0_9_1) -> + rabbit_framing_amqp_0_9_1:method_id(MethodName); +method_id(MethodName, amqp_0_8) -> + rabbit_framing_amqp_0_8:method_id(MethodName). + + + +%% These ones don't make any difference, let's just use 0-9-1. +method_has_content(X) -> rabbit_framing_amqp_0_9_1:method_has_content(X). +method_record(X) -> rabbit_framing_amqp_0_9_1:method_record(X). +method_fieldnames(X) -> rabbit_framing_amqp_0_9_1:method_fieldnames(X). +encode_properties(X) -> rabbit_framing_amqp_0_9_1:encode_properties(X). +amqp_exception(X) -> rabbit_framing_amqp_0_9_1:amqp_exception(X). +lookup_amqp_exception(X) -> rabbit_framing_amqp_0_9_1:lookup_amqp_exception(X). +is_method_synchronous(X) -> rabbit_framing_amqp_0_9_1:is_method_synchronous(X). +decode_properties(X, Y) -> rabbit_framing_amqp_0_9_1:decode_properties(X, Y). diff --git a/src/rabbit_framing_channel.erl b/src/rabbit_framing_channel.erl index 648a3fdd..c30cf451 100644 --- a/src/rabbit_framing_channel.erl +++ b/src/rabbit_framing_channel.erl @@ -74,7 +74,8 @@ read_frame(ChannelPid) -> mainloop(ChannelPid, Protocol) -> {method, MethodName, FieldsBin} = read_frame(ChannelPid), - Method = decode_method_fields(MethodName, FieldsBin, Protocol), + Method = rabbit_framing:decode_method_fields(MethodName, FieldsBin, + Protocol), case rabbit_framing:method_has_content(MethodName) of true -> rabbit_channel:do(ChannelPid, Method, collect_content(ChannelPid, MethodName)); @@ -82,24 +83,9 @@ mainloop(ChannelPid, Protocol) -> end, ?MODULE:mainloop(ChannelPid, Protocol). -%% Handle 0-8 version of channel.open-ok. In 0-9-1 it gained a longstr -%% "deprecated_channel_id". -decode_method_fields('channel.open_ok', FieldsBin, amqp_0_8) -> - Len = 0, - rabbit_framing:decode_method_fields( - 'channel.open_ok', <>); -%% Handle 0-8 version of basic.consume. In 0-9-1 it gained a table -%% "filter". -decode_method_fields('basic.consume', FieldsBin, amqp_0_8) -> - T = rabbit_binary_generator:generate_table([]), - TLen = size(T), - rabbit_framing:decode_method_fields( - 'basic.consume', <>); -decode_method_fields(MethodName, FieldsBin, _Protocol) -> - rabbit_framing:decode_method_fields(MethodName, FieldsBin). - collect_content(ChannelPid, MethodName) -> - {ClassId, _MethodId} = rabbit_framing:method_id(MethodName), + %% Protocol does not matter as we only want the class ID to match + {ClassId, _MethodId} = rabbit_framing:method_id(MethodName, amqp_0_9_1), case read_frame(ChannelPid) of {content_header, HeaderClassId, 0, BodySize, PropertiesBin} -> if HeaderClassId == ClassId -> diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 98b4d647..c324d008 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -508,9 +508,7 @@ handle_frame(Type, Channel, Payload, analyze_frame(?FRAME_METHOD, <>, Protocol) -> - {method, adjust_close( - rabbit_framing:lookup_method_name({ClassId, MethodId}), - Protocol), + {method, rabbit_framing:lookup_method_name({ClassId, MethodId}, Protocol), MethodFields}; analyze_frame(?FRAME_HEADER, <>, @@ -523,13 +521,6 @@ analyze_frame(?FRAME_HEARTBEAT, <<>>, _Protocol) -> analyze_frame(_Type, _Body, _Protocol) -> error. -adjust_close('connection.close08', amqp_0_8) -> - 'connection.close'; -adjust_close('connection.close08_ok', amqp_0_8) -> - 'connection.close_ok'; -adjust_close(MethodName, _Protocol) -> - MethodName. - handle_input(frame_header, <>, State) -> %%?LOGDEBUG("Got frame header: ~p/~p/~p~n", [Type, Channel, PayloadSize]), {State, {frame_payload, Type, Channel, PayloadSize}, PayloadSize + 1}; @@ -589,10 +580,11 @@ check_version(ClientVersion, ServerVersion) -> %%-------------------------------------------------------------------------- -handle_method0(MethodName, FieldsBin, State) -> +handle_method0(MethodName, FieldsBin, + State = #v1{connection = #connection{protocol = Protocol}}) -> try handle_method0(rabbit_framing:decode_method_fields( - MethodName, FieldsBin), + MethodName, FieldsBin, Protocol), State) catch exit:Reason -> CompleteReason = case Reason of @@ -653,7 +645,7 @@ handle_method0(#'connection.open'{virtual_host = VHostPath}, NewConnection = Connection#connection{vhost = VHostPath}, ok = send_on_channel0( Sock, - #'connection.open_ok'{deprecated_known_hosts = <<>>}, + #'connection.open_ok'{known_hosts = <<>>}, Protocol), State#v1{connection_state = running, connection = NewConnection}; @@ -755,7 +747,8 @@ handle_exception(State = #v1{connection_state = CS}, Channel, Reason) -> send_exception(State = #v1{connection = #connection{protocol = Protocol}}, Channel, Reason) -> - {ShouldClose, CloseChannel, CloseMethod} = map_exception(Channel, Reason), + {ShouldClose, CloseChannel, CloseMethod} = + map_exception(Channel, Reason, Protocol), NewState = case ShouldClose of true -> terminate_channels(), close_connection(State); @@ -765,14 +758,15 @@ send_exception(State = #v1{connection = #connection{protocol = Protocol}}, NewState#v1.sock, CloseChannel, CloseMethod, Protocol), NewState. -map_exception(Channel, Reason) -> +map_exception(Channel, Reason, Protocol) -> {SuggestedClose, ReplyCode, ReplyText, FailedMethod} = lookup_amqp_exception(Reason), ShouldClose = SuggestedClose or (Channel == 0), {ClassId, MethodId} = case FailedMethod of {_, _} -> FailedMethod; none -> {0, 0}; - _ -> rabbit_framing:method_id(FailedMethod) + _ -> rabbit_framing:method_id(FailedMethod, + Protocol) end, {CloseChannel, CloseMethod} = case ShouldClose of diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index ecc2613d..fc7beedd 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -913,7 +913,7 @@ test_memory_pressure() -> %% if we publish at this point, the channel should die Content = #content{class_id = element(1, rabbit_framing:method_id( - 'basic.publish')), + 'basic.publish', amqp_0_9_1)), properties = none, properties_bin = <<>>, payload_fragments_rev = []}, -- cgit v1.2.1 From b35cfe64c8274c65ef231408479b2ee0a13912b7 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 24 Jun 2010 16:58:11 +0100 Subject: Remove a * that crept in. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 102fe1fb..42712982 100644 --- a/Makefile +++ b/Makefile @@ -123,7 +123,7 @@ $(BASIC_PLT): $(BEAM_TARGETS) clean: rm -f $(EBIN_DIR)/*.beam rm -f $(EBIN_DIR)/rabbit.app $(EBIN_DIR)/rabbit.boot $(EBIN_DIR)/rabbit.script $(EBIN_DIR)/rabbit.rel - rm -f $(INCLUDE_DIR)/rabbit_framing*.hrl $(INCLUDE_DIR)/rabbit_framing_spec.hrl $(SOURCE_DIR)/rabbit_framing_amqp_*.erl codegen.pyc + rm -f $(INCLUDE_DIR)/rabbit_framing.hrl $(INCLUDE_DIR)/rabbit_framing_spec.hrl $(SOURCE_DIR)/rabbit_framing_amqp_*.erl codegen.pyc rm -f $(DOCS_DIR)/*.[0-9].gz $(DOCS_DIR)/*.man.xml $(DOCS_DIR)/*.erl $(USAGES_ERL) rm -f $(RABBIT_PLT) rm -f $(DEPS_FILE) -- cgit v1.2.1 From 4e4a1a6189c05fd1eaeb195e131b6611f5897f25 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 24 Jun 2010 17:27:38 +0100 Subject: This is used, by the Erlang client. --- src/rabbit_basic.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_basic.erl b/src/rabbit_basic.erl index 5e2d7d9a..e277d5b7 100644 --- a/src/rabbit_basic.erl +++ b/src/rabbit_basic.erl @@ -80,7 +80,7 @@ delivery(Mandatory, Immediate, Txn, Message) -> sender = self(), message = Message}. build_content(Properties, BodyBin) -> - %% TODO - is this ever used? If so remove hard coded amqp_0_9_1 + %% basic.publish hasn't changed so we can just hard-code amqp_0_9_1 {ClassId, _MethodId} = rabbit_framing:method_id('basic.publish', amqp_0_9_1), #content{class_id = ClassId, @@ -93,7 +93,7 @@ from_content(Content) -> properties = Props, payload_fragments_rev = FragmentsRev} = rabbit_binary_parser:ensure_content_decoded(Content), - %% TODO - is this ever used? If so remove hard coded amqp_0_9_1 + %% basic.publish hasn't changed so we can just hard-code amqp_0_9_1 {ClassId, _MethodId} = rabbit_framing:method_id('basic.publish', amqp_0_9_1), {Props, list_to_binary(lists:reverse(FragmentsRev))}. -- cgit v1.2.1 From e549da034f96538d91a3afdc740d1bb444496971 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 24 Jun 2010 17:44:02 +0100 Subject: Don't specify a value for cluster_id and known_hosts, they already have a default of "". --- src/rabbit_channel.erl | 2 +- src/rabbit_reader.erl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index d337df29..cd4bfc12 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -499,7 +499,7 @@ handle_method(#'basic.get'{queue = QueueNameBin, Content), {noreply, State1#ch{next_tag = DeliveryTag + 1}}; empty -> - {reply, #'basic.get_empty'{cluster_id = <<>>}, State} + {reply, #'basic.get_empty'{}, State} end; handle_method(#'basic.consume'{queue = QueueNameBin, diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index c324d008..7c90d980 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -645,7 +645,7 @@ handle_method0(#'connection.open'{virtual_host = VHostPath}, NewConnection = Connection#connection{vhost = VHostPath}, ok = send_on_channel0( Sock, - #'connection.open_ok'{known_hosts = <<>>}, + #'connection.open_ok'{}, Protocol), State#v1{connection_state = running, connection = NewConnection}; -- cgit v1.2.1 From 92f0982b01eed7920c52529b96723f3045422c5c Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 25 Jun 2010 12:00:48 +0100 Subject: Replace some stuff I stripped out that's used by the Erlang client. --- src/rabbit_reader.erl | 2 ++ src/rabbit_writer.erl | 10 +++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 7c90d980..24820496 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -41,6 +41,8 @@ -export([server_properties/0]). +-export([analyze_frame/3]). + -import(gen_tcp). -import(fprof). -import(inet). diff --git a/src/rabbit_writer.erl b/src/rabbit_writer.erl index 375e540e..403120f1 100644 --- a/src/rabbit_writer.erl +++ b/src/rabbit_writer.erl @@ -33,7 +33,7 @@ -include("rabbit.hrl"). -include("rabbit_framing.hrl"). --export([start/4, shutdown/1, mainloop/1]). +-export([start/4, start_link/4, shutdown/1, mainloop/1]). -export([send_command/2, send_command/3, send_command_and_signal_back/3, send_command_and_signal_back/4, send_command_and_notify/5]). -export([internal_send_command/4, internal_send_command/6]). @@ -50,6 +50,8 @@ -spec(start/4 :: (socket(), channel_number(), non_neg_integer(), protocol()) -> pid()). +-spec(start_link/4 :: + (socket(), channel_number(), non_neg_integer(), protocol()) -> pid()). -spec(send_command/2 :: (pid(), amqp_method_record()) -> 'ok'). -spec(send_command/3 :: (pid(), amqp_method_record(), content()) -> 'ok'). -spec(send_command_and_signal_back/3 :: (pid(), amqp_method(), pid()) -> 'ok'). @@ -73,6 +75,12 @@ start(Sock, Channel, FrameMax, Protocol) -> frame_max = FrameMax, protocol = Protocol}]). +start_link(Sock, Channel, FrameMax, Protocol) -> + spawn_link(?MODULE, mainloop, [#wstate{sock = Sock, + channel = Channel, + frame_max = FrameMax, + protocol = Protocol}]). + mainloop(State) -> receive Message -> ?MODULE:mainloop(handle_message(Message, State)) -- cgit v1.2.1 From ac615de9390e0bc9ee698729519d9a7b8eb1813b Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 25 Jun 2010 14:11:30 +0100 Subject: Multi-protocol broker. Let joy be unconfined. The resultant broker passes the entire 0-8 test suite on default. Unfortunately we can't run the full 0-9-1 test suite as that ends up trying to rebuild part of the broker which fails due to rabbitmq-codegen being on the wrong branch. However the Java functional tests all pass bar one; the exception is UnexpectedFrames.testMissingHeader which fails due to different error codes used on default. This will be fixed by bug 22886. --- include/rabbit.hrl | 2 +- src/rabbit_reader.erl | 80 +++++++++++++++++++++++++++++---------------------- 2 files changed, 47 insertions(+), 35 deletions(-) diff --git a/include/rabbit.hrl b/include/rabbit.hrl index 7e6ba8fc..4174110f 100644 --- a/include/rabbit.hrl +++ b/include/rabbit.hrl @@ -181,7 +181,7 @@ -define(COPYRIGHT_MESSAGE, "Copyright (C) 2007-2010 LShift Ltd., Cohesive Financial Technologies LLC., and Rabbit Technologies Ltd."). -define(INFORMATION_MESSAGE, "Licensed under the MPL. See http://www.rabbitmq.com/"). --define(PROTOCOL_VERSION, "AMQP 0-8"). +-define(PROTOCOL_VERSION, "AMQP 0-9-1 / 0-9 / 0-8"). -define(ERTS_MINIMUM, "5.6.3"). -define(MAX_WAIT, 16#ffffffff). diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 1ad80a00..1dab344e 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -550,48 +550,60 @@ handle_input({frame_payload, Type, Channel, PayloadSize}, PayloadAndMarker, Stat throw({bad_payload, PayloadAndMarker}) end; -handle_input(handshake, <<"AMQP",1,1,ProtocolMajor,ProtocolMinor>>, - State = #v1{sock = Sock, connection = Connection}) -> - case check_version({ProtocolMajor, ProtocolMinor}, - {0, 8}) of - true -> - Protocol = amqp_0_8, - ok = send_on_channel0( - Sock, - #'connection.start'{ - version_major = 0, - version_minor = 8, - server_properties = server_properties(), - mechanisms = <<"PLAIN AMQPLAIN">>, - locales = <<"en_US">> }, - Protocol), - {State#v1{connection = Connection#connection{ - timeout_sec = ?NORMAL_TIMEOUT, - protocol = Protocol}, - connection_state = starting}, - frame_header, 7}; - false -> - throw({bad_version, ProtocolMajor, ProtocolMinor}) - end; +%% The two rules pertaining to version negotiation: +%% +%% * If the server cannot support the protocol specified in the +%% protocol header, it MUST respond with a valid protocol header and +%% then close the socket connection. +%% +%% * The server MUST provide a protocol version that is lower than or +%% equal to that requested by the client in the protocol header. +%% +%% We support 0-9-1, 0-9 and 0-8, so by the first rule, we must close +%% the connection if we're sent anything else. Then, we must send +%% that version in the Connection.start method. +handle_input(handshake, <<"AMQP",0,0,9,1>>, State) -> + protocol_negotiate(0, 9, 1, State); + +handle_input(handshake, <<"AMQP",1,1,0,9>>, State) -> + protocol_negotiate(0, 9, 0, State); + +%% the 0-8 spec, confusingly, defines the version as 8-0 +handle_input(handshake, <<"AMQP",1,1,8,0>>, State) -> + protocol_negotiate(0, 8, 0, State); handle_input(handshake, Other, #v1{sock = Sock}) -> + rabbit_log:warning("Received unsupported protocol header ~w", [Other]), ok = inet_op(fun () -> rabbit_net:send( - Sock, <<"AMQP",1,1,0,8>>) end), + Sock, <<"AMQP",0,0,9,1>>) end), throw({bad_header, Other}); handle_input(Callback, Data, _State) -> throw({bad_input, Callback, Data}). -%% the 0-8 spec, confusingly, defines the version as 8-0 -adjust_version({8,0}) -> {0,8}; -adjust_version(Version) -> Version. -check_version(ClientVersion, ServerVersion) -> - {ClientMajor, ClientMinor} = adjust_version(ClientVersion), - {ServerMajor, ServerMinor} = adjust_version(ServerVersion), - ClientMajor > ServerMajor - orelse - (ClientMajor == ServerMajor andalso - ClientMinor >= ServerMinor). +%% Offer a protocol version to the client. Connection.start only +%% includes a major and minor version number, Luckily 0-9 and 0-9-1 +%% are similar enough that clients will be happy with either. +protocol_negotiate(ProtocolMajor, ProtocolMinor, _ProtocolRevision, + State = #v1{sock = Sock, connection = Connection}) -> + Protocol = case ProtocolMinor of + 8 -> amqp_0_8; + _ -> amqp_0_9_1 + end, + ok = send_on_channel0( + Sock, + #'connection.start'{ + version_major = ProtocolMajor, + version_minor = ProtocolMinor, + server_properties = server_properties(), + mechanisms = <<"PLAIN AMQPLAIN">>, + locales = <<"en_US">> }, + Protocol), + {State#v1{connection = Connection#connection{ + timeout_sec = ?NORMAL_TIMEOUT, + protocol = Protocol}, + connection_state = starting}, + frame_header, 7}. %%-------------------------------------------------------------------------- -- cgit v1.2.1 From c0f8df332c43a04615834bb45e843aae9f1347bf Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 25 Jun 2010 14:29:41 +0100 Subject: Show protocol in rabbitmqctl list_connections. I *think* this is interesting enough to put in the default list of info items. --- docs/rabbitmqctl.1.xml | 6 +++++- src/rabbit_control.erl | 3 ++- src/rabbit_reader.erl | 4 +++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/docs/rabbitmqctl.1.xml b/docs/rabbitmqctl.1.xml index a2038cf0..8223a690 100644 --- a/docs/rabbitmqctl.1.xml +++ b/docs/rabbitmqctl.1.xml @@ -857,6 +857,10 @@ Connection state (one of [starting, tuning, opening, running, closing, closed]). + + protocol + Version of the AMQP protocol in use (currently one of amqp_0_9_1 or amqp_0_8). Note that if a client requests an AMQP 0-9 connection, we treat it as AMQP 0-9-1. + channels Number of channels using the connection. @@ -905,7 +909,7 @@ If no connectioninfoitems are specified then user, peer - address, peer port and connection state are displayed. + address, peer port, connection state and protocol are displayed. diff --git a/src/rabbit_control.erl b/src/rabbit_control.erl index 323d4d2f..042f8284 100644 --- a/src/rabbit_control.erl +++ b/src/rabbit_control.erl @@ -240,7 +240,8 @@ action(list_bindings, Node, Args, Inform) -> action(list_connections, Node, Args, Inform) -> Inform("Listing connections", []), - ArgAtoms = default_if_empty(Args, [user, peer_address, peer_port, state]), + ArgAtoms = default_if_empty(Args, [user, peer_address, peer_port, state, + protocol]), display_info_list(rpc_call(Node, rabbit_networking, connection_info_all, [ArgAtoms]), ArgAtoms); diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 1dab344e..eba738f3 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -61,7 +61,7 @@ queue_collector}). -define(INFO_KEYS, - [pid, address, port, peer_address, peer_port, + [pid, address, port, peer_address, peer_port, protocol, recv_oct, recv_cnt, send_oct, send_cnt, send_pend, state, channels, user, vhost, timeout, frame_max, client_properties]). @@ -727,6 +727,8 @@ i(peer_address, #v1{sock = Sock}) -> i(peer_port, #v1{sock = Sock}) -> {ok, {_, P}} = rabbit_net:peername(Sock), P; +i(protocol, #v1{connection = #connection{protocol = Protocol}}) -> + Protocol; i(SockStat, #v1{sock = Sock}) when SockStat =:= recv_oct; SockStat =:= recv_cnt; SockStat =:= send_oct; -- cgit v1.2.1 From eb9af2acfd643aec80fbe81bc7c7ac53ae4c0534 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 1 Jul 2010 15:41:01 +0100 Subject: Protocol is not actually very interesting; take it out of the default list of connection parameters. --- docs/rabbitmqctl.1.xml | 2 +- src/rabbit_control.erl | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/rabbitmqctl.1.xml b/docs/rabbitmqctl.1.xml index 8223a690..6e374dfb 100644 --- a/docs/rabbitmqctl.1.xml +++ b/docs/rabbitmqctl.1.xml @@ -909,7 +909,7 @@ If no connectioninfoitems are specified then user, peer - address, peer port, connection state and protocol are displayed. + address, peer port and connection state are displayed. diff --git a/src/rabbit_control.erl b/src/rabbit_control.erl index 042f8284..323d4d2f 100644 --- a/src/rabbit_control.erl +++ b/src/rabbit_control.erl @@ -240,8 +240,7 @@ action(list_bindings, Node, Args, Inform) -> action(list_connections, Node, Args, Inform) -> Inform("Listing connections", []), - ArgAtoms = default_if_empty(Args, [user, peer_address, peer_port, state, - protocol]), + ArgAtoms = default_if_empty(Args, [user, peer_address, peer_port, state]), display_info_list(rpc_call(Node, rabbit_networking, connection_info_all, [ArgAtoms]), ArgAtoms); -- cgit v1.2.1 From 74807a366b3cb1056e6366b95e92a15850da6460 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 1 Jul 2010 16:42:10 +0100 Subject: Refactor connection negotiation a bit. --- src/rabbit_reader.erl | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index eba738f3..a9862367 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -558,25 +558,21 @@ handle_input({frame_payload, Type, Channel, PayloadSize}, PayloadAndMarker, Stat %% %% * The server MUST provide a protocol version that is lower than or %% equal to that requested by the client in the protocol header. -%% -%% We support 0-9-1, 0-9 and 0-8, so by the first rule, we must close -%% the connection if we're sent anything else. Then, we must send -%% that version in the Connection.start method. -handle_input(handshake, <<"AMQP",0,0,9,1>>, State) -> - protocol_negotiate(0, 9, 1, State); +handle_input(handshake, <<"AMQP", 0, 0, 9, 1>>, State) -> + start_connection({0, 9, 1}, amqp_0_9_1, State); -handle_input(handshake, <<"AMQP",1,1,0,9>>, State) -> - protocol_negotiate(0, 9, 0, State); +handle_input(handshake, <<"AMQP", 1, 1, 0, 9>>, State) -> + start_connection({0, 9, 0}, amqp_0_9_1, State); %% the 0-8 spec, confusingly, defines the version as 8-0 -handle_input(handshake, <<"AMQP",1,1,8,0>>, State) -> - protocol_negotiate(0, 8, 0, State); +handle_input(handshake, <<"AMQP", 1, 1, 8, 0>>, State) -> + start_connection({0, 8, 0}, amqp_0_8, State); + +handle_input(handshake, <<"AMQP", A, B, C, D>>, #v1{sock = Sock}) -> + refuse_connection(Sock, {bad_version, A, B, C, D}); handle_input(handshake, Other, #v1{sock = Sock}) -> - rabbit_log:warning("Received unsupported protocol header ~w", [Other]), - ok = inet_op(fun () -> rabbit_net:send( - Sock, <<"AMQP",0,0,9,1>>) end), - throw({bad_header, Other}); + refuse_connection(Sock, {bad_header, Other}); handle_input(Callback, Data, _State) -> throw({bad_input, Callback, Data}). @@ -584,12 +580,8 @@ handle_input(Callback, Data, _State) -> %% Offer a protocol version to the client. Connection.start only %% includes a major and minor version number, Luckily 0-9 and 0-9-1 %% are similar enough that clients will be happy with either. -protocol_negotiate(ProtocolMajor, ProtocolMinor, _ProtocolRevision, +start_connection({ProtocolMajor, ProtocolMinor, _ProtocolRevision}, Protocol, State = #v1{sock = Sock, connection = Connection}) -> - Protocol = case ProtocolMinor of - 8 -> amqp_0_8; - _ -> amqp_0_9_1 - end, ok = send_on_channel0( Sock, #'connection.start'{ @@ -605,6 +597,10 @@ protocol_negotiate(ProtocolMajor, ProtocolMinor, _ProtocolRevision, connection_state = starting}, frame_header, 7}. +refuse_connection(Sock, Exception) -> + ok = inet_op(fun () -> rabbit_net:send(Sock, <<"AMQP",0,0,9,1>>) end), + throw(Exception). + %%-------------------------------------------------------------------------- handle_method0(MethodName, FieldsBin, -- cgit v1.2.1 From f67c884da471b0ac015528a9ad54beaaa7a91c1f Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 1 Jul 2010 17:01:28 +0100 Subject: Protocol is negotiated first. --- include/rabbit.hrl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/rabbit.hrl b/include/rabbit.hrl index 4174110f..8afa97a6 100644 --- a/include/rabbit.hrl +++ b/include/rabbit.hrl @@ -36,8 +36,8 @@ -record(vhost, {virtual_host, dummy}). --record(connection, {user, timeout_sec, frame_max, vhost, client_properties, - protocol}). +-record(connection, {protocol, user, timeout_sec, frame_max, vhost, + client_properties}). -record(content, {class_id, -- cgit v1.2.1 From 520b986983eba8671141d2e87ebf3b369022aeb0 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 2 Jul 2010 13:08:02 +0100 Subject: Remove the rabbit_framing shim, pass around a module name and use that. --- include/rabbit.hrl | 5 +- src/rabbit_basic.erl | 4 +- src/rabbit_binary_generator.erl | 9 ++-- src/rabbit_binary_parser.erl | 2 +- src/rabbit_channel.erl | 2 +- src/rabbit_framing.erl | 102 ---------------------------------------- src/rabbit_framing_channel.erl | 7 ++- src/rabbit_reader.erl | 29 +++++++----- src/rabbit_tests.erl | 5 +- src/rabbit_writer.erl | 2 +- 10 files changed, 36 insertions(+), 131 deletions(-) delete mode 100644 src/rabbit_framing.erl diff --git a/include/rabbit.hrl b/include/rabbit.hrl index 8afa97a6..72fbb01e 100644 --- a/include/rabbit.hrl +++ b/include/rabbit.hrl @@ -36,8 +36,8 @@ -record(vhost, {virtual_host, dummy}). --record(connection, {protocol, user, timeout_sec, frame_max, vhost, - client_properties}). +-record(connection, {protocol, protocol_name, user, timeout_sec, frame_max, + vhost, client_properties}). -record(content, {class_id, @@ -175,6 +175,7 @@ method :: atom()}). -type(protocol() :: atom()). +-type(protocol_name() :: atom()). -endif. %%---------------------------------------------------------------------------- diff --git a/src/rabbit_basic.erl b/src/rabbit_basic.erl index e277d5b7..7b581b8d 100644 --- a/src/rabbit_basic.erl +++ b/src/rabbit_basic.erl @@ -82,7 +82,7 @@ delivery(Mandatory, Immediate, Txn, Message) -> build_content(Properties, BodyBin) -> %% basic.publish hasn't changed so we can just hard-code amqp_0_9_1 {ClassId, _MethodId} = - rabbit_framing:method_id('basic.publish', amqp_0_9_1), + rabbit_framing_amqp_0_9_1:method_id('basic.publish'), #content{class_id = ClassId, properties = Properties, properties_bin = none, @@ -95,7 +95,7 @@ from_content(Content) -> rabbit_binary_parser:ensure_content_decoded(Content), %% basic.publish hasn't changed so we can just hard-code amqp_0_9_1 {ClassId, _MethodId} = - rabbit_framing:method_id('basic.publish', amqp_0_9_1), + rabbit_framing_amqp_0_9_1:method_id('basic.publish'), {Props, list_to_binary(lists:reverse(FragmentsRev))}. message(ExchangeName, RoutingKeyBin, RawProperties, BodyBin) -> diff --git a/src/rabbit_binary_generator.erl b/src/rabbit_binary_generator.erl index 04251d11..d159f309 100644 --- a/src/rabbit_binary_generator.erl +++ b/src/rabbit_binary_generator.erl @@ -72,9 +72,9 @@ %%---------------------------------------------------------------------------- build_simple_method_frame(ChannelInt, MethodRecord, Protocol) -> - MethodFields = rabbit_framing:encode_method_fields(MethodRecord, Protocol), + MethodFields = Protocol:encode_method_fields(MethodRecord), MethodName = rabbit_misc:method_record_type(MethodRecord), - {ClassId, MethodId} = rabbit_framing:method_id(MethodName, Protocol), + {ClassId, MethodId} = Protocol:method_id(MethodName), create_frame(1, ChannelInt, [<>, MethodFields]). build_simple_content_frames(ChannelInt, @@ -93,7 +93,7 @@ maybe_encode_properties(_ContentProperties, ContentPropertiesBin) when is_binary(ContentPropertiesBin) -> ContentPropertiesBin; maybe_encode_properties(ContentProperties, none) -> - rabbit_framing:encode_properties(ContentProperties). + rabbit_framing_amqp_0_9_1:encode_properties(ContentProperties). build_content_frames(FragsRev, FrameMax, ChannelInt) -> BodyPayloadMax = if FrameMax == 0 -> @@ -281,7 +281,8 @@ ensure_content_encoded(Content = #content{properties_bin = PropsBin}) when PropsBin =/= 'none' -> Content; ensure_content_encoded(Content = #content{properties = Props}) -> - Content #content{properties_bin = rabbit_framing:encode_properties(Props)}. + Content #content{properties_bin = + rabbit_framing_amqp_0_9_1:encode_properties(Props)}. clear_encoded_content(Content = #content{properties_bin = none}) -> Content; diff --git a/src/rabbit_binary_parser.erl b/src/rabbit_binary_parser.erl index e022a1fa..7a32acae 100644 --- a/src/rabbit_binary_parser.erl +++ b/src/rabbit_binary_parser.erl @@ -164,7 +164,7 @@ ensure_content_decoded(Content = #content{properties = Props}) Content; ensure_content_decoded(Content = #content{properties_bin = PropBin}) when is_binary(PropBin) -> - Content#content{properties = rabbit_framing:decode_properties( + Content#content{properties = rabbit_framing_amqp_0_9_1:decode_properties( Content#content.class_id, PropBin)}. clear_decoded_content(Content = #content{properties = none}) -> diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 1a965f79..f2a3e071 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -930,7 +930,7 @@ basic_return(#basic_message{exchange_name = ExchangeName, content = Content}, WriterPid, Reason) -> {_Close, ReplyCode, ReplyText} = - rabbit_framing:lookup_amqp_exception(Reason), + rabbit_framing_amqp_0_9_1:lookup_amqp_exception(Reason), ok = rabbit_writer:send_command( WriterPid, #'basic.return'{reply_code = ReplyCode, diff --git a/src/rabbit_framing.erl b/src/rabbit_framing.erl deleted file mode 100644 index 2d4d1ce4..00000000 --- a/src/rabbit_framing.erl +++ /dev/null @@ -1,102 +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_framing). --include("rabbit.hrl"). --include("rabbit_framing.hrl"). - - --export([encode_method_fields/2]). --export([decode_method_fields/3]). --export([lookup_method_name/2]). --export([method_id/2]). - --export([method_has_content/1]). --export([is_method_synchronous/1]). --export([method_record/1]). --export([method_fieldnames/1]). --export([decode_properties/2]). --export([encode_properties/1]). --export([lookup_amqp_exception/1]). --export([amqp_exception/1]). - -%% Method signatures --ifdef(use_specs). --spec(encode_method_fields/2 :: (amqp_method_record(), protocol()) -> binary()). --spec(decode_method_fields/3 :: (amqp_method_name(), binary(), protocol()) -> - amqp_method_record()). --spec(lookup_method_name/2 :: (amqp_method(), protocol()) -> - amqp_method_name()). --spec(method_id/2 :: (amqp_method_name(), protocol()) -> amqp_method()). - --spec(method_has_content/1 :: (amqp_method_name()) -> boolean()). --spec(is_method_synchronous/1 :: (amqp_method_record()) -> boolean()). --spec(method_record/1 :: (amqp_method_name()) -> amqp_method_record()). --spec(method_fieldnames/1 :: (amqp_method_name()) -> - [amqp_method_field_name()]). --spec(decode_properties/2 :: (non_neg_integer(), binary()) -> - amqp_property_record()). --spec(encode_properties/1 :: (amqp_method_record()) -> binary()). --spec(lookup_amqp_exception/1 :: - (amqp_exception()) -> {boolean(), amqp_exception_code(), binary()}). --spec(amqp_exception/1 :: (amqp_exception_code()) -> amqp_exception()). --endif. % use_specs - -encode_method_fields(MethodRecord, amqp_0_9_1) -> - rabbit_framing_amqp_0_9_1:encode_method_fields(MethodRecord); -encode_method_fields(MethodRecord, amqp_0_8) -> - rabbit_framing_amqp_0_8:encode_method_fields(MethodRecord). - -decode_method_fields(MethodName, FieldsBin, amqp_0_9_1) -> - rabbit_framing_amqp_0_9_1:decode_method_fields(MethodName, FieldsBin); -decode_method_fields(MethodName, FieldsBin, amqp_0_8) -> - rabbit_framing_amqp_0_8:decode_method_fields(MethodName, FieldsBin). - -lookup_method_name(ClassMethod, amqp_0_9_1) -> - rabbit_framing_amqp_0_9_1:lookup_method_name(ClassMethod); -lookup_method_name(ClassMethod, amqp_0_8) -> - rabbit_framing_amqp_0_8:lookup_method_name(ClassMethod). - -method_id(MethodName, amqp_0_9_1) -> - rabbit_framing_amqp_0_9_1:method_id(MethodName); -method_id(MethodName, amqp_0_8) -> - rabbit_framing_amqp_0_8:method_id(MethodName). - - - -%% These ones don't make any difference, let's just use 0-9-1. -method_has_content(X) -> rabbit_framing_amqp_0_9_1:method_has_content(X). -method_record(X) -> rabbit_framing_amqp_0_9_1:method_record(X). -method_fieldnames(X) -> rabbit_framing_amqp_0_9_1:method_fieldnames(X). -encode_properties(X) -> rabbit_framing_amqp_0_9_1:encode_properties(X). -amqp_exception(X) -> rabbit_framing_amqp_0_9_1:amqp_exception(X). -lookup_amqp_exception(X) -> rabbit_framing_amqp_0_9_1:lookup_amqp_exception(X). -is_method_synchronous(X) -> rabbit_framing_amqp_0_9_1:is_method_synchronous(X). -decode_properties(X, Y) -> rabbit_framing_amqp_0_9_1:decode_properties(X, Y). diff --git a/src/rabbit_framing_channel.erl b/src/rabbit_framing_channel.erl index c30cf451..7b9607cb 100644 --- a/src/rabbit_framing_channel.erl +++ b/src/rabbit_framing_channel.erl @@ -74,9 +74,8 @@ read_frame(ChannelPid) -> mainloop(ChannelPid, Protocol) -> {method, MethodName, FieldsBin} = read_frame(ChannelPid), - Method = rabbit_framing:decode_method_fields(MethodName, FieldsBin, - Protocol), - case rabbit_framing:method_has_content(MethodName) of + Method = Protocol:decode_method_fields(MethodName, FieldsBin), + case Protocol:method_has_content(MethodName) of true -> rabbit_channel:do(ChannelPid, Method, collect_content(ChannelPid, MethodName)); false -> rabbit_channel:do(ChannelPid, Method) @@ -85,7 +84,7 @@ mainloop(ChannelPid, Protocol) -> collect_content(ChannelPid, MethodName) -> %% Protocol does not matter as we only want the class ID to match - {ClassId, _MethodId} = rabbit_framing:method_id(MethodName, amqp_0_9_1), + {ClassId, _MethodId} = rabbit_framing_amqp_0_9_1:method_id(MethodName), case read_frame(ChannelPid) of {content_header, HeaderClassId, 0, BodySize, PropertiesBin} -> if HeaderClassId == ClassId -> diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index a9862367..4a8b0f3c 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -523,7 +523,7 @@ handle_frame(Type, Channel, Payload, analyze_frame(?FRAME_METHOD, <>, Protocol) -> - {method, rabbit_framing:lookup_method_name({ClassId, MethodId}, Protocol), + {method, Protocol:lookup_method_name({ClassId, MethodId}), MethodFields}; analyze_frame(?FRAME_HEADER, <>, @@ -580,8 +580,13 @@ handle_input(Callback, Data, _State) -> %% Offer a protocol version to the client. Connection.start only %% includes a major and minor version number, Luckily 0-9 and 0-9-1 %% are similar enough that clients will be happy with either. -start_connection({ProtocolMajor, ProtocolMinor, _ProtocolRevision}, Protocol, - State = #v1{sock = Sock, connection = Connection}) -> +start_connection({ProtocolMajor, ProtocolMinor, _ProtocolRevision}, + ProtocolName, + State = #v1{sock = Sock, connection = Connection}) -> + Protocol = case ProtocolName of + amqp_0_9_1 -> rabbit_framing_amqp_0_9_1; + amqp_0_8 -> rabbit_framing_amqp_0_8 + end, ok = send_on_channel0( Sock, #'connection.start'{ @@ -593,7 +598,8 @@ start_connection({ProtocolMajor, ProtocolMinor, _ProtocolRevision}, Protocol, Protocol), {State#v1{connection = Connection#connection{ timeout_sec = ?NORMAL_TIMEOUT, - protocol = Protocol}, + protocol = Protocol, + protocol_name = ProtocolName}, connection_state = starting}, frame_header, 7}. @@ -606,8 +612,7 @@ refuse_connection(Sock, Exception) -> handle_method0(MethodName, FieldsBin, State = #v1{connection = #connection{protocol = Protocol}}) -> try - handle_method0(rabbit_framing:decode_method_fields( - MethodName, FieldsBin, Protocol), + handle_method0(Protocol:decode_method_fields(MethodName, FieldsBin), State) catch exit:Reason -> CompleteReason = case Reason of @@ -723,8 +728,8 @@ i(peer_address, #v1{sock = Sock}) -> i(peer_port, #v1{sock = Sock}) -> {ok, {_, P}} = rabbit_net:peername(Sock), P; -i(protocol, #v1{connection = #connection{protocol = Protocol}}) -> - Protocol; +i(protocol, #v1{connection = #connection{protocol_name = ProtocolName}}) -> + ProtocolName; i(SockStat, #v1{sock = Sock}) when SockStat =:= recv_oct; SockStat =:= recv_cnt; SockStat =:= send_oct; @@ -805,8 +810,7 @@ map_exception(Channel, Reason, Protocol) -> {ClassId, MethodId} = case FailedMethod of {_, _} -> FailedMethod; none -> {0, 0}; - _ -> rabbit_framing:method_id(FailedMethod, - Protocol) + _ -> Protocol:method_id(FailedMethod) end, {CloseChannel, CloseMethod} = case ShouldClose of @@ -830,13 +834,14 @@ lookup_amqp_exception(#amqp_error{name = precondition_failed, lookup_amqp_exception(#amqp_error{name = Name, explanation = Expl, method = Method}) -> - {ShouldClose, Code, Text} = rabbit_framing:lookup_amqp_exception(Name), + {ShouldClose, Code, Text} = + rabbit_framing_amqp_0_9_1:lookup_amqp_exception(Name), ExplBin = amqp_exception_explanation(Text, Expl), {ShouldClose, Code, ExplBin, Method}; lookup_amqp_exception(Other) -> rabbit_log:warning("Non-AMQP exit reason '~p'~n", [Other]), {ShouldClose, Code, Text} = - rabbit_framing:lookup_amqp_exception(internal_error), + rabbit_framing_0_9_1:lookup_amqp_exception(internal_error), {ShouldClose, Code, Text, none}. amqp_exception_explanation(Text, Expl) -> diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 87eaae9e..6d9d2d38 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -953,8 +953,9 @@ test_memory_pressure() -> ok = test_memory_pressure_receive_flow(true), %% if we publish at this point, the channel should die - Content = #content{class_id = element(1, rabbit_framing:method_id( - 'basic.publish', amqp_0_9_1)), + Content = + #content{class_id = element(1, rabbit_framing_amqp_0_9_1:method_id( + 'basic.publish')), properties = none, properties_bin = <<>>, payload_fragments_rev = []}, diff --git a/src/rabbit_writer.erl b/src/rabbit_writer.erl index 403120f1..17b8f53e 100644 --- a/src/rabbit_writer.erl +++ b/src/rabbit_writer.erl @@ -170,7 +170,7 @@ assemble_frames(Channel, MethodRecord, Protocol) -> assemble_frames(Channel, MethodRecord, Content, FrameMax, Protocol) -> ?LOGMESSAGE(out, Channel, MethodRecord, Content), MethodName = rabbit_misc:method_record_type(MethodRecord), - true = rabbit_framing:method_has_content(MethodName), % assertion + true = Protocol:method_has_content(MethodName), % assertion MethodFrame = rabbit_binary_generator:build_simple_method_frame( Channel, MethodRecord, Protocol), ContentFrames = rabbit_binary_generator:build_simple_content_frames( -- cgit v1.2.1 From 762c5b13c0ab3a47913c8d016f0bbb052b246a6c Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 2 Jul 2010 13:19:54 +0100 Subject: This clause can indeed go. --- src/rabbit_reader.erl | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 4a8b0f3c..d17d287c 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -825,12 +825,6 @@ map_exception(Channel, Reason, Protocol) -> end, {ShouldClose, CloseChannel, CloseMethod}. -%% FIXME: this clause can go when we move to AMQP spec >=8.1 -lookup_amqp_exception(#amqp_error{name = precondition_failed, - explanation = Expl, - method = Method}) -> - ExplBin = amqp_exception_explanation(<<"PRECONDITION_FAILED">>, Expl), - {false, 406, ExplBin, Method}; lookup_amqp_exception(#amqp_error{name = Name, explanation = Expl, method = Method}) -> -- cgit v1.2.1 From 22e69567a04c8c4abce37885d31d6af973dc7519 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 2 Jul 2010 13:22:49 +0100 Subject: Remove an instance of hard-coding. --- src/rabbit_reader.erl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index d17d287c..495b0141 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -805,7 +805,7 @@ send_exception(State = #v1{connection = #connection{protocol = Protocol}}, map_exception(Channel, Reason, Protocol) -> {SuggestedClose, ReplyCode, ReplyText, FailedMethod} = - lookup_amqp_exception(Reason), + lookup_amqp_exception(Reason, Protocol), ShouldClose = SuggestedClose or (Channel == 0), {ClassId, MethodId} = case FailedMethod of {_, _} -> FailedMethod; @@ -827,12 +827,12 @@ map_exception(Channel, Reason, Protocol) -> lookup_amqp_exception(#amqp_error{name = Name, explanation = Expl, - method = Method}) -> - {ShouldClose, Code, Text} = - rabbit_framing_amqp_0_9_1:lookup_amqp_exception(Name), + method = Method}, + Protocol) -> + {ShouldClose, Code, Text} = Protocol:lookup_amqp_exception(Name), ExplBin = amqp_exception_explanation(Text, Expl), {ShouldClose, Code, ExplBin, Method}; -lookup_amqp_exception(Other) -> +lookup_amqp_exception(Other, _Protocol) -> rabbit_log:warning("Non-AMQP exit reason '~p'~n", [Other]), {ShouldClose, Code, Text} = rabbit_framing_0_9_1:lookup_amqp_exception(internal_error), -- cgit v1.2.1 From 17514ede33558c260a98869f900091adb2781d1e Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 2 Jul 2010 13:31:43 +0100 Subject: Oops, missed one. --- src/rabbit_reader.erl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 495b0141..1eaabe34 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -832,10 +832,9 @@ lookup_amqp_exception(#amqp_error{name = Name, {ShouldClose, Code, Text} = Protocol:lookup_amqp_exception(Name), ExplBin = amqp_exception_explanation(Text, Expl), {ShouldClose, Code, ExplBin, Method}; -lookup_amqp_exception(Other, _Protocol) -> +lookup_amqp_exception(Other, Protocol) -> rabbit_log:warning("Non-AMQP exit reason '~p'~n", [Other]), - {ShouldClose, Code, Text} = - rabbit_framing_0_9_1:lookup_amqp_exception(internal_error), + {ShouldClose, Code, Text} = Protocol:lookup_amqp_exception(internal_error), {ShouldClose, Code, Text, none}. amqp_exception_explanation(Text, Expl) -> -- cgit v1.2.1 From e5bd3d8343b3fa442eba0aedb70f2f8b81aa0f05 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 2 Jul 2010 14:25:51 +0100 Subject: Update the names of our JSON files, don't use the 0-8 extensions file. --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index d3fd93f8..0c97b2db 100644 --- a/Makefile +++ b/Makefile @@ -56,8 +56,8 @@ TARGET_SRC_DIR=dist/$(TARBALL_NAME) SIBLING_CODEGEN_DIR=../rabbitmq-codegen/ AMQP_CODEGEN_DIR=$(shell [ -d $(SIBLING_CODEGEN_DIR) ] && echo $(SIBLING_CODEGEN_DIR) || echo codegen) -AMQP_SPEC_JSON_FILES_0_9_1=$(AMQP_CODEGEN_DIR)/amqp-rabbitmq.json -AMQP_SPEC_JSON_FILES_0_8=$(AMQP_CODEGEN_DIR)/amqp-0.8.json $(AMQP_CODEGEN_DIR)/rabbitmq-0.8-extensions.json +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 ERL_CALL=erl_call -sname $(RABBITMQ_NODENAME) -e -- cgit v1.2.1 From 46948314b5f28400001f2428fb4d90d076274499 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 2 Jul 2010 15:05:26 +0100 Subject: tweak: don't need protocol_name type --- include/rabbit.hrl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/rabbit.hrl b/include/rabbit.hrl index 72fbb01e..1bb89af9 100644 --- a/include/rabbit.hrl +++ b/include/rabbit.hrl @@ -173,9 +173,8 @@ #amqp_error{name :: atom(), explanation :: string(), method :: atom()}). - -type(protocol() :: atom()). --type(protocol_name() :: atom()). + -endif. %%---------------------------------------------------------------------------- -- cgit v1.2.1 From c79f265b08e6c8c219f745ec517fa3ed907022ef Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 2 Jul 2010 15:34:46 +0100 Subject: cosmetic: more sensible place for protocol in info items --- docs/rabbitmqctl.1.xml | 8 ++++---- src/rabbit_reader.erl | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/rabbitmqctl.1.xml b/docs/rabbitmqctl.1.xml index 6e374dfb..d4d7f70c 100644 --- a/docs/rabbitmqctl.1.xml +++ b/docs/rabbitmqctl.1.xml @@ -857,14 +857,14 @@ Connection state (one of [starting, tuning, opening, running, closing, closed]). - - protocol - Version of the AMQP protocol in use (currently one of amqp_0_9_1 or amqp_0_8). Note that if a client requests an AMQP 0-9 connection, we treat it as AMQP 0-9-1. - channels Number of channels using the connection. + + protocol + Version of the AMQP protocol in use (currently one of amqp_0_9_1 or amqp_0_8). Note that if a client requests an AMQP 0-9 connection, we treat it as AMQP 0-9-1. + user Username associated with the connection. diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 1eaabe34..e8e6bb19 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -61,9 +61,9 @@ queue_collector}). -define(INFO_KEYS, - [pid, address, port, peer_address, peer_port, protocol, - recv_oct, recv_cnt, send_oct, send_cnt, send_pend, - state, channels, user, vhost, timeout, frame_max, client_properties]). + [pid, address, port, peer_address, peer_port, + recv_oct, recv_cnt, send_oct, send_cnt, send_pend, state, channels, + protocol, user, vhost, timeout, frame_max, client_properties]). %% connection lifecycle %% @@ -728,8 +728,6 @@ i(peer_address, #v1{sock = Sock}) -> i(peer_port, #v1{sock = Sock}) -> {ok, {_, P}} = rabbit_net:peername(Sock), P; -i(protocol, #v1{connection = #connection{protocol_name = ProtocolName}}) -> - ProtocolName; i(SockStat, #v1{sock = Sock}) when SockStat =:= recv_oct; SockStat =:= recv_cnt; SockStat =:= send_oct; @@ -744,6 +742,8 @@ i(state, #v1{connection_state = S}) -> S; i(channels, #v1{}) -> length(all_channels()); +i(protocol, #v1{connection = #connection{protocol_name = ProtocolName}}) -> + ProtocolName; i(user, #v1{connection = #connection{user = #user{username = Username}}}) -> Username; i(user, #v1{connection = #connection{user = none}}) -> -- cgit v1.2.1 From ee54ae96b5172bea778f4e5c0ec42e9791c67d26 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 2 Jul 2010 16:03:59 +0100 Subject: Send the right connection.start in 0-8. Amusingly, this doesn't matter for the Java client due to http://hg.rabbitmq.com/rabbitmq-java-client/file/3dfa3f6eb6fd/src/com/rabbitmq/client/impl/Version.java#l95 --- 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 ac586de9..8a9e7077 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -566,7 +566,7 @@ handle_input(handshake, <<"AMQP", 1, 1, 0, 9>>, State) -> %% the 0-8 spec, confusingly, defines the version as 8-0 handle_input(handshake, <<"AMQP", 1, 1, 8, 0>>, State) -> - start_connection({0, 8, 0}, amqp_0_8, State); + start_connection({8, 0, 0}, amqp_0_8, State); handle_input(handshake, <<"AMQP", A, B, C, D>>, #v1{sock = Sock}) -> refuse_connection(Sock, {bad_version, A, B, C, D}); -- cgit v1.2.1 From 525e8062998085c80d4b9f9af8142be1313db363 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 2 Jul 2010 16:17:56 +0100 Subject: cosmetic --- src/rabbit_reader.erl | 55 ++++++++++++++++++++++----------------------------- 1 file changed, 24 insertions(+), 31 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 8a9e7077..ee95a462 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -439,7 +439,8 @@ wait_for_channel_termination(N, TimerRef) -> maybe_close(State = #v1{connection_state = closing, queue_collector = Collector, - connection = #connection{protocol = Protocol}}) -> + connection = #connection{protocol = Protocol}, + sock = Sock}) -> case all_channels() of [] -> %% Spec says "Exclusive queues may only be accessed by the current @@ -447,8 +448,7 @@ maybe_close(State = #v1{connection_state = closing, %% This does not strictly imply synchrony, but in practice it seems %% to be what people assume. rabbit_reader_queue_collector:delete_all(Collector), - ok = send_on_channel0(State#v1.sock, #'connection.close_ok'{}, - Protocol), + ok = send_on_channel0(Sock, #'connection.close_ok'{}, Protocol), close_connection(State); _ -> State end; @@ -521,10 +521,11 @@ handle_frame(Type, Channel, Payload, end end. -analyze_frame(?FRAME_METHOD, <>, +analyze_frame(?FRAME_METHOD, + <>, Protocol) -> - {method, Protocol:lookup_method_name({ClassId, MethodId}), - MethodFields}; + MethodName = Protocol:lookup_method_name({ClassId, MethodId}), + {method, MethodName, MethodFields}; analyze_frame(?FRAME_HEADER, <>, _Protocol) -> @@ -587,15 +588,12 @@ start_connection({ProtocolMajor, ProtocolMinor, _ProtocolRevision}, amqp_0_9_1 -> rabbit_framing_amqp_0_9_1; amqp_0_8 -> rabbit_framing_amqp_0_8 end, - ok = send_on_channel0( - Sock, - #'connection.start'{ - version_major = ProtocolMajor, - version_minor = ProtocolMinor, - server_properties = server_properties(), - mechanisms = <<"PLAIN AMQPLAIN">>, - locales = <<"en_US">> }, - Protocol), + Start = #'connection.start'{ version_major = ProtocolMajor, + version_minor = ProtocolMinor, + server_properties = server_properties(), + mechanisms = <<"PLAIN AMQPLAIN">>, + locales = <<"en_US">> }, + ok = send_on_channel0(Sock, Start, Protocol), {State#v1{connection = Connection#connection{ timeout_sec = ?NORMAL_TIMEOUT, protocol = Protocol, @@ -638,12 +636,10 @@ handle_method0(#'connection.start_ok'{mechanism = Mechanism, #connection{protocol = Protocol}, sock = Sock}) -> User = rabbit_access_control:check_login(Mechanism, Response), - ok = send_on_channel0( - Sock, - #'connection.tune'{channel_max = 0, + Tune = #'connection.tune'{channel_max = 0, frame_max = ?FRAME_MAX, heartbeat = 0}, - Protocol), + ok = send_on_channel0(Sock, Tune, Protocol), State#v1{connection_state = tuning, connection = Connection#connection{ user = User, @@ -678,10 +674,7 @@ handle_method0(#'connection.open'{virtual_host = VHostPath}, sock = Sock}) -> ok = rabbit_access_control:check_vhost_access(User, VHostPath), NewConnection = Connection#connection{vhost = VHostPath}, - ok = send_on_channel0( - Sock, - #'connection.open_ok'{}, - Protocol), + ok = send_on_channel0(Sock, #'connection.open_ok'{}, Protocol), State#v1{connection_state = running, connection = NewConnection}; handle_method0(#'connection.close'{}, @@ -763,14 +756,14 @@ i(Item, #v1{}) -> %%-------------------------------------------------------------------------- -send_to_new_channel(Channel, AnalyzedFrame, State = - #v1{queue_collector = Collector, - connection = #connection{protocol = Protocol}}) -> - #v1{sock = Sock, connection = #connection{ - frame_max = FrameMax, - user = #user{username = Username}, - vhost = VHost, - protocol = Protocol}} = State, +send_to_new_channel(Channel, AnalyzedFrame, + State = #v1{connection = #connection{ + frame_max = FrameMax, + user = #user{username = Username}, + vhost = VHost, + protocol = Protocol}, + sock = Sock, + queue_collector = Collector}) -> WriterPid = rabbit_writer:start(Sock, Channel, FrameMax, Protocol), ChPid = rabbit_framing_channel:start_link( fun rabbit_channel:start_link/6, -- cgit v1.2.1 From 055b445e6ff692c1354899e3889c3f3ea5a7bda6 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 2 Jul 2010 16:22:19 +0100 Subject: oops --- src/rabbit_reader.erl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index ee95a462..72d95c7b 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -757,13 +757,13 @@ i(Item, #v1{}) -> %%-------------------------------------------------------------------------- send_to_new_channel(Channel, AnalyzedFrame, - State = #v1{connection = #connection{ - frame_max = FrameMax, - user = #user{username = Username}, - vhost = VHost, - protocol = Protocol}, - sock = Sock, - queue_collector = Collector}) -> + #v1{connection = #connection{ + frame_max = FrameMax, + user = #user{username = Username}, + vhost = VHost, + protocol = Protocol}, + sock = Sock, + queue_collector = Collector}) -> WriterPid = rabbit_writer:start(Sock, Channel, FrameMax, Protocol), ChPid = rabbit_framing_channel:start_link( fun rabbit_channel:start_link/6, -- cgit v1.2.1 From 3920e36c2b6b4a0302162998396e1ba35eb1d7a2 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 2 Jul 2010 16:26:15 +0100 Subject: cosmetic --- src/rabbit_writer.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_writer.erl b/src/rabbit_writer.erl index 17b8f53e..33e12a24 100644 --- a/src/rabbit_writer.erl +++ b/src/rabbit_writer.erl @@ -214,7 +214,7 @@ internal_send_command_async(Sock, Channel, MethodRecord, Protocol) -> internal_send_command_async(Sock, Channel, MethodRecord, Content, FrameMax, Protocol) -> true = port_cmd(Sock, assemble_frames(Channel, MethodRecord, - Content, FrameMax, Protocol)), + Content, FrameMax, Protocol)), ok. port_cmd(Sock, Data) -> -- cgit v1.2.1 From 98054097344a20ce3a03724295d9e240643efc22 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 2 Jul 2010 17:11:48 +0100 Subject: Remove remaining hard coding of rabbit_framing_amqp_0_9_1 from the codec. --- src/rabbit_basic.erl | 5 +++-- src/rabbit_binary_generator.erl | 28 ++++++++++++++-------------- src/rabbit_binary_parser.erl | 10 +++++----- src/rabbit_channel.erl | 4 +++- src/rabbit_tests.erl | 3 ++- src/rabbit_writer.erl | 2 +- 6 files changed, 28 insertions(+), 24 deletions(-) diff --git a/src/rabbit_basic.erl b/src/rabbit_basic.erl index 7b581b8d..4a1d50df 100644 --- a/src/rabbit_basic.erl +++ b/src/rabbit_basic.erl @@ -92,8 +92,9 @@ from_content(Content) -> #content{class_id = ClassId, properties = Props, payload_fragments_rev = FragmentsRev} = - rabbit_binary_parser:ensure_content_decoded(Content), - %% basic.publish hasn't changed so we can just hard-code amqp_0_9_1 + %% basic.publish hasn't changed so we can just hard-code amqp_0_9_1 + rabbit_binary_parser:ensure_content_decoded(Content, + rabbit_framing_amqp_0_9_1), {ClassId, _MethodId} = rabbit_framing_amqp_0_9_1:method_id('basic.publish'), {Props, list_to_binary(lists:reverse(FragmentsRev))}. diff --git a/src/rabbit_binary_generator.erl b/src/rabbit_binary_generator.erl index d159f309..75cd643c 100644 --- a/src/rabbit_binary_generator.erl +++ b/src/rabbit_binary_generator.erl @@ -42,11 +42,11 @@ -define(EMPTY_CONTENT_BODY_FRAME_SIZE, 8). -export([build_simple_method_frame/3, - build_simple_content_frames/3, + build_simple_content_frames/4, build_heartbeat_frame/0]). -export([generate_table/1, encode_properties/2]). -export([check_empty_content_body_frame_size/0]). --export([ensure_content_encoded/1, clear_encoded_content/1]). +-export([ensure_content_encoded/2, clear_encoded_content/1]). -import(lists). @@ -58,13 +58,14 @@ -spec(build_simple_method_frame/3 :: (channel_number(), amqp_method_record(), protocol()) -> frame()). --spec(build_simple_content_frames/3 :: - (channel_number(), content(), non_neg_integer()) -> [frame()]). +-spec(build_simple_content_frames/4 :: + (channel_number(), content(), non_neg_integer(), protocol()) -> + [frame()]). -spec(build_heartbeat_frame/0 :: () -> frame()). -spec(generate_table/1 :: (amqp_table()) -> binary()). -spec(encode_properties/2 :: ([amqp_property_type()], [any()]) -> binary()). -spec(check_empty_content_body_frame_size/0 :: () -> 'ok'). --spec(ensure_content_encoded/1 :: (content()) -> encoded_content()). +-spec(ensure_content_encoded/2 :: (content(), protocol()) -> encoded_content()). -spec(clear_encoded_content/1 :: (content()) -> unencoded_content()). -endif. @@ -82,18 +83,18 @@ build_simple_content_frames(ChannelInt, properties = ContentProperties, properties_bin = ContentPropertiesBin, payload_fragments_rev = PayloadFragmentsRev}, - FrameMax) -> + FrameMax, Protocol) -> {BodySize, ContentFrames} = build_content_frames(PayloadFragmentsRev, FrameMax, ChannelInt), HeaderFrame = create_frame(2, ChannelInt, [<>, - maybe_encode_properties(ContentProperties, ContentPropertiesBin)]), + maybe_encode_properties(ContentProperties, ContentPropertiesBin, Protocol)]), [HeaderFrame | ContentFrames]. -maybe_encode_properties(_ContentProperties, ContentPropertiesBin) +maybe_encode_properties(_ContentProperties, ContentPropertiesBin, _Protocol) when is_binary(ContentPropertiesBin) -> ContentPropertiesBin; -maybe_encode_properties(ContentProperties, none) -> - rabbit_framing_amqp_0_9_1:encode_properties(ContentProperties). +maybe_encode_properties(ContentProperties, none, Protocol) -> + Protocol:encode_properties(ContentProperties). build_content_frames(FragsRev, FrameMax, ChannelInt) -> BodyPayloadMax = if FrameMax == 0 -> @@ -277,12 +278,11 @@ check_empty_content_body_frame_size() -> ComputedSize, ?EMPTY_CONTENT_BODY_FRAME_SIZE}) end. -ensure_content_encoded(Content = #content{properties_bin = PropsBin}) +ensure_content_encoded(Content = #content{properties_bin = PropsBin}, _Protocol) when PropsBin =/= 'none' -> Content; -ensure_content_encoded(Content = #content{properties = Props}) -> - Content #content{properties_bin = - rabbit_framing_amqp_0_9_1:encode_properties(Props)}. +ensure_content_encoded(Content = #content{properties = Props}, Protocol) -> + Content#content{properties_bin = Protocol:encode_properties(Props)}. clear_encoded_content(Content = #content{properties_bin = none}) -> Content; diff --git a/src/rabbit_binary_parser.erl b/src/rabbit_binary_parser.erl index 7a32acae..633be6f0 100644 --- a/src/rabbit_binary_parser.erl +++ b/src/rabbit_binary_parser.erl @@ -34,7 +34,7 @@ -include("rabbit.hrl"). -export([parse_table/1, parse_properties/2]). --export([ensure_content_decoded/1, clear_decoded_content/1]). +-export([ensure_content_decoded/2, clear_decoded_content/1]). -import(lists). @@ -44,7 +44,7 @@ -spec(parse_table/1 :: (binary()) -> amqp_table()). -spec(parse_properties/2 :: ([amqp_property_type()], binary()) -> [any()]). --spec(ensure_content_decoded/1 :: (content()) -> decoded_content()). +-spec(ensure_content_decoded/2 :: (content(), protocol()) -> decoded_content()). -spec(clear_decoded_content/1 :: (content()) -> undecoded_content()). -endif. @@ -159,12 +159,12 @@ parse_property(bit, Rest) -> parse_property(table, <>) -> {parse_table(Table), Rest}. -ensure_content_decoded(Content = #content{properties = Props}) +ensure_content_decoded(Content = #content{properties = Props}, _Protocol) when Props =/= 'none' -> Content; -ensure_content_decoded(Content = #content{properties_bin = PropBin}) +ensure_content_decoded(Content = #content{properties_bin = PropBin}, Protocol) when is_binary(PropBin) -> - Content#content{properties = rabbit_framing_amqp_0_9_1:decode_properties( + Content#content{properties = Protocol:decode_properties( Content#content.class_id, PropBin)}. clear_decoded_content(Content = #content{properties = none}) -> diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index da91bef8..c3440f84 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -414,7 +414,9 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, Exchange = rabbit_exchange:lookup_or_die(ExchangeName), %% 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), + DecodedContent = + rabbit_binary_parser:ensure_content_decoded(Content, + rabbit_framing_amqp_0_9_1), IsPersistent = is_message_persistent(DecodedContent), Message = #basic_message{exchange_name = ExchangeName, routing_key = RoutingKey, diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 960d9a9c..53f73b9d 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -361,7 +361,8 @@ test_content_framing(FrameMax, Fragments) -> 1, #content{class_id = 0, properties_bin = <<>>, payload_fragments_rev = Fragments}, - FrameMax), + FrameMax, + rabbit_framing_amqp_0_9_1), %% header is formatted correctly and the size is the total of the %% fragments <<_FrameHeader:7/binary, _ClassAndWeight:4/binary, diff --git a/src/rabbit_writer.erl b/src/rabbit_writer.erl index 17b8f53e..1e1a547b 100644 --- a/src/rabbit_writer.erl +++ b/src/rabbit_writer.erl @@ -174,7 +174,7 @@ assemble_frames(Channel, MethodRecord, Content, FrameMax, Protocol) -> MethodFrame = rabbit_binary_generator:build_simple_method_frame( Channel, MethodRecord, Protocol), ContentFrames = rabbit_binary_generator:build_simple_content_frames( - Channel, Content, FrameMax), + Channel, Content, FrameMax, Protocol), [MethodFrame | ContentFrames]. tcp_send(Sock, Data) -> -- cgit v1.2.1 From 4f4640a08f676cca8605299101265f6fd82277b8 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 2 Jul 2010 17:43:45 +0100 Subject: Get codegen to build Protocol:version(). --- codegen.py | 8 +++++++- include/rabbit.hrl | 4 ++-- src/rabbit_reader.erl | 19 +++++++------------ 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/codegen.py b/codegen.py index 9596f5b1..9eb6fca2 100644 --- a/codegen.py +++ b/codegen.py @@ -316,7 +316,7 @@ def genErl(spec): printFileHeader() module = "rabbit_framing_amqp_%d_%d" % (spec.major, spec.minor) - if spec.revision != '0': + if spec.revision != 0: module = "%s_%d" % (module, spec.revision) if module == "rabbit_framing_amqp_8_0": module = "rabbit_framing_amqp_0_8" @@ -336,6 +336,7 @@ def genErl(spec): -export([encode_properties/1]). -export([lookup_amqp_exception/1]). -export([amqp_exception/1]). +-export([version/0]). bitvalue(true) -> 1; bitvalue(false) -> 0; @@ -355,6 +356,7 @@ bitvalue(undefined) -> 0. -spec(encode_properties/1 :: (amqp_method_record()) -> binary()). -spec(lookup_amqp_exception/1 :: (amqp_exception()) -> {boolean(), amqp_exception_code(), binary()}). -spec(amqp_exception/1 :: (amqp_exception_code()) -> amqp_exception()). +-spec(version/0 :: () -> {integer, integer, integer}). -endif. % use_specs """ for m in methods: genLookupMethodName(m) @@ -396,6 +398,10 @@ bitvalue(undefined) -> 0. for(c,v,cls) in spec.constants: genAmqpException(c,v,cls) print "amqp_exception(_Code) -> undefined." + version = "{%d, %d, %d}" % (spec.major, spec.minor, spec.revision) + if version == '{8, 0, 0}': version = '{0, 8, 0}' + print "version() -> %s." % (version) + def genHrl(spec): def erlType(domain): return erlangTypeMap[spec.resolveDomain(domain)] diff --git a/include/rabbit.hrl b/include/rabbit.hrl index 1bb89af9..8c713a50 100644 --- a/include/rabbit.hrl +++ b/include/rabbit.hrl @@ -36,8 +36,8 @@ -record(vhost, {virtual_host, dummy}). --record(connection, {protocol, protocol_name, user, timeout_sec, frame_max, - vhost, client_properties}). +-record(connection, {protocol, user, timeout_sec, frame_max, vhost, + client_properties}). -record(content, {class_id, diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 72d95c7b..9fbf7ce8 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -560,14 +560,14 @@ handle_input({frame_payload, Type, Channel, PayloadSize}, PayloadAndMarker, Stat %% * The server MUST provide a protocol version that is lower than or %% equal to that requested by the client in the protocol header. handle_input(handshake, <<"AMQP", 0, 0, 9, 1>>, State) -> - start_connection({0, 9, 1}, amqp_0_9_1, State); + start_connection({0, 9, 1}, rabbit_framing_amqp_0_9_1, State); handle_input(handshake, <<"AMQP", 1, 1, 0, 9>>, State) -> - start_connection({0, 9, 0}, amqp_0_9_1, State); + start_connection({0, 9, 0}, rabbit_framing_amqp_0_9_1, State); %% the 0-8 spec, confusingly, defines the version as 8-0 handle_input(handshake, <<"AMQP", 1, 1, 8, 0>>, State) -> - start_connection({8, 0, 0}, amqp_0_8, State); + start_connection({8, 0, 0}, rabbit_framing_amqp_0_8, State); handle_input(handshake, <<"AMQP", A, B, C, D>>, #v1{sock = Sock}) -> refuse_connection(Sock, {bad_version, A, B, C, D}); @@ -582,12 +582,8 @@ handle_input(Callback, Data, _State) -> %% includes a major and minor version number, Luckily 0-9 and 0-9-1 %% are similar enough that clients will be happy with either. start_connection({ProtocolMajor, ProtocolMinor, _ProtocolRevision}, - ProtocolName, + Protocol, State = #v1{sock = Sock, connection = Connection}) -> - Protocol = case ProtocolName of - amqp_0_9_1 -> rabbit_framing_amqp_0_9_1; - amqp_0_8 -> rabbit_framing_amqp_0_8 - end, Start = #'connection.start'{ version_major = ProtocolMajor, version_minor = ProtocolMinor, server_properties = server_properties(), @@ -596,8 +592,7 @@ start_connection({ProtocolMajor, ProtocolMinor, _ProtocolRevision}, ok = send_on_channel0(Sock, Start, Protocol), {State#v1{connection = Connection#connection{ timeout_sec = ?NORMAL_TIMEOUT, - protocol = Protocol, - protocol_name = ProtocolName}, + protocol = Protocol}, connection_state = starting}, frame_header, 7}. @@ -736,8 +731,8 @@ i(state, #v1{connection_state = S}) -> S; i(channels, #v1{}) -> length(all_channels()); -i(protocol, #v1{connection = #connection{protocol_name = ProtocolName}}) -> - ProtocolName; +i(protocol, #v1{connection = #connection{protocol = Protocol}}) -> + Protocol:version(); i(user, #v1{connection = #connection{user = #user{username = Username}}}) -> Username; i(user, #v1{connection = #connection{user = none}}) -> -- cgit v1.2.1 From 34b1c0dc5ea524d05ae16c44014ff9d0ad192be8 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 2 Jul 2010 17:59:15 +0100 Subject: Correct spec; correct dependencies. --- Makefile | 4 ++-- codegen.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 0c97b2db..279da78b 100644 --- a/Makefile +++ b/Makefile @@ -100,10 +100,10 @@ $(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) +$(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 header $(AMQP_SPEC_JSON_FILES_0_9_1) $(AMQP_SPEC_JSON_FILES_0_8) $@ -$(INCLUDE_DIR)/rabbit_framing_spec.hrl: codegen.py $(AMQP_CODEGEN_DIR)/amqp_codegen.py $(AMQP_SPEC_JSON_FILES_0_9_1) +$(INCLUDE_DIR)/rabbit_framing_spec.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 spec $(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) diff --git a/codegen.py b/codegen.py index 9eb6fca2..d8981a48 100644 --- a/codegen.py +++ b/codegen.py @@ -356,7 +356,7 @@ bitvalue(undefined) -> 0. -spec(encode_properties/1 :: (amqp_method_record()) -> binary()). -spec(lookup_amqp_exception/1 :: (amqp_exception()) -> {boolean(), amqp_exception_code(), binary()}). -spec(amqp_exception/1 :: (amqp_exception_code()) -> amqp_exception()). --spec(version/0 :: () -> {integer, integer, integer}). +-spec(version/0 :: () -> {non_neg_integer(), non_neg_integer(), non_neg_integer()}). -endif. % use_specs """ for m in methods: genLookupMethodName(m) -- cgit v1.2.1 From e4ff8b27ba7a68cf1ace61d6f8db8ea2ddd3f846 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 2 Jul 2010 18:25:36 +0100 Subject: cosmetic --- src/rabbit_channel.erl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index c3440f84..3c2ae72d 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -414,9 +414,8 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, Exchange = rabbit_exchange:lookup_or_die(ExchangeName), %% 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, - rabbit_framing_amqp_0_9_1), + DecodedContent = rabbit_binary_parser:ensure_content_decoded( + Content, rabbit_framing_amqp_0_9_1), IsPersistent = is_message_persistent(DecodedContent), Message = #basic_message{exchange_name = ExchangeName, routing_key = RoutingKey, -- cgit v1.2.1 From cc80e6378c793c48c1e88cce6ce21f6fe2c61f57 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 5 Jul 2010 10:16:15 +0100 Subject: Update protocol format --- docs/rabbitmqctl.1.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/rabbitmqctl.1.xml b/docs/rabbitmqctl.1.xml index d4d7f70c..9280b95c 100644 --- a/docs/rabbitmqctl.1.xml +++ b/docs/rabbitmqctl.1.xml @@ -863,7 +863,7 @@ protocol - Version of the AMQP protocol in use (currently one of amqp_0_9_1 or amqp_0_8). Note that if a client requests an AMQP 0-9 connection, we treat it as AMQP 0-9-1. + Version of the AMQP protocol in use (currently one of {0,9,1} or {0,8,0}). Note that if a client requests an AMQP 0-9 connection, we treat it as AMQP 0-9-1. user -- cgit v1.2.1 From cde266e7a5f1428d98dc1d90594ab027eaaa52fa Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 5 Jul 2010 10:54:02 +0100 Subject: Fix compilation on R14A. --- src/rabbit_exchange.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index 512f248c..0f8f6e11 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -100,7 +100,7 @@ %%---------------------------------------------------------------------------- --define(INFO_KEYS, [name, type, durable, auto_delete, arguments]. +-define(INFO_KEYS, [name, type, durable, auto_delete, arguments]). recover() -> Exs = rabbit_misc:table_fold( -- cgit v1.2.1 From 95b81581ff119a65be1b08f82429161c9d148d40 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 5 Jul 2010 15:19:21 +0100 Subject: Use --allow-overwrite --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 279da78b..88e13512 100644 --- a/Makefile +++ b/Makefile @@ -101,10 +101,10 @@ $(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 header $(AMQP_SPEC_JSON_FILES_0_9_1) $(AMQP_SPEC_JSON_FILES_0_8) $@ + $(PYTHON) codegen.py --allow-overwrite header $(AMQP_SPEC_JSON_FILES_0_9_1) $(AMQP_SPEC_JSON_FILES_0_8) $@ $(INCLUDE_DIR)/rabbit_framing_spec.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 spec $(AMQP_SPEC_JSON_FILES_0_9_1) $(AMQP_SPEC_JSON_FILES_0_8) $@ + $(PYTHON) codegen.py --allow-overwrite spec $(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) $(PYTHON) codegen.py body $(AMQP_SPEC_JSON_FILES_0_9_1) $@ -- cgit v1.2.1 From 60960db6f5a8533037f7394726bcac75f1a9fdde Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 5 Jul 2010 18:46:16 +0100 Subject: Rename unknown protocol to none, fix tests. --- src/rabbit_reader.erl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 10118071..bac67b1d 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -250,7 +250,7 @@ start_connection(Parent, Deb, Sock, SockTransform) -> frame_max = ?FRAME_MIN_SIZE, vhost = none, client_properties = none, - protocol = unknown}, + protocol = none}, callback = uninitialized_callback, recv_ref = none, connection_state = pre_init, @@ -731,6 +731,8 @@ i(state, #v1{connection_state = S}) -> S; i(channels, #v1{}) -> length(all_channels()); +i(protocol, #v1{connection = #connection{protocol = none}}) -> + none; i(protocol, #v1{connection = #connection{protocol = Protocol}}) -> Protocol:version(); i(user, #v1{connection = #connection{user = #user{username = Username}}}) -> -- cgit v1.2.1 From 3602aff0c871ce07b3c2f23b29d2c76becd485ce Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 7 Jul 2010 13:07:11 +0100 Subject: --allow-overwrite became --allow-accumulate. --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 88e13512..216934ae 100644 --- a/Makefile +++ b/Makefile @@ -101,10 +101,10 @@ $(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 --allow-overwrite header $(AMQP_SPEC_JSON_FILES_0_9_1) $(AMQP_SPEC_JSON_FILES_0_8) $@ + $(PYTHON) codegen.py --allow-accumulate header $(AMQP_SPEC_JSON_FILES_0_9_1) $(AMQP_SPEC_JSON_FILES_0_8) $@ $(INCLUDE_DIR)/rabbit_framing_spec.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 --allow-overwrite spec $(AMQP_SPEC_JSON_FILES_0_9_1) $(AMQP_SPEC_JSON_FILES_0_8) $@ + $(PYTHON) codegen.py --allow-accumulate spec $(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) $(PYTHON) codegen.py body $(AMQP_SPEC_JSON_FILES_0_9_1) $@ -- cgit v1.2.1 From 02d0fa2f5d69b916bc62351a65b809b37c2324ed Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 7 Jul 2010 13:44:28 +0100 Subject: More renaming. --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 216934ae..a5327547 100644 --- a/Makefile +++ b/Makefile @@ -101,10 +101,10 @@ $(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 --allow-accumulate header $(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_spec.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 --allow-accumulate spec $(AMQP_SPEC_JSON_FILES_0_9_1) $(AMQP_SPEC_JSON_FILES_0_8) $@ + $(PYTHON) codegen.py --ignore-conflicts spec $(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) $(PYTHON) codegen.py body $(AMQP_SPEC_JSON_FILES_0_9_1) $@ -- cgit v1.2.1 From 10bbf7e7bd0afb04ddf0bfc240b2f03f06c24283 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 9 Jul 2010 11:56:10 +0100 Subject: Store protocol with encoded properties, use it to transcode if needed. --- include/rabbit.hrl | 1 + src/rabbit_basic.erl | 6 +++--- src/rabbit_binary_generator.erl | 24 ++++++++++++++++-------- src/rabbit_binary_parser.erl | 9 +++++---- src/rabbit_channel.erl | 3 +-- src/rabbit_framing_channel.erl | 6 ++++-- 6 files changed, 30 insertions(+), 19 deletions(-) diff --git a/include/rabbit.hrl b/include/rabbit.hrl index 8c713a50..96cf844e 100644 --- a/include/rabbit.hrl +++ b/include/rabbit.hrl @@ -45,6 +45,7 @@ properties_bin, %% either 'none', or an encoded properties binary %% Note: at most one of properties and properties_bin can be %% 'none' at once. + protocol, %% The protocol under which properties_bin was encoded payload_fragments_rev %% list of binaries, in reverse order (!) }). diff --git a/src/rabbit_basic.erl b/src/rabbit_basic.erl index 4a1d50df..357333a9 100644 --- a/src/rabbit_basic.erl +++ b/src/rabbit_basic.erl @@ -86,16 +86,16 @@ build_content(Properties, BodyBin) -> #content{class_id = ClassId, properties = Properties, properties_bin = none, + protocol = rabbit_framing_amqp_0_9_1, payload_fragments_rev = [BodyBin]}. from_content(Content) -> #content{class_id = ClassId, properties = Props, payload_fragments_rev = FragmentsRev} = - %% basic.publish hasn't changed so we can just hard-code amqp_0_9_1 - rabbit_binary_parser:ensure_content_decoded(Content, - rabbit_framing_amqp_0_9_1), + rabbit_binary_parser:ensure_content_decoded(Content), {ClassId, _MethodId} = + %% basic.publish hasn't changed so we can just hard-code amqp_0_9_1 rabbit_framing_amqp_0_9_1:method_id('basic.publish'), {Props, list_to_binary(lists:reverse(FragmentsRev))}. diff --git a/src/rabbit_binary_generator.erl b/src/rabbit_binary_generator.erl index 75cd643c..200bf2cd 100644 --- a/src/rabbit_binary_generator.erl +++ b/src/rabbit_binary_generator.erl @@ -82,18 +82,25 @@ build_simple_content_frames(ChannelInt, #content{class_id = ClassId, properties = ContentProperties, properties_bin = ContentPropertiesBin, + protocol = ContentProtocol, payload_fragments_rev = PayloadFragmentsRev}, FrameMax, Protocol) -> {BodySize, ContentFrames} = build_content_frames(PayloadFragmentsRev, FrameMax, ChannelInt), HeaderFrame = create_frame(2, ChannelInt, [<>, - maybe_encode_properties(ContentProperties, ContentPropertiesBin, Protocol)]), + maybe_encode_properties(ContentProperties, + ContentPropertiesBin, + ContentProtocol, + Protocol)]), [HeaderFrame | ContentFrames]. -maybe_encode_properties(_ContentProperties, ContentPropertiesBin, _Protocol) +maybe_encode_properties(_ContentProperties, + ContentPropertiesBin, + Protocol, + Protocol) when is_binary(ContentPropertiesBin) -> ContentPropertiesBin; -maybe_encode_properties(ContentProperties, none, Protocol) -> +maybe_encode_properties(ContentProperties, none, _ContentProtocol, Protocol) -> Protocol:encode_properties(ContentProperties). build_content_frames(FragsRev, FrameMax, ChannelInt) -> @@ -278,13 +285,14 @@ check_empty_content_body_frame_size() -> ComputedSize, ?EMPTY_CONTENT_BODY_FRAME_SIZE}) end. -ensure_content_encoded(Content = #content{properties_bin = PropsBin}, _Protocol) - when PropsBin =/= 'none' -> +ensure_content_encoded(Content = #content{protocol = Protocol}, Protocol) -> Content; ensure_content_encoded(Content = #content{properties = Props}, Protocol) -> - Content#content{properties_bin = Protocol:encode_properties(Props)}. + Content#content{properties_bin = Protocol:encode_properties(Props), + protocol = Protocol}. -clear_encoded_content(Content = #content{properties_bin = none}) -> +clear_encoded_content(Content = #content{properties_bin = none, + protocol = none}) -> Content; clear_encoded_content(Content = #content{properties = none}) -> %% Only clear when we can rebuild the properties_bin later in @@ -292,4 +300,4 @@ clear_encoded_content(Content = #content{properties = none}) -> %% one of properties and properties_bin can be 'none' Content; clear_encoded_content(Content = #content{}) -> - Content#content{properties_bin = none}. + Content#content{properties_bin = none, protocol = none}. diff --git a/src/rabbit_binary_parser.erl b/src/rabbit_binary_parser.erl index 633be6f0..b1c7c327 100644 --- a/src/rabbit_binary_parser.erl +++ b/src/rabbit_binary_parser.erl @@ -34,7 +34,7 @@ -include("rabbit.hrl"). -export([parse_table/1, parse_properties/2]). --export([ensure_content_decoded/2, clear_decoded_content/1]). +-export([ensure_content_decoded/1, clear_decoded_content/1]). -import(lists). @@ -44,7 +44,7 @@ -spec(parse_table/1 :: (binary()) -> amqp_table()). -spec(parse_properties/2 :: ([amqp_property_type()], binary()) -> [any()]). --spec(ensure_content_decoded/2 :: (content(), protocol()) -> decoded_content()). +-spec(ensure_content_decoded/1 :: (content()) -> decoded_content()). -spec(clear_decoded_content/1 :: (content()) -> undecoded_content()). -endif. @@ -159,10 +159,11 @@ parse_property(bit, Rest) -> parse_property(table, <>) -> {parse_table(Table), Rest}. -ensure_content_decoded(Content = #content{properties = Props}, _Protocol) +ensure_content_decoded(Content = #content{properties = Props}) when Props =/= 'none' -> Content; -ensure_content_decoded(Content = #content{properties_bin = PropBin}, Protocol) +ensure_content_decoded(Content = #content{properties_bin = PropBin, + protocol = Protocol}) when is_binary(PropBin) -> Content#content{properties = Protocol:decode_properties( Content#content.class_id, PropBin)}. diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 213b6624..55b5d875 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -414,8 +414,7 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, Exchange = rabbit_exchange:lookup_or_die(ExchangeName), %% 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, rabbit_framing_amqp_0_9_1), + DecodedContent = rabbit_binary_parser:ensure_content_decoded(Content), IsPersistent = is_message_persistent(DecodedContent), Message = #basic_message{exchange_name = ExchangeName, routing_key = RoutingKey, diff --git a/src/rabbit_framing_channel.erl b/src/rabbit_framing_channel.erl index 1fee6b56..3d74b122 100644 --- a/src/rabbit_framing_channel.erl +++ b/src/rabbit_framing_channel.erl @@ -78,18 +78,20 @@ mainloop(ChannelPid, Protocol) -> case Protocol:method_has_content(MethodName) of true -> {ClassId, _MethodId} = Protocol:method_id(MethodName), rabbit_channel:do(ChannelPid, Method, - collect_content(ChannelPid, ClassId)); + collect_content(ChannelPid, ClassId, + Protocol)); false -> rabbit_channel:do(ChannelPid, Method) end, ?MODULE:mainloop(ChannelPid, Protocol). -collect_content(ChannelPid, ClassId) -> +collect_content(ChannelPid, ClassId, Protocol) -> case read_frame(ChannelPid) of {content_header, ClassId, 0, BodySize, PropertiesBin} -> Payload = collect_content_payload(ChannelPid, BodySize, []), #content{class_id = ClassId, properties = none, properties_bin = PropertiesBin, + protocol = Protocol, payload_fragments_rev = Payload}; {content_header, HeaderClassId, 0, _BodySize, _PropertiesBin} -> rabbit_misc:protocol_error( -- cgit v1.2.1 From 532eebc108d6ec315e3a88375ab36e41d70572e0 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 9 Jul 2010 11:56:47 +0100 Subject: Make sure one does not accidentally build a server containing old rabbit_framing when switching branches. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index a5327547..c30dedf6 100644 --- a/Makefile +++ b/Makefile @@ -135,7 +135,7 @@ $(BASIC_PLT): $(BEAM_TARGETS) clean: rm -f $(EBIN_DIR)/*.beam rm -f $(EBIN_DIR)/rabbit.app $(EBIN_DIR)/rabbit.boot $(EBIN_DIR)/rabbit.script $(EBIN_DIR)/rabbit.rel - rm -f $(INCLUDE_DIR)/rabbit_framing.hrl $(INCLUDE_DIR)/rabbit_framing_spec.hrl $(SOURCE_DIR)/rabbit_framing_amqp_*.erl codegen.pyc + rm -f $(INCLUDE_DIR)/rabbit_framing.hrl $(INCLUDE_DIR)/rabbit_framing_spec.hrl $(SOURCE_DIR)/rabbit_framing*.erl codegen.pyc rm -f $(DOCS_DIR)/*.[0-9].gz $(DOCS_DIR)/*.man.xml $(DOCS_DIR)/*.erl $(USAGES_ERL) rm -f $(RABBIT_PLT) rm -f $(DEPS_FILE) -- cgit v1.2.1 From 9a23f57b495dd616671880dcdab841b174766a82 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 9 Jul 2010 12:00:12 +0100 Subject: Fix idiocy --- src/rabbit_basic.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_basic.erl b/src/rabbit_basic.erl index 357333a9..3117e98e 100644 --- a/src/rabbit_basic.erl +++ b/src/rabbit_basic.erl @@ -86,7 +86,7 @@ build_content(Properties, BodyBin) -> #content{class_id = ClassId, properties = Properties, properties_bin = none, - protocol = rabbit_framing_amqp_0_9_1, + protocol = none, payload_fragments_rev = [BodyBin]}. from_content(Content) -> -- cgit v1.2.1 From 790be96b599b1089a86a0941aa9b07f5c38a13f9 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 9 Jul 2010 12:21:59 +0100 Subject: Don't change the signature of ensure_content_decoded. --- src/rabbit_basic.erl | 5 +++-- src/rabbit_binary_parser.erl | 9 ++++----- src/rabbit_channel.erl | 3 ++- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/rabbit_basic.erl b/src/rabbit_basic.erl index 3117e98e..6e8481ad 100644 --- a/src/rabbit_basic.erl +++ b/src/rabbit_basic.erl @@ -93,9 +93,10 @@ from_content(Content) -> #content{class_id = ClassId, properties = Props, payload_fragments_rev = FragmentsRev} = - rabbit_binary_parser:ensure_content_decoded(Content), - {ClassId, _MethodId} = %% basic.publish hasn't changed so we can just hard-code amqp_0_9_1 + rabbit_binary_parser:ensure_content_decoded(Content, + rabbit_framing_amqp_0_9_1), + {ClassId, _MethodId} = rabbit_framing_amqp_0_9_1:method_id('basic.publish'), {Props, list_to_binary(lists:reverse(FragmentsRev))}. diff --git a/src/rabbit_binary_parser.erl b/src/rabbit_binary_parser.erl index b1c7c327..633be6f0 100644 --- a/src/rabbit_binary_parser.erl +++ b/src/rabbit_binary_parser.erl @@ -34,7 +34,7 @@ -include("rabbit.hrl"). -export([parse_table/1, parse_properties/2]). --export([ensure_content_decoded/1, clear_decoded_content/1]). +-export([ensure_content_decoded/2, clear_decoded_content/1]). -import(lists). @@ -44,7 +44,7 @@ -spec(parse_table/1 :: (binary()) -> amqp_table()). -spec(parse_properties/2 :: ([amqp_property_type()], binary()) -> [any()]). --spec(ensure_content_decoded/1 :: (content()) -> decoded_content()). +-spec(ensure_content_decoded/2 :: (content(), protocol()) -> decoded_content()). -spec(clear_decoded_content/1 :: (content()) -> undecoded_content()). -endif. @@ -159,11 +159,10 @@ parse_property(bit, Rest) -> parse_property(table, <>) -> {parse_table(Table), Rest}. -ensure_content_decoded(Content = #content{properties = Props}) +ensure_content_decoded(Content = #content{properties = Props}, _Protocol) when Props =/= 'none' -> Content; -ensure_content_decoded(Content = #content{properties_bin = PropBin, - protocol = Protocol}) +ensure_content_decoded(Content = #content{properties_bin = PropBin}, Protocol) when is_binary(PropBin) -> Content#content{properties = Protocol:decode_properties( Content#content.class_id, PropBin)}. diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 55b5d875..213b6624 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -414,7 +414,8 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, Exchange = rabbit_exchange:lookup_or_die(ExchangeName), %% 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), + DecodedContent = rabbit_binary_parser:ensure_content_decoded( + Content, rabbit_framing_amqp_0_9_1), IsPersistent = is_message_persistent(DecodedContent), Message = #basic_message{exchange_name = ExchangeName, routing_key = RoutingKey, -- cgit v1.2.1 From 9b97df88bd72f2421e60dc9f5202927b49fab26e Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 9 Jul 2010 12:42:19 +0100 Subject: This wiped out rabbit_framing_channel. Never mind. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 360d0d2d..7c9690e3 100644 --- a/Makefile +++ b/Makefile @@ -132,7 +132,7 @@ $(BASIC_PLT): $(BEAM_TARGETS) clean: rm -f $(EBIN_DIR)/*.beam rm -f $(EBIN_DIR)/rabbit.app $(EBIN_DIR)/rabbit.boot $(EBIN_DIR)/rabbit.script $(EBIN_DIR)/rabbit.rel - rm -f $(INCLUDE_DIR)/rabbit_framing.hrl $(SOURCE_DIR)/rabbit_framing*.erl codegen.pyc + rm -f $(INCLUDE_DIR)/rabbit_framing.hrl $(SOURCE_DIR)/rabbit_framing_amqp_*.erl codegen.pyc rm -f $(DOCS_DIR)/*.[0-9].gz $(DOCS_DIR)/*.man.xml $(DOCS_DIR)/*.erl $(USAGES_ERL) rm -f $(RABBIT_PLT) rm -f $(DEPS_FILE) -- cgit v1.2.1 From 7fe083d06280c780af93caacbfad865fbfeb36a8 Mon Sep 17 00:00:00 2001 From: Emile Joubert Date: Fri, 9 Jul 2010 12:55:04 +0100 Subject: 0-9-1 error codes in a multi-prococol broker --- src/rabbit_framing_channel.erl | 34 ++++++++++++++++++++++------------ src/rabbit_reader.erl | 6 ------ 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/src/rabbit_framing_channel.erl b/src/rabbit_framing_channel.erl index bc1a2a08..889e9663 100644 --- a/src/rabbit_framing_channel.erl +++ b/src/rabbit_framing_channel.erl @@ -73,15 +73,25 @@ read_frame(ChannelPid) -> end. mainloop(ChannelPid) -> - {method, MethodName, FieldsBin} = read_frame(ChannelPid), - Method = rabbit_framing:decode_method_fields(MethodName, FieldsBin), - case rabbit_framing:method_has_content(MethodName) of - true -> {ClassId, _MethodId} = rabbit_framing:method_id(MethodName), - rabbit_channel:do(ChannelPid, Method, - collect_content(ChannelPid, ClassId)); - false -> rabbit_channel:do(ChannelPid, Method) - end, - ?MODULE:mainloop(ChannelPid). + Decoded = read_frame(ChannelPid), + case Decoded of + {method, MethodName, FieldsBin} -> + Method = rabbit_framing:decode_method_fields(MethodName, FieldsBin), + case rabbit_framing:method_has_content(MethodName) of + true -> {ClassId, _MethodId} = + rabbit_framing:method_id(MethodName), + rabbit_channel:do(ChannelPid, Method, + collect_content(ChannelPid, + ClassId)); + false -> rabbit_channel:do(ChannelPid, Method) + end, + ?MODULE:mainloop(ChannelPid); + _ -> + rabbit_misc:protocol_error( + unexpected_frame, + "expected method frame, got ~p instead", + [Decoded]) + end. collect_content(ChannelPid, ClassId) -> case read_frame(ChannelPid) of @@ -93,13 +103,13 @@ collect_content(ChannelPid, ClassId) -> payload_fragments_rev = Payload}; {content_header, HeaderClassId, 0, _BodySize, _PropertiesBin} -> rabbit_misc:protocol_error( - command_invalid, + unexpected_frame, "expected content header for class ~w, " "got one for class ~w instead", [ClassId, HeaderClassId]); _ -> rabbit_misc:protocol_error( - command_invalid, + unexpected_frame, "expected content header for class ~w, " "got non content header frame instead", [ClassId]) @@ -115,7 +125,7 @@ collect_content_payload(ChannelPid, RemainingByteCount, Acc) -> [FragmentBin | Acc]); _ -> rabbit_misc:protocol_error( - command_invalid, + unexpected_frame, "expected content body, got non content body frame instead", []) end. diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index b5514c82..ccd51ffa 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -823,12 +823,6 @@ map_exception(Channel, Reason) -> end, {ShouldClose, CloseChannel, CloseMethod}. -%% FIXME: this clause can go when we move to AMQP spec >=8.1 -lookup_amqp_exception(#amqp_error{name = precondition_failed, - explanation = Expl, - method = Method}) -> - ExplBin = amqp_exception_explanation(<<"PRECONDITION_FAILED">>, Expl), - {false, 406, ExplBin, Method}; lookup_amqp_exception(#amqp_error{name = Name, explanation = Expl, method = Method}) -> -- cgit v1.2.1 From 6a7051a092ed7d64e20a69c885f714914bf3ca1a Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 9 Jul 2010 14:14:50 +0100 Subject: don't log content on framing error plus some minor refactors and cosmetic changes --- src/rabbit_framing_channel.erl | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/rabbit_framing_channel.erl b/src/rabbit_framing_channel.erl index 889e9663..6f14371f 100644 --- a/src/rabbit_framing_channel.erl +++ b/src/rabbit_framing_channel.erl @@ -73,24 +73,22 @@ read_frame(ChannelPid) -> end. mainloop(ChannelPid) -> - Decoded = read_frame(ChannelPid), - case Decoded of + case read_frame(ChannelPid) of {method, MethodName, FieldsBin} -> Method = rabbit_framing:decode_method_fields(MethodName, FieldsBin), case rabbit_framing:method_has_content(MethodName) of - true -> {ClassId, _MethodId} = + true -> {ClassId, _MethodId} = rabbit_framing:method_id(MethodName), - rabbit_channel:do(ChannelPid, Method, - collect_content(ChannelPid, - ClassId)); + Content = collect_content(ChannelPid, ClassId), + rabbit_channel:do(ChannelPid, Method, Content); false -> rabbit_channel:do(ChannelPid, Method) end, ?MODULE:mainloop(ChannelPid); _ -> rabbit_misc:protocol_error( unexpected_frame, - "expected method frame, got ~p instead", - [Decoded]) + "expected method frame, got non method frame instead", + []) end. collect_content(ChannelPid, ClassId) -> -- cgit v1.2.1 From f3de9bddb339f665a314cb73440d5afbcf5dfcf2 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 9 Jul 2010 14:25:44 +0100 Subject: refactor: extract error helper --- src/rabbit_framing_channel.erl | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/src/rabbit_framing_channel.erl b/src/rabbit_framing_channel.erl index 6f14371f..cb06ac7b 100644 --- a/src/rabbit_framing_channel.erl +++ b/src/rabbit_framing_channel.erl @@ -85,10 +85,9 @@ mainloop(ChannelPid) -> end, ?MODULE:mainloop(ChannelPid); _ -> - rabbit_misc:protocol_error( - unexpected_frame, - "expected method frame, got non method frame instead", - []) + unexpected_frame("expected method frame, " + "got non method frame instead", + []) end. collect_content(ChannelPid, ClassId) -> @@ -100,17 +99,13 @@ collect_content(ChannelPid, ClassId) -> properties_bin = PropertiesBin, payload_fragments_rev = Payload}; {content_header, HeaderClassId, 0, _BodySize, _PropertiesBin} -> - rabbit_misc:protocol_error( - unexpected_frame, - "expected content header for class ~w, " - "got one for class ~w instead", - [ClassId, HeaderClassId]); + unexpected_frame("expected content header for class ~w, " + "got one for class ~w instead", + [ClassId, HeaderClassId]); _ -> - rabbit_misc:protocol_error( - unexpected_frame, - "expected content header for class ~w, " - "got non content header frame instead", - [ClassId]) + unexpected_frame("expected content header for class ~w, " + "got non content header frame instead", + [ClassId]) end. collect_content_payload(_ChannelPid, 0, Acc) -> @@ -122,8 +117,10 @@ collect_content_payload(ChannelPid, RemainingByteCount, Acc) -> RemainingByteCount - size(FragmentBin), [FragmentBin | Acc]); _ -> - rabbit_misc:protocol_error( - unexpected_frame, - "expected content body, got non content body frame instead", - []) + unexpected_frame("expected content body, " + "got non content body frame instead", + []) end. + +unexpected_frame(ExplanationFormat, Params) -> + rabbit_misc:protocol_error(unexpected_frame, ExplanationFormat, Params). -- cgit v1.2.1 From 74290848777c991d4fce71a879139df7f15dcef7 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 9 Jul 2010 17:19:05 +0100 Subject: put the 'none' check back into ensure_content_encoded it's safer and more obviously correct that way --- src/rabbit_binary_generator.erl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/rabbit_binary_generator.erl b/src/rabbit_binary_generator.erl index e2c56710..07f25c15 100644 --- a/src/rabbit_binary_generator.erl +++ b/src/rabbit_binary_generator.erl @@ -292,7 +292,9 @@ check_empty_content_body_frame_size() -> ComputedSize, ?EMPTY_CONTENT_BODY_FRAME_SIZE}) end. -ensure_content_encoded(Content = #content{protocol = Protocol}, Protocol) -> +ensure_content_encoded(Content = #content{properties_bin = PropsBin, + protocol = Protocol}, Protocol) + when PropsBin =/= 'none' -> Content; ensure_content_encoded(Content = #content{properties = Props}, Protocol) -> Content#content{properties_bin = Protocol:encode_properties(Props), -- cgit v1.2.1 From dee4454459f6f98b04512f900f4e848e10559b44 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 9 Jul 2010 17:21:23 +0100 Subject: export protocol/0 type to avoid compiler warning --- src/rabbit_types.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_types.erl b/src/rabbit_types.erl index 402ab1e8..3aaf1917 100644 --- a/src/rabbit_types.erl +++ b/src/rabbit_types.erl @@ -39,8 +39,8 @@ delivery/0, content/0, decoded_content/0, undecoded_content/0, unencoded_content/0, encoded_content/0, vhost/0, ctag/0, amqp_error/0, r/1, r2/2, r3/3, ssl_socket/0, listener/0, - binding/0, amqqueue/0, exchange/0, connection/0, user/0, - error/1, ok_or_error/1, ok_or_error2/2, ok/1]). + binding/0, amqqueue/0, exchange/0, connection/0, protocol/0, + user/0, error/1, ok_or_error/1, ok_or_error2/2, ok/1]). -type(maybe(T) :: T | 'none'). -type(vhost() :: binary()). -- cgit v1.2.1 From 67837fa15b562bd69ab44bfe9d0986f4ad8761ae Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 9 Jul 2010 17:29:19 +0100 Subject: version should come first --- codegen.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/codegen.py b/codegen.py index 668a9d5a..230d785e 100644 --- a/codegen.py +++ b/codegen.py @@ -323,8 +323,8 @@ def genErl(spec): print "-module(%s)." % module print """-include("rabbit_framing.hrl"). +-export([version/0]). -export([lookup_method_name/1]). - -export([method_id/1]). -export([method_has_content/1]). -export([is_method_synchronous/1]). @@ -336,7 +336,6 @@ def genErl(spec): -export([encode_properties/1]). -export([lookup_amqp_exception/1]). -export([amqp_exception/1]). --export([version/0]). """ print "%% Various types" @@ -401,6 +400,7 @@ def genErl(spec): print """ %% Method signatures -ifdef(use_specs). +-spec(version/0 :: () -> {non_neg_integer(), non_neg_integer(), non_neg_integer()}). -spec(lookup_method_name/1 :: (amqp_method()) -> amqp_method_name()). -spec(method_id/1 :: (amqp_method_name()) -> amqp_method()). -spec(method_has_content/1 :: (amqp_method_name()) -> boolean()). @@ -413,13 +413,16 @@ def genErl(spec): -spec(encode_properties/1 :: (amqp_method_record()) -> binary()). -spec(lookup_amqp_exception/1 :: (amqp_exception()) -> {boolean(), amqp_exception_code(), binary()}). -spec(amqp_exception/1 :: (amqp_exception_code()) -> amqp_exception()). --spec(version/0 :: () -> {non_neg_integer(), non_neg_integer(), non_neg_integer()}). -endif. % use_specs bitvalue(true) -> 1; bitvalue(false) -> 0; bitvalue(undefined) -> 0. """ + version = "{%d, %d, %d}" % (spec.major, spec.minor, spec.revision) + if version == '{8, 0, 0}': version = '{0, 8, 0}' + print "version() -> %s." % (version) + for m in methods: genLookupMethodName(m) print "lookup_method_name({_ClassId, _MethodId} = Id) -> exit({unknown_method_id, Id})." @@ -459,10 +462,6 @@ bitvalue(undefined) -> 0. for(c,v,cls) in spec.constants: genAmqpException(c,v,cls) print "amqp_exception(_Code) -> undefined." - version = "{%d, %d, %d}" % (spec.major, spec.minor, spec.revision) - if version == '{8, 0, 0}': version = '{0, 8, 0}' - print "version() -> %s." % (version) - def genHrl(spec): def erlType(domain): return erlangTypeMap[spec.resolveDomain(domain)] -- cgit v1.2.1 From e7841144e411079a7e65e0457532461caf9abfa4 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 9 Jul 2010 17:51:50 +0100 Subject: Remove maybe_encode_properties --- src/rabbit_binary_generator.erl | 29 ++++++++--------------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/src/rabbit_binary_generator.erl b/src/rabbit_binary_generator.erl index 07f25c15..f0ec6180 100644 --- a/src/rabbit_binary_generator.erl +++ b/src/rabbit_binary_generator.erl @@ -85,31 +85,18 @@ build_simple_method_frame(ChannelInt, MethodRecord, Protocol) -> {ClassId, MethodId} = Protocol:method_id(MethodName), create_frame(1, ChannelInt, [<>, MethodFields]). -build_simple_content_frames(ChannelInt, - #content{class_id = ClassId, - properties = ContentProperties, - properties_bin = ContentPropertiesBin, - protocol = ContentProtocol, - payload_fragments_rev = PayloadFragmentsRev}, - FrameMax, Protocol) -> - {BodySize, ContentFrames} = build_content_frames(PayloadFragmentsRev, FrameMax, ChannelInt), +build_simple_content_frames(ChannelInt, Content, FrameMax, Protocol) -> + #content{class_id = ClassId, + properties_bin = ContentPropertiesBin, + payload_fragments_rev = PayloadFragmentsRev} = + ensure_content_encoded(Content, Protocol), + {BodySize, ContentFrames} = + build_content_frames(PayloadFragmentsRev, FrameMax, ChannelInt), HeaderFrame = create_frame(2, ChannelInt, [<>, - maybe_encode_properties(ContentProperties, - ContentPropertiesBin, - ContentProtocol, - Protocol)]), + ContentPropertiesBin]), [HeaderFrame | ContentFrames]. -maybe_encode_properties(_ContentProperties, - ContentPropertiesBin, - Protocol, - Protocol) - when is_binary(ContentPropertiesBin) -> - ContentPropertiesBin; -maybe_encode_properties(ContentProperties, none, _ContentProtocol, Protocol) -> - Protocol:encode_properties(ContentProperties). - build_content_frames(FragsRev, FrameMax, ChannelInt) -> BodyPayloadMax = if FrameMax == 0 -> iolist_size(FragsRev); -- cgit v1.2.1 From 71e7faa6624a7cbde728398392b671b7927bb17b Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Tue, 13 Jul 2010 15:16:45 +0100 Subject: added necessary fields to records --- include/rabbit.hrl | 2 +- src/rabbit_access_control.erl | 15 +++++++++++++-- src/rabbit_control.erl | 6 ++++++ 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/include/rabbit.hrl b/include/rabbit.hrl index 3fd52568..e888b430 100644 --- a/include/rabbit.hrl +++ b/include/rabbit.hrl @@ -30,7 +30,7 @@ %% -record(user, {username, password}). --record(permission, {configure, write, read}). +-record(permission, {check_all, configure, write, read}). -record(user_vhost, {username, virtual_host}). -record(user_permission, {user_vhost, permission}). diff --git a/src/rabbit_access_control.erl b/src/rabbit_access_control.erl index 7d1839bb..a5d624d7 100644 --- a/src/rabbit_access_control.erl +++ b/src/rabbit_access_control.erl @@ -38,7 +38,7 @@ -export([add_user/2, delete_user/1, change_password/2, list_users/0, lookup_user/1]). -export([add_vhost/1, delete_vhost/1, list_vhosts/0]). --export([set_permissions/5, clear_permissions/2, +-export([set_permissions/5, set_permissions_all/5, clear_permissions/2, list_vhost_permissions/1, list_user_permissions/1]). %%---------------------------------------------------------------------------- @@ -149,6 +149,7 @@ check_vhost_access(#user{username = Username}, VHostPath) -> [VHostPath, Username]) end. +permission_index(check_all) -> #permission.check_all; permission_index(configure) -> #permission.configure; permission_index(write) -> #permission.write; permission_index(read) -> #permission.read. @@ -306,7 +307,8 @@ validate_regexp(RegexpBin) -> {error, Reason} -> throw({error, {invalid_regexp, Regexp, Reason}}) end. -set_permissions(Username, VHostPath, ConfigurePerm, WritePerm, ReadPerm) -> +set_permissions_internal(Username, VHostPath, CheckAll, ConfigurePerm, + WritePerm, ReadPerm) -> lists:map(fun validate_regexp/1, [ConfigurePerm, WritePerm, ReadPerm]), rabbit_misc:execute_mnesia_transaction( rabbit_misc:with_user_and_vhost( @@ -317,12 +319,21 @@ set_permissions(Username, VHostPath, ConfigurePerm, WritePerm, ReadPerm) -> username = Username, virtual_host = VHostPath}, permission = #permission{ + check_all = CheckAll, configure = ConfigurePerm, write = WritePerm, read = ReadPerm}}, write) end)). +set_permissions(Username, VHostPath, ConfigurePerm, WritePerm, ReadPerm) -> + set_permissions_internal(Username, VHostPath, 'false', ConfigurePerm, + WritePerm, ReadPerm). + +set_permissions_all(Username, VHostPath, ConfigurePerm, WritePerm, ReadPerm) -> + set_permissions_internal(Username, VHostPath, 'true', ConfigurePerm, + WritePerm, ReadPerm). + clear_permissions(Username, VHostPath) -> rabbit_misc:execute_mnesia_transaction( rabbit_misc:with_user_and_vhost( diff --git a/src/rabbit_control.erl b/src/rabbit_control.erl index 6e6ad06c..89068329 100644 --- a/src/rabbit_control.erl +++ b/src/rabbit_control.erl @@ -276,6 +276,12 @@ action(set_permissions, Node, VHost, [Username, CPerm, WPerm, RPerm], Inform) -> call(Node, {rabbit_access_control, set_permissions, [Username, VHost, CPerm, WPerm, RPerm]}); +action(set_permissions_all, Node, VHost, [Username, CPerm, WPerm, RPerm], Inform) -> + Inform("Setting permissions for all resources for user ~p in vhost ~p", + [Username, VHost]), + call(Node, {rabbit_access_control, set_permissions_all, + [Username, VHost, CPerm, WPerm, RPerm]}); + action(clear_permissions, Node, VHost, [Username], Inform) -> Inform("Clearing permissions for user ~p in vhost ~p", [Username, VHost]), call(Node, {rabbit_access_control, clear_permissions, [Username, VHost]}); -- cgit v1.2.1 From c744c947d82f7d08d19dce5fd88069fbd796d949 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Tue, 13 Jul 2010 15:37:40 +0100 Subject: check_all == 'true' => permissions are checked for server-generated names as well --- src/rabbit_access_control.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_access_control.erl b/src/rabbit_access_control.erl index a5d624d7..d14aaedf 100644 --- a/src/rabbit_access_control.erl +++ b/src/rabbit_access_control.erl @@ -162,7 +162,7 @@ check_resource_access(Username, Permission); check_resource_access(_Username, #resource{name = <<"amq.gen",_/binary>>}, - _Permission) -> + #permission{check_all = 'false'}) -> ok; check_resource_access(Username, R = #resource{virtual_host = VHostPath, name = Name}, -- cgit v1.2.1 From 8d51b804004b78eb1fa6eeb80559919e5aac6008 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Tue, 13 Jul 2010 15:51:41 +0100 Subject: permission tags are now displayed --- include/rabbit.hrl | 2 +- src/rabbit_access_control.erl | 23 ++++++++++++----------- src/rabbit_control.erl | 2 ++ 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/include/rabbit.hrl b/include/rabbit.hrl index e888b430..653ad2fd 100644 --- a/include/rabbit.hrl +++ b/include/rabbit.hrl @@ -30,7 +30,7 @@ %% -record(user, {username, password}). --record(permission, {check_all, configure, write, read}). +-record(permission, {check, configure, write, read}). -record(user_vhost, {username, virtual_host}). -record(user_permission, {user_vhost, permission}). diff --git a/src/rabbit_access_control.erl b/src/rabbit_access_control.erl index d14aaedf..86acbe43 100644 --- a/src/rabbit_access_control.erl +++ b/src/rabbit_access_control.erl @@ -149,7 +149,7 @@ check_vhost_access(#user{username = Username}, VHostPath) -> [VHostPath, Username]) end. -permission_index(check_all) -> #permission.check_all; +permission_index(check) -> #permission.check; permission_index(configure) -> #permission.configure; permission_index(write) -> #permission.write; permission_index(read) -> #permission.read. @@ -162,7 +162,7 @@ check_resource_access(Username, Permission); check_resource_access(_Username, #resource{name = <<"amq.gen",_/binary>>}, - #permission{check_all = 'false'}) -> + #permission{check = 'check_user_named'}) -> ok; check_resource_access(Username, R = #resource{virtual_host = VHostPath, name = Name}, @@ -307,7 +307,7 @@ validate_regexp(RegexpBin) -> {error, Reason} -> throw({error, {invalid_regexp, Regexp, Reason}}) end. -set_permissions_internal(Username, VHostPath, CheckAll, ConfigurePerm, +set_permissions_internal(Username, VHostPath, Check, ConfigurePerm, WritePerm, ReadPerm) -> lists:map(fun validate_regexp/1, [ConfigurePerm, WritePerm, ReadPerm]), rabbit_misc:execute_mnesia_transaction( @@ -319,7 +319,7 @@ set_permissions_internal(Username, VHostPath, CheckAll, ConfigurePerm, username = Username, virtual_host = VHostPath}, permission = #permission{ - check_all = CheckAll, + check = Check, configure = ConfigurePerm, write = WritePerm, read = ReadPerm}}, @@ -327,11 +327,11 @@ set_permissions_internal(Username, VHostPath, CheckAll, ConfigurePerm, end)). set_permissions(Username, VHostPath, ConfigurePerm, WritePerm, ReadPerm) -> - set_permissions_internal(Username, VHostPath, 'false', ConfigurePerm, + set_permissions_internal(Username, VHostPath, 'check_user_named', ConfigurePerm, WritePerm, ReadPerm). set_permissions_all(Username, VHostPath, ConfigurePerm, WritePerm, ReadPerm) -> - set_permissions_internal(Username, VHostPath, 'true', ConfigurePerm, + set_permissions_internal(Username, VHostPath, 'check_all_resources', ConfigurePerm, WritePerm, ReadPerm). clear_permissions(Username, VHostPath) -> @@ -345,22 +345,23 @@ clear_permissions(Username, VHostPath) -> end)). list_vhost_permissions(VHostPath) -> - [{Username, ConfigurePerm, WritePerm, ReadPerm} || - {Username, _, ConfigurePerm, WritePerm, ReadPerm} <- + [{Username, ConfigurePerm, WritePerm, ReadPerm, Check} || + {Username, _, ConfigurePerm, WritePerm, ReadPerm, Check} <- list_permissions(rabbit_misc:with_vhost( VHostPath, match_user_vhost('_', VHostPath)))]. list_user_permissions(Username) -> - [{VHostPath, ConfigurePerm, WritePerm, ReadPerm} || - {_, VHostPath, ConfigurePerm, WritePerm, ReadPerm} <- + [{VHostPath, ConfigurePerm, WritePerm, ReadPerm, Check} || + {_, VHostPath, ConfigurePerm, WritePerm, ReadPerm, Check} <- list_permissions(rabbit_misc:with_user( Username, match_user_vhost(Username, '_')))]. list_permissions(QueryThunk) -> - [{Username, VHostPath, ConfigurePerm, WritePerm, ReadPerm} || + [{Username, VHostPath, ConfigurePerm, WritePerm, ReadPerm, Check} || #user_permission{user_vhost = #user_vhost{username = Username, virtual_host = VHostPath}, permission = #permission{ + check = Check, configure = ConfigurePerm, write = WritePerm, read = ReadPerm}} <- diff --git a/src/rabbit_control.erl b/src/rabbit_control.erl index 89068329..2f13a0a6 100644 --- a/src/rabbit_control.erl +++ b/src/rabbit_control.erl @@ -363,6 +363,8 @@ rpc_call(Node, Mod, Fun, Args) -> %% characters. We don't escape characters above 127, since they may %% form part of UTF-8 strings. +escape(Atom) when is_atom(Atom) -> + escape(atom_to_list(Atom)); escape(Bin) when is_binary(Bin) -> escape(binary_to_list(Bin)); escape(L) when is_list(L) -> -- cgit v1.2.1 From 223bb54880d57c520938a3f884b684b955d5d0d9 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Tue, 13 Jul 2010 16:10:37 +0100 Subject: fixed compile errors; all tests pass --- src/rabbit_access_control.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_access_control.erl b/src/rabbit_access_control.erl index 86acbe43..4863c9d9 100644 --- a/src/rabbit_access_control.erl +++ b/src/rabbit_access_control.erl @@ -290,7 +290,7 @@ internal_delete_vhost(VHostPath) -> ok = rabbit_exchange:delete(Name, false) end, rabbit_exchange:list(VHostPath)), - lists:foreach(fun ({Username, _, _, _}) -> + lists:foreach(fun ({Username, _, _, _, _}) -> ok = clear_permissions(Username, VHostPath) end, list_vhost_permissions(VHostPath)), -- cgit v1.2.1 From 0ad661ff7d87d9ef68b9ba41f7a335d4a3a83b77 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Wed, 14 Jul 2010 10:56:38 +0100 Subject: added a flag to set_permissions to control their scope set_permissions -check_user_named is the default and checks permissions only for user named permissions. I.e. server generated names are not checked. set_permissions -check_all_resources enables the check for server generated names. I had to shorten the argument names in the man page because otherwise the set_permissions line would have exceeded 80 characters. All parameters passed from rabbit_control to rabbit_access_control are binary(), hence why we're passing <<"check_user_name">> rather than 'check_user_named'. Rabbit_access_control:set_permissions now takes 6 parameters. There's also a 5 parameter version that sets the default value for Check and calls the other one. I've added it because I don't want the default value for Check in 10 different places. --- docs/rabbitmqctl.1.xml | 13 ++++++++++--- src/rabbit_access_control.erl | 26 +++++++++++++++----------- src/rabbit_control.erl | 17 +++++++++-------- 3 files changed, 34 insertions(+), 22 deletions(-) diff --git a/docs/rabbitmqctl.1.xml b/docs/rabbitmqctl.1.xml index 26863ae7..74ac9568 100644 --- a/docs/rabbitmqctl.1.xml +++ b/docs/rabbitmqctl.1.xml @@ -547,7 +547,7 @@ - set_permissions -p vhostpath username configure write read + set_permissions -p vhostpath -check user conf write read @@ -555,11 +555,18 @@ The name of the virtual host to which to grant the user access, defaulting to /. - username + check + Which resources should permissions be + checked for? Either + check_user_named (the default) or + check_all_resources. + + + user The name of the user to grant access to the specified virtual host. - configure + conf A regular expression matching resource names for which the user is granted configure permissions. diff --git a/src/rabbit_access_control.erl b/src/rabbit_access_control.erl index 4863c9d9..e4f557cc 100644 --- a/src/rabbit_access_control.erl +++ b/src/rabbit_access_control.erl @@ -38,7 +38,7 @@ -export([add_user/2, delete_user/1, change_password/2, list_users/0, lookup_user/1]). -export([add_vhost/1, delete_vhost/1, list_vhosts/0]). --export([set_permissions/5, set_permissions_all/5, clear_permissions/2, +-export([set_permissions/5, set_permissions/6, clear_permissions/2, list_vhost_permissions/1, list_user_permissions/1]). %%---------------------------------------------------------------------------- @@ -51,6 +51,7 @@ -type(username() :: binary()). -type(password() :: binary()). -type(regexp() :: binary()). +-type(check_flag() :: binary()). -spec(check_login/2 :: (binary(), binary()) -> rabbit_types:user()). -spec(user_pass_login/2 :: (username(), password()) -> rabbit_types:user()). @@ -70,6 +71,8 @@ -spec(list_vhosts/0 :: () -> [rabbit_types:vhost()]). -spec(set_permissions/5 ::(username(), rabbit_types:vhost(), regexp(), regexp(), regexp()) -> 'ok'). +-spec(set_permissions/6 ::(check_flag(), username(), rabbit_types:vhost(), + regexp(), regexp(), regexp()) -> 'ok'). -spec(clear_permissions/2 :: (username(), rabbit_types:vhost()) -> 'ok'). -spec(list_vhost_permissions/1 :: (rabbit_types:vhost()) @@ -307,9 +310,17 @@ validate_regexp(RegexpBin) -> {error, Reason} -> throw({error, {invalid_regexp, Regexp, Reason}}) end. -set_permissions_internal(Username, VHostPath, Check, ConfigurePerm, - WritePerm, ReadPerm) -> +set_permissions(Username, VHostPath, ConfigurePerm, WritePerm, ReadPerm) -> + set_permissions(<<"check_user_named">>, Username, VHostPath, ConfigurePerm, + WritePerm, ReadPerm). + +set_permissions(Check, Username, VHostPath, ConfigurePerm, WritePerm, ReadPerm) -> lists:map(fun validate_regexp/1, [ConfigurePerm, WritePerm, ReadPerm]), + Check1 = case Check of + <<"check_user_named">> -> check_user_named; + <<"check_all_resources">> -> check_all_resources; + _ -> throw({error, {invalid_check_flag, Check}}) + end, rabbit_misc:execute_mnesia_transaction( rabbit_misc:with_user_and_vhost( Username, VHostPath, @@ -319,20 +330,13 @@ set_permissions_internal(Username, VHostPath, Check, ConfigurePerm, username = Username, virtual_host = VHostPath}, permission = #permission{ - check = Check, + check = Check1, configure = ConfigurePerm, write = WritePerm, read = ReadPerm}}, write) end)). -set_permissions(Username, VHostPath, ConfigurePerm, WritePerm, ReadPerm) -> - set_permissions_internal(Username, VHostPath, 'check_user_named', ConfigurePerm, - WritePerm, ReadPerm). - -set_permissions_all(Username, VHostPath, ConfigurePerm, WritePerm, ReadPerm) -> - set_permissions_internal(Username, VHostPath, 'check_all_resources', ConfigurePerm, - WritePerm, ReadPerm). clear_permissions(Username, VHostPath) -> rabbit_misc:execute_mnesia_transaction( diff --git a/src/rabbit_control.erl b/src/rabbit_control.erl index 2f13a0a6..602b4660 100644 --- a/src/rabbit_control.erl +++ b/src/rabbit_control.erl @@ -271,16 +271,17 @@ action(Command, Node, Args, Inform) -> {VHost, RemainingArgs} = parse_vhost_flag(Args), action(Command, Node, VHost, RemainingArgs, Inform). -action(set_permissions, Node, VHost, [Username, CPerm, WPerm, RPerm], Inform) -> +action(set_permissions, Node, VHost, Args, Inform) -> + {Check, [Username, CPerm, WPerm, RPerm]} = + case Args of + [[$- | Flag] | RemainingArgs] -> + {Flag, RemainingArgs}; + RemainingArgs -> + {"check_user_named", RemainingArgs} + end, Inform("Setting permissions for user ~p in vhost ~p", [Username, VHost]), call(Node, {rabbit_access_control, set_permissions, - [Username, VHost, CPerm, WPerm, RPerm]}); - -action(set_permissions_all, Node, VHost, [Username, CPerm, WPerm, RPerm], Inform) -> - Inform("Setting permissions for all resources for user ~p in vhost ~p", - [Username, VHost]), - call(Node, {rabbit_access_control, set_permissions_all, - [Username, VHost, CPerm, WPerm, RPerm]}); + [Check, Username, VHost, CPerm, WPerm, RPerm]}); action(clear_permissions, Node, VHost, [Username], Inform) -> Inform("Clearing permissions for user ~p in vhost ~p", [Username, VHost]), -- cgit v1.2.1 From 7418d7a8459f94bbb305c0ae7faaec5a9293105d Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Wed, 14 Jul 2010 12:03:25 +0100 Subject: added coverage tests --- src/rabbit_tests.erl | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index ff7c07e3..e1c4db33 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -747,6 +747,8 @@ test_user_management() -> control_action(list_permissions, ["-p", "/testhost"]), {error, {invalid_regexp, _, _}} = control_action(set_permissions, ["guest", "+foo", ".*", ".*"]), + {error, {invalid_check_flag, _}} = + control_action(set_permissions, ["-check_mate", "guest", "foo", ".*", ".*"]), %% user creation ok = control_action(add_user, ["foo", "bar"]), @@ -766,6 +768,13 @@ test_user_management() -> "foo", ".*", ".*", ".*"]), ok = control_action(set_permissions, ["-p", "/testhost", "foo", ".*", ".*", ".*"]), + ok = control_action(set_permissions, ["-p", "/testhost", + "-check_user_named", + "foo", ".*", ".*", ".*"]), + ok = control_action(set_permissions, ["-p", "/testhost", + "-check_all_resources", + "foo", ".*", ".*", ".*"]), + ok = control_action(list_permissions, ["-p", "/testhost"]), ok = control_action(list_permissions, ["-p", "/testhost"]), ok = control_action(list_user_permissions, ["foo"]), -- cgit v1.2.1 From 2d0c1097cfef5dd950586e495c447b744a624aae Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Wed, 14 Jul 2010 12:52:28 +0100 Subject: scope is not set via the -s flag --- docs/rabbitmqctl.1.xml | 11 +++++------ include/rabbit.hrl | 2 +- src/rabbit_access_control.erl | 34 +++++++++++++++++----------------- src/rabbit_control.erl | 10 +++++----- src/rabbit_tests.erl | 8 ++++---- 5 files changed, 32 insertions(+), 33 deletions(-) diff --git a/docs/rabbitmqctl.1.xml b/docs/rabbitmqctl.1.xml index 74ac9568..836af264 100644 --- a/docs/rabbitmqctl.1.xml +++ b/docs/rabbitmqctl.1.xml @@ -547,7 +547,7 @@ - set_permissions -p vhostpath -check user conf write read + set_permissions -p vhostpath -s scope user conf write read @@ -555,11 +555,10 @@ The name of the virtual host to which to grant the user access, defaulting to /. - check - Which resources should permissions be - checked for? Either - check_user_named (the default) or - check_all_resources. + scope + Scope of the permissions: either + client (the default) or + all. user diff --git a/include/rabbit.hrl b/include/rabbit.hrl index 653ad2fd..49039978 100644 --- a/include/rabbit.hrl +++ b/include/rabbit.hrl @@ -30,7 +30,7 @@ %% -record(user, {username, password}). --record(permission, {check, configure, write, read}). +-record(permission, {scope, configure, write, read}). -record(user_vhost, {username, virtual_host}). -record(user_permission, {user_vhost, permission}). diff --git a/src/rabbit_access_control.erl b/src/rabbit_access_control.erl index e4f557cc..5578e24b 100644 --- a/src/rabbit_access_control.erl +++ b/src/rabbit_access_control.erl @@ -51,7 +51,7 @@ -type(username() :: binary()). -type(password() :: binary()). -type(regexp() :: binary()). --type(check_flag() :: binary()). +-type(scope() :: binary()). -spec(check_login/2 :: (binary(), binary()) -> rabbit_types:user()). -spec(user_pass_login/2 :: (username(), password()) -> rabbit_types:user()). @@ -71,7 +71,7 @@ -spec(list_vhosts/0 :: () -> [rabbit_types:vhost()]). -spec(set_permissions/5 ::(username(), rabbit_types:vhost(), regexp(), regexp(), regexp()) -> 'ok'). --spec(set_permissions/6 ::(check_flag(), username(), rabbit_types:vhost(), +-spec(set_permissions/6 ::(scope(), username(), rabbit_types:vhost(), regexp(), regexp(), regexp()) -> 'ok'). -spec(clear_permissions/2 :: (username(), rabbit_types:vhost()) -> 'ok'). -spec(list_vhost_permissions/1 :: @@ -152,7 +152,7 @@ check_vhost_access(#user{username = Username}, VHostPath) -> [VHostPath, Username]) end. -permission_index(check) -> #permission.check; +permission_index(scope) -> #permission.scope; permission_index(configure) -> #permission.configure; permission_index(write) -> #permission.write; permission_index(read) -> #permission.read. @@ -165,7 +165,7 @@ check_resource_access(Username, Permission); check_resource_access(_Username, #resource{name = <<"amq.gen",_/binary>>}, - #permission{check = 'check_user_named'}) -> + #permission{scope = 'client'}) -> ok; check_resource_access(Username, R = #resource{virtual_host = VHostPath, name = Name}, @@ -311,15 +311,15 @@ validate_regexp(RegexpBin) -> end. set_permissions(Username, VHostPath, ConfigurePerm, WritePerm, ReadPerm) -> - set_permissions(<<"check_user_named">>, Username, VHostPath, ConfigurePerm, + set_permissions(<<"client">>, Username, VHostPath, ConfigurePerm, WritePerm, ReadPerm). -set_permissions(Check, Username, VHostPath, ConfigurePerm, WritePerm, ReadPerm) -> +set_permissions(Scope, Username, VHostPath, ConfigurePerm, WritePerm, ReadPerm) -> lists:map(fun validate_regexp/1, [ConfigurePerm, WritePerm, ReadPerm]), - Check1 = case Check of - <<"check_user_named">> -> check_user_named; - <<"check_all_resources">> -> check_all_resources; - _ -> throw({error, {invalid_check_flag, Check}}) + Scope1 = case Scope of + <<"client">> -> client; + <<"all">> -> all; + _ -> throw({error, {invalid_scope, Scope}}) end, rabbit_misc:execute_mnesia_transaction( rabbit_misc:with_user_and_vhost( @@ -330,7 +330,7 @@ set_permissions(Check, Username, VHostPath, ConfigurePerm, WritePerm, ReadPerm) username = Username, virtual_host = VHostPath}, permission = #permission{ - check = Check1, + scope = Scope1, configure = ConfigurePerm, write = WritePerm, read = ReadPerm}}, @@ -349,23 +349,23 @@ clear_permissions(Username, VHostPath) -> end)). list_vhost_permissions(VHostPath) -> - [{Username, ConfigurePerm, WritePerm, ReadPerm, Check} || - {Username, _, ConfigurePerm, WritePerm, ReadPerm, Check} <- + [{Username, ConfigurePerm, WritePerm, ReadPerm, Scope} || + {Username, _, ConfigurePerm, WritePerm, ReadPerm, Scope} <- list_permissions(rabbit_misc:with_vhost( VHostPath, match_user_vhost('_', VHostPath)))]. list_user_permissions(Username) -> - [{VHostPath, ConfigurePerm, WritePerm, ReadPerm, Check} || - {_, VHostPath, ConfigurePerm, WritePerm, ReadPerm, Check} <- + [{VHostPath, ConfigurePerm, WritePerm, ReadPerm, Scope} || + {_, VHostPath, ConfigurePerm, WritePerm, ReadPerm, Scope} <- list_permissions(rabbit_misc:with_user( Username, match_user_vhost(Username, '_')))]. list_permissions(QueryThunk) -> - [{Username, VHostPath, ConfigurePerm, WritePerm, ReadPerm, Check} || + [{Username, VHostPath, ConfigurePerm, WritePerm, ReadPerm, Scope} || #user_permission{user_vhost = #user_vhost{username = Username, virtual_host = VHostPath}, permission = #permission{ - check = Check, + scope = Scope, configure = ConfigurePerm, write = WritePerm, read = ReadPerm}} <- diff --git a/src/rabbit_control.erl b/src/rabbit_control.erl index 602b4660..21d88bbc 100644 --- a/src/rabbit_control.erl +++ b/src/rabbit_control.erl @@ -272,16 +272,16 @@ action(Command, Node, Args, Inform) -> action(Command, Node, VHost, RemainingArgs, Inform). action(set_permissions, Node, VHost, Args, Inform) -> - {Check, [Username, CPerm, WPerm, RPerm]} = + {Scope, [Username, CPerm, WPerm, RPerm]} = case Args of - [[$- | Flag] | RemainingArgs] -> - {Flag, RemainingArgs}; + ["-s", ScopeArg | RemainingArgs] -> + {ScopeArg, RemainingArgs}; RemainingArgs -> - {"check_user_named", RemainingArgs} + {"client", RemainingArgs} end, Inform("Setting permissions for user ~p in vhost ~p", [Username, VHost]), call(Node, {rabbit_access_control, set_permissions, - [Check, Username, VHost, CPerm, WPerm, RPerm]}); + [Scope, Username, VHost, CPerm, WPerm, RPerm]}); action(clear_permissions, Node, VHost, [Username], Inform) -> Inform("Clearing permissions for user ~p in vhost ~p", [Username, VHost]), diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index e1c4db33..71d7902f 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -747,8 +747,8 @@ test_user_management() -> control_action(list_permissions, ["-p", "/testhost"]), {error, {invalid_regexp, _, _}} = control_action(set_permissions, ["guest", "+foo", ".*", ".*"]), - {error, {invalid_check_flag, _}} = - control_action(set_permissions, ["-check_mate", "guest", "foo", ".*", ".*"]), + {error, {invalid_scope, _}} = + control_action(set_permissions, ["-s", "cilent", "guest", "foo", ".*", ".*"]), %% user creation ok = control_action(add_user, ["foo", "bar"]), @@ -769,10 +769,10 @@ test_user_management() -> ok = control_action(set_permissions, ["-p", "/testhost", "foo", ".*", ".*", ".*"]), ok = control_action(set_permissions, ["-p", "/testhost", - "-check_user_named", + "-s", "client", "foo", ".*", ".*", ".*"]), ok = control_action(set_permissions, ["-p", "/testhost", - "-check_all_resources", + "-s", "all", "foo", ".*", ".*", ".*"]), ok = control_action(list_permissions, ["-p", "/testhost"]), ok = control_action(list_permissions, ["-p", "/testhost"]), -- cgit v1.2.1 From 64434621264d6af2ee38b4b251e7ea3bec4dc2fe Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Thu, 15 Jul 2010 10:40:41 +0100 Subject: renamed variables; alligned code --- src/rabbit_access_control.erl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/rabbit_access_control.erl b/src/rabbit_access_control.erl index 5eb80325..bebaef8e 100644 --- a/src/rabbit_access_control.erl +++ b/src/rabbit_access_control.erl @@ -318,12 +318,12 @@ set_permissions(Username, VHostPath, ConfigurePerm, WritePerm, ReadPerm) -> set_permissions(<<"client">>, Username, VHostPath, ConfigurePerm, WritePerm, ReadPerm). -set_permissions(Scope, Username, VHostPath, ConfigurePerm, WritePerm, ReadPerm) -> +set_permissions(ScopeBin, Username, VHostPath, ConfigurePerm, WritePerm, ReadPerm) -> lists:map(fun validate_regexp/1, [ConfigurePerm, WritePerm, ReadPerm]), - Scope1 = case Scope of + Scope = case ScopeBin of <<"client">> -> client; - <<"all">> -> all; - _ -> throw({error, {invalid_scope, Scope}}) + <<"all">> -> all; + _ -> throw({error, {invalid_scope, ScopeBin}}) end, rabbit_misc:execute_mnesia_transaction( rabbit_misc:with_user_and_vhost( @@ -334,7 +334,7 @@ set_permissions(Scope, Username, VHostPath, ConfigurePerm, WritePerm, ReadPerm) username = Username, virtual_host = VHostPath}, permission = #permission{ - scope = Scope1, + scope = Scope, configure = ConfigurePerm, write = WritePerm, read = ReadPerm}}, -- cgit v1.2.1 From 17960c865edad12d5b44ae65a3384bbce534f81c Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 16 Jul 2010 14:52:26 +0100 Subject: Get queues to emit events for statistics. --- include/rabbit.hrl | 4 ++++ src/rabbit.erl | 7 +++++++ src/rabbit_amqqueue_process.erl | 34 ++++++++++++++++++++++++++++++---- src/rabbit_event.erl | 41 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 82 insertions(+), 4 deletions(-) create mode 100644 src/rabbit_event.erl diff --git a/include/rabbit.hrl b/include/rabbit.hrl index 3fd52568..b991aa7d 100644 --- a/include/rabbit.hrl +++ b/include/rabbit.hrl @@ -70,6 +70,10 @@ -record(delivery, {mandatory, immediate, txn, sender, message}). -record(amqp_error, {name, explanation, method = none}). +-record(event_queue_stats, {q_pid, messages_ready, messages_unacknowledged, + consumers, memory, exclusive_consumer_pid, + exclusive_consumer_tag, backing_queue_status}). + %%---------------------------------------------------------------------------- -define(COPYRIGHT_MESSAGE, "Copyright (C) 2007-2010 LShift Ltd., Cohesive Financial Technologies LLC., and Rabbit Technologies Ltd."). diff --git a/src/rabbit.erl b/src/rabbit.erl index 18045b94..88d1a318 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -89,6 +89,13 @@ {requires, external_infrastructure}, {enables, kernel_ready}]}). +-rabbit_boot_step({rabbit_event, + [{description, "statistics event handler"}, + {mfa, {rabbit_sup, start_restartable_child, + [gen_event, [{local, rabbit_event}]]}}, + {requires, external_infrastructure}, + {enables, kernel_ready}]}). + -rabbit_boot_step({kernel_ready, [{description, "kernel ready"}, {requires, external_infrastructure}]}). diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 2fb60e96..808e1117 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -38,6 +38,7 @@ -define(UNSENT_MESSAGE_LIMIT, 100). -define(SYNC_INTERVAL, 5). %% milliseconds -define(RAM_DURATION_UPDATE_INTERVAL, 5000). +-define(STATISTICS_UPDATE_INTERVAL, 5000). -export([start_link/1, info_keys/0]). @@ -57,7 +58,8 @@ active_consumers, blocked_consumers, sync_timer_ref, - rate_timer_ref + rate_timer_ref, + last_statistics_update }). -record(consumer, {tag, ack_required}). @@ -110,7 +112,8 @@ init(Q) -> active_consumers = queue:new(), blocked_consumers = queue:new(), sync_timer_ref = undefined, - rate_timer_ref = undefined}, hibernate, + rate_timer_ref = undefined, + last_statistics_update = 0}, hibernate, {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}. terminate(shutdown, State = #q{backing_queue = BQ}) -> @@ -179,9 +182,10 @@ noreply(NewState) -> next_state(State) -> State1 = #q{backing_queue = BQ, backing_queue_state = BQS} = ensure_rate_timer(State), + State2 = maybe_emit_stats(State1), case BQ:needs_sync(BQS)of - true -> {ensure_sync_timer(State1), 0}; - false -> {stop_sync_timer(State1), hibernate} + true -> {ensure_sync_timer(State2), 0}; + false -> {stop_sync_timer(State2), hibernate} end. ensure_sync_timer(State = #q{sync_timer_ref = undefined, backing_queue = BQ}) -> @@ -530,6 +534,28 @@ i(Item, _) -> %--------------------------------------------------------------------------- +maybe_emit_stats(State = #q{last_statistics_update = LastUpdate}) -> + {MegaSecs, Secs, MicroSecs} = os:timestamp(), + Now = MegaSecs * 1000000 + Secs * 1000 + MicroSecs / 1000, + case Now - LastUpdate > ?STATISTICS_UPDATE_INTERVAL of + true -> + S = {queue_stats, #event_queue_stats{ + q_pid = self(), + messages_ready = i(messages_ready, State), + messages_unacknowledged = i(messages_unacknowledged, State), + consumers = i(consumers, State), + memory = i(memory, State), + exclusive_consumer_tag = i(exclusive_consumer_tag, State), + exclusive_consumer_pid = i(exclusive_consumer_pid, State), + backing_queue_status = i(backing_queue_status, State) + }}, + rabbit_event:notify(S), + State#q{last_statistics_update = Now}; + _ -> + State + end. +%--------------------------------------------------------------------------- + handle_call({init, Recover}, From, State = #q{q = #amqqueue{exclusive_owner = none}}) -> declare(Recover, From, State); diff --git a/src/rabbit_event.erl b/src/rabbit_event.erl new file mode 100644 index 00000000..618b8dfc --- /dev/null +++ b/src/rabbit_event.erl @@ -0,0 +1,41 @@ +%% The contents of this file are subject to the Mozilla Public License +%% Version 1.1 (the "License"); you may not use this file except in +%% compliance with the License. You may obtain a copy of the License at +%% http://www.mozilla.org/MPL/ +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +%% License for the specific language governing rights and limitations +%% under the License. +%% +%% The Original Code is RabbitMQ. +%% +%% The Initial 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_event). + +-include("rabbit.hrl"). + +-export([notify/1]). + +%%---------------------------------------------------------------------------- + +notify(Event) -> + gen_event:notify(rabbit_event, Event). -- cgit v1.2.1 From 5eb190e22cf16edbb4ecede8c9147fd441ea5a51 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 16 Jul 2010 15:25:13 +0100 Subject: q_pid -> qpid --- include/rabbit.hrl | 2 +- src/rabbit_amqqueue_process.erl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/rabbit.hrl b/include/rabbit.hrl index b991aa7d..1998de35 100644 --- a/include/rabbit.hrl +++ b/include/rabbit.hrl @@ -70,7 +70,7 @@ -record(delivery, {mandatory, immediate, txn, sender, message}). -record(amqp_error, {name, explanation, method = none}). --record(event_queue_stats, {q_pid, messages_ready, messages_unacknowledged, +-record(event_queue_stats, {qpid, messages_ready, messages_unacknowledged, consumers, memory, exclusive_consumer_pid, exclusive_consumer_tag, backing_queue_status}). diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 808e1117..7fa11a26 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -540,7 +540,7 @@ maybe_emit_stats(State = #q{last_statistics_update = LastUpdate}) -> case Now - LastUpdate > ?STATISTICS_UPDATE_INTERVAL of true -> S = {queue_stats, #event_queue_stats{ - q_pid = self(), + qpid = self(), messages_ready = i(messages_ready, State), messages_unacknowledged = i(messages_unacknowledged, State), consumers = i(consumers, State), -- cgit v1.2.1 From 69b3c746f9047857bca32f4a0ae566e1d4e43321 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 16 Jul 2010 15:37:17 +0100 Subject: Use timer:now_diff, don't double-wrap. --- src/rabbit_amqqueue_process.erl | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 7fa11a26..707fa684 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -38,7 +38,7 @@ -define(UNSENT_MESSAGE_LIMIT, 100). -define(SYNC_INTERVAL, 5). %% milliseconds -define(RAM_DURATION_UPDATE_INTERVAL, 5000). --define(STATISTICS_UPDATE_INTERVAL, 5000). +-define(STATISTICS_UPDATE_INTERVAL, 5000000). %% microseconds -export([start_link/1, info_keys/0]). @@ -113,7 +113,7 @@ init(Q) -> blocked_consumers = queue:new(), sync_timer_ref = undefined, rate_timer_ref = undefined, - last_statistics_update = 0}, hibernate, + last_statistics_update = {0,0,0}}, hibernate, {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}. terminate(shutdown, State = #q{backing_queue = BQ}) -> @@ -535,21 +535,19 @@ i(Item, _) -> %--------------------------------------------------------------------------- maybe_emit_stats(State = #q{last_statistics_update = LastUpdate}) -> - {MegaSecs, Secs, MicroSecs} = os:timestamp(), - Now = MegaSecs * 1000000 + Secs * 1000 + MicroSecs / 1000, - case Now - LastUpdate > ?STATISTICS_UPDATE_INTERVAL of + Now = os:timestamp(), + case timer:now_diff(Now, LastUpdate) > ?STATISTICS_UPDATE_INTERVAL of true -> - S = {queue_stats, #event_queue_stats{ - qpid = self(), - messages_ready = i(messages_ready, State), - messages_unacknowledged = i(messages_unacknowledged, State), - consumers = i(consumers, State), - memory = i(memory, State), - exclusive_consumer_tag = i(exclusive_consumer_tag, State), - exclusive_consumer_pid = i(exclusive_consumer_pid, State), - backing_queue_status = i(backing_queue_status, State) - }}, - rabbit_event:notify(S), + rabbit_event:notify(#event_queue_stats{ + qpid = self(), + messages_ready = i(messages_ready, State), + messages_unacknowledged = i(messages_unacknowledged, State), + consumers = i(consumers, State), + memory = i(memory, State), + exclusive_consumer_tag = i(exclusive_consumer_tag, State), + exclusive_consumer_pid = i(exclusive_consumer_pid, State), + backing_queue_status = i(backing_queue_status, State) + }), State#q{last_statistics_update = Now}; _ -> State -- cgit v1.2.1 From 09c71b183e0ecff11794d8b11758c575ab13cdf7 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 16 Jul 2010 15:40:22 +0100 Subject: cosmetics --- src/rabbit_amqqueue_process.erl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 707fa684..e31f8210 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -539,14 +539,14 @@ maybe_emit_stats(State = #q{last_statistics_update = LastUpdate}) -> case timer:now_diff(Now, LastUpdate) > ?STATISTICS_UPDATE_INTERVAL of true -> rabbit_event:notify(#event_queue_stats{ - qpid = self(), - messages_ready = i(messages_ready, State), + qpid = self(), + messages_ready = i(messages_ready, State), messages_unacknowledged = i(messages_unacknowledged, State), - consumers = i(consumers, State), - memory = i(memory, State), - exclusive_consumer_tag = i(exclusive_consumer_tag, State), - exclusive_consumer_pid = i(exclusive_consumer_pid, State), - backing_queue_status = i(backing_queue_status, State) + consumers = i(consumers, State), + memory = i(memory, State), + exclusive_consumer_tag = i(exclusive_consumer_tag, State), + exclusive_consumer_pid = i(exclusive_consumer_pid, State), + backing_queue_status = i(backing_queue_status, State) }), State#q{last_statistics_update = Now}; _ -> -- cgit v1.2.1 From 84d7e8b90fa7ed0fecad3e0f8bafb06407a232ba Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Fri, 16 Jul 2010 16:30:25 +0100 Subject: cleared up manpage --- docs/rabbitmqctl.1.xml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/rabbitmqctl.1.xml b/docs/rabbitmqctl.1.xml index 3327a31d..e74b9b29 100644 --- a/docs/rabbitmqctl.1.xml +++ b/docs/rabbitmqctl.1.xml @@ -558,7 +558,11 @@ scope Scope of the permissions: either client (the default) or - all. + all. This determines whether + permissisons are checked for server-generated resource + names (all) or only for + client-specified resource names + (client). user -- cgit v1.2.1 From 151f714b38b4f7152a759907c57514f987cede3f Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 19 Jul 2010 14:11:48 +0100 Subject: Emit events for connection and channel statistics and creation / deletion. --- include/rabbit.hrl | 19 +++++++++ src/rabbit_amqqueue_process.erl | 1 - src/rabbit_channel.erl | 93 ++++++++++++++++++++++++++++++++++------- src/rabbit_reader.erl | 45 +++++++++++++++++--- 4 files changed, 138 insertions(+), 20 deletions(-) diff --git a/include/rabbit.hrl b/include/rabbit.hrl index 1998de35..f400495c 100644 --- a/include/rabbit.hrl +++ b/include/rabbit.hrl @@ -70,10 +70,28 @@ -record(delivery, {mandatory, immediate, txn, sender, message}). -record(amqp_error, {name, explanation, method = none}). + +-record(event_connection_stats, {connection_pid, state, channels, + recv_oct, recv_cnt, + send_oct, send_cnt, send_pend}). + +-record(event_channel_stats, {channel_pid, per_exchange_statistics, + per_queue_statistics}). + -record(event_queue_stats, {qpid, messages_ready, messages_unacknowledged, consumers, memory, exclusive_consumer_pid, exclusive_consumer_tag, backing_queue_status}). +-record(event_connection_created, {connection_pid, address, port, + peer_address, peer_port, user, vhost, + timeout, frame_max, client_properties}). +-record(event_connection_closed, {connection_pid}). +-record(event_channel_created, {channel_pid, connection_pid, channel, user, + vhost}). +-record(event_channel_closed, {channel_pid}). + + + %%---------------------------------------------------------------------------- -define(COPYRIGHT_MESSAGE, "Copyright (C) 2007-2010 LShift Ltd., Cohesive Financial Technologies LLC., and Rabbit Technologies Ltd."). @@ -84,6 +102,7 @@ -define(HIBERNATE_AFTER_MIN, 1000). -define(DESIRED_HIBERNATE, 10000). +-define(STATISTICS_UPDATE_INTERVAL, 5000000). %% microseconds -ifdef(debug). -define(LOGDEBUG0(F), rabbit_log:debug(F)). diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index e31f8210..9af77e78 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -38,7 +38,6 @@ -define(UNSENT_MESSAGE_LIMIT, 100). -define(SYNC_INTERVAL, 5). %% milliseconds -define(RAM_DURATION_UPDATE_INTERVAL, 5000). --define(STATISTICS_UPDATE_INTERVAL, 5000000). %% microseconds -export([start_link/1, info_keys/0]). diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index c4db3ace..44699af6 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -48,7 +48,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, flow}). + consumer_mapping, blocking, queue_collector_pid, flow, + exchange_statistics, queue_statistics, last_statistics_update}). -record(flow, {server, client, pending}). @@ -157,6 +158,11 @@ init([Channel, ReaderPid, WriterPid, Username, VHost, CollectorPid]) -> process_flag(trap_exit, true), link(WriterPid), ok = pg_local:join(rabbit_channels, self()), + rabbit_event:notify(#event_channel_created{channel_pid = self(), + connection_pid = ReaderPid, + channel = Channel, + user = Username, + vhost = VHost}), {ok, #ch{state = starting, channel = Channel, reader_pid = ReaderPid, @@ -174,7 +180,10 @@ init([Channel, ReaderPid, WriterPid, Username, VHost, CollectorPid]) -> blocking = dict:new(), queue_collector_pid = CollectorPid, flow = #flow{server = true, client = true, - pending = none}}, + pending = none}, + exchange_statistics = dict:new(), + queue_statistics = dict:new(), + last_statistics_update = {0,0,0}}, hibernate, {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}. @@ -225,7 +234,13 @@ handle_cast({deliver, ConsumerTag, AckRequired, Msg}, next_tag = DeliveryTag}) -> State1 = lock_message(AckRequired, {DeliveryTag, ConsumerTag, Msg}, State), ok = internal_deliver(WriterPid, true, ConsumerTag, DeliveryTag, Msg), - noreply(State1#ch{next_tag = DeliveryTag + 1}); + {_QName, QPid, _MsgId, _Redelivered, _Msg} = Msg, + State2 = incr_queue_stats([{QPid, 1}], + case AckRequired of + true -> deliver; + false -> deliver_no_ack + end, State1), + noreply(State2#ch{next_tag = DeliveryTag + 1}); handle_cast({conserve_memory, true}, State = #ch{state = starting}) -> noreply(State); @@ -276,9 +291,13 @@ code_change(_OldVsn, State, _Extra) -> %%--------------------------------------------------------------------------- -reply(Reply, NewState) -> {reply, Reply, NewState, hibernate}. +reply(Reply, NewState) -> + NewState1 = maybe_emit_stats(NewState), + {reply, Reply, NewState1, hibernate}. -noreply(NewState) -> {noreply, NewState, hibernate}. +noreply(NewState) -> + NewState1 = maybe_emit_stats(NewState), + {noreply, NewState1, hibernate}. return_ok(State, true, _Msg) -> {noreply, State}; return_ok(State, false, Msg) -> {reply, Msg, State}. @@ -437,9 +456,10 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, unroutable -> ok = basic_return(Message, WriterPid, no_route); not_delivered -> ok = basic_return(Message, WriterPid, no_consumers) end, + State1 = incr_exchange_stats(ExchangeName, State), {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, @@ -447,16 +467,18 @@ handle_method(#'basic.ack'{delivery_tag = DeliveryTag, _, State = #ch{transaction_id = TxnKey, unacked_message_q = UAMQ}) -> {Acked, Remaining} = collect_acks(UAMQ, DeliveryTag, Multiple), - Participants = ack(TxnKey, Acked), + QsCounts = ack(TxnKey, Acked), + Participants = [QPid || {QPid, _} <- QsCounts], + State1 = incr_queue_stats(QsCounts, ack, State), {noreply, case TxnKey of none -> ok = notify_limiter(State#ch.limiter_pid, Acked), - State#ch{unacked_message_q = Remaining}; + State1#ch{unacked_message_q = Remaining}; _ -> NewUAQ = queue:join(State#ch.uncommitted_ack_q, Acked), add_tx_participants( Participants, - State#ch{unacked_message_q = Remaining, - uncommitted_ack_q = NewUAQ}) + State1#ch{unacked_message_q = Remaining, + uncommitted_ack_q = NewUAQ}) end}; handle_method(#'basic.get'{queue = QueueNameBin, @@ -470,11 +492,16 @@ handle_method(#'basic.get'{queue = QueueNameBin, QueueName, ReaderPid, fun (Q) -> rabbit_amqqueue:basic_get(Q, self(), NoAck) end) of {ok, MessageCount, - Msg = {_QName, _QPid, _MsgId, Redelivered, + Msg = {_QName, QPid, _MsgId, Redelivered, #basic_message{exchange_name = ExchangeName, routing_key = RoutingKey, content = Content}}} -> State1 = lock_message(not(NoAck), {DeliveryTag, none, Msg}, State), + State2 = incr_queue_stats([{QPid, 1}], + case NoAck of + true -> get_no_ack; + false -> get + end, State1), ok = rabbit_writer:send_command( WriterPid, #'basic.get_ok'{delivery_tag = DeliveryTag, @@ -483,7 +510,7 @@ handle_method(#'basic.get'{queue = QueueNameBin, routing_key = RoutingKey, message_count = MessageCount}, Content), - {noreply, State1#ch{next_tag = DeliveryTag + 1}}; + {noreply, State2#ch{next_tag = DeliveryTag + 1}}; empty -> {reply, #'basic.get_empty'{}, State} end; @@ -978,7 +1005,7 @@ ack(TxnKey, UAQ) -> fold_per_queue( fun (QPid, MsgIds, L) -> ok = rabbit_amqqueue:ack(QPid, TxnKey, MsgIds, self()), - [QPid | L] + [{QPid, length(MsgIds)} | L] end, [], UAQ). make_tx_id() -> rabbit_guid:guid(). @@ -1105,6 +1132,7 @@ internal_deliver(WriterPid, Notify, ConsumerTag, DeliveryTag, terminate(#ch{writer_pid = WriterPid, limiter_pid = LimiterPid}) -> pg_local:leave(rabbit_channels, self()), + rabbit_event:notify(#event_channel_closed{channel_pid = self()}), rabbit_writer:shutdown(WriterPid), rabbit_limiter:shutdown(LimiterPid). @@ -1127,3 +1155,40 @@ i(prefetch_count, #ch{limiter_pid = LimiterPid}) -> rabbit_limiter:get_limit(LimiterPid); i(Item, _) -> throw({bad_argument, Item}). + +incr_exchange_stats(ExchangeName, State = #ch{exchange_statistics = Stats}) -> + Stats1 = dict:update(ExchangeName, fun(Old) -> Old + 1 end, 0, Stats), + State#ch{exchange_statistics = Stats1}. + +incr_queue_stats(Counts, Key, State = #ch{queue_statistics = Stats}) -> + Stats1 = lists:foldl( + fun ({QPid, Incr}, Stats0) -> + dict:update(QPid, + fun(D) -> + Count = case orddict:find(Key, D) of + error -> 0; + {ok, C} -> C + end, + orddict:store(Key, Count + Incr, D) + end, + [], + Stats0) + end, Stats, Counts), + State#ch{queue_statistics = Stats1}. + +maybe_emit_stats(State = #ch{exchange_statistics = ExchangeStatistics, + queue_statistics = QueueStatistics, + last_statistics_update = LastUpdate}) -> + Now = os:timestamp(), + case timer:now_diff(Now, LastUpdate) > ?STATISTICS_UPDATE_INTERVAL of + true -> + rabbit_event:notify( + #event_channel_stats{channel_pid = self(), + per_exchange_statistics = + dict:to_list(ExchangeStatistics), + per_queue_statistics = + dict:to_list(QueueStatistics)}), + State#ch{last_statistics_update = Now}; + _ -> + State + end. diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index b5514c82..2dc6d933 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -58,7 +58,7 @@ %--------------------------------------------------------------------------- -record(v1, {sock, connection, callback, recv_ref, connection_state, - queue_collector}). + queue_collector, last_statistics_update}). -define(INFO_KEYS, [pid, address, port, peer_address, peer_port, @@ -253,7 +253,8 @@ start_connection(Parent, Deb, Sock, SockTransform) -> callback = uninitialized_callback, recv_ref = none, connection_state = pre_init, - queue_collector = Collector}, + queue_collector = Collector, + last_statistics_update = {0,0,0}}, handshake, 8)) catch Ex -> (if Ex == connection_closed_abruptly -> @@ -273,12 +274,14 @@ start_connection(Parent, Deb, Sock, SockTransform) -> %% gen_tcp:close(ClientSock), teardown_profiling(ProfilingValue), rabbit_queue_collector:shutdown(Collector), - rabbit_misc:unlink_and_capture_exit(Collector) + rabbit_misc:unlink_and_capture_exit(Collector), + rabbit_event:notify(#event_connection_closed{connection_pid = self()}) end, done. -mainloop(Parent, Deb, State = #v1{sock= Sock, recv_ref = Ref}) -> +mainloop(Parent, Deb, State_ = #v1{sock= Sock, recv_ref = Ref}) -> %%?LOGDEBUG("Reader mainloop: ~p bytes available, need ~p~n", [HaveBytes, WaitUntilNBytes]), + State = maybe_emit_stats(State_), receive {inet_async, Sock, Ref, {ok, Data}} -> {State1, Callback1, Length1} = @@ -649,7 +652,8 @@ handle_method0(#'connection.open'{virtual_host = VHostPath, insist = Insist}, State = #v1{connection_state = opening, connection = Connection = #connection{ - user = User}, + user = User, + vhost = VHost}, sock = Sock}) -> ok = rabbit_access_control:check_vhost_access(User, VHostPath), NewConnection = Connection#connection{vhost = VHostPath}, @@ -659,6 +663,19 @@ handle_method0(#'connection.open'{virtual_host = VHostPath, ok = send_on_channel0( Sock, #'connection.open_ok'{known_hosts = KnownHosts}), + rabbit_event:notify( + #event_connection_created{connection_pid = self(), + address = i(address, State), + port = i(port, State), + peer_address = i(peer_address, State), + peer_port = i(peer_port, State), + user = User, + vhost = VHost, + timeout = i(timeout, State), + frame_max = i(frame_max, State), + client_properties = + i(client_properties, State) + }), State#v1{connection_state = running, connection = NewConnection}; true -> @@ -847,3 +864,21 @@ amqp_exception_explanation(Text, Expl) -> if size(CompleteTextBin) > 255 -> <>; true -> CompleteTextBin end. + +maybe_emit_stats(State = #v1{last_statistics_update = LastUpdate}) -> + Now = os:timestamp(), + case timer:now_diff(Now, LastUpdate) > ?STATISTICS_UPDATE_INTERVAL of + true -> + rabbit_event:notify( + #event_connection_stats{connection_pid = self(), + state = i(state, State), + channels = i(channels, State), + recv_oct = i(recv_oct, State), + recv_cnt = i(recv_cnt, State), + send_oct = i(send_oct, State), + send_cnt = i(send_cnt, State), + send_pend = i(send_pend, State)}), + State#v1{last_statistics_update = Now}; + _ -> + State + end. -- cgit v1.2.1 From a671de2be322525ec9ce01eaecc09e34be6e34d7 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 19 Jul 2010 14:33:51 +0100 Subject: That's not needed. --- src/rabbit_event.erl | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/rabbit_event.erl b/src/rabbit_event.erl index 618b8dfc..c74d7220 100644 --- a/src/rabbit_event.erl +++ b/src/rabbit_event.erl @@ -31,8 +31,6 @@ -module(rabbit_event). --include("rabbit.hrl"). - -export([notify/1]). %%---------------------------------------------------------------------------- -- cgit v1.2.1 From 26bcbe8230e3c4fe7692768a143b61ea809dd1fb Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 19 Jul 2010 14:56:34 +0100 Subject: Make channel monitor queues for which it is gathering statistics, and remove the stats when the queue goes away. --- src/rabbit_channel.erl | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 44699af6..dd7c5d4f 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -269,7 +269,9 @@ handle_info({'EXIT', WriterPid, Reason = {writer, send_failed, _Error}}, handle_info({'EXIT', _Pid, Reason}, State) -> {stop, Reason, State}; handle_info({'DOWN', _MRef, process, QPid, _Reason}, State) -> - {noreply, queue_blocked(QPid, State)}. + State1 = queue_blocked(QPid, State), + State2 = erase_stats(QPid, State1), + {noreply, State2}. handle_pre_hibernate(State) -> ok = clear_permission_cache(), @@ -1163,6 +1165,10 @@ incr_exchange_stats(ExchangeName, State = #ch{exchange_statistics = Stats}) -> incr_queue_stats(Counts, Key, State = #ch{queue_statistics = Stats}) -> Stats1 = lists:foldl( fun ({QPid, Incr}, Stats0) -> + case dict:is_key(QPid, Stats0) of + false -> erlang:monitor(process, QPid); + _ -> ok + end, dict:update(QPid, fun(D) -> Count = case orddict:find(Key, D) of @@ -1192,3 +1198,6 @@ maybe_emit_stats(State = #ch{exchange_statistics = ExchangeStatistics, _ -> State end. + +erase_stats(QPid, State = #ch{queue_statistics = QueueStatistics}) -> + State#ch{queue_statistics = dict:erase(QPid, QueueStatistics)}. -- cgit v1.2.1 From e6f5dee609d82930c28cca0446b486a4759706db Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 19 Jul 2010 16:21:59 +0100 Subject: Switch to using proplists rather than records for events. --- include/rabbit.hrl | 22 +-------------------- src/rabbit_amqqueue_process.erl | 20 ++++++++++---------- src/rabbit_channel.erl | 21 ++++++++++----------- src/rabbit_event.erl | 10 +++++++--- src/rabbit_reader.erl | 42 ++++++++++++++++++++--------------------- 5 files changed, 49 insertions(+), 66 deletions(-) diff --git a/include/rabbit.hrl b/include/rabbit.hrl index f400495c..ac7e3851 100644 --- a/include/rabbit.hrl +++ b/include/rabbit.hrl @@ -70,27 +70,7 @@ -record(delivery, {mandatory, immediate, txn, sender, message}). -record(amqp_error, {name, explanation, method = none}). - --record(event_connection_stats, {connection_pid, state, channels, - recv_oct, recv_cnt, - send_oct, send_cnt, send_pend}). - --record(event_channel_stats, {channel_pid, per_exchange_statistics, - per_queue_statistics}). - --record(event_queue_stats, {qpid, messages_ready, messages_unacknowledged, - consumers, memory, exclusive_consumer_pid, - exclusive_consumer_tag, backing_queue_status}). - --record(event_connection_created, {connection_pid, address, port, - peer_address, peer_port, user, vhost, - timeout, frame_max, client_properties}). --record(event_connection_closed, {connection_pid}). --record(event_channel_created, {channel_pid, connection_pid, channel, user, - vhost}). --record(event_channel_closed, {channel_pid}). - - +-record(event, {type, props, timestamp}). %%---------------------------------------------------------------------------- diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 9af77e78..cec36553 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -537,16 +537,16 @@ maybe_emit_stats(State = #q{last_statistics_update = LastUpdate}) -> Now = os:timestamp(), case timer:now_diff(Now, LastUpdate) > ?STATISTICS_UPDATE_INTERVAL of true -> - rabbit_event:notify(#event_queue_stats{ - qpid = self(), - messages_ready = i(messages_ready, State), - messages_unacknowledged = i(messages_unacknowledged, State), - consumers = i(consumers, State), - memory = i(memory, State), - exclusive_consumer_tag = i(exclusive_consumer_tag, State), - exclusive_consumer_pid = i(exclusive_consumer_pid, State), - backing_queue_status = i(backing_queue_status, State) - }), + rabbit_event:notify( + queue_stats, + [{qpid, self()}, + {messages_ready, i(messages_ready, State)}, + {messages_unacknowledged, i(messages_unacknowledged, State)}, + {consumers, i(consumers, State)}, + {memory, i(memory, State)}, + {exclusive_consumer_tag, i(exclusive_consumer_tag, State)}, + {exclusive_consumer_pid, i(exclusive_consumer_pid, State)}, + {backing_queue_status, i(backing_queue_status, State)}]), State#q{last_statistics_update = Now}; _ -> State diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index dd7c5d4f..2a6e51e1 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -158,11 +158,11 @@ init([Channel, ReaderPid, WriterPid, Username, VHost, CollectorPid]) -> process_flag(trap_exit, true), link(WriterPid), ok = pg_local:join(rabbit_channels, self()), - rabbit_event:notify(#event_channel_created{channel_pid = self(), - connection_pid = ReaderPid, - channel = Channel, - user = Username, - vhost = VHost}), + rabbit_event:notify(channel_created, [{channel_pid, self()}, + {connection_pid, ReaderPid}, + {channel, Channel}, + {user, Username}, + {vhost, VHost}]), {ok, #ch{state = starting, channel = Channel, reader_pid = ReaderPid, @@ -1134,7 +1134,7 @@ internal_deliver(WriterPid, Notify, ConsumerTag, DeliveryTag, terminate(#ch{writer_pid = WriterPid, limiter_pid = LimiterPid}) -> pg_local:leave(rabbit_channels, self()), - rabbit_event:notify(#event_channel_closed{channel_pid = self()}), + rabbit_event:notify(channel_closed, [{channel_pid, self()}]), rabbit_writer:shutdown(WriterPid), rabbit_limiter:shutdown(LimiterPid). @@ -1189,11 +1189,10 @@ maybe_emit_stats(State = #ch{exchange_statistics = ExchangeStatistics, case timer:now_diff(Now, LastUpdate) > ?STATISTICS_UPDATE_INTERVAL of true -> rabbit_event:notify( - #event_channel_stats{channel_pid = self(), - per_exchange_statistics = - dict:to_list(ExchangeStatistics), - per_queue_statistics = - dict:to_list(QueueStatistics)}), + channel_stats, + [{channel_pid, self()}, + {per_exchange_statistics, dict:to_list(ExchangeStatistics)}, + {per_queue_statistics, dict:to_list(QueueStatistics)}]), State#ch{last_statistics_update = Now}; _ -> State diff --git a/src/rabbit_event.erl b/src/rabbit_event.erl index c74d7220..08c13007 100644 --- a/src/rabbit_event.erl +++ b/src/rabbit_event.erl @@ -31,9 +31,13 @@ -module(rabbit_event). --export([notify/1]). +-include("rabbit.hrl"). + +-export([notify/2]). %%---------------------------------------------------------------------------- -notify(Event) -> - gen_event:notify(rabbit_event, Event). +notify(Type, Props) -> + gen_event:notify(rabbit_event, #event{type = Type, + props = Props, + timestamp = os:timestamp()}). diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 2dc6d933..fe7d17e8 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -275,7 +275,7 @@ start_connection(Parent, Deb, Sock, SockTransform) -> teardown_profiling(ProfilingValue), rabbit_queue_collector:shutdown(Collector), rabbit_misc:unlink_and_capture_exit(Collector), - rabbit_event:notify(#event_connection_closed{connection_pid = self()}) + rabbit_event:notify(connection_closed, [{connection_pid, self()}]) end, done. @@ -664,18 +664,17 @@ handle_method0(#'connection.open'{virtual_host = VHostPath, Sock, #'connection.open_ok'{known_hosts = KnownHosts}), rabbit_event:notify( - #event_connection_created{connection_pid = self(), - address = i(address, State), - port = i(port, State), - peer_address = i(peer_address, State), - peer_port = i(peer_port, State), - user = User, - vhost = VHost, - timeout = i(timeout, State), - frame_max = i(frame_max, State), - client_properties = - i(client_properties, State) - }), + connection_created, + [{connection_pid, self()}, + {address, i(address, State)}, + {port, i(port, State)}, + {peer_address, i(peer_address, State)}, + {peer_port, i(peer_port, State)}, + {user, User}, + {vhost, VHost}, + {timeout, i(timeout, State)}, + {frame_max, i(frame_max, State)}, + {client_properties, i(client_properties, State)}]), State#v1{connection_state = running, connection = NewConnection}; true -> @@ -870,14 +869,15 @@ maybe_emit_stats(State = #v1{last_statistics_update = LastUpdate}) -> case timer:now_diff(Now, LastUpdate) > ?STATISTICS_UPDATE_INTERVAL of true -> rabbit_event:notify( - #event_connection_stats{connection_pid = self(), - state = i(state, State), - channels = i(channels, State), - recv_oct = i(recv_oct, State), - recv_cnt = i(recv_cnt, State), - send_oct = i(send_oct, State), - send_cnt = i(send_cnt, State), - send_pend = i(send_pend, State)}), + connection_stats, + [{connection_pid, self()}, + {state, i(state, State)}, + {channels, i(channels, State)}, + {recv_oct, i(recv_oct, State)}, + {recv_cnt, i(recv_cnt, State)}, + {send_oct, i(send_oct, State)}, + {send_cnt, i(send_cnt, State)}, + {send_pend, i(send_pend, State)}]), State#v1{last_statistics_update = Now}; _ -> State -- cgit v1.2.1 From d449558d35b786d3e0ab6ea7981fdc2527d978e5 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 19 Jul 2010 16:51:14 +0100 Subject: Use list comprehensions to build the stats proplists. --- src/rabbit_amqqueue_process.erl | 26 +++++++++++--------------- src/rabbit_channel.erl | 6 +++--- src/rabbit_reader.erl | 36 +++++++++++------------------------- 3 files changed, 25 insertions(+), 43 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index cec36553..6222bd7a 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -73,13 +73,8 @@ txn, unsent_message_count}). --define(INFO_KEYS, - [name, - durable, - auto_delete, - arguments, - pid, - owner_pid, +-define(STATISTICS_KEYS, + [pid, exclusive_consumer_pid, exclusive_consumer_tag, messages_ready, @@ -90,6 +85,14 @@ backing_queue_status ]). +-define(INFO_KEYS, + [name, + durable, + auto_delete, + arguments, + owner_pid] ++ + ?STATISTICS_KEYS). + %%---------------------------------------------------------------------------- start_link(Q) -> gen_server2:start_link(?MODULE, Q, []). @@ -539,14 +542,7 @@ maybe_emit_stats(State = #q{last_statistics_update = LastUpdate}) -> true -> rabbit_event:notify( queue_stats, - [{qpid, self()}, - {messages_ready, i(messages_ready, State)}, - {messages_unacknowledged, i(messages_unacknowledged, State)}, - {consumers, i(consumers, State)}, - {memory, i(memory, State)}, - {exclusive_consumer_tag, i(exclusive_consumer_tag, State)}, - {exclusive_consumer_pid, i(exclusive_consumer_pid, State)}, - {backing_queue_status, i(backing_queue_status, State)}]), + [{Item, i(Item, State)} || Item <- ?STATISTICS_KEYS]), State#q{last_statistics_update = Now}; _ -> State diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 2a6e51e1..b0ccf9fb 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -158,7 +158,7 @@ init([Channel, ReaderPid, WriterPid, Username, VHost, CollectorPid]) -> process_flag(trap_exit, true), link(WriterPid), ok = pg_local:join(rabbit_channels, self()), - rabbit_event:notify(channel_created, [{channel_pid, self()}, + rabbit_event:notify(channel_created, [{pid, self()}, {connection_pid, ReaderPid}, {channel, Channel}, {user, Username}, @@ -1134,7 +1134,7 @@ internal_deliver(WriterPid, Notify, ConsumerTag, DeliveryTag, terminate(#ch{writer_pid = WriterPid, limiter_pid = LimiterPid}) -> pg_local:leave(rabbit_channels, self()), - rabbit_event:notify(channel_closed, [{channel_pid, self()}]), + rabbit_event:notify(channel_closed, [{pid, self()}]), rabbit_writer:shutdown(WriterPid), rabbit_limiter:shutdown(LimiterPid). @@ -1190,7 +1190,7 @@ maybe_emit_stats(State = #ch{exchange_statistics = ExchangeStatistics, true -> rabbit_event:notify( channel_stats, - [{channel_pid, self()}, + [{pid, self()}, {per_exchange_statistics, dict:to_list(ExchangeStatistics)}, {per_queue_statistics, dict:to_list(QueueStatistics)}]), State#ch{last_statistics_update = Now}; diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index fe7d17e8..bb1005b0 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -60,10 +60,13 @@ -record(v1, {sock, connection, callback, recv_ref, connection_state, queue_collector, last_statistics_update}). --define(INFO_KEYS, - [pid, address, port, peer_address, peer_port, - recv_oct, recv_cnt, send_oct, send_cnt, send_pend, - state, channels, user, vhost, timeout, frame_max, client_properties]). +-define(STATISTICS_KEYS, [pid, recv_oct, recv_cnt, send_oct, send_cnt, + send_pend, state, channels]). + +-define(CREATION_EVENT_KEYS, [address, port, peer_address, peer_port, + user, vhost, timeout, frame_max, client_properties]). + +-define(INFO_KEYS, ?CREATION_EVENT_KEYS ++ ?STATISTICS_KEYS). %% connection lifecycle %% @@ -275,7 +278,7 @@ start_connection(Parent, Deb, Sock, SockTransform) -> teardown_profiling(ProfilingValue), rabbit_queue_collector:shutdown(Collector), rabbit_misc:unlink_and_capture_exit(Collector), - rabbit_event:notify(connection_closed, [{connection_pid, self()}]) + rabbit_event:notify(connection_closed, [{pid, self()}]) end, done. @@ -652,8 +655,7 @@ handle_method0(#'connection.open'{virtual_host = VHostPath, insist = Insist}, State = #v1{connection_state = opening, connection = Connection = #connection{ - user = User, - vhost = VHost}, + user = User}, sock = Sock}) -> ok = rabbit_access_control:check_vhost_access(User, VHostPath), NewConnection = Connection#connection{vhost = VHostPath}, @@ -665,16 +667,7 @@ handle_method0(#'connection.open'{virtual_host = VHostPath, #'connection.open_ok'{known_hosts = KnownHosts}), rabbit_event:notify( connection_created, - [{connection_pid, self()}, - {address, i(address, State)}, - {port, i(port, State)}, - {peer_address, i(peer_address, State)}, - {peer_port, i(peer_port, State)}, - {user, User}, - {vhost, VHost}, - {timeout, i(timeout, State)}, - {frame_max, i(frame_max, State)}, - {client_properties, i(client_properties, State)}]), + [{Item, i(Item, State)} || Item <- [pid|?CREATION_EVENT_KEYS]]), State#v1{connection_state = running, connection = NewConnection}; true -> @@ -870,14 +863,7 @@ maybe_emit_stats(State = #v1{last_statistics_update = LastUpdate}) -> true -> rabbit_event:notify( connection_stats, - [{connection_pid, self()}, - {state, i(state, State)}, - {channels, i(channels, State)}, - {recv_oct, i(recv_oct, State)}, - {recv_cnt, i(recv_cnt, State)}, - {send_oct, i(send_oct, State)}, - {send_cnt, i(send_cnt, State)}, - {send_pend, i(send_pend, State)}]), + [{Item, i(Item, State)} || Item <- ?STATISTICS_KEYS]), State#v1{last_statistics_update = Now}; _ -> State -- cgit v1.2.1 From 2bc1bc06e8a15f32d65413f4aacd3c1a3c929feb Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 19 Jul 2010 17:13:40 +0100 Subject: The channel has a few statistics / state of its own too. --- src/rabbit_channel.erl | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index b0ccf9fb..d99a1c03 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -56,18 +56,21 @@ -define(MAX_PERMISSION_CACHE_SIZE, 12). -define(FLOW_OK_TIMEOUT, 10000). %% 10 seconds --define(INFO_KEYS, +-define(STATISTICS_KEYS, [pid, - connection, - number, - user, - vhost, transactional, consumer_count, messages_unacknowledged, acks_uncommitted, prefetch_count]). +-define(INFO_KEYS, + [connection, + number, + user, + vhost] + ++ ?STATISTICS_KEYS). + %%---------------------------------------------------------------------------- -ifdef(use_specs). @@ -1190,9 +1193,9 @@ maybe_emit_stats(State = #ch{exchange_statistics = ExchangeStatistics, true -> rabbit_event:notify( channel_stats, - [{pid, self()}, - {per_exchange_statistics, dict:to_list(ExchangeStatistics)}, - {per_queue_statistics, dict:to_list(QueueStatistics)}]), + [{Item, i(Item, State)} || Item <- ?STATISTICS_KEYS] ++ + [{per_exchange_statistics, dict:to_list(ExchangeStatistics)}, + {per_queue_statistics, dict:to_list(QueueStatistics)}]), State#ch{last_statistics_update = Now}; _ -> State -- cgit v1.2.1 From 4734f1a664ec4a91769beca9f87ea52216b61451 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 19 Jul 2010 17:39:40 +0100 Subject: Lifecycle events for queues, exchanges and bindings. --- src/rabbit_amqqueue_process.erl | 13 ++++++++++--- src/rabbit_exchange.erl | 13 +++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 6222bd7a..82a8916c 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -85,13 +85,15 @@ backing_queue_status ]). --define(INFO_KEYS, +-define(CREATION_EVENT_KEYS, [name, durable, auto_delete, arguments, - owner_pid] ++ - ?STATISTICS_KEYS). + owner_pid + ]). + +-define(INFO_KEYS, ?CREATION_EVENT_KEYS ++ ?STATISTICS_KEYS). %%---------------------------------------------------------------------------- @@ -150,6 +152,10 @@ declare(Recover, From, self(), {rabbit_amqqueue, set_ram_duration_target, [self()]}), BQS = BQ:init(QName, IsDurable, Recover), + rabbit_event:notify( + queue_created, + [{Item, i(Item, State)} || + Item <- [pid|?CREATION_EVENT_KEYS]]), noreply(State#q{backing_queue_state = BQS}); Q1 -> {stop, normal, {existing, Q1}, State} end. @@ -168,6 +174,7 @@ terminate_shutdown(Fun, State) -> BQ:tx_rollback(Txn, BQSN), BQSN1 end, BQS, all_ch_record()), + rabbit_event:notify(queue_deleted, [{pid, self()}]), State1#q{backing_queue_state = Fun(BQS1)} end. diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index d91ebe9b..0bb0419d 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -190,6 +190,9 @@ declare(ExchangeName, Type, Durable, AutoDelete, Args) -> end end) of {new, X} -> TypeModule:create(X), + rabbit_event:notify( + exchange_created, + [{Item, i(Item, Exchange)} || Item <- ?INFO_KEYS]), X; {existing, X} -> X; Err -> Err @@ -437,6 +440,11 @@ add_binding(ExchangeName, QueueName, RoutingKey, Arguments, InnerFun) -> X#exchange.durable andalso Q#amqqueue.durable, fun mnesia:write/3), + rabbit_event:notify( + binding_created, + [{exchange_name, ExchangeName}, + {queue_name, QueueName}, + {routing_key, RoutingKey}]), {new, X, B}; [_R] -> {existing, X, B} @@ -469,6 +477,10 @@ delete_binding(ExchangeName, QueueName, RoutingKey, Arguments, InnerFun) -> X#exchange.durable andalso Q#amqqueue.durable, fun mnesia:delete_object/3), + rabbit_event:notify( + binding_deleted, + [{exchange_name, ExchangeName}, + {queue_name, QueueName}]), {maybe_auto_delete(X), B}; {error, _} = E -> E @@ -587,6 +599,7 @@ unconditional_delete(Exchange = #exchange{name = ExchangeName}) -> Bindings = delete_exchange_bindings(ExchangeName), ok = mnesia:delete({rabbit_durable_exchange, ExchangeName}), ok = mnesia:delete({rabbit_exchange, ExchangeName}), + rabbit_event:notify(exchange_deleted, [{name, ExchangeName}]), {deleted, Exchange, Bindings}. %%---------------------------------------------------------------------------- -- cgit v1.2.1 From 3cc47bb8691579c2cbe2d1dcd5ea2e8d25c90135 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 19 Jul 2010 17:42:01 +0100 Subject: Missed out arguments in binding_created. --- src/rabbit_exchange.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index 0bb0419d..67eaf8e6 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -444,7 +444,8 @@ add_binding(ExchangeName, QueueName, RoutingKey, Arguments, InnerFun) -> binding_created, [{exchange_name, ExchangeName}, {queue_name, QueueName}, - {routing_key, RoutingKey}]), + {routing_key, RoutingKey}, + {arguments, Arguments}]), {new, X, B}; [_R] -> {existing, X, B} -- cgit v1.2.1 From 46825a5a2a69df4c70547d20bc4655caca575e1e Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 20 Jul 2010 12:25:56 +0100 Subject: This function was merged wrong; fix. --- src/rabbit_framing_channel.erl | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/src/rabbit_framing_channel.erl b/src/rabbit_framing_channel.erl index 6739b3d6..333604c8 100644 --- a/src/rabbit_framing_channel.erl +++ b/src/rabbit_framing_channel.erl @@ -73,16 +73,25 @@ read_frame(ChannelPid) -> end. mainloop(ChannelPid, Protocol) -> - {method, MethodName, FieldsBin} = read_frame(ChannelPid), - Method = Protocol:decode_method_fields(MethodName, FieldsBin), - case Protocol:method_has_content(MethodName) of - true -> {ClassId, _MethodId} = Protocol:method_id(MethodName), - rabbit_channel:do(ChannelPid, Method, - collect_content(ChannelPid, ClassId, - Protocol)); - false -> rabbit_channel:do(ChannelPid, Method) - end, - ?MODULE:mainloop(ChannelPid, Protocol). + Decoded = read_frame(ChannelPid), + case Decoded of + {method, MethodName, FieldsBin} -> + Method = Protocol:decode_method_fields(MethodName, FieldsBin), + case Protocol:method_has_content(MethodName) of + true -> {ClassId, _MethodId} = Protocol:method_id(MethodName), + rabbit_channel:do(ChannelPid, Method, + collect_content(ChannelPid, + ClassId, + Protocol)); + false -> rabbit_channel:do(ChannelPid, Method) + end, + ?MODULE:mainloop(ChannelPid, Protocol); + _ -> + rabbit_misc:protocol_error( + unexpected_frame, + "expected method frame, got ~p instead", + [Decoded]) + end. collect_content(ChannelPid, ClassId, Protocol) -> case read_frame(ChannelPid) of -- cgit v1.2.1 From 557c3f72986b9b1691dcc1ec0ec6a7ad139a0cbb Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 20 Jul 2010 12:54:12 +0100 Subject: Make more consistent with bug 22889 - see comment 83 on that bug --- codegen.py | 1 - src/rabbit_channel.erl | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/codegen.py b/codegen.py index 3d4c2e2b..230d785e 100644 --- a/codegen.py +++ b/codegen.py @@ -481,7 +481,6 @@ def genHrl(spec): methods = spec.allMethods() printFileHeader() - print "-define(PROTOCOL_VERSION_REVISION, %d)." % (spec.revision) print "-define(PROTOCOL_PORT, %d)." % (spec.port) for (c,v,cls) in spec.constants: diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 51fc5c75..911c5352 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -402,6 +402,9 @@ handle_method(#'channel.close'{}, _, State = #ch{writer_pid = WriterPid}) -> ok = rabbit_writer:send_command(WriterPid, #'channel.close_ok'{}), stop; +handle_method(#'access.request'{},_, State) -> + {reply, #'access.request_ok'{ticket = 1}, State}; + handle_method(#'basic.publish'{}, _, #ch{flow = #flow{client = false}}) -> rabbit_misc:protocol_error( command_invalid, -- cgit v1.2.1 From 36bec708a311dbc3415d4130ce2d23429ebd93fb Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 20 Jul 2010 13:01:41 +0100 Subject: Cherry-pick the last differences between bug 22889 and amqp_0_9_1. --- src/rabbit_exchange.erl | 1 - src/rabbit_framing_channel.erl | 35 ++++++++++++++++++++++------------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl index d91ebe9b..44b001d3 100644 --- a/src/rabbit_exchange.erl +++ b/src/rabbit_exchange.erl @@ -388,7 +388,6 @@ cleanup_deleted_queue_bindings1(ExchangeName, Bindings) -> [X] = mnesia:read({rabbit_exchange, ExchangeName}), {maybe_auto_delete(X), Bindings}. - delete_forward_routes(Route) -> ok = mnesia:delete_object(rabbit_route, Route, write), ok = mnesia:delete_object(rabbit_durable_route, Route, write). diff --git a/src/rabbit_framing_channel.erl b/src/rabbit_framing_channel.erl index 3d74b122..333604c8 100644 --- a/src/rabbit_framing_channel.erl +++ b/src/rabbit_framing_channel.erl @@ -73,16 +73,25 @@ read_frame(ChannelPid) -> end. mainloop(ChannelPid, Protocol) -> - {method, MethodName, FieldsBin} = read_frame(ChannelPid), - Method = Protocol:decode_method_fields(MethodName, FieldsBin), - case Protocol:method_has_content(MethodName) of - true -> {ClassId, _MethodId} = Protocol:method_id(MethodName), - rabbit_channel:do(ChannelPid, Method, - collect_content(ChannelPid, ClassId, - Protocol)); - false -> rabbit_channel:do(ChannelPid, Method) - end, - ?MODULE:mainloop(ChannelPid, Protocol). + Decoded = read_frame(ChannelPid), + case Decoded of + {method, MethodName, FieldsBin} -> + Method = Protocol:decode_method_fields(MethodName, FieldsBin), + case Protocol:method_has_content(MethodName) of + true -> {ClassId, _MethodId} = Protocol:method_id(MethodName), + rabbit_channel:do(ChannelPid, Method, + collect_content(ChannelPid, + ClassId, + Protocol)); + false -> rabbit_channel:do(ChannelPid, Method) + end, + ?MODULE:mainloop(ChannelPid, Protocol); + _ -> + rabbit_misc:protocol_error( + unexpected_frame, + "expected method frame, got ~p instead", + [Decoded]) + end. collect_content(ChannelPid, ClassId, Protocol) -> case read_frame(ChannelPid) of @@ -95,13 +104,13 @@ collect_content(ChannelPid, ClassId, Protocol) -> payload_fragments_rev = Payload}; {content_header, HeaderClassId, 0, _BodySize, _PropertiesBin} -> rabbit_misc:protocol_error( - command_invalid, + unexpected_frame, "expected content header for class ~w, " "got one for class ~w instead", [ClassId, HeaderClassId]); _ -> rabbit_misc:protocol_error( - command_invalid, + unexpected_frame, "expected content header for class ~w, " "got non content header frame instead", [ClassId]) @@ -117,7 +126,7 @@ collect_content_payload(ChannelPid, RemainingByteCount, Acc) -> [FragmentBin | Acc]); _ -> rabbit_misc:protocol_error( - command_invalid, + unexpected_frame, "expected content body, got non content body frame instead", []) end. -- cgit v1.2.1 From d4fc96d5e90a9852041fcfe0304c4129fd9d1a48 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 20 Jul 2010 17:04:31 +0100 Subject: Store both channel-exchange stats and channel-queue stats in the same format. --- src/rabbit_channel.erl | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index d99a1c03..b05d6c56 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -461,7 +461,7 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, unroutable -> ok = basic_return(Message, WriterPid, no_route); not_delivered -> ok = basic_return(Message, WriterPid, no_consumers) end, - State1 = incr_exchange_stats(ExchangeName, State), + State1 = incr_exchange_stats([{ExchangeName, 1}], publish, State), {noreply, case TxnKey of none -> State1; _ -> add_tx_participants(DeliveredQPids, State1) @@ -1161,29 +1161,31 @@ i(prefetch_count, #ch{limiter_pid = LimiterPid}) -> i(Item, _) -> throw({bad_argument, Item}). -incr_exchange_stats(ExchangeName, State = #ch{exchange_statistics = Stats}) -> - Stats1 = dict:update(ExchangeName, fun(Old) -> Old + 1 end, 0, Stats), - State#ch{exchange_statistics = Stats1}. +incr_exchange_stats(Counts, Item, State = #ch{exchange_statistics = Stats}) -> + State#ch{exchange_statistics = incr_stats(Counts, Item, Stats)}. -incr_queue_stats(Counts, Key, State = #ch{queue_statistics = Stats}) -> +incr_queue_stats(Counts, Item, State = #ch{queue_statistics = Stats}) -> + State#ch{queue_statistics = incr_stats(Counts, Item, Stats)}. + +incr_stats(Counts, Item, Stats) -> Stats1 = lists:foldl( - fun ({QPid, Incr}, Stats0) -> - case dict:is_key(QPid, Stats0) of - false -> erlang:monitor(process, QPid); - _ -> ok + fun ({Key, Incr}, Stats0) -> + case is_pid(Key) andalso not dict:is_key(Key, Stats0) of + true -> erlang:monitor(process, Key); + _ -> ok end, - dict:update(QPid, + dict:update(Key, fun(D) -> - Count = case orddict:find(Key, D) of + Count = case orddict:find(Item, D) of error -> 0; {ok, C} -> C end, - orddict:store(Key, Count + Incr, D) + orddict:store(Item, Count + Incr, D) end, [], Stats0) end, Stats, Counts), - State#ch{queue_statistics = Stats1}. + Stats1. maybe_emit_stats(State = #ch{exchange_statistics = ExchangeStatistics, queue_statistics = QueueStatistics, -- cgit v1.2.1 From 51f96c6b66e839922bfc864a57444712b9024f35 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 20 Jul 2010 17:07:20 +0100 Subject: Standardise on "stats" rather than "statistics" since we were getting rather verbose. And inconsistent. --- src/rabbit_amqqueue_process.erl | 8 ++++---- src/rabbit_channel.erl | 32 ++++++++++++++++---------------- src/rabbit_reader.erl | 8 ++++---- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 82a8916c..78f3de97 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -58,7 +58,7 @@ blocked_consumers, sync_timer_ref, rate_timer_ref, - last_statistics_update + last_stats_update }). -record(consumer, {tag, ack_required}). @@ -117,7 +117,7 @@ init(Q) -> blocked_consumers = queue:new(), sync_timer_ref = undefined, rate_timer_ref = undefined, - last_statistics_update = {0,0,0}}, hibernate, + last_stats_update = {0,0,0}}, hibernate, {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}. terminate(shutdown, State = #q{backing_queue = BQ}) -> @@ -543,14 +543,14 @@ i(Item, _) -> %--------------------------------------------------------------------------- -maybe_emit_stats(State = #q{last_statistics_update = LastUpdate}) -> +maybe_emit_stats(State = #q{last_stats_update = LastUpdate}) -> Now = os:timestamp(), case timer:now_diff(Now, LastUpdate) > ?STATISTICS_UPDATE_INTERVAL of true -> rabbit_event:notify( queue_stats, [{Item, i(Item, State)} || Item <- ?STATISTICS_KEYS]), - State#q{last_statistics_update = Now}; + State#q{last_stats_update = Now}; _ -> State end. diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index b05d6c56..367290d7 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, flow, - exchange_statistics, queue_statistics, last_statistics_update}). + exchange_stats, queue_stats, last_stats_update}). -record(flow, {server, client, pending}). @@ -184,9 +184,9 @@ init([Channel, ReaderPid, WriterPid, Username, VHost, CollectorPid]) -> queue_collector_pid = CollectorPid, flow = #flow{server = true, client = true, pending = none}, - exchange_statistics = dict:new(), - queue_statistics = dict:new(), - last_statistics_update = {0,0,0}}, + exchange_stats = dict:new(), + queue_stats = dict:new(), + last_stats_update = {0,0,0}}, hibernate, {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}. @@ -1161,11 +1161,11 @@ i(prefetch_count, #ch{limiter_pid = LimiterPid}) -> i(Item, _) -> throw({bad_argument, Item}). -incr_exchange_stats(Counts, Item, State = #ch{exchange_statistics = Stats}) -> - State#ch{exchange_statistics = incr_stats(Counts, Item, Stats)}. +incr_exchange_stats(Counts, Item, State = #ch{exchange_stats = Stats}) -> + State#ch{exchange_stats = incr_stats(Counts, Item, Stats)}. -incr_queue_stats(Counts, Item, State = #ch{queue_statistics = Stats}) -> - State#ch{queue_statistics = incr_stats(Counts, Item, Stats)}. +incr_queue_stats(Counts, Item, State = #ch{queue_stats = Stats}) -> + State#ch{queue_stats = incr_stats(Counts, Item, Stats)}. incr_stats(Counts, Item, Stats) -> Stats1 = lists:foldl( @@ -1187,21 +1187,21 @@ incr_stats(Counts, Item, Stats) -> end, Stats, Counts), Stats1. -maybe_emit_stats(State = #ch{exchange_statistics = ExchangeStatistics, - queue_statistics = QueueStatistics, - last_statistics_update = LastUpdate}) -> +maybe_emit_stats(State = #ch{exchange_stats = ExchangeStats, + queue_stats = QueueStats, + last_stats_update = LastUpdate}) -> Now = os:timestamp(), case timer:now_diff(Now, LastUpdate) > ?STATISTICS_UPDATE_INTERVAL of true -> rabbit_event:notify( channel_stats, [{Item, i(Item, State)} || Item <- ?STATISTICS_KEYS] ++ - [{per_exchange_statistics, dict:to_list(ExchangeStatistics)}, - {per_queue_statistics, dict:to_list(QueueStatistics)}]), - State#ch{last_statistics_update = Now}; + [{per_exchange_stats, dict:to_list(ExchangeStats)}, + {per_queue_stats, dict:to_list(QueueStats)}]), + State#ch{last_stats_update = Now}; _ -> State end. -erase_stats(QPid, State = #ch{queue_statistics = QueueStatistics}) -> - State#ch{queue_statistics = dict:erase(QPid, QueueStatistics)}. +erase_stats(QPid, State = #ch{queue_stats = QueueStats}) -> + State#ch{queue_stats = dict:erase(QPid, QueueStats)}. diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index bb1005b0..4be191ae 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -58,7 +58,7 @@ %--------------------------------------------------------------------------- -record(v1, {sock, connection, callback, recv_ref, connection_state, - queue_collector, last_statistics_update}). + queue_collector, last_stats_update}). -define(STATISTICS_KEYS, [pid, recv_oct, recv_cnt, send_oct, send_cnt, send_pend, state, channels]). @@ -257,7 +257,7 @@ start_connection(Parent, Deb, Sock, SockTransform) -> recv_ref = none, connection_state = pre_init, queue_collector = Collector, - last_statistics_update = {0,0,0}}, + last_stats_update = {0,0,0}}, handshake, 8)) catch Ex -> (if Ex == connection_closed_abruptly -> @@ -857,14 +857,14 @@ amqp_exception_explanation(Text, Expl) -> true -> CompleteTextBin end. -maybe_emit_stats(State = #v1{last_statistics_update = LastUpdate}) -> +maybe_emit_stats(State = #v1{last_stats_update = LastUpdate}) -> Now = os:timestamp(), case timer:now_diff(Now, LastUpdate) > ?STATISTICS_UPDATE_INTERVAL of true -> rabbit_event:notify( connection_stats, [{Item, i(Item, State)} || Item <- ?STATISTICS_KEYS]), - State#v1{last_statistics_update = Now}; + State#v1{last_stats_update = Now}; _ -> State end. -- cgit v1.2.1 From 0cb2eb7b8754fac0078e8fb8db32f9f893c262a1 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 20 Jul 2010 18:02:03 +0100 Subject: Rename some variables. --- src/rabbit_channel.erl | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 367290d7..b1a67640 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -1161,30 +1161,30 @@ i(prefetch_count, #ch{limiter_pid = LimiterPid}) -> i(Item, _) -> throw({bad_argument, Item}). -incr_exchange_stats(Counts, Item, State = #ch{exchange_stats = Stats}) -> - State#ch{exchange_stats = incr_stats(Counts, Item, Stats)}. +incr_exchange_stats(XCounts, Item, State = #ch{exchange_stats = Stats}) -> + State#ch{exchange_stats = incr_stats(XCounts, Item, Stats)}. -incr_queue_stats(Counts, Item, State = #ch{queue_stats = Stats}) -> - State#ch{queue_stats = incr_stats(Counts, Item, Stats)}. +incr_queue_stats(QCounts, Item, State = #ch{queue_stats = Stats}) -> + State#ch{queue_stats = incr_stats(QCounts, Item, Stats)}. -incr_stats(Counts, Item, Stats) -> +incr_stats(QXCounts, Item, Stats) -> Stats1 = lists:foldl( - fun ({Key, Incr}, Stats0) -> - case is_pid(Key) andalso not dict:is_key(Key, Stats0) of - true -> erlang:monitor(process, Key); + fun ({QX, Inc}, Stats0) -> + case is_pid(QX) andalso not dict:is_key(QX, Stats) of + true -> erlang:monitor(process, QX); _ -> ok end, - dict:update(Key, + dict:update(QX, fun(D) -> Count = case orddict:find(Item, D) of error -> 0; {ok, C} -> C end, - orddict:store(Item, Count + Incr, D) + orddict:store(Item, Count + Inc, D) end, [], Stats0) - end, Stats, Counts), + end, Stats, QXCounts), Stats1. maybe_emit_stats(State = #ch{exchange_stats = ExchangeStats, -- cgit v1.2.1 From a4d652ec66431782a6907609b7a87fd2aa77fca5 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 21 Jul 2010 10:32:13 +0100 Subject: Store (channel, queue, exchange) stats instead of (channel, exchange). Unify the two stats dictionaries in the channel. --- src/rabbit_channel.erl | 72 ++++++++++++++++++++++++++------------------------ 1 file changed, 38 insertions(+), 34 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index b1a67640..2ff337f8 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, flow, - exchange_stats, queue_stats, last_stats_update}). + queue_exchange_stats, last_stats_update}). -record(flow, {server, client, pending}). @@ -184,8 +184,7 @@ init([Channel, ReaderPid, WriterPid, Username, VHost, CollectorPid]) -> queue_collector_pid = CollectorPid, flow = #flow{server = true, client = true, pending = none}, - exchange_stats = dict:new(), - queue_stats = dict:new(), + queue_exchange_stats = dict:new(), last_stats_update = {0,0,0}}, hibernate, {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}. @@ -238,11 +237,11 @@ handle_cast({deliver, ConsumerTag, AckRequired, Msg}, State1 = lock_message(AckRequired, {DeliveryTag, ConsumerTag, Msg}, State), ok = internal_deliver(WriterPid, true, ConsumerTag, DeliveryTag, Msg), {_QName, QPid, _MsgId, _Redelivered, _Msg} = Msg, - State2 = incr_queue_stats([{QPid, 1}], - case AckRequired of - true -> deliver; - false -> deliver_no_ack - end, State1), + State2 = incr_stats([{QPid, 1}], + case AckRequired of + true -> deliver; + false -> deliver_no_ack + end, State1), noreply(State2#ch{next_tag = DeliveryTag + 1}); handle_cast({conserve_memory, true}, State = #ch{state = starting}) -> @@ -273,7 +272,7 @@ handle_info({'EXIT', _Pid, Reason}, State) -> {stop, Reason, State}; handle_info({'DOWN', _MRef, process, QPid, _Reason}, State) -> State1 = queue_blocked(QPid, State), - State2 = erase_stats(QPid, State1), + State2 = erase_queue_stats(QPid, State1), {noreply, State2}. handle_pre_hibernate(State) -> @@ -461,7 +460,9 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, unroutable -> ok = basic_return(Message, WriterPid, no_route); not_delivered -> ok = basic_return(Message, WriterPid, no_consumers) end, - State1 = incr_exchange_stats([{ExchangeName, 1}], publish, State), + State1 = incr_stats( + [{{QPid, ExchangeName}, 1} || QPid <- DeliveredQPids], publish, + State), {noreply, case TxnKey of none -> State1; _ -> add_tx_participants(DeliveredQPids, State1) @@ -474,7 +475,7 @@ handle_method(#'basic.ack'{delivery_tag = DeliveryTag, {Acked, Remaining} = collect_acks(UAMQ, DeliveryTag, Multiple), QsCounts = ack(TxnKey, Acked), Participants = [QPid || {QPid, _} <- QsCounts], - State1 = incr_queue_stats(QsCounts, ack, State), + State1 = incr_stats(QsCounts, ack, State), {noreply, case TxnKey of none -> ok = notify_limiter(State#ch.limiter_pid, Acked), State1#ch{unacked_message_q = Remaining}; @@ -502,11 +503,11 @@ handle_method(#'basic.get'{queue = QueueNameBin, routing_key = RoutingKey, content = Content}}} -> State1 = lock_message(not(NoAck), {DeliveryTag, none, Msg}, State), - State2 = incr_queue_stats([{QPid, 1}], - case NoAck of - true -> get_no_ack; - false -> get - end, State1), + State2 = incr_stats([{QPid, 1}], + case NoAck of + true -> get_no_ack; + false -> get + end, State1), ok = rabbit_writer:send_command( WriterPid, #'basic.get_ok'{delivery_tag = DeliveryTag, @@ -1161,18 +1162,16 @@ i(prefetch_count, #ch{limiter_pid = LimiterPid}) -> i(Item, _) -> throw({bad_argument, Item}). -incr_exchange_stats(XCounts, Item, State = #ch{exchange_stats = Stats}) -> - State#ch{exchange_stats = incr_stats(XCounts, Item, Stats)}. - -incr_queue_stats(QCounts, Item, State = #ch{queue_stats = Stats}) -> - State#ch{queue_stats = incr_stats(QCounts, Item, Stats)}. - -incr_stats(QXCounts, Item, Stats) -> +incr_stats(QXCounts, Item, State = #ch{queue_exchange_stats = Stats}) -> Stats1 = lists:foldl( fun ({QX, Inc}, Stats0) -> - case is_pid(QX) andalso not dict:is_key(QX, Stats) of - true -> erlang:monitor(process, QX); - _ -> ok + QPid = case QX of + {Q, _X} -> Q; + Q -> Q + end, + case dict:is_key(QPid, Stats) of + false -> erlang:monitor(process, QPid); + _ -> ok end, dict:update(QX, fun(D) -> @@ -1185,23 +1184,28 @@ incr_stats(QXCounts, Item, Stats) -> [], Stats0) end, Stats, QXCounts), - Stats1. + State#ch{queue_exchange_stats = Stats1}. -maybe_emit_stats(State = #ch{exchange_stats = ExchangeStats, - queue_stats = QueueStats, +maybe_emit_stats(State = #ch{queue_exchange_stats = QueueExchangeStats, last_stats_update = LastUpdate}) -> Now = os:timestamp(), case timer:now_diff(Now, LastUpdate) > ?STATISTICS_UPDATE_INTERVAL of true -> rabbit_event:notify( channel_stats, - [{Item, i(Item, State)} || Item <- ?STATISTICS_KEYS] ++ - [{per_exchange_stats, dict:to_list(ExchangeStats)}, - {per_queue_stats, dict:to_list(QueueStats)}]), + [{queue_exchange_stats, dict:to_list(QueueExchangeStats)} | + [{Item, i(Item, State)} || Item <- ?STATISTICS_KEYS]]), State#ch{last_stats_update = Now}; _ -> State end. -erase_stats(QPid, State = #ch{queue_stats = QueueStats}) -> - State#ch{queue_stats = dict:erase(QPid, QueueStats)}. +erase_queue_stats(QPid, State = #ch{queue_exchange_stats = Stats}) -> + Stats1 = dict:erase(QPid, Stats), + Stats2 = dict:filter(fun (K, _V) -> + case K of + {QPid, _} -> false; + _ -> true + end + end, Stats1), + State#ch{queue_exchange_stats = Stats2}. -- cgit v1.2.1 From ce04648364101332f298ffda7558496990d465b4 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 21 Jul 2010 10:53:09 +0100 Subject: If a new QPid appears more than once in the list, only monitor it once. --- 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 2ff337f8..0c878617 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -1169,7 +1169,7 @@ incr_stats(QXCounts, Item, State = #ch{queue_exchange_stats = Stats}) -> {Q, _X} -> Q; Q -> Q end, - case dict:is_key(QPid, Stats) of + case dict:is_key(QPid, Stats0) of false -> erlang:monitor(process, QPid); _ -> ok end, -- cgit v1.2.1 From ea28306cba88cbc4df6552840f27bfee217ff38a Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 21 Jul 2010 14:15:31 +0100 Subject: Invoking os:timestamp and timer:now_diff all the time is too expensive, use timers instead. --- include/rabbit.hrl | 2 +- src/rabbit_amqqueue.erl | 8 ++++++ src/rabbit_amqqueue_process.erl | 47 ++++++++++++++++++++++------------- src/rabbit_channel.erl | 55 +++++++++++++++++++++++++++-------------- src/rabbit_reader.erl | 41 ++++++++++++++++++------------ 5 files changed, 100 insertions(+), 53 deletions(-) diff --git a/include/rabbit.hrl b/include/rabbit.hrl index ac7e3851..5df9e690 100644 --- a/include/rabbit.hrl +++ b/include/rabbit.hrl @@ -82,7 +82,7 @@ -define(HIBERNATE_AFTER_MIN, 1000). -define(DESIRED_HIBERNATE, 10000). --define(STATISTICS_UPDATE_INTERVAL, 5000000). %% microseconds +-define(STATS_INTERVAL, 5000). -ifdef(debug). -define(LOGDEBUG0(F), rabbit_log:debug(F)). diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index f1b52768..7bc85f2e 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -41,6 +41,7 @@ check_exclusive_access/2, with_exclusive_access_or_die/3, stat/1, deliver/2, requeue/3, ack/4]). -export([list/1, info_keys/0, info/1, info/2, info_all/1, info_all/2]). +-export([emit_stats/1]). -export([consumers/1, consumers_all/1]). -export([basic_get/3, basic_consume/7, basic_cancel/4]). -export([notify_sent/2, unblock/2, flush_all/2]). @@ -104,6 +105,7 @@ -spec(stat/1 :: (rabbit_types:amqqueue()) -> {'ok', non_neg_integer(), non_neg_integer()}). +-spec(emit_stats/1 :: (rabbit_types:amqqueue()) -> 'ok'). -spec(delete/3 :: (rabbit_types:amqqueue(), 'false', 'false') -> qlen(); @@ -305,6 +307,9 @@ consumers_all(VHostPath) -> stat(#amqqueue{pid = QPid}) -> delegate_call(QPid, stat, infinity). +emit_stats(#amqqueue{pid = QPid}) -> + delegate_cast(QPid, emit_stats). + delete(#amqqueue{ pid = QPid }, IfUnused, IfEmpty) -> delegate_call(QPid, {delete, IfUnused, IfEmpty}, infinity). @@ -449,6 +454,9 @@ safe_delegate_call_ok(H, F, Pids) -> delegate_call(Pid, Msg, Timeout) -> delegate:invoke(Pid, fun (P) -> gen_server2:call(P, Msg, Timeout) end). +delegate_cast(Pid, Msg) -> + delegate:invoke(Pid, fun (P) -> gen_server2:cast(P, Msg) end). + delegate_pcall(Pid, Pri, Msg, Timeout) -> delegate:invoke(Pid, fun (P) -> gen_server2:pcall(P, Pri, Msg, Timeout) end). diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 78f3de97..e7d4817d 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -58,7 +58,7 @@ blocked_consumers, sync_timer_ref, rate_timer_ref, - last_stats_update + stats_timer_ref }). -record(consumer, {tag, ack_required}). @@ -117,7 +117,7 @@ init(Q) -> blocked_consumers = queue:new(), sync_timer_ref = undefined, rate_timer_ref = undefined, - last_stats_update = {0,0,0}}, hibernate, + stats_timer_ref = undefined}, hibernate, {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}. terminate(shutdown, State = #q{backing_queue = BQ}) -> @@ -191,7 +191,7 @@ noreply(NewState) -> next_state(State) -> State1 = #q{backing_queue = BQ, backing_queue_state = BQS} = ensure_rate_timer(State), - State2 = maybe_emit_stats(State1), + State2 = ensure_stats_timer(State1), case BQ:needs_sync(BQS)of true -> {ensure_sync_timer(State2), 0}; false -> {stop_sync_timer(State2), hibernate} @@ -231,6 +231,23 @@ stop_rate_timer(State = #q{rate_timer_ref = TRef}) -> {ok, cancel} = timer:cancel(TRef), State#q{rate_timer_ref = undefined}. +ensure_stats_timer(State = #q{stats_timer_ref = undefined, q = Q}) -> + {ok, TRef} = timer:apply_after(?STATS_INTERVAL, + rabbit_amqqueue, emit_stats, + [Q]), + State#q{stats_timer_ref = TRef}; +ensure_stats_timer(State) -> + State. + +stop_stats_timer(State = #q{stats_timer_ref = undefined}) -> + emit_stats(State), + State; +stop_stats_timer(State = #q{stats_timer_ref = TRef}) -> + {ok, cancel} = timer:cancel(TRef), + emit_stats(State), + State#q{stats_timer_ref = undefined}. + + assert_invariant(#q{active_consumers = AC, backing_queue = BQ, backing_queue_state = BQS}) -> true = (queue:is_empty(AC) orelse BQ:is_empty(BQS)). @@ -541,19 +558,10 @@ i(backing_queue_status, #q{backing_queue_state = BQS, backing_queue = BQ}) -> i(Item, _) -> throw({bad_argument, Item}). -%--------------------------------------------------------------------------- +emit_stats(State) -> + rabbit_event:notify(queue_stats, + [{Item, i(Item, State)} || Item <- ?STATISTICS_KEYS]). -maybe_emit_stats(State = #q{last_stats_update = LastUpdate}) -> - Now = os:timestamp(), - case timer:now_diff(Now, LastUpdate) > ?STATISTICS_UPDATE_INTERVAL of - true -> - rabbit_event:notify( - queue_stats, - [{Item, i(Item, State)} || Item <- ?STATISTICS_KEYS]), - State#q{last_stats_update = Now}; - _ -> - State - end. %--------------------------------------------------------------------------- handle_call({init, Recover}, From, @@ -829,7 +837,11 @@ handle_cast({set_ram_duration_target, Duration}, handle_cast({set_maximum_since_use, Age}, State) -> ok = file_handle_cache:set_maximum_since_use(Age), - noreply(State). + noreply(State); + +handle_cast(emit_stats, State) -> + emit_stats(State), + noreply(State#q{stats_timer_ref = undefined}). handle_info({'DOWN', _MonitorRef, process, DownPid, _Reason}, State = #q{q = #amqqueue{exclusive_owner = DownPid}}) -> @@ -866,4 +878,5 @@ 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), - {hibernate, stop_rate_timer(State#q{backing_queue_state = BQS2})}. + {hibernate, stop_stats_timer( + stop_rate_timer(State#q{backing_queue_state = BQS2}))}. diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 0c878617..21d2b3de 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -38,6 +38,7 @@ -export([start_link/6, do/2, do/3, shutdown/1]). -export([send_command/2, deliver/4, conserve_memory/2, flushed/2]). -export([list/0, info_keys/0, info/1, info/2, info_all/0, info_all/1]). +-export([emit_stats/1]). -export([flow_timeout/2]). @@ -49,7 +50,7 @@ uncommitted_ack_q, unacked_message_q, username, virtual_host, most_recently_declared_queue, consumer_mapping, blocking, queue_collector_pid, flow, - queue_exchange_stats, last_stats_update}). + queue_exchange_stats, stats_timer_ref}). -record(flow, {server, client, pending}). @@ -100,6 +101,7 @@ -spec(info/2 :: (pid(), [rabbit_types:info_key()]) -> [rabbit_types:info()]). -spec(info_all/0 :: () -> [[rabbit_types:info()]]). -spec(info_all/1 :: ([rabbit_types:info_key()]) -> [[rabbit_types:info()]]). +-spec(emit_stats/1 :: (pid()) -> 'ok'). -endif. @@ -155,6 +157,9 @@ info_all() -> info_all(Items) -> rabbit_misc:filter_exit_map(fun (C) -> info(C, Items) end, list()). +emit_stats(Pid) -> + gen_server2:cast(Pid, emit_stats). + %%--------------------------------------------------------------------------- init([Channel, ReaderPid, WriterPid, Username, VHost, CollectorPid]) -> @@ -185,7 +190,7 @@ init([Channel, ReaderPid, WriterPid, Username, VHost, CollectorPid]) -> flow = #flow{server = true, client = true, pending = none}, queue_exchange_stats = dict:new(), - last_stats_update = {0,0,0}}, + stats_timer_ref = undefined}, hibernate, {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}. @@ -262,7 +267,11 @@ handle_cast({flow_timeout, Ref}, "timeout waiting for channel.flow_ok{active=~w}", [not Flow], none), State)}; handle_cast({flow_timeout, _Ref}, State) -> - {noreply, State}. + {noreply, State}; + +handle_cast(emit_stats, State) -> + internal_emit_stats(State), + noreply(State#ch{stats_timer_ref = undefined}). handle_info({'EXIT', WriterPid, Reason = {writer, send_failed, _Error}}, State = #ch{writer_pid = WriterPid}) -> @@ -277,7 +286,7 @@ handle_info({'DOWN', _MRef, process, QPid, _Reason}, State) -> handle_pre_hibernate(State) -> ok = clear_permission_cache(), - {hibernate, State}. + {hibernate, stop_stats_timer(State)}. terminate(_Reason, State = #ch{state = terminating}) -> terminate(State); @@ -296,13 +305,29 @@ code_change(_OldVsn, State, _Extra) -> %%--------------------------------------------------------------------------- reply(Reply, NewState) -> - NewState1 = maybe_emit_stats(NewState), + NewState1 = ensure_stats_timer(NewState), {reply, Reply, NewState1, hibernate}. noreply(NewState) -> - NewState1 = maybe_emit_stats(NewState), + NewState1 = ensure_stats_timer(NewState), {noreply, NewState1, hibernate}. +ensure_stats_timer(State = #ch{stats_timer_ref = undefined}) -> + {ok, TRef} = timer:apply_after(?STATS_INTERVAL, + rabbit_channel, emit_stats, + [self()]), + State#ch{stats_timer_ref = TRef}; +ensure_stats_timer(State) -> + State. + +stop_stats_timer(State = #ch{stats_timer_ref = undefined}) -> + internal_emit_stats(State), + State; +stop_stats_timer(State = #ch{stats_timer_ref = TRef}) -> + {ok, cancel} = timer:cancel(TRef), + internal_emit_stats(State), + State#ch{stats_timer_ref = undefined}. + return_ok(State, true, _Msg) -> {noreply, State}; return_ok(State, false, Msg) -> {reply, Msg, State}. @@ -1186,19 +1211,11 @@ incr_stats(QXCounts, Item, State = #ch{queue_exchange_stats = Stats}) -> end, Stats, QXCounts), State#ch{queue_exchange_stats = Stats1}. -maybe_emit_stats(State = #ch{queue_exchange_stats = QueueExchangeStats, - last_stats_update = LastUpdate}) -> - Now = os:timestamp(), - case timer:now_diff(Now, LastUpdate) > ?STATISTICS_UPDATE_INTERVAL of - true -> - rabbit_event:notify( - channel_stats, - [{queue_exchange_stats, dict:to_list(QueueExchangeStats)} | - [{Item, i(Item, State)} || Item <- ?STATISTICS_KEYS]]), - State#ch{last_stats_update = Now}; - _ -> - State - end. +internal_emit_stats(State = #ch{queue_exchange_stats = QueueExchangeStats}) -> + rabbit_event:notify( + channel_stats, + [{queue_exchange_stats, dict:to_list(QueueExchangeStats)} | + [{Item, i(Item, State)} || Item <- ?STATISTICS_KEYS]]). erase_queue_stats(QPid, State = #ch{queue_exchange_stats = Stats}) -> Stats1 = dict:erase(QPid, Stats), diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 4be191ae..1143794e 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -43,6 +43,8 @@ -export([analyze_frame/2]). +-export([emit_stats/1]). + -import(gen_tcp). -import(fprof). -import(inet). @@ -58,7 +60,7 @@ %--------------------------------------------------------------------------- -record(v1, {sock, connection, callback, recv_ref, connection_state, - queue_collector, last_stats_update}). + queue_collector, stats_timer_ref}). -define(STATISTICS_KEYS, [pid, recv_oct, recv_cnt, send_oct, send_cnt, send_pend, state, channels]). @@ -144,6 +146,7 @@ -spec(info_keys/0 :: () -> [rabbit_types:info_key()]). -spec(info/1 :: (pid()) -> [rabbit_types:info()]). -spec(info/2 :: (pid(), [rabbit_types:info_key()]) -> [rabbit_types:info()]). +-spec(emit_stats/1 :: (pid()) -> 'ok'). -spec(shutdown/2 :: (pid(), string()) -> 'ok'). -spec(server_properties/0 :: () -> rabbit_framing:amqp_table()). @@ -184,6 +187,9 @@ info(Pid, Items) -> {error, Error} -> throw(Error) end. +emit_stats(Pid) -> + gen_server2:cast(Pid, emit_stats). + setup_profiling() -> Value = rabbit_misc:get_config(profiling_enabled, false), case Value of @@ -257,7 +263,7 @@ start_connection(Parent, Deb, Sock, SockTransform) -> recv_ref = none, connection_state = pre_init, queue_collector = Collector, - last_stats_update = {0,0,0}}, + stats_timer_ref = undefined}, handshake, 8)) catch Ex -> (if Ex == connection_closed_abruptly -> @@ -282,9 +288,8 @@ start_connection(Parent, Deb, Sock, SockTransform) -> end, done. -mainloop(Parent, Deb, State_ = #v1{sock= Sock, recv_ref = Ref}) -> +mainloop(Parent, Deb, State = #v1{sock= Sock, recv_ref = Ref}) -> %%?LOGDEBUG("Reader mainloop: ~p bytes available, need ~p~n", [HaveBytes, WaitUntilNBytes]), - State = maybe_emit_stats(State_), receive {inet_async, Sock, Ref, {ok, Data}} -> {State1, Callback1, Length1} = @@ -345,6 +350,9 @@ mainloop(Parent, Deb, State_ = #v1{sock= Sock, recv_ref = Ref}) -> catch Error -> {error, Error} end), mainloop(Parent, Deb, State); + {'$gen_cast', emit_stats} -> + internal_emit_stats(State), + mainloop(Parent, Deb, State#v1{stats_timer_ref = undefined}); {system, From, Request} -> sys:handle_system_msg(Request, From, Parent, ?MODULE, Deb, State); @@ -537,7 +545,8 @@ analyze_frame(_Type, _Body) -> handle_input(frame_header, <>, State) -> %%?LOGDEBUG("Got frame header: ~p/~p/~p~n", [Type, Channel, PayloadSize]), - {State, {frame_payload, Type, Channel, PayloadSize}, PayloadSize + 1}; + {ensure_stats_timer(State), {frame_payload, Type, Channel, PayloadSize}, + PayloadSize + 1}; handle_input({frame_payload, Type, Channel, PayloadSize}, PayloadAndMarker, State) -> case PayloadAndMarker of @@ -591,6 +600,14 @@ check_version(ClientVersion, ServerVersion) -> (ClientMajor == ServerMajor andalso ClientMinor >= ServerMinor). +ensure_stats_timer(State = #v1{stats_timer_ref = undefined}) -> + {ok, TRef} = timer:apply_after(?STATS_INTERVAL, + rabbit_reader, emit_stats, + [self()]), + State#v1{stats_timer_ref = TRef}; +ensure_stats_timer(State) -> + State. + %%-------------------------------------------------------------------------- handle_method0(MethodName, FieldsBin, State) -> @@ -857,14 +874,6 @@ amqp_exception_explanation(Text, Expl) -> true -> CompleteTextBin end. -maybe_emit_stats(State = #v1{last_stats_update = LastUpdate}) -> - Now = os:timestamp(), - case timer:now_diff(Now, LastUpdate) > ?STATISTICS_UPDATE_INTERVAL of - true -> - rabbit_event:notify( - connection_stats, - [{Item, i(Item, State)} || Item <- ?STATISTICS_KEYS]), - State#v1{last_stats_update = Now}; - _ -> - State - end. +internal_emit_stats(State) -> + rabbit_event:notify(connection_stats, + [{Item, i(Item, State)} || Item <- ?STATISTICS_KEYS]). -- cgit v1.2.1 From f1ffcf3fc686d596180f486150a0ff39a94202e8 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 21 Jul 2010 16:17:01 +0100 Subject: Store queue/exchange stats in the process dictionary since that's rather a lot faster than dict: --- src/rabbit_channel.erl | 115 +++++++++++++++++++++++-------------------------- 1 file changed, 55 insertions(+), 60 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 21d2b3de..7fe29d3e 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -242,12 +242,12 @@ handle_cast({deliver, ConsumerTag, AckRequired, Msg}, State1 = lock_message(AckRequired, {DeliveryTag, ConsumerTag, Msg}, State), ok = internal_deliver(WriterPid, true, ConsumerTag, DeliveryTag, Msg), {_QName, QPid, _MsgId, _Redelivered, _Msg} = Msg, - State2 = incr_stats([{QPid, 1}], - case AckRequired of - true -> deliver; - false -> deliver_no_ack - end, State1), - noreply(State2#ch{next_tag = DeliveryTag + 1}); + incr_stats([{QPid, 1}], + case AckRequired of + true -> deliver; + false -> deliver_no_ack + end), + noreply(State1#ch{next_tag = DeliveryTag + 1}); handle_cast({conserve_memory, true}, State = #ch{state = starting}) -> noreply(State); @@ -281,8 +281,8 @@ handle_info({'EXIT', _Pid, Reason}, State) -> {stop, Reason, State}; handle_info({'DOWN', _MRef, process, QPid, _Reason}, State) -> State1 = queue_blocked(QPid, State), - State2 = erase_queue_stats(QPid, State1), - {noreply, State2}. + erase_queue_stats(QPid), + {noreply, State1}. handle_pre_hibernate(State) -> ok = clear_permission_cache(), @@ -485,12 +485,10 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, unroutable -> ok = basic_return(Message, WriterPid, no_route); not_delivered -> ok = basic_return(Message, WriterPid, no_consumers) end, - State1 = incr_stats( - [{{QPid, ExchangeName}, 1} || QPid <- DeliveredQPids], publish, - State), + incr_stats([{{QPid, ExchangeName}, 1} || QPid <- DeliveredQPids], publish), {noreply, case TxnKey of - none -> State1; - _ -> add_tx_participants(DeliveredQPids, State1) + none -> State; + _ -> add_tx_participants(DeliveredQPids, State) end}; handle_method(#'basic.ack'{delivery_tag = DeliveryTag, @@ -498,18 +496,18 @@ handle_method(#'basic.ack'{delivery_tag = DeliveryTag, _, State = #ch{transaction_id = TxnKey, unacked_message_q = UAMQ}) -> {Acked, Remaining} = collect_acks(UAMQ, DeliveryTag, Multiple), - QsCounts = ack(TxnKey, Acked), - Participants = [QPid || {QPid, _} <- QsCounts], - State1 = incr_stats(QsCounts, ack, State), + QsIncs = ack(TxnKey, Acked), + Participants = [QPid || {QPid, _} <- QsIncs], + incr_stats(QsIncs, ack), {noreply, case TxnKey of none -> ok = notify_limiter(State#ch.limiter_pid, Acked), - State1#ch{unacked_message_q = Remaining}; + State#ch{unacked_message_q = Remaining}; _ -> NewUAQ = queue:join(State#ch.uncommitted_ack_q, Acked), add_tx_participants( Participants, - State1#ch{unacked_message_q = Remaining, - uncommitted_ack_q = NewUAQ}) + State#ch{unacked_message_q = Remaining, + uncommitted_ack_q = NewUAQ}) end}; handle_method(#'basic.get'{queue = QueueNameBin, @@ -528,11 +526,11 @@ handle_method(#'basic.get'{queue = QueueNameBin, routing_key = RoutingKey, content = Content}}} -> State1 = lock_message(not(NoAck), {DeliveryTag, none, Msg}, State), - State2 = incr_stats([{QPid, 1}], - case NoAck of - true -> get_no_ack; - false -> get - end, State1), + incr_stats([{QPid, 1}], + case NoAck of + true -> get_no_ack; + false -> get + end), ok = rabbit_writer:send_command( WriterPid, #'basic.get_ok'{delivery_tag = DeliveryTag, @@ -541,7 +539,7 @@ handle_method(#'basic.get'{queue = QueueNameBin, routing_key = RoutingKey, message_count = MessageCount}, Content), - {noreply, State2#ch{next_tag = DeliveryTag + 1}}; + {noreply, State1#ch{next_tag = DeliveryTag + 1}}; empty -> {reply, #'basic.get_empty'{}, State} end; @@ -1187,42 +1185,39 @@ i(prefetch_count, #ch{limiter_pid = LimiterPid}) -> i(Item, _) -> throw({bad_argument, Item}). -incr_stats(QXCounts, Item, State = #ch{queue_exchange_stats = Stats}) -> - Stats1 = lists:foldl( - fun ({QX, Inc}, Stats0) -> - QPid = case QX of - {Q, _X} -> Q; - Q -> Q - end, - case dict:is_key(QPid, Stats0) of - false -> erlang:monitor(process, QPid); - _ -> ok - end, - dict:update(QX, - fun(D) -> - Count = case orddict:find(Item, D) of - error -> 0; - {ok, C} -> C - end, - orddict:store(Item, Count + Inc, D) - end, - [], - Stats0) - end, Stats, QXCounts), - State#ch{queue_exchange_stats = Stats1}. - -internal_emit_stats(State = #ch{queue_exchange_stats = QueueExchangeStats}) -> +incr_stats(QXIncs, Item) -> + [incr_stats(QX, Inc, Item) || {QX, Inc} <- QXIncs]. + +incr_stats(QX, Inc, Item) -> + QPid = case QX of + {Q, _X} -> Q; + Q -> Q + end, + case get({monitoring, QPid}) of + undefined -> erlang:monitor(process, QPid), + put({monitoring, QPid}, true); + _ -> ok + end, + Measures = case get({queue_exchange_stats, QX}) of + undefined -> []; + D -> D + end, + Cur = case orddict:find(Item, Measures) of + error -> 0; + {ok, C} -> C + end, + put({queue_exchange_stats, QX}, orddict:store(Item, Cur + Inc, Measures)). + +internal_emit_stats(State) -> rabbit_event:notify( channel_stats, - [{queue_exchange_stats, dict:to_list(QueueExchangeStats)} | + [{queue_exchange_stats, + [{QX, Stats} || {{queue_exchange_stats, QX}, Stats} <- get()]} | [{Item, i(Item, State)} || Item <- ?STATISTICS_KEYS]]). -erase_queue_stats(QPid, State = #ch{queue_exchange_stats = Stats}) -> - Stats1 = dict:erase(QPid, Stats), - Stats2 = dict:filter(fun (K, _V) -> - case K of - {QPid, _} -> false; - _ -> true - end - end, Stats1), - State#ch{queue_exchange_stats = Stats2}. +erase_queue_stats(QPid) -> + erase({monitoring, QPid}), + erase({queue_exchange_stats, QPid}). + %% TODO make this work + %% [erase({queue_exchange_stats, QX}) || + %% {{queue_exchange_stats, QX}, _} <- get(), {QPid, _} == QX]. -- cgit v1.2.1 From fdd1b2f0ee18f49860a99b4b3fdbabda7550c13d Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 21 Jul 2010 16:36:54 +0100 Subject: Store (channel, exchange) stats as well as (channel, exchange, queue) since we can't derive the former from the latter. --- src/rabbit_channel.erl | 53 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 19 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 7fe29d3e..8597e477 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -485,7 +485,9 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, unroutable -> ok = basic_return(Message, WriterPid, no_route); not_delivered -> ok = basic_return(Message, WriterPid, no_consumers) end, - incr_stats([{{QPid, ExchangeName}, 1} || QPid <- DeliveredQPids], publish), + incr_stats([{ExchangeName, 1} | + [{{QPid, ExchangeName}, 1} || + QPid <- DeliveredQPids]], publish), {noreply, case TxnKey of none -> State; _ -> add_tx_participants(DeliveredQPids, State) @@ -1185,39 +1187,52 @@ i(prefetch_count, #ch{limiter_pid = LimiterPid}) -> i(Item, _) -> throw({bad_argument, Item}). -incr_stats(QXIncs, Item) -> - [incr_stats(QX, Inc, Item) || {QX, Inc} <- QXIncs]. +incr_stats(QXIncs, Measure) -> + [incr_stats(QX, Inc, Measure) || {QX, Inc} <- QXIncs]. -incr_stats(QX, Inc, Item) -> - QPid = case QX of - {Q, _X} -> Q; - Q -> Q - end, +incr_stats({QPid, _} = QX, Inc, Measure) -> + maybe_monitor(QPid), + update_measures(queue_exchange_stats, QX, Inc, Measure); + +incr_stats(QPid, Inc, Measure) when is_pid(QPid) -> + maybe_monitor(QPid), + update_measures(queue_stats, QPid, Inc, Measure); + +incr_stats(X, Inc, Measure) -> + update_measures(exchange_stats, X, Inc, Measure). + +maybe_monitor(QPid) -> case get({monitoring, QPid}) of undefined -> erlang:monitor(process, QPid), put({monitoring, QPid}, true); _ -> ok - end, - Measures = case get({queue_exchange_stats, QX}) of + end. + +update_measures(Type, QX, Inc, Measure) -> + Measures = case get({Type, QX}) of undefined -> []; D -> D end, - Cur = case orddict:find(Item, Measures) of + Cur = case orddict:find(Measure, Measures) of error -> 0; {ok, C} -> C end, - put({queue_exchange_stats, QX}, orddict:store(Item, Cur + Inc, Measures)). + put({Type, QX}, + orddict:store(Measure, Cur + Inc, Measures)). internal_emit_stats(State) -> rabbit_event:notify( channel_stats, - [{queue_exchange_stats, - [{QX, Stats} || {{queue_exchange_stats, QX}, Stats} <- get()]} | - [{Item, i(Item, State)} || Item <- ?STATISTICS_KEYS]]). + [{queue_stats, + [{QPid, Stats} || {{queue_stats, QPid}, Stats} <- get()]}, + {exchange_stats, + [{X, Stats} || {{exchange_stats, X}, Stats} <- get()]}, + {queue_exchange_stats, + [{QX, Stats} || {{queue_exchange_stats, QX}, Stats} <- get()]}] ++ + [{Item, i(Item, State)} || Item <- ?STATISTICS_KEYS]). erase_queue_stats(QPid) -> erase({monitoring, QPid}), - erase({queue_exchange_stats, QPid}). - %% TODO make this work - %% [erase({queue_exchange_stats, QX}) || - %% {{queue_exchange_stats, QX}, _} <- get(), {QPid, _} == QX]. + erase({queue_stats, QPid}), + [erase({queue_exchange_stats, QX}) || + {{queue_exchange_stats, QX = {QPid0, _}}, _} <- get(), QPid == QPid0]. -- cgit v1.2.1 From 07151068cc291547ea4c349af80f97e40795bd87 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 21 Jul 2010 17:27:33 +0100 Subject: Increase priority of stats messages for channel and queue. Don't use gen_server2 for reader since it's not a real gen_server anyway. --- src/rabbit_amqqueue.erl | 5 +---- src/rabbit_channel.erl | 2 +- src/rabbit_reader.erl | 2 +- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 7bc85f2e..2c5465e6 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -308,7 +308,7 @@ consumers_all(VHostPath) -> stat(#amqqueue{pid = QPid}) -> delegate_call(QPid, stat, infinity). emit_stats(#amqqueue{pid = QPid}) -> - delegate_cast(QPid, emit_stats). + delegate_pcast(QPid, 8, emit_stats). delete(#amqqueue{ pid = QPid }, IfUnused, IfEmpty) -> delegate_call(QPid, {delete, IfUnused, IfEmpty}, infinity). @@ -454,9 +454,6 @@ safe_delegate_call_ok(H, F, Pids) -> delegate_call(Pid, Msg, Timeout) -> delegate:invoke(Pid, fun (P) -> gen_server2:call(P, Msg, Timeout) end). -delegate_cast(Pid, Msg) -> - delegate:invoke(Pid, fun (P) -> gen_server2:cast(P, Msg) end). - delegate_pcall(Pid, Pri, Msg, Timeout) -> delegate:invoke(Pid, fun (P) -> gen_server2:pcall(P, Pri, Msg, Timeout) end). diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 8597e477..aedd17c8 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -158,7 +158,7 @@ info_all(Items) -> rabbit_misc:filter_exit_map(fun (C) -> info(C, Items) end, list()). emit_stats(Pid) -> - gen_server2:cast(Pid, emit_stats). + gen_server2:pcast(Pid, 8, emit_stats). %%--------------------------------------------------------------------------- diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 1143794e..d96d5066 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -188,7 +188,7 @@ info(Pid, Items) -> end. emit_stats(Pid) -> - gen_server2:cast(Pid, emit_stats). + gen_server:cast(Pid, emit_stats). setup_profiling() -> Value = rabbit_misc:get_config(profiling_enabled, false), -- cgit v1.2.1 From e251316ee3ecff05d062432b89d2117eebde0fcd Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 21 Jul 2010 19:17:11 +0100 Subject: Cosmetics --- src/rabbit_channel.erl | 12 +++++------- src/rabbit_reader.erl | 3 +-- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index aedd17c8..ef33a64c 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -50,7 +50,7 @@ uncommitted_ack_q, unacked_message_q, username, virtual_host, most_recently_declared_queue, consumer_mapping, blocking, queue_collector_pid, flow, - queue_exchange_stats, stats_timer_ref}). + stats_timer_ref}). -record(flow, {server, client, pending}). @@ -189,7 +189,6 @@ init([Channel, ReaderPid, WriterPid, Username, VHost, CollectorPid]) -> queue_collector_pid = CollectorPid, flow = #flow{server = true, client = true, pending = none}, - queue_exchange_stats = dict:new(), stats_timer_ref = undefined}, hibernate, {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}. @@ -314,8 +313,7 @@ noreply(NewState) -> ensure_stats_timer(State = #ch{stats_timer_ref = undefined}) -> {ok, TRef} = timer:apply_after(?STATS_INTERVAL, - rabbit_channel, emit_stats, - [self()]), + rabbit_channel, emit_stats, [self()]), State#ch{stats_timer_ref = TRef}; ensure_stats_timer(State) -> State. @@ -498,9 +496,9 @@ handle_method(#'basic.ack'{delivery_tag = DeliveryTag, _, State = #ch{transaction_id = TxnKey, unacked_message_q = UAMQ}) -> {Acked, Remaining} = collect_acks(UAMQ, DeliveryTag, Multiple), - QsIncs = ack(TxnKey, Acked), - Participants = [QPid || {QPid, _} <- QsIncs], - incr_stats(QsIncs, ack), + QIncs = ack(TxnKey, Acked), + Participants = [QPid || {QPid, _} <- QIncs], + incr_stats(QIncs, ack), {noreply, case TxnKey of none -> ok = notify_limiter(State#ch.limiter_pid, Acked), State#ch{unacked_message_q = Remaining}; diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index d96d5066..ab157063 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -602,8 +602,7 @@ check_version(ClientVersion, ServerVersion) -> ensure_stats_timer(State = #v1{stats_timer_ref = undefined}) -> {ok, TRef} = timer:apply_after(?STATS_INTERVAL, - rabbit_reader, emit_stats, - [self()]), + rabbit_reader, emit_stats, [self()]), State#v1{stats_timer_ref = TRef}; ensure_stats_timer(State) -> State. -- cgit v1.2.1 From 23054839976ab35667c18094cf553b776d8aa1de Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 21 Jul 2010 21:26:23 +0100 Subject: Rewrite the channel stats again to use ets. Not sure if this is any faster, maybe a bit. --- src/rabbit_channel.erl | 89 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 53 insertions(+), 36 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index ef33a64c..aff63b61 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -50,7 +50,7 @@ uncommitted_ack_q, unacked_message_q, username, virtual_host, most_recently_declared_queue, consumer_mapping, blocking, queue_collector_pid, flow, - stats_timer_ref}). + stats_table, stats_timer_ref}). -record(flow, {server, client, pending}). @@ -189,6 +189,7 @@ init([Channel, ReaderPid, WriterPid, Username, VHost, CollectorPid]) -> queue_collector_pid = CollectorPid, flow = #flow{server = true, client = true, pending = none}, + stats_table = ets:new(anon, [private]), stats_timer_ref = undefined}, hibernate, {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}. @@ -245,7 +246,7 @@ handle_cast({deliver, ConsumerTag, AckRequired, Msg}, case AckRequired of true -> deliver; false -> deliver_no_ack - end), + end, State), noreply(State1#ch{next_tag = DeliveryTag + 1}); handle_cast({conserve_memory, true}, State = #ch{state = starting}) -> @@ -280,7 +281,7 @@ handle_info({'EXIT', _Pid, Reason}, State) -> {stop, Reason, State}; handle_info({'DOWN', _MRef, process, QPid, _Reason}, State) -> State1 = queue_blocked(QPid, State), - erase_queue_stats(QPid), + erase_queue_stats(QPid, State), {noreply, State1}. handle_pre_hibernate(State) -> @@ -485,7 +486,7 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, end, incr_stats([{ExchangeName, 1} | [{{QPid, ExchangeName}, 1} || - QPid <- DeliveredQPids]], publish), + QPid <- DeliveredQPids]], publish, State), {noreply, case TxnKey of none -> State; _ -> add_tx_participants(DeliveredQPids, State) @@ -498,7 +499,7 @@ handle_method(#'basic.ack'{delivery_tag = DeliveryTag, {Acked, Remaining} = collect_acks(UAMQ, DeliveryTag, Multiple), QIncs = ack(TxnKey, Acked), Participants = [QPid || {QPid, _} <- QIncs], - incr_stats(QIncs, ack), + incr_stats(QIncs, ack, State), {noreply, case TxnKey of none -> ok = notify_limiter(State#ch.limiter_pid, Acked), State#ch{unacked_message_q = Remaining}; @@ -530,7 +531,7 @@ handle_method(#'basic.get'{queue = QueueNameBin, case NoAck of true -> get_no_ack; false -> get - end), + end, State1), ok = rabbit_writer:send_command( WriterPid, #'basic.get_ok'{delivery_tag = DeliveryTag, @@ -1185,19 +1186,19 @@ i(prefetch_count, #ch{limiter_pid = LimiterPid}) -> i(Item, _) -> throw({bad_argument, Item}). -incr_stats(QXIncs, Measure) -> - [incr_stats(QX, Inc, Measure) || {QX, Inc} <- QXIncs]. +incr_stats(QXIncs, Measure, State) -> + [incr_stats(QX, Inc, Measure, State) || {QX, Inc} <- QXIncs]. -incr_stats({QPid, _} = QX, Inc, Measure) -> +incr_stats({QPid, _} = QX, Inc, Measure, State) -> maybe_monitor(QPid), - update_measures(queue_exchange_stats, QX, Inc, Measure); + update_measures(queue_exchange_stats, QX, Inc, Measure, State); -incr_stats(QPid, Inc, Measure) when is_pid(QPid) -> +incr_stats(QPid, Inc, Measure, State) when is_pid(QPid) -> maybe_monitor(QPid), - update_measures(queue_stats, QPid, Inc, Measure); + update_measures(queue_stats, QPid, Inc, Measure, State); -incr_stats(X, Inc, Measure) -> - update_measures(exchange_stats, X, Inc, Measure). +incr_stats(X, Inc, Measure, State) -> + update_measures(exchange_stats, X, Inc, Measure, State). maybe_monitor(QPid) -> case get({monitoring, QPid}) of @@ -1206,31 +1207,47 @@ maybe_monitor(QPid) -> _ -> ok end. -update_measures(Type, QX, Inc, Measure) -> - Measures = case get({Type, QX}) of - undefined -> []; - D -> D - end, - Cur = case orddict:find(Measure, Measures) of - error -> 0; - {ok, C} -> C - end, - put({Type, QX}, - orddict:store(Measure, Cur + Inc, Measures)). +update_measures(Type, QX, Inc, Measure, #ch{stats_table = Table}) -> + try + ets:update_counter(Table, {Type, QX, Measure}, Inc) + catch error:badarg -> + ets:insert_new(Table, {{Type, QX, Measure}, Inc}) + end. -internal_emit_stats(State) -> +internal_emit_stats(State = #ch{stats_table = Table}) -> + {QStats, XStats, QXStats} = + lists:foldl( + fun ({{Type, QX, Measure}, Val}, Stats) -> + store(Type, QX, Measure, Val, Stats) + end, + {[], [], []}, + [T || T <- ets:tab2list(Table)] + ), rabbit_event:notify( channel_stats, - [{queue_stats, - [{QPid, Stats} || {{queue_stats, QPid}, Stats} <- get()]}, - {exchange_stats, - [{X, Stats} || {{exchange_stats, X}, Stats} <- get()]}, - {queue_exchange_stats, - [{QX, Stats} || {{queue_exchange_stats, QX}, Stats} <- get()]}] ++ + [{queue_stats, QStats}, + {exchange_stats, XStats}, + {queue_exchange_stats, QXStats}] ++ [{Item, i(Item, State)} || Item <- ?STATISTICS_KEYS]). -erase_queue_stats(QPid) -> +store(Type, QX, Measure, Val, Stats) -> + Index = case Type of + queue_stats -> 1; + exchange_stats -> 2; + queue_exchange_stats -> 3 + end, + setelement(Index, Stats, + orddict:update( + QX, + fun (Measures) -> + orddict:store(Measure, Val, Measures) + end, + [{Measure, Val}], element(Index, Stats))). + +erase_queue_stats(QPid, #ch{stats_table = Table}) -> erase({monitoring, QPid}), - erase({queue_stats, QPid}), - [erase({queue_exchange_stats, QX}) || - {{queue_exchange_stats, QX = {QPid0, _}}, _} <- get(), QPid == QPid0]. + [ets:delete(Table, K) || + {K, _V} <- ets:match(Table, {{queue_stats, QPid, '_'}, '_'})], + [ets:delete(Table, K) || + {K, _V} <- + ets:match(Table, {{queue_exchange_stats, {QPid, '_'}, '_'}, '_'})]. -- cgit v1.2.1 From 80aa575844878ff3b5337d8392f781c975252f9f Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 22 Jul 2010 10:57:26 +0100 Subject: Spelling --- docs/rabbitmqctl.1.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/rabbitmqctl.1.xml b/docs/rabbitmqctl.1.xml index e74b9b29..3ecdaeef 100644 --- a/docs/rabbitmqctl.1.xml +++ b/docs/rabbitmqctl.1.xml @@ -559,7 +559,7 @@ Scope of the permissions: either client (the default) or all. This determines whether - permissisons are checked for server-generated resource + permissions are checked for server-generated resource names (all) or only for client-specified resource names (client). -- cgit v1.2.1 From 0382262fb47ae6b38a7dccdead2f4a737c0a26fa Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 22 Jul 2010 14:05:41 +0100 Subject: Unit test, and fix a bug found by the test. --- src/rabbit_channel.erl | 7 +-- src/rabbit_tests.erl | 96 ++++++++++++++++++++++++++++++++++++- src/rabbit_tests_event_receiver.erl | 66 +++++++++++++++++++++++++ 3 files changed, 162 insertions(+), 7 deletions(-) create mode 100644 src/rabbit_tests_event_receiver.erl diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index aff63b61..ac52a7f1 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -1246,8 +1246,5 @@ store(Type, QX, Measure, Val, Stats) -> erase_queue_stats(QPid, #ch{stats_table = Table}) -> erase({monitoring, QPid}), - [ets:delete(Table, K) || - {K, _V} <- ets:match(Table, {{queue_stats, QPid, '_'}, '_'})], - [ets:delete(Table, K) || - {K, _V} <- - ets:match(Table, {{queue_exchange_stats, {QPid, '_'}, '_'}, '_'})]. + ets:match_delete(Table, {{queue_stats, QPid, '_'}, '_'}), + ets:match_delete(Table, {{queue_exchange_stats, {QPid, '_'}, '_'}, '_'}). diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index de06c048..53717774 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -61,6 +61,7 @@ all_tests() -> passed = test_app_management(), passed = test_log_management_during_startup(), passed = test_memory_pressure(), + passed = test_statistics(), passed = test_cluster_management(), passed = test_user_management(), passed = test_server_status(), @@ -922,9 +923,12 @@ test_memory_pressure_sync(Ch, Writer) -> end. test_memory_pressure_spawn() -> + test_spawn(fun test_memory_pressure_receiver/1). + +test_spawn(Receiver) -> Me = self(), - Writer = spawn(fun () -> test_memory_pressure_receiver(Me) end), - Ch = rabbit_channel:start_link(1, self(), Writer, <<"user">>, <<"/">>, + Writer = spawn(fun () -> Receiver(Me) end), + Ch = rabbit_channel:start_link(1, self(), Writer, <<"guest">>, <<"/">>, self()), ok = rabbit_channel:do(Ch, #'channel.open'{}), MRef = erlang:monitor(process, Ch), @@ -1008,6 +1012,94 @@ test_memory_pressure() -> passed. +test_statistics_receiver(Pid) -> + receive + shutdown -> + ok; + {send_command, Method} -> + Pid ! Method, + test_statistics_receiver(Pid) + end. + +test_statistics_event_receiver(Pid) -> + receive + Foo -> + Pid ! Foo, + test_statistics_event_receiver(Pid) + end. + +test_statistics_receive_event(Ch, Retries, Matcher) -> + rabbit_channel:emit_stats(Ch), + receive #event{type = channel_stats, props = Props} -> + case Matcher(Props) of + true -> + Props; + _ -> + case Retries of + 0 -> throw(failed_to_receive_matching_event); + _ -> timer:sleep(10), + test_statistics_receive_event(Ch, Retries - 1, + Matcher) + end + end + after 1000 -> throw(failed_to_receive_event) + end. + +test_statistics() -> + %% ATM this just tests the queue / exchange stats in channels. That's + %% by far the most complex code though. + + %% Set up a channel and queue + {_Writer, Ch, _MRef} = test_spawn(fun test_statistics_receiver/1), + rabbit_channel:do(Ch, #'queue.declare'{}), + QName = receive #'queue.declare_ok'{queue = Q0} -> + Q0 + after 1000 -> throw(failed_to_receive_queue_declare_ok) + end, + {ok, Q} = rabbit_amqqueue:lookup(rabbit_misc:r(<<"/">>, queue, QName)), + QPid = Q#amqqueue.pid, + X = rabbit_misc:r(<<"/">>, exchange, <<"">>), + + rabbit_tests_event_receiver:start(self()), + + %% Check stats empty + Event = test_statistics_receive_event(Ch, 10, fun (_) -> true end), + [] = proplists:get_value(queue_stats, Event), + [] = proplists:get_value(exchange_stats, Event), + [] = proplists:get_value(queue_exchange_stats, Event), + + %% Publish and get a message + rabbit_channel:do(Ch, #'basic.publish'{exchange = <<"">>, + routing_key = QName}, + rabbit_basic:build_content(#'P_basic'{}, <<"">>)), + rabbit_channel:do(Ch, #'basic.get'{queue = QName}), + + %% Check the stats reflect that + Event2 = test_statistics_receive_event( + Ch, 10, + fun (E) -> + length(proplists:get_value(queue_exchange_stats, E)) > 0 + end), + [{QPid,[{get,1}]}] = proplists:get_value(queue_stats, Event2), + [{X,[{publish,1}]}] = proplists:get_value(exchange_stats, Event2), + [{{QPid,X},[{publish,1}]}] = + proplists:get_value(queue_exchange_stats, Event2), + + %% Check the stats remove stuff on queue deletion + rabbit_channel:do(Ch, #'queue.delete'{queue = QName}), + Event3 = test_statistics_receive_event( + Ch, 10, + fun (E) -> + length(proplists:get_value(queue_exchange_stats, E)) == 0 + end), + + [] = proplists:get_value(queue_stats, Event3), + [{X,[{publish,1}]}] = proplists:get_value(exchange_stats, Event3), + [] = proplists:get_value(queue_exchange_stats, Event3), + + rabbit_tests_event_receiver:stop(), + passed. + test_delegates_async(SecondaryNode) -> Self = self(), Sender = fun (Pid) -> Pid ! {invoked, Self} end, diff --git a/src/rabbit_tests_event_receiver.erl b/src/rabbit_tests_event_receiver.erl new file mode 100644 index 00000000..a92e3da7 --- /dev/null +++ b/src/rabbit_tests_event_receiver.erl @@ -0,0 +1,66 @@ +%% 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_tests_event_receiver). + +-export([start/1, stop/0]). + +-export([init/1, handle_call/2, handle_event/2, handle_info/2, + terminate/2, code_change/3]). + +start(Pid) -> + gen_event:add_handler(rabbit_event, ?MODULE, [Pid]). + +stop() -> + gen_event:delete_handler(rabbit_event, ?MODULE, []). + +%%---------------------------------------------------------------------------- + +init([Pid]) -> + {ok, Pid}. + +handle_call(_Request, Pid) -> + {ok, not_understood, Pid}. + +handle_event(Event, Pid) -> + Pid ! Event, + {ok, Pid}. + +handle_info(_Info, Pid) -> + {ok, Pid}. + +terminate(_Arg, _Pid) -> + ok. + +code_change(_OldVsn, Pid, _Extra) -> + {ok, Pid}. + +%%---------------------------------------------------------------------------- -- cgit v1.2.1 From 8902d86cd90286dc78c2649bb8cf90897cfc538e Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 22 Jul 2010 16:02:10 +0100 Subject: Allow the complete test suite to run. --- src/rabbit_event.erl | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/rabbit_event.erl b/src/rabbit_event.erl index 08c13007..72a9f8ba 100644 --- a/src/rabbit_event.erl +++ b/src/rabbit_event.erl @@ -38,6 +38,13 @@ %%---------------------------------------------------------------------------- notify(Type, Props) -> - gen_event:notify(rabbit_event, #event{type = Type, - props = Props, - timestamp = os:timestamp()}). + try + gen_event:notify(rabbit_event, #event{type = Type, + props = Props, + timestamp = os:timestamp()}) + catch error:badarg -> + %% badarg means rabbit_event is no longer registered. We never + %% unregister it so the great likelihood is that we're shutting + %% down the broker but some events were backed up. Ignore it. + ok + end. -- cgit v1.2.1 From 9a911110abd55978674f3ba59b5111a267f0e6a7 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 23 Jul 2010 10:36:04 +0100 Subject: Base the connection_created event on the updated state; needed to get vhost name. --- src/rabbit_reader.erl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index ab157063..4cf47208 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -681,11 +681,12 @@ handle_method0(#'connection.open'{virtual_host = VHostPath, ok = send_on_channel0( Sock, #'connection.open_ok'{known_hosts = KnownHosts}), + State1 = State#v1{connection_state = running, + connection = NewConnection}, rabbit_event:notify( connection_created, - [{Item, i(Item, State)} || Item <- [pid|?CREATION_EVENT_KEYS]]), - State#v1{connection_state = running, - connection = NewConnection}; + [{Item, i(Item, State1)} || Item <- [pid|?CREATION_EVENT_KEYS]]), + State1; true -> %% FIXME: 'host' is supposed to only contain one %% address; but which one do we pick? This is -- cgit v1.2.1 From 4322cc5fa45c0726db7811726713fab62e0e1a99 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 26 Jul 2010 13:12:12 +0100 Subject: Revert to the prior, process dictionary-based way of storing statistics. --- src/rabbit_channel.erl | 90 +++++++++++++++++++++----------------------------- 1 file changed, 38 insertions(+), 52 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index ac52a7f1..d18329ff 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -50,7 +50,7 @@ uncommitted_ack_q, unacked_message_q, username, virtual_host, most_recently_declared_queue, consumer_mapping, blocking, queue_collector_pid, flow, - stats_table, stats_timer_ref}). + stats_timer_ref}). -record(flow, {server, client, pending}). @@ -189,7 +189,6 @@ init([Channel, ReaderPid, WriterPid, Username, VHost, CollectorPid]) -> queue_collector_pid = CollectorPid, flow = #flow{server = true, client = true, pending = none}, - stats_table = ets:new(anon, [private]), stats_timer_ref = undefined}, hibernate, {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}. @@ -246,7 +245,7 @@ handle_cast({deliver, ConsumerTag, AckRequired, Msg}, case AckRequired of true -> deliver; false -> deliver_no_ack - end, State), + end), noreply(State1#ch{next_tag = DeliveryTag + 1}); handle_cast({conserve_memory, true}, State = #ch{state = starting}) -> @@ -281,7 +280,7 @@ handle_info({'EXIT', _Pid, Reason}, State) -> {stop, Reason, State}; handle_info({'DOWN', _MRef, process, QPid, _Reason}, State) -> State1 = queue_blocked(QPid, State), - erase_queue_stats(QPid, State), + erase_queue_stats(QPid), {noreply, State1}. handle_pre_hibernate(State) -> @@ -486,7 +485,7 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, end, incr_stats([{ExchangeName, 1} | [{{QPid, ExchangeName}, 1} || - QPid <- DeliveredQPids]], publish, State), + QPid <- DeliveredQPids]], publish), {noreply, case TxnKey of none -> State; _ -> add_tx_participants(DeliveredQPids, State) @@ -499,7 +498,7 @@ handle_method(#'basic.ack'{delivery_tag = DeliveryTag, {Acked, Remaining} = collect_acks(UAMQ, DeliveryTag, Multiple), QIncs = ack(TxnKey, Acked), Participants = [QPid || {QPid, _} <- QIncs], - incr_stats(QIncs, ack, State), + incr_stats(QIncs, ack), {noreply, case TxnKey of none -> ok = notify_limiter(State#ch.limiter_pid, Acked), State#ch{unacked_message_q = Remaining}; @@ -531,7 +530,7 @@ handle_method(#'basic.get'{queue = QueueNameBin, case NoAck of true -> get_no_ack; false -> get - end, State1), + end), ok = rabbit_writer:send_command( WriterPid, #'basic.get_ok'{delivery_tag = DeliveryTag, @@ -1186,19 +1185,19 @@ i(prefetch_count, #ch{limiter_pid = LimiterPid}) -> i(Item, _) -> throw({bad_argument, Item}). -incr_stats(QXIncs, Measure, State) -> - [incr_stats(QX, Inc, Measure, State) || {QX, Inc} <- QXIncs]. +incr_stats(QXIncs, Measure) -> + [incr_stats(QX, Inc, Measure) || {QX, Inc} <- QXIncs]. -incr_stats({QPid, _} = QX, Inc, Measure, State) -> +incr_stats({QPid, _} = QX, Inc, Measure) -> maybe_monitor(QPid), - update_measures(queue_exchange_stats, QX, Inc, Measure, State); + update_measures(queue_exchange_stats, QX, Inc, Measure); -incr_stats(QPid, Inc, Measure, State) when is_pid(QPid) -> +incr_stats(QPid, Inc, Measure) when is_pid(QPid) -> maybe_monitor(QPid), - update_measures(queue_stats, QPid, Inc, Measure, State); + update_measures(queue_stats, QPid, Inc, Measure); -incr_stats(X, Inc, Measure, State) -> - update_measures(exchange_stats, X, Inc, Measure, State). +incr_stats(X, Inc, Measure) -> + update_measures(exchange_stats, X, Inc, Measure). maybe_monitor(QPid) -> case get({monitoring, QPid}) of @@ -1207,44 +1206,31 @@ maybe_monitor(QPid) -> _ -> ok end. -update_measures(Type, QX, Inc, Measure, #ch{stats_table = Table}) -> - try - ets:update_counter(Table, {Type, QX, Measure}, Inc) - catch error:badarg -> - ets:insert_new(Table, {{Type, QX, Measure}, Inc}) - end. - -internal_emit_stats(State = #ch{stats_table = Table}) -> - {QStats, XStats, QXStats} = - lists:foldl( - fun ({{Type, QX, Measure}, Val}, Stats) -> - store(Type, QX, Measure, Val, Stats) +update_measures(Type, QX, Inc, Measure) -> + Measures = case get({Type, QX}) of + undefined -> []; + D -> D + end, + Cur = case orddict:find(Measure, Measures) of + error -> 0; + {ok, C} -> C end, - {[], [], []}, - [T || T <- ets:tab2list(Table)] - ), + put({Type, QX}, + orddict:store(Measure, Cur + Inc, Measures)). + +internal_emit_stats(State) -> rabbit_event:notify( channel_stats, - [{queue_stats, QStats}, - {exchange_stats, XStats}, - {queue_exchange_stats, QXStats}] ++ - [{Item, i(Item, State)} || Item <- ?STATISTICS_KEYS]). - -store(Type, QX, Measure, Val, Stats) -> - Index = case Type of - queue_stats -> 1; - exchange_stats -> 2; - queue_exchange_stats -> 3 - end, - setelement(Index, Stats, - orddict:update( - QX, - fun (Measures) -> - orddict:store(Measure, Val, Measures) - end, - [{Measure, Val}], element(Index, Stats))). - -erase_queue_stats(QPid, #ch{stats_table = Table}) -> + [{queue_stats, + [{QPid, Stats} || {{queue_stats, QPid}, Stats} <- get()]}, + {exchange_stats, + [{X, Stats} || {{exchange_stats, X}, Stats} <- get()]}, + {queue_exchange_stats, + [{QX, Stats} || {{queue_exchange_stats, QX}, Stats} <- get()]}] ++ + [{Item, i(Item, State)} || Item <- ?STATISTICS_KEYS]). + +erase_queue_stats(QPid) -> erase({monitoring, QPid}), - ets:match_delete(Table, {{queue_stats, QPid, '_'}, '_'}), - ets:match_delete(Table, {{queue_exchange_stats, {QPid, '_'}, '_'}, '_'}). + erase({queue_stats, QPid}), + [erase({queue_exchange_stats, QX}) || + {{queue_exchange_stats, QX = {QPid0, _}}, _} <- get(), QPid == QPid0]. -- cgit v1.2.1 From 5006a8e57a70fb703bf9818a5f6243c75a54a326 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 26 Jul 2010 13:16:07 +0100 Subject: Tweak priorities a bit. --- src/rabbit_amqqueue.erl | 4 ++-- src/rabbit_channel.erl | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index 0489f139..a3a912b9 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -315,7 +315,7 @@ consumers_all(VHostPath) -> stat(#amqqueue{pid = QPid}) -> delegate_call(QPid, stat, infinity). emit_stats(#amqqueue{pid = QPid}) -> - delegate_pcast(QPid, 8, emit_stats). + delegate_pcast(QPid, 7, emit_stats). delete(#amqqueue{ pid = QPid }, IfUnused, IfEmpty) -> delegate_call(QPid, {delete, IfUnused, IfEmpty}, infinity). @@ -411,7 +411,7 @@ internal_delete(QueueName) -> end. maybe_run_queue_via_backing_queue(QPid, Fun) -> - gen_server2:pcall(QPid, 7, {maybe_run_queue_via_backing_queue, Fun}, + gen_server2:pcall(QPid, 6, {maybe_run_queue_via_backing_queue, Fun}, infinity). update_ram_duration(QPid) -> diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index d18329ff..c16f9026 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -158,7 +158,7 @@ info_all(Items) -> rabbit_misc:filter_exit_map(fun (C) -> info(C, Items) end, list()). emit_stats(Pid) -> - gen_server2:pcast(Pid, 8, emit_stats). + gen_server2:pcast(Pid, 7, emit_stats). %%--------------------------------------------------------------------------- -- cgit v1.2.1 From 4019ae31d80cff08b542d84e7edcbf446b0201fc Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 26 Jul 2010 13:59:12 +0100 Subject: Make statistics collection into an application parameter, switched off by default. --- ebin/rabbit_app.in | 3 +- src/rabbit_amqqueue_process.erl | 11 +++++-- src/rabbit_channel.erl | 69 +++++++++++++++++++++++++---------------- src/rabbit_reader.erl | 8 +++-- src/rabbit_tests.erl | 2 ++ 5 files changed, 61 insertions(+), 32 deletions(-) diff --git a/ebin/rabbit_app.in b/ebin/rabbit_app.in index 2cd28abb..48e19ff8 100644 --- a/ebin/rabbit_app.in +++ b/ebin/rabbit_app.in @@ -27,4 +27,5 @@ {default_user, <<"guest">>}, {default_pass, <<"guest">>}, {default_vhost, <<"/">>}, - {default_permissions, [<<".*">>, <<".*">>, <<".*">>]}]}]}. + {default_permissions, [<<".*">>, <<".*">>, <<".*">>]}, + {collect_statistics, none}]}]}. diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index c099c051..bf90ef23 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -58,7 +58,8 @@ blocked_consumers, sync_timer_ref, rate_timer_ref, - stats_timer_ref + stats_timer_ref, + stats_level }). -record(consumer, {tag, ack_required}). @@ -107,6 +108,7 @@ init(Q) -> ?LOGDEBUG("Queue starting - ~p~n", [Q]), process_flag(trap_exit, true), {ok, BQ} = application:get_env(backing_queue_module), + {ok, StatsLevel} = application:get_env(rabbit, collect_statistics), {ok, #q{q = Q#amqqueue{pid = self()}, exclusive_consumer = none, @@ -117,7 +119,8 @@ init(Q) -> blocked_consumers = queue:new(), sync_timer_ref = undefined, rate_timer_ref = undefined, - stats_timer_ref = undefined}, hibernate, + stats_timer_ref = undefined, + stats_level = StatsLevel}, hibernate, {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}. terminate(shutdown, State = #q{backing_queue = BQ}) -> @@ -231,6 +234,8 @@ stop_rate_timer(State = #q{rate_timer_ref = TRef}) -> {ok, cancel} = timer:cancel(TRef), State#q{rate_timer_ref = undefined}. +ensure_stats_timer(State = #q{stats_level = none}) -> + State; ensure_stats_timer(State = #q{stats_timer_ref = undefined, q = Q}) -> {ok, TRef} = timer:apply_after(?STATS_INTERVAL, rabbit_amqqueue, emit_stats, @@ -239,6 +244,8 @@ ensure_stats_timer(State = #q{stats_timer_ref = undefined, q = Q}) -> ensure_stats_timer(State) -> State. +stop_stats_timer(State = #q{stats_level = none}) -> + State; stop_stats_timer(State = #q{stats_timer_ref = undefined}) -> emit_stats(State), State; diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index c16f9026..5696d82d 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -50,7 +50,7 @@ uncommitted_ack_q, unacked_message_q, username, virtual_host, most_recently_declared_queue, consumer_mapping, blocking, queue_collector_pid, flow, - stats_timer_ref}). + stats_timer_ref, stats_level}). -record(flow, {server, client, pending}). @@ -171,6 +171,7 @@ init([Channel, ReaderPid, WriterPid, Username, VHost, CollectorPid]) -> {channel, Channel}, {user, Username}, {vhost, VHost}]), + {ok, StatsLevel} = application:get_env(rabbit, collect_statistics), {ok, #ch{state = starting, channel = Channel, reader_pid = ReaderPid, @@ -189,7 +190,8 @@ init([Channel, ReaderPid, WriterPid, Username, VHost, CollectorPid]) -> queue_collector_pid = CollectorPid, flow = #flow{server = true, client = true, pending = none}, - stats_timer_ref = undefined}, + stats_timer_ref = undefined, + stats_level = StatsLevel}, hibernate, {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}. @@ -241,11 +243,11 @@ handle_cast({deliver, ConsumerTag, AckRequired, Msg}, State1 = lock_message(AckRequired, {DeliveryTag, ConsumerTag, Msg}, State), ok = internal_deliver(WriterPid, true, ConsumerTag, DeliveryTag, Msg), {_QName, QPid, _MsgId, _Redelivered, _Msg} = Msg, - incr_stats([{QPid, 1}], - case AckRequired of - true -> deliver; - false -> deliver_no_ack - end), + maybe_incr_stats([{QPid, 1}], + case AckRequired of + true -> deliver; + false -> deliver_no_ack + end, State), noreply(State1#ch{next_tag = DeliveryTag + 1}); handle_cast({conserve_memory, true}, State = #ch{state = starting}) -> @@ -311,6 +313,9 @@ noreply(NewState) -> NewState1 = ensure_stats_timer(NewState), {noreply, NewState1, hibernate}. +ensure_stats_timer(State = #ch{stats_level = none}) -> + State; + ensure_stats_timer(State = #ch{stats_timer_ref = undefined}) -> {ok, TRef} = timer:apply_after(?STATS_INTERVAL, rabbit_channel, emit_stats, [self()]), @@ -318,6 +323,8 @@ ensure_stats_timer(State = #ch{stats_timer_ref = undefined}) -> ensure_stats_timer(State) -> State. +stop_stats_timer(State = #ch{stats_level = none}) -> + State; stop_stats_timer(State = #ch{stats_timer_ref = undefined}) -> internal_emit_stats(State), State; @@ -483,9 +490,9 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, unroutable -> ok = basic_return(Message, WriterPid, no_route); not_delivered -> ok = basic_return(Message, WriterPid, no_consumers) end, - incr_stats([{ExchangeName, 1} | - [{{QPid, ExchangeName}, 1} || - QPid <- DeliveredQPids]], publish), + maybe_incr_stats([{ExchangeName, 1} | + [{{QPid, ExchangeName}, 1} || + QPid <- DeliveredQPids]], publish, State), {noreply, case TxnKey of none -> State; _ -> add_tx_participants(DeliveredQPids, State) @@ -498,7 +505,7 @@ handle_method(#'basic.ack'{delivery_tag = DeliveryTag, {Acked, Remaining} = collect_acks(UAMQ, DeliveryTag, Multiple), QIncs = ack(TxnKey, Acked), Participants = [QPid || {QPid, _} <- QIncs], - incr_stats(QIncs, ack), + maybe_incr_stats(QIncs, ack, State), {noreply, case TxnKey of none -> ok = notify_limiter(State#ch.limiter_pid, Acked), State#ch{unacked_message_q = Remaining}; @@ -526,11 +533,11 @@ handle_method(#'basic.get'{queue = QueueNameBin, routing_key = RoutingKey, content = Content}}} -> State1 = lock_message(not(NoAck), {DeliveryTag, none, Msg}, State), - incr_stats([{QPid, 1}], - case NoAck of - true -> get_no_ack; - false -> get - end), + maybe_incr_stats([{QPid, 1}], + case NoAck of + true -> get_no_ack; + false -> get + end, State), ok = rabbit_writer:send_command( WriterPid, #'basic.get_ok'{delivery_tag = DeliveryTag, @@ -1185,7 +1192,9 @@ i(prefetch_count, #ch{limiter_pid = LimiterPid}) -> i(Item, _) -> throw({bad_argument, Item}). -incr_stats(QXIncs, Measure) -> +maybe_incr_stats(_QXIncs, _Measure, #ch{stats_level = none}) -> + ok; +maybe_incr_stats(QXIncs, Measure, _State) -> [incr_stats(QX, Inc, Measure) || {QX, Inc} <- QXIncs]. incr_stats({QPid, _} = QX, Inc, Measure) -> @@ -1218,16 +1227,22 @@ update_measures(Type, QX, Inc, Measure) -> put({Type, QX}, orddict:store(Measure, Cur + Inc, Measures)). -internal_emit_stats(State) -> - rabbit_event:notify( - channel_stats, - [{queue_stats, - [{QPid, Stats} || {{queue_stats, QPid}, Stats} <- get()]}, - {exchange_stats, - [{X, Stats} || {{exchange_stats, X}, Stats} <- get()]}, - {queue_exchange_stats, - [{QX, Stats} || {{queue_exchange_stats, QX}, Stats} <- get()]}] ++ - [{Item, i(Item, State)} || Item <- ?STATISTICS_KEYS]). +internal_emit_stats(State = #ch{stats_level = Level}) -> + CoarseStats = [{Item, i(Item, State)} || Item <- ?STATISTICS_KEYS], + case Level of + coarse -> + rabbit_event:notify(channel_stats, CoarseStats); + fine -> + FineStats = + [{queue_stats, + [{QPid, Stats} || {{queue_stats, QPid}, Stats} <- get()]}, + {exchange_stats, + [{X, Stats} || {{exchange_stats, X}, Stats} <- get()]}, + {queue_exchange_stats, + [{QX, Stats} || + {{queue_exchange_stats, QX}, Stats} <- get()]}], + rabbit_event:notify(channel_stats, CoarseStats ++ FineStats) + end. erase_queue_stats(QPid) -> erase({monitoring, QPid}), diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 4cf47208..ed48e29a 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -60,7 +60,7 @@ %--------------------------------------------------------------------------- -record(v1, {sock, connection, callback, recv_ref, connection_state, - queue_collector, stats_timer_ref}). + queue_collector, stats_timer_ref, stats_level}). -define(STATISTICS_KEYS, [pid, recv_oct, recv_cnt, send_oct, send_cnt, send_pend, state, channels]). @@ -250,6 +250,7 @@ start_connection(Parent, Deb, Sock, SockTransform) -> handshake_timeout), ProfilingValue = setup_profiling(), {ok, Collector} = rabbit_queue_collector:start_link(), + {ok, StatsLevel} = application:get_env(rabbit, collect_statistics), try mainloop(Parent, Deb, switch_callback( #v1{sock = ClientSock, @@ -263,7 +264,8 @@ start_connection(Parent, Deb, Sock, SockTransform) -> recv_ref = none, connection_state = pre_init, queue_collector = Collector, - stats_timer_ref = undefined}, + stats_timer_ref = undefined, + stats_level = StatsLevel}, handshake, 8)) catch Ex -> (if Ex == connection_closed_abruptly -> @@ -600,6 +602,8 @@ check_version(ClientVersion, ServerVersion) -> (ClientMajor == ServerMajor andalso ClientMinor >= ServerMinor). +ensure_stats_timer(State = #v1{stats_level = none}) -> + State; ensure_stats_timer(State = #v1{stats_timer_ref = undefined}) -> {ok, TRef} = timer:apply_after(?STATS_INTERVAL, rabbit_reader, emit_stats, [self()]), diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index e2d59737..fc755898 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -1189,6 +1189,8 @@ test_statistics_receive_event(Ch, Retries, Matcher) -> end. test_statistics() -> + application:set_env(rabbit, collect_statistics, fine), + %% ATM this just tests the queue / exchange stats in channels. That's %% by far the most complex code though. -- cgit v1.2.1 From 8ed9a53e3c075532be290debaf8b5d4303790bc1 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 26 Jul 2010 16:12:52 +0100 Subject: Use apply_interval rather than apply_after, cosmetics. --- src/rabbit_amqqueue_process.erl | 5 ++--- src/rabbit_channel.erl | 13 +++++-------- src/rabbit_reader.erl | 3 ++- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index bf90ef23..c7c8cee3 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -237,9 +237,8 @@ stop_rate_timer(State = #q{rate_timer_ref = TRef}) -> ensure_stats_timer(State = #q{stats_level = none}) -> State; ensure_stats_timer(State = #q{stats_timer_ref = undefined, q = Q}) -> - {ok, TRef} = timer:apply_after(?STATS_INTERVAL, - rabbit_amqqueue, emit_stats, - [Q]), + {ok, TRef} = timer:apply_interval(?STATS_INTERVAL, + rabbit_amqqueue, emit_stats, [Q]), State#q{stats_timer_ref = TRef}; ensure_stats_timer(State) -> State. diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 5696d82d..4072a8df 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -281,9 +281,8 @@ handle_info({'EXIT', WriterPid, Reason = {writer, send_failed, _Error}}, handle_info({'EXIT', _Pid, Reason}, State) -> {stop, Reason, State}; handle_info({'DOWN', _MRef, process, QPid, _Reason}, State) -> - State1 = queue_blocked(QPid, State), erase_queue_stats(QPid), - {noreply, State1}. + {noreply, queue_blocked(QPid, State)}. handle_pre_hibernate(State) -> ok = clear_permission_cache(), @@ -306,19 +305,17 @@ code_change(_OldVsn, State, _Extra) -> %%--------------------------------------------------------------------------- reply(Reply, NewState) -> - NewState1 = ensure_stats_timer(NewState), - {reply, Reply, NewState1, hibernate}. + {reply, Reply, ensure_stats_timer(NewState), hibernate}. noreply(NewState) -> - NewState1 = ensure_stats_timer(NewState), - {noreply, NewState1, hibernate}. + {noreply, ensure_stats_timer(NewState), hibernate}. ensure_stats_timer(State = #ch{stats_level = none}) -> State; ensure_stats_timer(State = #ch{stats_timer_ref = undefined}) -> - {ok, TRef} = timer:apply_after(?STATS_INTERVAL, - rabbit_channel, emit_stats, [self()]), + {ok, TRef} = timer:apply_interval(?STATS_INTERVAL, + rabbit_channel, emit_stats, [self()]), State#ch{stats_timer_ref = TRef}; ensure_stats_timer(State) -> State. diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index ed48e29a..a2d92d2b 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -66,7 +66,8 @@ send_pend, state, channels]). -define(CREATION_EVENT_KEYS, [address, port, peer_address, peer_port, - user, vhost, timeout, frame_max, client_properties]). + user, vhost, timeout, frame_max, + client_properties]). -define(INFO_KEYS, ?CREATION_EVENT_KEYS ++ ?STATISTICS_KEYS). -- cgit v1.2.1 From 5d520ec46e6178d6cd7df32b33aeb8bd3cb7028b Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 26 Jul 2010 16:18:28 +0100 Subject: Send a stats event as soon as you wake up. --- src/rabbit_amqqueue_process.erl | 1 + src/rabbit_channel.erl | 1 + 2 files changed, 2 insertions(+) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index c7c8cee3..cce93719 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -239,6 +239,7 @@ ensure_stats_timer(State = #q{stats_level = none}) -> ensure_stats_timer(State = #q{stats_timer_ref = undefined, q = Q}) -> {ok, TRef} = timer:apply_interval(?STATS_INTERVAL, rabbit_amqqueue, emit_stats, [Q]), + emit_stats(State), State#q{stats_timer_ref = TRef}; ensure_stats_timer(State) -> State. diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 4072a8df..6a1c40a6 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -314,6 +314,7 @@ ensure_stats_timer(State = #ch{stats_level = none}) -> State; ensure_stats_timer(State = #ch{stats_timer_ref = undefined}) -> + internal_emit_stats(State), {ok, TRef} = timer:apply_interval(?STATS_INTERVAL, rabbit_channel, emit_stats, [self()]), State#ch{stats_timer_ref = TRef}; -- cgit v1.2.1 From 6c42b10cf24fb5ea1f8170066897060217326756 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 26 Jul 2010 16:24:03 +0100 Subject: Oops, don't turn into a timer bomb. --- src/rabbit_amqqueue_process.erl | 2 +- src/rabbit_channel.erl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index cce93719..30e3e234 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -848,7 +848,7 @@ handle_cast({set_maximum_since_use, Age}, State) -> handle_cast(emit_stats, State) -> emit_stats(State), - noreply(State#q{stats_timer_ref = undefined}). + noreply(State). handle_info({'DOWN', _MonitorRef, process, DownPid, _Reason}, State = #q{q = #amqqueue{exclusive_owner = DownPid}}) -> diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 6a1c40a6..3c5111cf 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -272,7 +272,7 @@ handle_cast({flow_timeout, _Ref}, State) -> handle_cast(emit_stats, State) -> internal_emit_stats(State), - noreply(State#ch{stats_timer_ref = undefined}). + noreply(State). handle_info({'EXIT', WriterPid, Reason = {writer, send_failed, _Error}}, State = #ch{writer_pid = WriterPid}) -> -- cgit v1.2.1 From f9a7b783b817029e1046514d2d753466d1a47394 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 27 Jul 2010 11:22:06 +0100 Subject: implement basic.reject the simplest flavour that seems useful --- src/rabbit_amqqueue.erl | 6 +++++- src/rabbit_amqqueue_process.erl | 15 +++++++++++++++ src/rabbit_channel.erl | 11 +++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl index df947443..e744e669 100644 --- a/src/rabbit_amqqueue.erl +++ b/src/rabbit_amqqueue.erl @@ -39,7 +39,7 @@ -export([pseudo_queue/2]). -export([lookup/1, with/2, with_or_die/2, assert_equivalence/5, check_exclusive_access/2, with_exclusive_access_or_die/3, - stat/1, deliver/2, requeue/3, ack/4]). + stat/1, deliver/2, requeue/3, ack/4, reject/4]). -export([list/1, info_keys/0, info/1, info/2, info_all/1, info_all/2]). -export([consumers/1, consumers_all/1]). -export([basic_get/3, basic_consume/7, basic_cancel/4]). @@ -122,6 +122,7 @@ -spec(ack/4 :: (pid(), rabbit_types:maybe(rabbit_types:txn()), [msg_id()], pid()) -> 'ok'). +-spec(reject/4 :: (pid(), [msg_id()], boolean(), pid()) -> 'ok'). -spec(commit_all/3 :: ([pid()], rabbit_types:txn(), pid()) -> ok_or_errors()). -spec(rollback_all/3 :: ([pid()], rabbit_types:txn(), pid()) -> 'ok'). -spec(notify_down_all/2 :: ([pid()], pid()) -> ok_or_errors()). @@ -335,6 +336,9 @@ requeue(QPid, MsgIds, ChPid) -> ack(QPid, Txn, MsgIds, ChPid) -> delegate_pcast(QPid, 7, {ack, Txn, MsgIds, ChPid}). +reject(QPid, MsgIds, Requeue, ChPid) -> + delegate_pcast(QPid, 7, {reject, MsgIds, Requeue, ChPid}). + commit_all(QPids, Txn, ChPid) -> safe_delegate_call_ok( fun (QPid) -> exit({queue_disappeared, QPid}) end, diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 468a41b2..700e8b0b 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -750,6 +750,21 @@ handle_cast({ack, Txn, AckTags, ChPid}, noreply(State #q { backing_queue_state = BQS1 }) end; +handle_cast({reject, AckTags, Requeue, ChPid}, + State = #q{backing_queue = BQ, backing_queue_state = BQS}) -> + case lookup_ch(ChPid) of + not_found -> + noreply(State); + C = #cr{acktags = ChAckTags} -> + ChAckTags1 = subtract_acks(ChAckTags, AckTags), + 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 } + end) + end; + handle_cast({rollback, Txn, ChPid}, State) -> noreply(rollback_transaction(Txn, ChPid, State)); diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index c4db3ace..c94b80aa 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -638,6 +638,17 @@ handle_method(#'basic.recover'{requeue = Requeue}, Content, State) -> ok = rabbit_writer:send_command(WriterPid, #'basic.recover_ok'{}), {noreply, State2}; +handle_method(#'basic.reject'{delivery_tag = DeliveryTag, + requeue = Requeue}, + _, State = #ch{ unacked_message_q = UAMQ}) -> + {Acked, Remaining} = collect_acks(UAMQ, DeliveryTag, false), + ok = fold_per_queue( + fun (QPid, MsgIds, ok) -> + rabbit_amqqueue:reject(QPid, MsgIds, Requeue, self()) + end, ok, Acked), + ok = notify_limiter(State#ch.limiter_pid, Acked), + {noreply, State#ch{unacked_message_q = Remaining}}; + handle_method(#'exchange.declare'{exchange = ExchangeNameBin, type = TypeNameBin, passive = false, -- cgit v1.2.1 From 747c9b9b683e15362a997df0f175f6df7c5736ad Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 27 Jul 2010 14:49:49 +0100 Subject: Rename these to avoid name clashes in the management plugin. --- src/rabbit_channel.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 3c5111cf..90628a2b 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -1232,11 +1232,11 @@ internal_emit_stats(State = #ch{stats_level = Level}) -> rabbit_event:notify(channel_stats, CoarseStats); fine -> FineStats = - [{queue_stats, + [{channel_queue_stats, [{QPid, Stats} || {{queue_stats, QPid}, Stats} <- get()]}, - {exchange_stats, + {channel_exchange_stats, [{X, Stats} || {{exchange_stats, X}, Stats} <- get()]}, - {queue_exchange_stats, + {channel_queue_exchange_stats, [{QX, Stats} || {{queue_exchange_stats, QX}, Stats} <- get()]}], rabbit_event:notify(channel_stats, CoarseStats ++ FineStats) -- cgit v1.2.1 From 00d2a4fbe30effa0c130de7446284169b890a12e Mon Sep 17 00:00:00 2001 From: Matthew Sackman Date: Tue, 27 Jul 2010 18:15:11 +0100 Subject: cosmetics --- src/rabbit_channel.erl | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 90628a2b..b276efe2 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -312,7 +312,6 @@ noreply(NewState) -> ensure_stats_timer(State = #ch{stats_level = none}) -> State; - ensure_stats_timer(State = #ch{stats_timer_ref = undefined}) -> internal_emit_stats(State), {ok, TRef} = timer:apply_interval(?STATS_INTERVAL, @@ -1198,11 +1197,9 @@ maybe_incr_stats(QXIncs, Measure, _State) -> incr_stats({QPid, _} = QX, Inc, Measure) -> maybe_monitor(QPid), update_measures(queue_exchange_stats, QX, Inc, Measure); - incr_stats(QPid, Inc, Measure) when is_pid(QPid) -> maybe_monitor(QPid), update_measures(queue_stats, QPid, Inc, Measure); - incr_stats(X, Inc, Measure) -> update_measures(exchange_stats, X, Inc, Measure). -- cgit v1.2.1 From 949a030c38494e83124ed7f7e169cb865a255dda Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 28 Jul 2010 16:53:45 +0100 Subject: Don't need retries the first time through. --- src/rabbit_tests.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index e19989a0..33c6ee5e 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -1218,7 +1218,7 @@ test_statistics() -> rabbit_tests_event_receiver:start(self()), %% Check stats empty - Event = test_statistics_receive_event(Ch, 10, fun (_) -> true end), + Event = test_statistics_receive_event(Ch, 0, fun (_) -> true end), [] = proplists:get_value(channel_queue_stats, Event), [] = proplists:get_value(channel_exchange_stats, Event), [] = proplists:get_value(channel_queue_exchange_stats, Event), -- cgit v1.2.1 From 04d6a61e59cbc56c6f33cd7226948b34c2c787ab Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 28 Jul 2010 17:36:27 +0100 Subject: Clean up tests a bit, avoid the retry count and sleeping. --- src/rabbit_channel.erl | 8 +++++++- src/rabbit_tests.erl | 31 ++++++++++++++----------------- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 7d45cec9..67809880 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, conserve_memory/2, flushed/2]). -export([list/0, info_keys/0, info/1, info/2, info_all/0, info_all/1]). --export([emit_stats/1]). +-export([emit_stats/1, flush/1]). -export([flow_timeout/2]). @@ -160,6 +160,9 @@ info_all(Items) -> emit_stats(Pid) -> gen_server2:pcast(Pid, 7, emit_stats). +flush(Pid) -> + gen_server2:call(Pid, flush). + %%--------------------------------------------------------------------------- init([Channel, ReaderPid, WriterPid, Username, VHost, CollectorPid]) -> @@ -204,6 +207,9 @@ handle_call({info, Items}, _From, State) -> catch Error -> reply({error, Error}, State) end; +handle_call(flush, _from, State) -> + reply(ok, State); + handle_call(_Request, _From, State) -> noreply(State). diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 33c6ee5e..d2dca4a2 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -1181,19 +1181,16 @@ test_statistics_event_receiver(Pid) -> test_statistics_event_receiver(Pid) end. -test_statistics_receive_event(Ch, Retries, Matcher) -> +test_statistics_receive_event(Ch, Matcher) -> + rabbit_channel:flush(Ch), rabbit_channel:emit_stats(Ch), + test_statistics_receive_event1(Ch, Matcher). + +test_statistics_receive_event1(Ch, Matcher) -> receive #event{type = channel_stats, props = Props} -> case Matcher(Props) of - true -> - Props; - _ -> - case Retries of - 0 -> throw(failed_to_receive_matching_event); - _ -> timer:sleep(10), - test_statistics_receive_event(Ch, Retries - 1, - Matcher) - end + true -> Props; + _ -> test_statistics_receive_event1(Ch, Matcher) end after 1000 -> throw(failed_to_receive_event) end. @@ -1203,11 +1200,11 @@ test_statistics() -> %% ATM this just tests the queue / exchange stats in channels. That's %% by far the most complex code though. - + %% Set up a channel and queue {_Writer, Ch, _MRef} = test_spawn(fun test_statistics_receiver/1), rabbit_channel:do(Ch, #'queue.declare'{}), - QName = receive #'queue.declare_ok'{queue = Q0} -> + QName = receive #'queue.declare_ok'{queue = Q0} -> Q0 after 1000 -> throw(failed_to_receive_queue_declare_ok) end, @@ -1216,13 +1213,13 @@ test_statistics() -> X = rabbit_misc:r(<<"/">>, exchange, <<"">>), rabbit_tests_event_receiver:start(self()), - + %% Check stats empty - Event = test_statistics_receive_event(Ch, 0, fun (_) -> true end), + Event = test_statistics_receive_event(Ch, fun (_) -> true end), [] = proplists:get_value(channel_queue_stats, Event), [] = proplists:get_value(channel_exchange_stats, Event), [] = proplists:get_value(channel_queue_exchange_stats, Event), - + %% Publish and get a message rabbit_channel:do(Ch, #'basic.publish'{exchange = <<"">>, routing_key = QName}, @@ -1231,7 +1228,7 @@ test_statistics() -> %% Check the stats reflect that Event2 = test_statistics_receive_event( - Ch, 10, + Ch, fun (E) -> length(proplists:get_value( channel_queue_exchange_stats, E)) > 0 @@ -1244,7 +1241,7 @@ test_statistics() -> %% Check the stats remove stuff on queue deletion rabbit_channel:do(Ch, #'queue.delete'{queue = QName}), Event3 = test_statistics_receive_event( - Ch, 10, + Ch, fun (E) -> length(proplists:get_value( channel_queue_exchange_stats, E)) == 0 -- cgit v1.2.1 From 7d01f5deadbed371020870faeed742a8579dd93d Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 28 Jul 2010 17:37:55 +0100 Subject: Typo. --- 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 67809880..d219404d 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -207,7 +207,7 @@ handle_call({info, Items}, _From, State) -> catch Error -> reply({error, Error}, State) end; -handle_call(flush, _from, State) -> +handle_call(flush, _From, State) -> reply(ok, State); handle_call(_Request, _From, State) -> -- cgit v1.2.1 From 916b6291d7a9ae3a6dbc5cf581696f8ec375db4c Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 29 Jul 2010 10:35:57 +0100 Subject: Construct channel creation event as a comprehension. --- src/rabbit_channel.erl | 57 +++++++++++++++++++++++++------------------------- 1 file changed, 28 insertions(+), 29 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index d219404d..c2431ccb 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -65,12 +65,13 @@ acks_uncommitted, prefetch_count]). --define(INFO_KEYS, +-define(CREATION_EVENT_KEYS, [connection, number, user, - vhost] - ++ ?STATISTICS_KEYS). + vhost]). + +-define(INFO_KEYS, ?CREATION_EVENT_KEYS ++ ?STATISTICS_KEYS). %%---------------------------------------------------------------------------- @@ -169,33 +170,31 @@ init([Channel, ReaderPid, WriterPid, Username, VHost, CollectorPid]) -> process_flag(trap_exit, true), link(WriterPid), ok = pg_local:join(rabbit_channels, self()), - rabbit_event:notify(channel_created, [{pid, self()}, - {connection_pid, ReaderPid}, - {channel, Channel}, - {user, Username}, - {vhost, VHost}]), {ok, StatsLevel} = application:get_env(rabbit, collect_statistics), - {ok, #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, - flow = #flow{server = true, client = true, - pending = none}, - stats_timer_ref = undefined, - stats_level = StatsLevel}, - hibernate, + 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, + flow = #flow{server = true, client = true, + pending = none}, + stats_timer_ref = undefined, + stats_level = StatsLevel}, + rabbit_event:notify( + channel_created, + [{Item, i(Item, State)} || Item <- [pid|?CREATION_EVENT_KEYS]]), + {ok, State, hibernate, {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}. handle_call(info, _From, State) -> -- cgit v1.2.1 From 1d801e1f4a3675644430c773a0ef745ca8b19ad4 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 29 Jul 2010 13:27:44 +0100 Subject: Refactor: abstract some things into rabbit_event. --- src/rabbit_amqqueue_process.erl | 40 ++++++++++++-------------------- src/rabbit_channel.erl | 51 ++++++++++++++++++----------------------- src/rabbit_event.erl | 33 ++++++++++++++++++++++++++ src/rabbit_reader.erl | 34 ++++++++++++++++----------- src/rabbit_tests.erl | 1 + 5 files changed, 91 insertions(+), 68 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index 78697841..a69891e4 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -60,8 +60,7 @@ sync_timer_ref, rate_timer_ref, expiry_timer_ref, - stats_timer_ref, - stats_level + stats_timer }). -record(consumer, {tag, ack_required}). @@ -110,7 +109,6 @@ init(Q) -> ?LOGDEBUG("Queue starting - ~p~n", [Q]), process_flag(trap_exit, true), {ok, BQ} = application:get_env(backing_queue_module), - {ok, StatsLevel} = application:get_env(rabbit, collect_statistics), {ok, #q{q = Q#amqqueue{pid = self()}, exclusive_consumer = none, @@ -123,8 +121,7 @@ init(Q) -> sync_timer_ref = undefined, rate_timer_ref = undefined, expiry_timer_ref = undefined, - stats_timer_ref = undefined, - stats_level = StatsLevel}, hibernate, + stats_timer = rabbit_event:init_stats_timer()}, hibernate, {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}. terminate(shutdown, State = #q{backing_queue = BQ}) -> @@ -265,26 +262,19 @@ ensure_expiry_timer(State = #q{expires = Expires}) -> State end. -ensure_stats_timer(State = #q{stats_level = none}) -> - State; -ensure_stats_timer(State = #q{stats_timer_ref = undefined, q = Q}) -> - {ok, TRef} = timer:apply_interval(?STATS_INTERVAL, - rabbit_amqqueue, emit_stats, [Q]), - emit_stats(State), - State#q{stats_timer_ref = TRef}; -ensure_stats_timer(State) -> - State. - -stop_stats_timer(State = #q{stats_level = none}) -> - State; -stop_stats_timer(State = #q{stats_timer_ref = undefined}) -> - emit_stats(State), - State; -stop_stats_timer(State = #q{stats_timer_ref = TRef}) -> - {ok, cancel} = timer:cancel(TRef), - emit_stats(State), - State#q{stats_timer_ref = undefined}. - +ensure_stats_timer(State = #q{stats_timer = StatsTimer, + q = Q}) -> + StatsTimer1 = rabbit_event:ensure_stats_timer( + StatsTimer, + fun() -> emit_stats(State) end, + fun() -> rabbit_amqqueue:emit_stats(Q) end), + State#q{stats_timer = StatsTimer1}. + +stop_stats_timer(State = #q{stats_timer = StatsTimer}) -> + StatsTimer1 = rabbit_event:stop_stats_timer( + StatsTimer, + fun() -> emit_stats(State) end), + State#q{stats_timer = StatsTimer1}. assert_invariant(#q{active_consumers = AC, backing_queue = BQ, backing_queue_state = BQS}) -> diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index c2431ccb..0b3fd380 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -50,7 +50,7 @@ uncommitted_ack_q, unacked_message_q, username, virtual_host, most_recently_declared_queue, consumer_mapping, blocking, queue_collector_pid, flow, - stats_timer_ref, stats_level}). + stats_timer}). -record(flow, {server, client, pending}). @@ -170,7 +170,6 @@ init([Channel, ReaderPid, WriterPid, Username, VHost, CollectorPid]) -> process_flag(trap_exit, true), link(WriterPid), ok = pg_local:join(rabbit_channels, self()), - {ok, StatsLevel} = application:get_env(rabbit, collect_statistics), State = #ch{state = starting, channel = Channel, reader_pid = ReaderPid, @@ -189,8 +188,7 @@ init([Channel, ReaderPid, WriterPid, Username, VHost, CollectorPid]) -> queue_collector_pid = CollectorPid, flow = #flow{server = true, client = true, pending = none}, - stats_timer_ref = undefined, - stats_level = StatsLevel}, + stats_timer = rabbit_event:init_stats_timer()}, rabbit_event:notify( channel_created, [{Item, i(Item, State)} || Item <- [pid|?CREATION_EVENT_KEYS]]), @@ -277,7 +275,7 @@ handle_cast({flow_timeout, _Ref}, State) -> handle_cast(emit_stats, State) -> internal_emit_stats(State), - noreply(State). + {noreply, State}. handle_info({'EXIT', WriterPid, Reason = {writer, send_failed, _Error}}, State = #ch{writer_pid = WriterPid}) -> @@ -315,25 +313,19 @@ reply(Reply, NewState) -> noreply(NewState) -> {noreply, ensure_stats_timer(NewState), hibernate}. -ensure_stats_timer(State = #ch{stats_level = none}) -> - State; -ensure_stats_timer(State = #ch{stats_timer_ref = undefined}) -> - internal_emit_stats(State), - {ok, TRef} = timer:apply_interval(?STATS_INTERVAL, - rabbit_channel, emit_stats, [self()]), - State#ch{stats_timer_ref = TRef}; -ensure_stats_timer(State) -> - State. +ensure_stats_timer(State = #ch{stats_timer = StatsTimer}) -> + ChPid = self(), + StatsTimer1 = rabbit_event:ensure_stats_timer( + StatsTimer, + fun() -> internal_emit_stats(State) end, + fun() -> emit_stats(ChPid) end), + State#ch{stats_timer = StatsTimer1}. -stop_stats_timer(State = #ch{stats_level = none}) -> - State; -stop_stats_timer(State = #ch{stats_timer_ref = undefined}) -> - internal_emit_stats(State), - State; -stop_stats_timer(State = #ch{stats_timer_ref = TRef}) -> - {ok, cancel} = timer:cancel(TRef), - internal_emit_stats(State), - State#ch{stats_timer_ref = undefined}. +stop_stats_timer(State = #ch{stats_timer = StatsTimer}) -> + StatsTimer1 = rabbit_event:stop_stats_timer( + StatsTimer, + fun() -> internal_emit_stats(State) end), + State#ch{stats_timer = StatsTimer1}. return_ok(State, true, _Msg) -> {noreply, State}; return_ok(State, false, Msg) -> {reply, Msg, State}. @@ -1194,10 +1186,11 @@ i(prefetch_count, #ch{limiter_pid = LimiterPid}) -> i(Item, _) -> throw({bad_argument, Item}). -maybe_incr_stats(_QXIncs, _Measure, #ch{stats_level = none}) -> - ok; -maybe_incr_stats(QXIncs, Measure, _State) -> - [incr_stats(QX, Inc, Measure) || {QX, Inc} <- QXIncs]. +maybe_incr_stats(QXIncs, Measure, #ch{stats_timer = StatsTimer}) -> + case rabbit_event:stats_level(StatsTimer) of + fine -> [incr_stats(QX, Inc, Measure) || {QX, Inc} <- QXIncs]; + _ -> ok + end. incr_stats({QPid, _} = QX, Inc, Measure) -> maybe_monitor(QPid), @@ -1227,9 +1220,9 @@ update_measures(Type, QX, Inc, Measure) -> put({Type, QX}, orddict:store(Measure, Cur + Inc, Measures)). -internal_emit_stats(State = #ch{stats_level = Level}) -> +internal_emit_stats(State = #ch{stats_timer = StatsTimer}) -> CoarseStats = [{Item, i(Item, State)} || Item <- ?STATISTICS_KEYS], - case Level of + case rabbit_event:stats_level(StatsTimer) of coarse -> rabbit_event:notify(channel_stats, CoarseStats); fine -> diff --git a/src/rabbit_event.erl b/src/rabbit_event.erl index 72a9f8ba..6cd48078 100644 --- a/src/rabbit_event.erl +++ b/src/rabbit_event.erl @@ -33,10 +33,43 @@ -include("rabbit.hrl"). +-export([init_stats_timer/0, ensure_stats_timer/3, stop_stats_timer/2]). +-export([stats_level/1]). -export([notify/2]). + +-record(state, {level, timer}). + %%---------------------------------------------------------------------------- +init_stats_timer() -> + {ok, StatsLevel} = application:get_env(rabbit, collect_statistics), + #state{level = StatsLevel, + timer = undefined}. + +ensure_stats_timer(State = #state{level = none}, _NowFun, _TimerFun) -> + State; +ensure_stats_timer(State = #state{timer = undefined}, NowFun, TimerFun) -> + NowFun(), + {ok, TRef} = timer:apply_interval(?STATS_INTERVAL, + erlang, apply, [TimerFun, []]), + State#state{timer = TRef}; +ensure_stats_timer(State, _NowFun, _TimerFun) -> + State. + +stop_stats_timer(State = #state{level = none}, _NowFun) -> + State; +stop_stats_timer(State = #state{timer = undefined}, NowFun) -> + NowFun(), + State; +stop_stats_timer(State = #state{timer = TRef}, NowFun) -> + {ok, cancel} = timer:cancel(TRef), + NowFun(), + State#state{timer = undefined}. + +stats_level(#state{level = Level}) -> + Level. + notify(Type, Props) -> try gen_event:notify(rabbit_event, #event{type = Type, diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index a2d92d2b..e1e4662b 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -60,7 +60,7 @@ %--------------------------------------------------------------------------- -record(v1, {sock, connection, callback, recv_ref, connection_state, - queue_collector, stats_timer_ref, stats_level}). + queue_collector, stats_timer}). -define(STATISTICS_KEYS, [pid, recv_oct, recv_cnt, send_oct, send_cnt, send_pend, state, channels]). @@ -251,7 +251,6 @@ start_connection(Parent, Deb, Sock, SockTransform) -> handshake_timeout), ProfilingValue = setup_profiling(), {ok, Collector} = rabbit_queue_collector:start_link(), - {ok, StatsLevel} = application:get_env(rabbit, collect_statistics), try mainloop(Parent, Deb, switch_callback( #v1{sock = ClientSock, @@ -265,8 +264,8 @@ start_connection(Parent, Deb, Sock, SockTransform) -> recv_ref = none, connection_state = pre_init, queue_collector = Collector, - stats_timer_ref = undefined, - stats_level = StatsLevel}, + stats_timer = + rabbit_event:init_stats_timer()}, handshake, 8)) catch Ex -> (if Ex == connection_closed_abruptly -> @@ -354,8 +353,7 @@ mainloop(Parent, Deb, State = #v1{sock= Sock, recv_ref = Ref}) -> end), mainloop(Parent, Deb, State); {'$gen_cast', emit_stats} -> - internal_emit_stats(State), - mainloop(Parent, Deb, State#v1{stats_timer_ref = undefined}); + mainloop(Parent, Deb, stop_stats_timer(State)); {system, From, Request} -> sys:handle_system_msg(Request, From, Parent, ?MODULE, Deb, State); @@ -603,14 +601,22 @@ check_version(ClientVersion, ServerVersion) -> (ClientMajor == ServerMajor andalso ClientMinor >= ServerMinor). -ensure_stats_timer(State = #v1{stats_level = none}) -> - State; -ensure_stats_timer(State = #v1{stats_timer_ref = undefined}) -> - {ok, TRef} = timer:apply_after(?STATS_INTERVAL, - rabbit_reader, emit_stats, [self()]), - State#v1{stats_timer_ref = TRef}; -ensure_stats_timer(State) -> - State. +ensure_stats_timer(State = #v1{stats_timer = StatsTimer}) -> + ReaderPid = self(), + StatsTimer1 = rabbit_event:ensure_stats_timer( + StatsTimer, + %% Don't run internal_emit_stats here, in normal use + %% ensure_stats_timer will get invoked almost immediately + %% after stop_stats_timer and we'll emit double events + fun() -> ok end, + fun() -> emit_stats(ReaderPid) end), + State#v1{stats_timer = StatsTimer1}. + +stop_stats_timer(State = #v1{stats_timer = StatsTimer}) -> + StatsTimer1 = rabbit_event:stop_stats_timer( + StatsTimer, + fun() -> internal_emit_stats(State) end), + State#v1{stats_timer = StatsTimer1}. %%-------------------------------------------------------------------------- diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index d2dca4a2..ef36945e 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -1251,6 +1251,7 @@ test_statistics() -> [{X,[{publish,1}]}] = proplists:get_value(channel_exchange_stats, Event3), [] = proplists:get_value(channel_queue_exchange_stats, Event3), + rabbit_channel:shutdown(Ch), rabbit_tests_event_receiver:stop(), passed. -- cgit v1.2.1 From c35a63df59fe511b5b26bc843c4db6527e324881 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 29 Jul 2010 17:16:09 +0100 Subject: Cosmetics, add pid to CREATION_EVENT_KEYS, don't emit stats when stopping when timer does not exist. --- src/rabbit_amqqueue_process.erl | 23 +++++++++++------------ src/rabbit_channel.erl | 23 +++++++++++------------ src/rabbit_event.erl | 1 - src/rabbit_reader.erl | 29 ++++++++++++++--------------- 4 files changed, 36 insertions(+), 40 deletions(-) diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index a69891e4..ab30ea3a 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -88,14 +88,15 @@ ]). -define(CREATION_EVENT_KEYS, - [name, + [pid, + name, durable, auto_delete, arguments, owner_pid ]). --define(INFO_KEYS, ?CREATION_EVENT_KEYS ++ ?STATISTICS_KEYS). +-define(INFO_KEYS, ?CREATION_EVENT_KEYS ++ ?STATISTICS_KEYS -- [pid]). %%---------------------------------------------------------------------------- @@ -165,7 +166,7 @@ declare(Recover, From, rabbit_event:notify( queue_created, [{Item, i(Item, State)} || - Item <- [pid|?CREATION_EVENT_KEYS]]), + Item <- ?CREATION_EVENT_KEYS]), noreply(init_expires(State#q{backing_queue_state = BQS})); Q1 -> {stop, normal, {existing, Q1}, State} end. @@ -264,17 +265,15 @@ ensure_expiry_timer(State = #q{expires = Expires}) -> ensure_stats_timer(State = #q{stats_timer = StatsTimer, q = Q}) -> - StatsTimer1 = rabbit_event:ensure_stats_timer( - StatsTimer, - fun() -> emit_stats(State) end, - fun() -> rabbit_amqqueue:emit_stats(Q) end), - State#q{stats_timer = StatsTimer1}. + State#q{stats_timer = rabbit_event:ensure_stats_timer( + StatsTimer, + fun() -> emit_stats(State) end, + fun() -> rabbit_amqqueue:emit_stats(Q) end)}. stop_stats_timer(State = #q{stats_timer = StatsTimer}) -> - StatsTimer1 = rabbit_event:stop_stats_timer( - StatsTimer, - fun() -> emit_stats(State) end), - State#q{stats_timer = StatsTimer1}. + State#q{stats_timer = rabbit_event:stop_stats_timer( + StatsTimer, + fun() -> emit_stats(State) end)}. assert_invariant(#q{active_consumers = AC, backing_queue = BQ, backing_queue_state = BQS}) -> diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 0b3fd380..5117fc37 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -66,12 +66,13 @@ prefetch_count]). -define(CREATION_EVENT_KEYS, - [connection, + [pid, + connection, number, user, vhost]). --define(INFO_KEYS, ?CREATION_EVENT_KEYS ++ ?STATISTICS_KEYS). +-define(INFO_KEYS, ?CREATION_EVENT_KEYS ++ ?STATISTICS_KEYS -- [pid]). %%---------------------------------------------------------------------------- @@ -191,7 +192,7 @@ init([Channel, ReaderPid, WriterPid, Username, VHost, CollectorPid]) -> stats_timer = rabbit_event:init_stats_timer()}, rabbit_event:notify( channel_created, - [{Item, i(Item, State)} || Item <- [pid|?CREATION_EVENT_KEYS]]), + [{Item, i(Item, State)} || Item <- ?CREATION_EVENT_KEYS]), {ok, State, hibernate, {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}. @@ -315,17 +316,15 @@ noreply(NewState) -> ensure_stats_timer(State = #ch{stats_timer = StatsTimer}) -> ChPid = self(), - StatsTimer1 = rabbit_event:ensure_stats_timer( - StatsTimer, - fun() -> internal_emit_stats(State) end, - fun() -> emit_stats(ChPid) end), - State#ch{stats_timer = StatsTimer1}. + State#ch{stats_timer = rabbit_event:ensure_stats_timer( + StatsTimer, + fun() -> internal_emit_stats(State) end, + fun() -> emit_stats(ChPid) end)}. stop_stats_timer(State = #ch{stats_timer = StatsTimer}) -> - StatsTimer1 = rabbit_event:stop_stats_timer( - StatsTimer, - fun() -> internal_emit_stats(State) end), - State#ch{stats_timer = StatsTimer1}. + State#ch{stats_timer = rabbit_event:stop_stats_timer( + StatsTimer, + fun() -> internal_emit_stats(State) end)}. return_ok(State, true, _Msg) -> {noreply, State}; return_ok(State, false, Msg) -> {reply, Msg, State}. diff --git a/src/rabbit_event.erl b/src/rabbit_event.erl index 6cd48078..629f5a77 100644 --- a/src/rabbit_event.erl +++ b/src/rabbit_event.erl @@ -60,7 +60,6 @@ ensure_stats_timer(State, _NowFun, _TimerFun) -> stop_stats_timer(State = #state{level = none}, _NowFun) -> State; stop_stats_timer(State = #state{timer = undefined}, NowFun) -> - NowFun(), State; stop_stats_timer(State = #state{timer = TRef}, NowFun) -> {ok, cancel} = timer:cancel(TRef), diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index e1e4662b..c24729a2 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -65,11 +65,11 @@ -define(STATISTICS_KEYS, [pid, recv_oct, recv_cnt, send_oct, send_cnt, send_pend, state, channels]). --define(CREATION_EVENT_KEYS, [address, port, peer_address, peer_port, +-define(CREATION_EVENT_KEYS, [pid, address, port, peer_address, peer_port, user, vhost, timeout, frame_max, client_properties]). --define(INFO_KEYS, ?CREATION_EVENT_KEYS ++ ?STATISTICS_KEYS). +-define(INFO_KEYS, ?CREATION_EVENT_KEYS ++ ?STATISTICS_KEYS -- [pid]). %% connection lifecycle %% @@ -603,20 +603,19 @@ check_version(ClientVersion, ServerVersion) -> ensure_stats_timer(State = #v1{stats_timer = StatsTimer}) -> ReaderPid = self(), - StatsTimer1 = rabbit_event:ensure_stats_timer( - StatsTimer, - %% Don't run internal_emit_stats here, in normal use - %% ensure_stats_timer will get invoked almost immediately - %% after stop_stats_timer and we'll emit double events - fun() -> ok end, - fun() -> emit_stats(ReaderPid) end), - State#v1{stats_timer = StatsTimer1}. + State#v1{stats_timer = rabbit_event:ensure_stats_timer( + StatsTimer, + %% Don't run internal_emit_stats here, in normal + %% use ensure_stats_timer will get invoked almost + %% immediately after stop_stats_timer and we'll + %% emit double events + fun() -> ok end, + fun() -> emit_stats(ReaderPid) end)}. stop_stats_timer(State = #v1{stats_timer = StatsTimer}) -> - StatsTimer1 = rabbit_event:stop_stats_timer( - StatsTimer, - fun() -> internal_emit_stats(State) end), - State#v1{stats_timer = StatsTimer1}. + State#v1{stats_timer = rabbit_event:stop_stats_timer( + StatsTimer, + fun() -> internal_emit_stats(State) end)}. %%-------------------------------------------------------------------------- @@ -696,7 +695,7 @@ handle_method0(#'connection.open'{virtual_host = VHostPath, connection = NewConnection}, rabbit_event:notify( connection_created, - [{Item, i(Item, State1)} || Item <- [pid|?CREATION_EVENT_KEYS]]), + [{Item, i(Item, State1)} || Item <- ?CREATION_EVENT_KEYS]), State1; true -> %% FIXME: 'host' is supposed to only contain one -- cgit v1.2.1 From 3fe0f79fb112f51e586fec07d07c8fb392066033 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 29 Jul 2010 17:36:26 +0100 Subject: Revert reader changes in 1451c9523971 sicne they force us to start / stop a timer all the time. --- src/rabbit_reader.erl | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index c24729a2..ffd40bed 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -60,7 +60,7 @@ %--------------------------------------------------------------------------- -record(v1, {sock, connection, callback, recv_ref, connection_state, - queue_collector, stats_timer}). + queue_collector, stats_timer_ref, stats_level}). -define(STATISTICS_KEYS, [pid, recv_oct, recv_cnt, send_oct, send_cnt, send_pend, state, channels]). @@ -251,6 +251,7 @@ start_connection(Parent, Deb, Sock, SockTransform) -> handshake_timeout), ProfilingValue = setup_profiling(), {ok, Collector} = rabbit_queue_collector:start_link(), + {ok, StatsLevel} = application:get_env(rabbit, collect_statistics), try mainloop(Parent, Deb, switch_callback( #v1{sock = ClientSock, @@ -264,8 +265,8 @@ start_connection(Parent, Deb, Sock, SockTransform) -> recv_ref = none, connection_state = pre_init, queue_collector = Collector, - stats_timer = - rabbit_event:init_stats_timer()}, + stats_timer_ref = undefined, + stats_level = StatsLevel}, handshake, 8)) catch Ex -> (if Ex == connection_closed_abruptly -> @@ -353,7 +354,8 @@ mainloop(Parent, Deb, State = #v1{sock= Sock, recv_ref = Ref}) -> end), mainloop(Parent, Deb, State); {'$gen_cast', emit_stats} -> - mainloop(Parent, Deb, stop_stats_timer(State)); + internal_emit_stats(State), + mainloop(Parent, Deb, State#v1{stats_timer_ref = undefined}); {system, From, Request} -> sys:handle_system_msg(Request, From, Parent, ?MODULE, Deb, State); @@ -601,21 +603,14 @@ check_version(ClientVersion, ServerVersion) -> (ClientMajor == ServerMajor andalso ClientMinor >= ServerMinor). -ensure_stats_timer(State = #v1{stats_timer = StatsTimer}) -> - ReaderPid = self(), - State#v1{stats_timer = rabbit_event:ensure_stats_timer( - StatsTimer, - %% Don't run internal_emit_stats here, in normal - %% use ensure_stats_timer will get invoked almost - %% immediately after stop_stats_timer and we'll - %% emit double events - fun() -> ok end, - fun() -> emit_stats(ReaderPid) end)}. - -stop_stats_timer(State = #v1{stats_timer = StatsTimer}) -> - State#v1{stats_timer = rabbit_event:stop_stats_timer( - StatsTimer, - fun() -> internal_emit_stats(State) end)}. +ensure_stats_timer(State = #v1{stats_level = none}) -> + State; +ensure_stats_timer(State = #v1{stats_timer_ref = undefined}) -> + {ok, TRef} = timer:apply_after(?STATS_INTERVAL, + rabbit_reader, emit_stats, [self()]), + State#v1{stats_timer_ref = TRef}; +ensure_stats_timer(State) -> + State. %%-------------------------------------------------------------------------- -- cgit v1.2.1 From e74eee10c760d0f661607b5b5a55f064b6bf9d27 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 29 Jul 2010 17:36:40 +0100 Subject: Fix warning. --- src/rabbit_event.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_event.erl b/src/rabbit_event.erl index 629f5a77..ddb12873 100644 --- a/src/rabbit_event.erl +++ b/src/rabbit_event.erl @@ -59,7 +59,7 @@ ensure_stats_timer(State, _NowFun, _TimerFun) -> stop_stats_timer(State = #state{level = none}, _NowFun) -> State; -stop_stats_timer(State = #state{timer = undefined}, NowFun) -> +stop_stats_timer(State = #state{timer = undefined}, _NowFun) -> State; stop_stats_timer(State = #state{timer = TRef}, NowFun) -> {ok, cancel} = timer:cancel(TRef), -- cgit v1.2.1 From a93946cc56774a1d3ae5e6510e2d319e6b1ce513 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 29 Jul 2010 18:02:21 +0100 Subject: Add an attempt at specs. The second part of the state type should not be an atom but a timer; however I can't find that easily. --- src/rabbit_event.erl | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/rabbit_event.erl b/src/rabbit_event.erl index ddb12873..d784a6aa 100644 --- a/src/rabbit_event.erl +++ b/src/rabbit_event.erl @@ -37,6 +37,14 @@ -export([stats_level/1]). -export([notify/2]). +-opaque(state() :: {atom(), atom()}). + +-spec(init_stats_timer/0 :: () -> state()). +-spec(ensure_stats_timer/3 :: + (state(), fun (() -> 'ok'), fun (() -> 'ok')) -> state()). +-spec(stop_stats_timer/2 :: (state(), fun (() -> 'ok')) -> state()). +-spec(stats_level/1 :: (state()) -> atom()). +-spec(notify/2 :: (atom(), term()) -> 'ok'). -record(state, {level, timer}). -- cgit v1.2.1 From 568730fa6f898d23305aeb3a96ef53007987047e Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 29 Jul 2010 23:51:39 +0100 Subject: consistently return {ok, Pid} from start_{link} functions --- src/rabbit_channel.erl | 10 ++++------ src/rabbit_framing_channel.erl | 15 ++++++++------- src/rabbit_limiter.erl | 5 ++--- src/rabbit_reader.erl | 8 ++++---- src/rabbit_tests.erl | 12 ++++++------ src/rabbit_writer.erl | 18 ++++++++---------- 6 files changed, 32 insertions(+), 36 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index dafc3075..dd639dd0 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -78,7 +78,7 @@ -spec(start_link/6 :: (channel_number(), pid(), pid(), rabbit_access_control:username(), - rabbit_types:vhost(), pid()) -> pid()). + rabbit_types:vhost(), pid()) -> rabbit_types:ok(pid())). -spec(do/2 :: (pid(), rabbit_framing:amqp_method_record()) -> 'ok'). -spec(do/3 :: (pid(), rabbit_framing:amqp_method_record(), rabbit_types:maybe(rabbit_types:content())) -> 'ok'). @@ -102,10 +102,8 @@ %%---------------------------------------------------------------------------- start_link(Channel, ReaderPid, WriterPid, Username, VHost, CollectorPid) -> - {ok, Pid} = gen_server2:start_link( - ?MODULE, [Channel, ReaderPid, WriterPid, - Username, VHost, CollectorPid], []), - Pid. + gen_server2:start_link(?MODULE, [Channel, ReaderPid, WriterPid, + Username, VHost, CollectorPid], []). do(Pid, Method) -> do(Pid, Method, none). @@ -1031,7 +1029,7 @@ fold_per_queue(F, Acc0, UAQ) -> Acc0, D). start_limiter(State = #ch{unacked_message_q = UAMQ}) -> - LPid = rabbit_limiter:start_link(self(), queue:len(UAMQ)), + {ok, LPid} = rabbit_limiter:start_link(self(), queue:len(UAMQ)), ok = limit_queues(LPid, State), LPid. diff --git a/src/rabbit_framing_channel.erl b/src/rabbit_framing_channel.erl index bc1a2a08..31be1cf7 100644 --- a/src/rabbit_framing_channel.erl +++ b/src/rabbit_framing_channel.erl @@ -40,13 +40,14 @@ %%-------------------------------------------------------------------- start_link(StartFun, StartArgs) -> - spawn_link( - fun () -> - %% we trap exits so that a normal termination of the - %% channel or reader process terminates us too. - process_flag(trap_exit, true), - mainloop(apply(StartFun, StartArgs)) - end). + {ok, spawn_link( + fun () -> + %% we trap exits so that a normal termination of + %% the channel or reader process terminates us too. + process_flag(trap_exit, true), + {ok, ChannelPid} = apply(StartFun, StartArgs), + mainloop(ChannelPid) + end)}. process(Pid, Frame) -> Pid ! {frame, Frame}, diff --git a/src/rabbit_limiter.erl b/src/rabbit_limiter.erl index 878af029..491ae7d6 100644 --- a/src/rabbit_limiter.erl +++ b/src/rabbit_limiter.erl @@ -45,7 +45,7 @@ -type(maybe_pid() :: pid() | 'undefined'). --spec(start_link/2 :: (pid(), non_neg_integer()) -> pid()). +-spec(start_link/2 :: (pid(), non_neg_integer()) -> {'ok', pid()}). -spec(shutdown/1 :: (maybe_pid()) -> 'ok'). -spec(limit/2 :: (maybe_pid(), non_neg_integer()) -> 'ok' | 'stopped'). -spec(can_send/3 :: (maybe_pid(), pid(), boolean()) -> boolean()). @@ -74,8 +74,7 @@ %%---------------------------------------------------------------------------- start_link(ChPid, UnackedMsgCount) -> - {ok, Pid} = gen_server2:start_link(?MODULE, [ChPid, UnackedMsgCount], []), - Pid. + gen_server2:start_link(?MODULE, [ChPid, UnackedMsgCount], []). shutdown(undefined) -> ok; diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index b5514c82..83bc7e68 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -771,10 +771,10 @@ send_to_new_channel(Channel, AnalyzedFrame, frame_max = FrameMax, user = #user{username = Username}, vhost = VHost}} = State, - WriterPid = rabbit_writer:start(Sock, Channel, FrameMax), - ChPid = rabbit_framing_channel:start_link( - fun rabbit_channel:start_link/6, - [Channel, self(), WriterPid, Username, VHost, Collector]), + {ok, WriterPid} = rabbit_writer:start(Sock, Channel, FrameMax), + {ok, ChPid} = rabbit_framing_channel:start_link( + fun rabbit_channel:start_link/6, + [Channel, self(), WriterPid, Username, VHost, Collector]), put({channel, Channel}, {chpid, ChPid}), put({chpid, ChPid}, {channel, Channel}), ok = rabbit_framing_channel:process(ChPid, AnalyzedFrame). diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 090c714b..dc77dbbf 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -938,8 +938,8 @@ test_user_management() -> test_server_status() -> %% create a few things so there is some useful information to list Writer = spawn(fun () -> receive shutdown -> ok end end), - Ch = rabbit_channel:start_link(1, self(), Writer, <<"user">>, <<"/">>, - self()), + {ok, Ch} = rabbit_channel:start_link(1, self(), Writer, + <<"user">>, <<"/">>, self()), [Q, Q2] = [Queue || Name <- [<<"foo">>, <<"bar">>], {new, Queue = #amqqueue{}} <- [rabbit_amqqueue:declare( @@ -1068,8 +1068,8 @@ test_memory_pressure_sync(Ch, Writer) -> test_memory_pressure_spawn() -> Me = self(), Writer = spawn(fun () -> test_memory_pressure_receiver(Me) end), - Ch = rabbit_channel:start_link(1, self(), Writer, <<"user">>, <<"/">>, - self()), + {ok, Ch} = rabbit_channel:start_link(1, self(), Writer, + <<"user">>, <<"/">>, self()), ok = rabbit_channel:do(Ch, #'channel.open'{}), MRef = erlang:monitor(process, Ch), receive #'channel.open_ok'{} -> ok @@ -1142,8 +1142,8 @@ test_memory_pressure() -> alarm_handler:set_alarm({vm_memory_high_watermark, []}), Me = self(), Writer4 = spawn(fun () -> test_memory_pressure_receiver(Me) end), - Ch4 = rabbit_channel:start_link(1, self(), Writer4, <<"user">>, <<"/">>, - self()), + {ok, Ch4} = rabbit_channel:start_link(1, self(), Writer4, + <<"user">>, <<"/">>, self()), ok = rabbit_channel:do(Ch4, #'channel.open'{}), MRef4 = erlang:monitor(process, Ch4), Writer4 ! sync, diff --git a/src/rabbit_writer.erl b/src/rabbit_writer.erl index 80602038..8857784b 100644 --- a/src/rabbit_writer.erl +++ b/src/rabbit_writer.erl @@ -50,12 +50,10 @@ -spec(start/3 :: (rabbit_net:socket(), rabbit_channel:channel_number(), - non_neg_integer()) - -> pid()). + non_neg_integer()) -> rabbit_types:ok(pid())). -spec(start_link/3 :: (rabbit_net:socket(), rabbit_channel:channel_number(), - non_neg_integer()) - -> pid()). + non_neg_integer()) -> rabbit_types:ok(pid())). -spec(send_command/2 :: (pid(), rabbit_framing:amqp_method_record()) -> 'ok'). -spec(send_command/3 :: @@ -85,14 +83,14 @@ %%---------------------------------------------------------------------------- start(Sock, Channel, FrameMax) -> - spawn(?MODULE, mainloop, [#wstate{sock = Sock, - channel = Channel, - frame_max = FrameMax}]). + {ok, spawn(?MODULE, mainloop, [#wstate{sock = Sock, + channel = Channel, + frame_max = FrameMax}])}. start_link(Sock, Channel, FrameMax) -> - spawn_link(?MODULE, mainloop, [#wstate{sock = Sock, - channel = Channel, - frame_max = FrameMax}]). + {ok, spawn_link(?MODULE, mainloop, [#wstate{sock = Sock, + channel = Channel, + frame_max = FrameMax}])}. mainloop(State) -> receive -- cgit v1.2.1 From d10027bce07d24b8b0580497b1e171d676f3992b Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 30 Jul 2010 12:30:59 +0100 Subject: Re-abstract the stats timer thing in reader. --- src/rabbit_event.erl | 15 +++++++++++++++ src/rabbit_reader.erl | 25 ++++++++++++------------- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/src/rabbit_event.erl b/src/rabbit_event.erl index d784a6aa..e2d0094d 100644 --- a/src/rabbit_event.erl +++ b/src/rabbit_event.erl @@ -34,6 +34,7 @@ -include("rabbit.hrl"). -export([init_stats_timer/0, ensure_stats_timer/3, stop_stats_timer/2]). +-export([ensure_stats_timer_after/2, reset_stats_timer_after/1]). -export([stats_level/1]). -export([notify/2]). @@ -43,6 +44,8 @@ -spec(ensure_stats_timer/3 :: (state(), fun (() -> 'ok'), fun (() -> 'ok')) -> state()). -spec(stop_stats_timer/2 :: (state(), fun (() -> 'ok')) -> state()). +-spec(ensure_stats_timer_after/2 :: (state(), fun (() -> 'ok')) -> state()). +-spec(reset_stats_timer_after/1 :: (state()) -> 'ok'). -spec(stats_level/1 :: (state()) -> atom()). -spec(notify/2 :: (atom(), term()) -> 'ok'). @@ -74,6 +77,18 @@ stop_stats_timer(State = #state{timer = TRef}, NowFun) -> NowFun(), State#state{timer = undefined}. +ensure_stats_timer_after(State = #state{level = none}, _TimerFun) -> + State; +ensure_stats_timer_after(State = #state{timer = undefined}, TimerFun) -> + {ok, TRef} = timer:apply_after(?STATS_INTERVAL, + erlang, apply, [TimerFun, []]), + State#state{timer = TRef}; +ensure_stats_timer_after(State, _TimerFun) -> + State. + +reset_stats_timer_after(State) -> + State#state{timer = undefined}. + stats_level(#state{level = Level}) -> Level. diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index ffd40bed..7a45922e 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -60,7 +60,7 @@ %--------------------------------------------------------------------------- -record(v1, {sock, connection, callback, recv_ref, connection_state, - queue_collector, stats_timer_ref, stats_level}). + queue_collector, stats_timer}). -define(STATISTICS_KEYS, [pid, recv_oct, recv_cnt, send_oct, send_cnt, send_pend, state, channels]). @@ -251,7 +251,6 @@ start_connection(Parent, Deb, Sock, SockTransform) -> handshake_timeout), ProfilingValue = setup_profiling(), {ok, Collector} = rabbit_queue_collector:start_link(), - {ok, StatsLevel} = application:get_env(rabbit, collect_statistics), try mainloop(Parent, Deb, switch_callback( #v1{sock = ClientSock, @@ -265,8 +264,8 @@ start_connection(Parent, Deb, Sock, SockTransform) -> recv_ref = none, connection_state = pre_init, queue_collector = Collector, - stats_timer_ref = undefined, - stats_level = StatsLevel}, + stats_timer = + rabbit_event:init_stats_timer()}, handshake, 8)) catch Ex -> (if Ex == connection_closed_abruptly -> @@ -355,7 +354,10 @@ mainloop(Parent, Deb, State = #v1{sock= Sock, recv_ref = Ref}) -> mainloop(Parent, Deb, State); {'$gen_cast', emit_stats} -> internal_emit_stats(State), - mainloop(Parent, Deb, State#v1{stats_timer_ref = undefined}); + mainloop(Parent, Deb, + State#v1{stats_timer = + rabbit_event:reset_stats_timer_after( + State#v1.stats_timer)}); {system, From, Request} -> sys:handle_system_msg(Request, From, Parent, ?MODULE, Deb, State); @@ -603,14 +605,11 @@ check_version(ClientVersion, ServerVersion) -> (ClientMajor == ServerMajor andalso ClientMinor >= ServerMinor). -ensure_stats_timer(State = #v1{stats_level = none}) -> - State; -ensure_stats_timer(State = #v1{stats_timer_ref = undefined}) -> - {ok, TRef} = timer:apply_after(?STATS_INTERVAL, - rabbit_reader, emit_stats, [self()]), - State#v1{stats_timer_ref = TRef}; -ensure_stats_timer(State) -> - State. +ensure_stats_timer(State = #v1{stats_timer = StatsTimer}) -> + Self = self(), + State#v1{stats_timer = rabbit_event:ensure_stats_timer_after( + StatsTimer, + fun() -> emit_stats(Self) end)}. %%-------------------------------------------------------------------------- -- cgit v1.2.1 From 9689eaa9cec4a31fed2c2f1457f18c236511c438 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 2 Aug 2010 09:19:01 +0100 Subject: cosmetic --- src/rabbit_framing_channel.erl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rabbit_framing_channel.erl b/src/rabbit_framing_channel.erl index f96e69eb..00b74ad0 100644 --- a/src/rabbit_framing_channel.erl +++ b/src/rabbit_framing_channel.erl @@ -74,8 +74,7 @@ read_frame(ChannelPid) -> end. mainloop(ChannelPid, Protocol) -> - Decoded = read_frame(ChannelPid), - case Decoded of + case read_frame(ChannelPid) of {method, MethodName, FieldsBin} -> Method = Protocol:decode_method_fields(MethodName, FieldsBin), case Protocol:method_has_content(MethodName) of -- cgit v1.2.1 From c0784b916ae1c911bc9090abfb2569027cb7d6e1 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 2 Aug 2010 10:17:50 +0100 Subject: react to memory pressure by pausing socket receive which is much more prompt and effective than issuing a channel.flow --- src/rabbit_channel.erl | 88 +++----------------------------------------------- src/rabbit_reader.erl | 31 ++++++++++++++---- 2 files changed, 30 insertions(+), 89 deletions(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index c4ff361d..1928e21d 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -36,11 +36,9 @@ -behaviour(gen_server2). -export([start_link/6, do/2, do/3, shutdown/1]). --export([send_command/2, deliver/4, conserve_memory/2, flushed/2]). +-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([flow_timeout/2]). - -export([init/1, terminate/2, code_change/3, handle_call/3, handle_cast/2, handle_info/2, handle_pre_hibernate/1]). @@ -48,12 +46,9 @@ 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, flow}). - --record(flow, {server, client, pending}). + consumer_mapping, blocking, queue_collector_pid}). -define(MAX_PERMISSION_CACHE_SIZE, 12). --define(FLOW_OK_TIMEOUT, 10000). %% 10 seconds -define(INFO_KEYS, [pid, @@ -87,9 +82,7 @@ -spec(deliver/4 :: (pid(), rabbit_types:ctag(), boolean(), rabbit_amqqueue:qmsg()) -> 'ok'). --spec(conserve_memory/2 :: (pid(), boolean()) -> 'ok'). -spec(flushed/2 :: (pid(), pid()) -> 'ok'). --spec(flow_timeout/2 :: (pid(), ref()) -> 'ok'). -spec(list/0 :: () -> [pid()]). -spec(info_keys/0 :: () -> [rabbit_types:info_key()]). -spec(info/1 :: (pid()) -> [rabbit_types:info()]). @@ -120,15 +113,9 @@ send_command(Pid, Msg) -> deliver(Pid, ConsumerTag, AckRequired, Msg) -> gen_server2:cast(Pid, {deliver, ConsumerTag, AckRequired, Msg}). -conserve_memory(Pid, Conserve) -> - gen_server2:pcast(Pid, 8, {conserve_memory, Conserve}). - flushed(Pid, QPid) -> gen_server2:cast(Pid, {flushed, QPid}). -flow_timeout(Pid, Ref) -> - gen_server2:pcast(Pid, 7, {flow_timeout, Ref}). - list() -> pg_local:get_members(rabbit_channels). @@ -170,9 +157,7 @@ init([Channel, ReaderPid, WriterPid, Username, VHost, CollectorPid]) -> most_recently_declared_queue = <<>>, consumer_mapping = dict:new(), blocking = dict:new(), - queue_collector_pid = CollectorPid, - flow = #flow{server = true, client = true, - pending = none}}, + queue_collector_pid = CollectorPid}, hibernate, {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}}. @@ -223,27 +208,7 @@ handle_cast({deliver, ConsumerTag, AckRequired, Msg}, next_tag = DeliveryTag}) -> State1 = lock_message(AckRequired, {DeliveryTag, ConsumerTag, Msg}, State), ok = internal_deliver(WriterPid, true, ConsumerTag, DeliveryTag, Msg), - noreply(State1#ch{next_tag = DeliveryTag + 1}); - -handle_cast({conserve_memory, true}, State = #ch{state = starting}) -> - noreply(State); -handle_cast({conserve_memory, false}, State = #ch{state = starting}) -> - ok = rabbit_writer:send_command(State#ch.writer_pid, #'channel.open_ok'{}), - noreply(State#ch{state = running}); -handle_cast({conserve_memory, Conserve}, State = #ch{state = running}) -> - flow_control(not Conserve, State); -handle_cast({conserve_memory, _Conserve}, State) -> - noreply(State); - -handle_cast({flow_timeout, Ref}, - State = #ch{flow = #flow{client = Flow, pending = {Ref, _TRef}}}) -> - {stop, normal, terminating( - rabbit_misc:amqp_error( - precondition_failed, - "timeout waiting for channel.flow_ok{active=~w}", - [not Flow], none), State)}; -handle_cast({flow_timeout, _Ref}, State) -> - {noreply, State}. + noreply(State1#ch{next_tag = DeliveryTag + 1}). handle_info({'EXIT', WriterPid, Reason = {writer, send_failed, _Error}}, State = #ch{writer_pid = WriterPid}) -> @@ -383,10 +348,7 @@ queue_blocked(QPid, State = #ch{blocking = Blocking}) -> end. handle_method(#'channel.open'{}, _, State = #ch{state = starting}) -> - case rabbit_alarm:register(self(), {?MODULE, conserve_memory, []}) of - true -> {noreply, State}; - false -> {reply, #'channel.open_ok'{}, State#ch{state = running}} - end; + {reply, #'channel.open_ok'{}, State#ch{state = running}}; handle_method(#'channel.open'{}, _, _State) -> rabbit_misc:protocol_error( @@ -403,10 +365,6 @@ handle_method(#'channel.close'{}, _, State = #ch{writer_pid = WriterPid}) -> handle_method(#'access.request'{},_, State) -> {reply, #'access.request_ok'{ticket = 1}, State}; -handle_method(#'basic.publish'{}, _, #ch{flow = #flow{client = false}}) -> - rabbit_misc:protocol_error( - command_invalid, - "basic.publish received after channel.flow_ok{active=false}", []); handle_method(#'basic.publish'{exchange = ExchangeNameBin, routing_key = RoutingKey, mandatory = Mandatory, @@ -863,48 +821,12 @@ handle_method(#'channel.flow'{active = false}, _, blocking = dict:from_list(Queues)}} end; -handle_method(#'channel.flow_ok'{active = Active}, _, - State = #ch{flow = #flow{server = Active, client = Flow, - pending = {_Ref, TRef}} = F}) - when Flow =:= not Active -> - {ok, cancel} = timer:cancel(TRef), - {noreply, State#ch{flow = F#flow{client = Active, pending = none}}}; -handle_method(#'channel.flow_ok'{active = Active}, _, - State = #ch{flow = #flow{server = Flow, client = Flow, - pending = {_Ref, TRef}}}) - when Flow =:= not Active -> - {ok, cancel} = timer:cancel(TRef), - {noreply, issue_flow(Flow, State)}; -handle_method(#'channel.flow_ok'{}, _, #ch{flow = #flow{pending = none}}) -> - rabbit_misc:protocol_error( - command_invalid, "unsolicited channel.flow_ok", []); -handle_method(#'channel.flow_ok'{active = Active}, _, _State) -> - rabbit_misc:protocol_error( - command_invalid, - "received channel.flow_ok{active=~w} has incorrect polarity", [Active]); - handle_method(_MethodRecord, _Content, _State) -> rabbit_misc:protocol_error( command_invalid, "unimplemented method", []). %%---------------------------------------------------------------------------- -flow_control(Active, State = #ch{flow = #flow{server = Flow, pending = none}}) - when Flow =:= not Active -> - ok = clear_permission_cache(), - noreply(issue_flow(Active, State)); -flow_control(Active, State = #ch{flow = F}) -> - noreply(State#ch{flow = F#flow{server = Active}}). - -issue_flow(Active, State) -> - ok = rabbit_writer:send_command( - State#ch.writer_pid, #'channel.flow'{active = Active}), - Ref = make_ref(), - {ok, TRef} = timer:apply_after(?FLOW_OK_TIMEOUT, ?MODULE, flow_timeout, - [self(), Ref]), - State#ch{flow = #flow{server = Active, client = not Active, - pending = {Ref, TRef}}}. - binding_action(Fun, ExchangeNameBin, QueueNameBin, RoutingKey, Arguments, ReturnMethod, NoWait, State = #ch{virtual_host = VHostPath, diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 9603faf5..46171aec 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -39,7 +39,7 @@ -export([init/1, mainloop/3]). --export([server_properties/0]). +-export([conserve_memory/2, server_properties/0]). -export([analyze_frame/3]). @@ -58,7 +58,7 @@ %--------------------------------------------------------------------------- -record(v1, {sock, connection, callback, recv_ref, connection_state, - queue_collector}). + queue_collector, conserving_memory}). -define(INFO_KEYS, [pid, address, port, peer_address, peer_port, @@ -142,6 +142,7 @@ -spec(info/1 :: (pid()) -> [rabbit_types:info()]). -spec(info/2 :: (pid(), [rabbit_types:info_key()]) -> [rabbit_types:info()]). -spec(shutdown/2 :: (pid(), string()) -> 'ok'). +-spec(conserve_memory/2 :: (pid(), boolean()) -> 'ok'). -spec(server_properties/0 :: () -> rabbit_framing:amqp_table()). -endif. @@ -208,6 +209,10 @@ teardown_profiling(Value) -> fprof:analyse([{dest, []}, {cols, 100}]) end. +conserve_memory(Pid, Conserve) -> + Pid ! {conserve_memory, Conserve}, + ok. + server_properties() -> {ok, Product} = application:get_key(rabbit, id), {ok, Version} = application:get_key(rabbit, vsn), @@ -295,6 +300,8 @@ mainloop(Parent, Deb, State = #v1{sock= Sock, recv_ref = Ref}) -> end; {inet_async, Sock, Ref, {error, Reason}} -> throw({inet_error, Reason}); + {conserve_memory, Conserve} -> + mainloop(Parent, Deb, internal_conserve_memory(Conserve, State)); {'EXIT', Parent, Reason} -> terminate(io_lib:format("broker forced connection closure " "with reason '~w'", [Reason]), State), @@ -348,11 +355,14 @@ mainloop(Parent, Deb, State = #v1{sock= Sock, recv_ref = Ref}) -> exit({unexpected_message, Other}) end. -switch_callback(OldState, NewCallback, Length) -> +switch_callback(State = #v1{conserving_memory = true}, Callback, Length) -> + %% TODO: pause heartbeat monitor + %% TODO: only do this after receiving a content-bearing method + State#v1{callback = {Callback, Length}, recv_ref = none}; +switch_callback(State, Callback, Length) -> Ref = inet_op(fun () -> rabbit_net:async_recv( - OldState#v1.sock, Length, infinity) end), - OldState#v1{callback = NewCallback, - recv_ref = Ref}. + State#v1.sock, Length, infinity) end), + State#v1{callback = Callback, recv_ref = Ref}. terminate(Explanation, State = #v1{connection_state = running}) -> {normal, send_exception(State, 0, @@ -361,6 +371,14 @@ terminate(Explanation, State = #v1{connection_state = running}) -> terminate(_Explanation, State) -> {force, State}. +internal_conserve_memory(false, State = #v1{conserving_memory = true, + callback = {Callback, Length}, + recv_ref = none}) -> + %% TODO: resume heartbeat monitor + switch_callback(State#v1{conserving_memory = false}, Callback, Length); +internal_conserve_memory(Conserve, State) -> + State#v1{conserving_memory = Conserve}. + close_connection(State = #v1{connection = #connection{ timeout_sec = TimeoutSec}}) -> %% We terminate the connection after the specified interval, but @@ -670,6 +688,7 @@ handle_method0(#'connection.open'{virtual_host = VHostPath}, ok = rabbit_access_control:check_vhost_access(User, VHostPath), NewConnection = Connection#connection{vhost = VHostPath}, ok = send_on_channel0(Sock, #'connection.open_ok'{}, Protocol), + rabbit_alarm:register(self(), {?MODULE, conserve_memory, []}), State#v1{connection_state = running, connection = NewConnection}; handle_method0(#'connection.close'{}, -- cgit v1.2.1 From e89b9c806bcf671bd2879c76927cf9014e14df2e Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 2 Aug 2010 10:24:40 +0100 Subject: Work around broken py-amqplib handshaking --- src/rabbit_reader.erl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 9603faf5..f687f814 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -569,6 +569,10 @@ handle_input(handshake, <<"AMQP", 1, 1, 0, 9>>, State) -> handle_input(handshake, <<"AMQP", 1, 1, 8, 0>>, State) -> start_connection({8, 0, 0}, rabbit_framing_amqp_0_8, State); +%% py-amqplib has always sent this broken version. It wants 0-8. +handle_input(handshake, <<"AMQP", 1, 1, 9, 1>>, State) -> + start_connection({8, 0, 0}, rabbit_framing_amqp_0_8, State); + handle_input(handshake, <<"AMQP", A, B, C, D>>, #v1{sock = Sock}) -> refuse_connection(Sock, {bad_version, A, B, C, D}); -- cgit v1.2.1 From bb7bc187767f89b48bb0ab464b2244eeefe7c658 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 2 Aug 2010 11:22:22 +0100 Subject: remove unused type --- src/rabbit_channel.erl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 1928e21d..9c2f9ee8 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -68,7 +68,6 @@ -export_type([channel_number/0]). --type(ref() :: any()). -type(channel_number() :: non_neg_integer()). -spec(start_link/6 :: -- cgit v1.2.1 From 46d48171d63b40e1cdc4650a278a9af48717e7ed Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 2 Aug 2010 11:50:08 +0100 Subject: pause/resume heartbeat monitor as required --- src/rabbit_heartbeat.erl | 34 ++++++++++++++++++++++++++++++---- src/rabbit_reader.erl | 20 +++++++++++++------- 2 files changed, 43 insertions(+), 11 deletions(-) diff --git a/src/rabbit_heartbeat.erl b/src/rabbit_heartbeat.erl index 1989fb7b..faddffc1 100644 --- a/src/rabbit_heartbeat.erl +++ b/src/rabbit_heartbeat.erl @@ -31,14 +31,17 @@ -module(rabbit_heartbeat). --export([start_heartbeat/2]). +-export([start_heartbeat/2, pause_monitor/1, resume_monitor/1]). %%---------------------------------------------------------------------------- -ifdef(use_specs). --spec(start_heartbeat/2 :: (rabbit_net:socket(), non_neg_integer()) -> - rabbit_types:maybe({pid(), pid()})). +-type(pids() :: rabbit_types:maybe({pid(), pid()})). + +-spec(start_heartbeat/2 :: (rabbit_net:socket(), non_neg_integer()) -> pids()). +-spec(pause_monitor/1 :: (pids()) -> 'ok'). +-spec(resume_monitor/1 :: (pids()) -> 'ok'). -endif. @@ -70,20 +73,43 @@ start_heartbeat(Sock, TimeoutSec) -> end}, Parent) end), {Sender, Receiver}. +pause_monitor(none) -> + ok; +pause_monitor({_Sender, Receiver}) -> + Receiver ! pause, + ok. + +resume_monitor(none) -> + ok; +resume_monitor({_Sender, Receiver}) -> + Receiver ! resume, + ok. + +%%---------------------------------------------------------------------------- + heartbeater(Params, Parent) -> heartbeater(Params, erlang:monitor(process, Parent), {0, 0}). heartbeater({Sock, TimeoutMillisec, StatName, Threshold, Handler} = Params, MonitorRef, {StatVal, SameCount}) -> + Recurse = fun (V) -> heartbeater(Params, MonitorRef, V) end, receive {'DOWN', MonitorRef, process, _Object, _Info} -> ok; + pause -> + receive + {'DOWN', MonitorRef, process, _Object, _Info} -> + ok; + resume -> + Recurse({0, 0}); + Other -> + exit({unexpected_message, Other}) + end; Other -> exit({unexpected_message, Other}) after TimeoutMillisec -> case rabbit_net:getstat(Sock, [StatName]) of {ok, [{StatName, NewStatVal}]} -> - Recurse = fun (V) -> heartbeater(Params, MonitorRef, V) end, if NewStatVal =/= StatVal -> Recurse({NewStatVal, 0}); SameCount < Threshold -> diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 46171aec..2c1a24c4 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -58,7 +58,7 @@ %--------------------------------------------------------------------------- -record(v1, {sock, connection, callback, recv_ref, connection_state, - queue_collector, conserving_memory}). + queue_collector, heartbeater, conserving_memory}). -define(INFO_KEYS, [pid, address, port, peer_address, peer_port, @@ -259,7 +259,9 @@ start_connection(Parent, Deb, Sock, SockTransform) -> callback = uninitialized_callback, recv_ref = none, connection_state = pre_init, - queue_collector = Collector}, + queue_collector = Collector, + heartbeater = none, + conserving_memory = false}, handshake, 8)) catch Ex -> (if Ex == connection_closed_abruptly -> @@ -355,8 +357,9 @@ mainloop(Parent, Deb, State = #v1{sock= Sock, recv_ref = Ref}) -> exit({unexpected_message, Other}) end. -switch_callback(State = #v1{conserving_memory = true}, Callback, Length) -> - %% TODO: pause heartbeat monitor +switch_callback(State = #v1{conserving_memory = true, + heartbeater = Heartbeater}, Callback, Length) -> + ok = rabbit_heartbeat:pause_monitor(Heartbeater), %% TODO: only do this after receiving a content-bearing method State#v1{callback = {Callback, Length}, recv_ref = none}; switch_callback(State, Callback, Length) -> @@ -372,9 +375,10 @@ terminate(_Explanation, State) -> {force, State}. internal_conserve_memory(false, State = #v1{conserving_memory = true, + heartbeater = Heartbeater, callback = {Callback, Length}, recv_ref = none}) -> - %% TODO: resume heartbeat monitor + ok = rabbit_heartbeat:resume_monitor(Heartbeater), switch_callback(State#v1{conserving_memory = false}, Callback, Length); internal_conserve_memory(Conserve, State) -> State#v1{conserving_memory = Conserve}. @@ -671,11 +675,13 @@ handle_method0(#'connection.tune_ok'{frame_max = FrameMax, not_allowed, "frame_max=~w > ~w max size", [FrameMax, ?FRAME_MAX]); true -> - rabbit_heartbeat:start_heartbeat(Sock, ClientHeartbeat), + Heartbeater = rabbit_heartbeat:start_heartbeat( + Sock, ClientHeartbeat), State#v1{connection_state = opening, connection = Connection#connection{ timeout_sec = ClientHeartbeat, - frame_max = FrameMax}} + frame_max = FrameMax}, + heartbeater = Heartbeater} end; handle_method0(#'connection.open'{virtual_host = VHostPath}, -- cgit v1.2.1 From cdecea3f168dcb083a22d1fa6d7b447a75f59688 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Mon, 2 Aug 2010 12:02:12 +0100 Subject: The crazy protocol header is the 0-8 spec's fault; adjust comment to not impugn py-amqp's good name (and document other headers) --- 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 f687f814..a8b2ae54 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -562,14 +562,19 @@ handle_input({frame_payload, Type, Channel, PayloadSize}, PayloadAndMarker, Stat handle_input(handshake, <<"AMQP", 0, 0, 9, 1>>, State) -> start_connection({0, 9, 1}, rabbit_framing_amqp_0_9_1, State); +%% This is the protocol header for 0-9, which we can safely treat as +%% though it were 0-9-1. handle_input(handshake, <<"AMQP", 1, 1, 0, 9>>, State) -> start_connection({0, 9, 0}, rabbit_framing_amqp_0_9_1, State); -%% the 0-8 spec, confusingly, defines the version as 8-0 +%% This is what most clients send for 0-8. The 0-8 spec, confusingly, +%% defines the version as 8-0. handle_input(handshake, <<"AMQP", 1, 1, 8, 0>>, State) -> start_connection({8, 0, 0}, rabbit_framing_amqp_0_8, State); -%% py-amqplib has always sent this broken version. It wants 0-8. +%% The 0-8 spec as on the AMQP web site actually has this as the +%% protocol header; some libraries e.g., py-amqplib, send it when they +%% want 0-8. handle_input(handshake, <<"AMQP", 1, 1, 9, 1>>, State) -> start_connection({8, 0, 0}, rabbit_framing_amqp_0_8, State); -- cgit v1.2.1 From c63c043677c34c7ed26dfb0eebfd7ece0bec5613 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 2 Aug 2010 14:02:25 +0100 Subject: only stop reading from clients that publish while memory is tight --- src/rabbit_reader.erl | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 2c1a24c4..f9dd75ad 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -357,10 +357,9 @@ mainloop(Parent, Deb, State = #v1{sock= Sock, recv_ref = Ref}) -> exit({unexpected_message, Other}) end. -switch_callback(State = #v1{conserving_memory = true, +switch_callback(State = #v1{conserving_memory = active, heartbeater = Heartbeater}, Callback, Length) -> ok = rabbit_heartbeat:pause_monitor(Heartbeater), - %% TODO: only do this after receiving a content-bearing method State#v1{callback = {Callback, Length}, recv_ref = none}; switch_callback(State, Callback, Length) -> Ref = inet_op(fun () -> rabbit_net:async_recv( @@ -374,7 +373,7 @@ terminate(Explanation, State = #v1{connection_state = running}) -> terminate(_Explanation, State) -> {force, State}. -internal_conserve_memory(false, State = #v1{conserving_memory = true, +internal_conserve_memory(false, State = #v1{conserving_memory = active, heartbeater = Heartbeater, callback = {Callback, Length}, recv_ref = none}) -> @@ -507,13 +506,20 @@ handle_frame(Type, Channel, Payload, %%?LOGDEBUG("Ch ~p Frame ~p~n", [Channel, AnalyzedFrame]), case get({channel, Channel}) of {chpid, ChPid} -> + ok = rabbit_framing_channel:process(ChPid, AnalyzedFrame), case AnalyzedFrame of {method, 'channel.close', _} -> - erase({channel, Channel}); - _ -> ok - end, - ok = rabbit_framing_channel:process(ChPid, AnalyzedFrame), - State; + erase({channel, Channel}), + State; + {method, MethodName, _} -> + case (State#v1.conserving_memory == true andalso + Protocol:method_has_content(MethodName)) of + true -> State#v1{conserving_memory = active}; + false -> State + end; + _ -> + State + end; closing -> %% According to the spec, after sending a %% channel.close we must ignore all frames except -- cgit v1.2.1 From d2d7e8dc326e31caddd2e3829b127dcd1fef8212 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 2 Aug 2010 14:41:06 +0100 Subject: fold the memory conservation status into the connection state --- src/rabbit_reader.erl | 57 +++++++++++++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index f9dd75ad..2efda4fb 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -58,7 +58,7 @@ %--------------------------------------------------------------------------- -record(v1, {sock, connection, callback, recv_ref, connection_state, - queue_collector, heartbeater, conserving_memory}). + queue_collector, heartbeater}). -define(INFO_KEYS, [pid, address, port, peer_address, peer_port, @@ -134,6 +134,11 @@ %% %% TODO: refactor the code so that the above is obvious +-define(IS_RUNNING(State), + (State#v1.connection_state =:= running orelse + State#v1.connection_state =:= blocking orelse + State#v1.connection_state =:= blocked)). + %%---------------------------------------------------------------------------- -ifdef(use_specs). @@ -260,8 +265,7 @@ start_connection(Parent, Deb, Sock, SockTransform) -> recv_ref = none, connection_state = pre_init, queue_collector = Collector, - heartbeater = none, - conserving_memory = false}, + heartbeater = none}, handshake, 8)) catch Ex -> (if Ex == connection_closed_abruptly -> @@ -325,7 +329,7 @@ mainloop(Parent, Deb, State = #v1{sock= Sock, recv_ref = Ref}) -> terminate_connection -> State; handshake_timeout -> - if State#v1.connection_state =:= running orelse + if ?IS_RUNNING(State) orelse State#v1.connection_state =:= closing orelse State#v1.connection_state =:= closed -> mainloop(Parent, Deb, State); @@ -357,7 +361,7 @@ mainloop(Parent, Deb, State = #v1{sock= Sock, recv_ref = Ref}) -> exit({unexpected_message, Other}) end. -switch_callback(State = #v1{conserving_memory = active, +switch_callback(State = #v1{connection_state = blocked, heartbeater = Heartbeater}, Callback, Length) -> ok = rabbit_heartbeat:pause_monitor(Heartbeater), State#v1{callback = {Callback, Length}, recv_ref = none}; @@ -366,21 +370,25 @@ switch_callback(State, Callback, Length) -> State#v1.sock, Length, infinity) end), State#v1{callback = Callback, recv_ref = Ref}. -terminate(Explanation, State = #v1{connection_state = running}) -> +terminate(Explanation, State) when ?IS_RUNNING(State) -> {normal, send_exception(State, 0, rabbit_misc:amqp_error( connection_forced, Explanation, [], none))}; terminate(_Explanation, State) -> {force, State}. -internal_conserve_memory(false, State = #v1{conserving_memory = active, +internal_conserve_memory(true, State = #v1{connection_state = running}) -> + State#v1{connection_state = blocking}; +internal_conserve_memory(false, State = #v1{connection_state = blocking}) -> + State#v1{connection_state = running}; +internal_conserve_memory(false, State = #v1{connection_state = blocked, heartbeater = Heartbeater, callback = {Callback, Length}, recv_ref = none}) -> ok = rabbit_heartbeat:resume_monitor(Heartbeater), - switch_callback(State#v1{conserving_memory = false}, Callback, Length); -internal_conserve_memory(Conserve, State) -> - State#v1{conserving_memory = Conserve}. + switch_callback(State#v1{connection_state = running}, Callback, Length); +internal_conserve_memory(_Conserve, State) -> + State. close_connection(State = #v1{connection = #connection{ timeout_sec = TimeoutSec}}) -> @@ -512,9 +520,9 @@ handle_frame(Type, Channel, Payload, erase({channel, Channel}), State; {method, MethodName, _} -> - case (State#v1.conserving_memory == true andalso + case (State#v1.connection_state == blocking andalso Protocol:method_has_content(MethodName)) of - true -> State#v1{conserving_memory = active}; + true -> State#v1{connection_state = blocked}; false -> State end; _ -> @@ -539,12 +547,13 @@ handle_frame(Type, Channel, Payload, end, State; undefined -> - case State#v1.connection_state of - running -> ok = send_to_new_channel( - Channel, AnalyzedFrame, State), - State; - Other -> throw({channel_frame_while_starting, - Channel, Other, AnalyzedFrame}) + case ?IS_RUNNING(State) of + true -> ok = send_to_new_channel( + Channel, AnalyzedFrame, State), + State; + false -> throw({channel_frame_while_starting, + Channel, State#v1.connection_state, + AnalyzedFrame}) end end end. @@ -641,13 +650,14 @@ handle_method0(MethodName, FieldsBin, Reason#amqp_error{method = MethodName}; OtherReason -> OtherReason end, - case State#v1.connection_state of - running -> send_exception(State, 0, CompleteReason); + case ?IS_RUNNING(State) of + true -> send_exception(State, 0, CompleteReason); %% We don't trust the client at this point - force %% them to wait for a bit so they can't DOS us with %% repeated failed logins etc. - Other -> timer:sleep(?SILENT_CLOSE_DELAY * 1000), - throw({channel0_error, Other, CompleteReason}) + false -> timer:sleep(?SILENT_CLOSE_DELAY * 1000), + throw({channel0_error, State#v1.connection_state, + CompleteReason}) end end. @@ -703,8 +713,7 @@ handle_method0(#'connection.open'{virtual_host = VHostPath}, rabbit_alarm:register(self(), {?MODULE, conserve_memory, []}), State#v1{connection_state = running, connection = NewConnection}; -handle_method0(#'connection.close'{}, - State = #v1{connection_state = running}) -> +handle_method0(#'connection.close'{}, State) when ?IS_RUNNING(State) -> lists:foreach(fun rabbit_framing_channel:shutdown/1, all_channels()), maybe_close(State#v1{connection_state = closing}); handle_method0(#'connection.close'{}, -- cgit v1.2.1 From 4e61804a04ea425becc0b89f37f4d36978157766 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 2 Aug 2010 14:51:02 +0100 Subject: don't overload meaning of 'callback' --- src/rabbit_reader.erl | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 2efda4fb..2a1ae34e 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -57,8 +57,8 @@ %--------------------------------------------------------------------------- --record(v1, {sock, connection, callback, recv_ref, connection_state, - queue_collector, heartbeater}). +-record(v1, {sock, connection, callback, recv_length, recv_ref, + connection_state, queue_collector, heartbeater}). -define(INFO_KEYS, [pid, address, port, peer_address, peer_port, @@ -262,6 +262,7 @@ start_connection(Parent, Deb, Sock, SockTransform) -> client_properties = none, protocol = none}, callback = uninitialized_callback, + recv_length = 0, recv_ref = none, connection_state = pre_init, queue_collector = Collector, @@ -364,11 +365,11 @@ mainloop(Parent, Deb, State = #v1{sock= Sock, recv_ref = Ref}) -> switch_callback(State = #v1{connection_state = blocked, heartbeater = Heartbeater}, Callback, Length) -> ok = rabbit_heartbeat:pause_monitor(Heartbeater), - State#v1{callback = {Callback, Length}, recv_ref = none}; + State#v1{callback = Callback, recv_length = Length, recv_ref = none}; switch_callback(State, Callback, Length) -> Ref = inet_op(fun () -> rabbit_net:async_recv( State#v1.sock, Length, infinity) end), - State#v1{callback = Callback, recv_ref = Ref}. + State#v1{callback = Callback, recv_length = Length, recv_ref = Ref}. terminate(Explanation, State) when ?IS_RUNNING(State) -> {normal, send_exception(State, 0, @@ -382,9 +383,10 @@ internal_conserve_memory(true, State = #v1{connection_state = running}) -> internal_conserve_memory(false, State = #v1{connection_state = blocking}) -> State#v1{connection_state = running}; internal_conserve_memory(false, State = #v1{connection_state = blocked, - heartbeater = Heartbeater, - callback = {Callback, Length}, - recv_ref = none}) -> + heartbeater = Heartbeater, + callback = Callback, + recv_length = Length, + recv_ref = none}) -> ok = rabbit_heartbeat:resume_monitor(Heartbeater), switch_callback(State#v1{connection_state = running}, Callback, Length); internal_conserve_memory(_Conserve, State) -> -- cgit v1.2.1 From 25040ea8fb2b3a2348e29bf0a522540b9a2d6b05 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 2 Aug 2010 15:05:22 +0100 Subject: update reader state docs --- src/rabbit_reader.erl | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 2a1ae34e..d6c15170 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -101,6 +101,17 @@ %% -> log error, mark channel as closing, *running* %% handshake_timeout -> ignore, *running* %% heartbeat timeout -> *throw* +%% conserve_memory=true -> *blocking* +%% blocking: +%% conserve_memory=true -> *blocking* +%% conserve_memory=false -> *running* +%% receive a method frame for a content-bearing method +%% -> process, stop receiving, *blocked* +%% ...rest same as 'running' +%% blocked: +%% conserve_memory=true -> *blocked* +%% conserve_memory=false -> resume receiving, *running* +%% ...rest same as 'running' %% closing: %% socket close -> *terminate* %% receive connection.close -> send connection.close_ok, -- cgit v1.2.1 From a211fd73ce5b7a2fdc7129cc3b341faf55de64c5 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 2 Aug 2010 16:56:21 +0100 Subject: Attempt to improve specs. --- src/rabbit_event.erl | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/rabbit_event.erl b/src/rabbit_event.erl index e2d0094d..e9406c12 100644 --- a/src/rabbit_event.erl +++ b/src/rabbit_event.erl @@ -38,17 +38,27 @@ -export([stats_level/1]). -export([notify/2]). --opaque(state() :: {atom(), atom()}). +-ifdef(use_specs). + +-type(level() :: 'none' | 'coarse' | 'fine'). + +-opaque(state() :: #state { + level :: level(), + timer :: atom() + }). + +-type(timer_fun() :: fun (() -> 'ok')). -spec(init_stats_timer/0 :: () -> state()). --spec(ensure_stats_timer/3 :: - (state(), fun (() -> 'ok'), fun (() -> 'ok')) -> state()). --spec(stop_stats_timer/2 :: (state(), fun (() -> 'ok')) -> state()). --spec(ensure_stats_timer_after/2 :: (state(), fun (() -> 'ok')) -> state()). +-spec(ensure_stats_timer/3 :: (state(), timer_fun(), timer_fun()) -> state()). +-spec(stop_stats_timer/2 :: (state(), timer_fun()) -> state()). +-spec(ensure_stats_timer_after/2 :: (state(), timer_fun()) -> state()). -spec(reset_stats_timer_after/1 :: (state()) -> 'ok'). --spec(stats_level/1 :: (state()) -> atom()). +-spec(stats_level/1 :: (state()) -> level()). -spec(notify/2 :: (atom(), term()) -> 'ok'). +-endif. + -record(state, {level, timer}). %%---------------------------------------------------------------------------- -- cgit v1.2.1 From b9bc0e250c3bae6611b84a65b4bf9bb50ac0a32d Mon Sep 17 00:00:00 2001 From: Matthew Sackman Date: Mon, 2 Aug 2010 17:21:27 +0100 Subject: Corrections to specs and export suitable event specs --- src/rabbit_event.erl | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/rabbit_event.erl b/src/rabbit_event.erl index e9406c12..709709a3 100644 --- a/src/rabbit_event.erl +++ b/src/rabbit_event.erl @@ -38,8 +38,27 @@ -export([stats_level/1]). -export([notify/2]). +%%---------------------------------------------------------------------------- + +-record(state, {level, timer}). + +%%---------------------------------------------------------------------------- + -ifdef(use_specs). +-export_type([event_type/0, event_props/0, event_timestamp/0, event/0]). + +-type(event_type() :: atom()). +-type(event_props() :: term()). +-type(event_timestamp() :: + {non_neg_integer(), non_neg_integer(), non_neg_integer()}). + +-type(event() :: #event { + type :: event_type(), + props :: event_props(), + timestamp :: event_timestamp() + }). + -type(level() :: 'none' | 'coarse' | 'fine'). -opaque(state() :: #state { @@ -53,14 +72,12 @@ -spec(ensure_stats_timer/3 :: (state(), timer_fun(), timer_fun()) -> state()). -spec(stop_stats_timer/2 :: (state(), timer_fun()) -> state()). -spec(ensure_stats_timer_after/2 :: (state(), timer_fun()) -> state()). --spec(reset_stats_timer_after/1 :: (state()) -> 'ok'). +-spec(reset_stats_timer_after/1 :: (state()) -> state()). -spec(stats_level/1 :: (state()) -> level()). --spec(notify/2 :: (atom(), term()) -> 'ok'). +-spec(notify/2 :: (event_type(), event_props()) -> 'ok'). -endif. --record(state, {level, timer}). - %%---------------------------------------------------------------------------- init_stats_timer() -> -- cgit v1.2.1 From 044dc5748dc047e2a1686c709472547f6cf03da8 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 2 Aug 2010 17:23:57 +0100 Subject: add Makefile targets to simulate memory pressure to be used in tests --- Makefile | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Makefile b/Makefile index a97838cc..1f15921b 100644 --- a/Makefile +++ b/Makefile @@ -179,6 +179,14 @@ stop-rabbit-on-node: all 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) + +clear-memory-alarm: all + echo "alarm_handler:clear_alarm(vm_memory_high_watermark)." | \ + $(ERL_CALL) + stop-node: -$(ERL_CALL) -q -- cgit v1.2.1 From 5ae7d968ef83183ec4f36ebec657eb5f2141b6ff Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 2 Aug 2010 17:53:04 +0100 Subject: remove dead code --- src/rabbit_tests.erl | 123 --------------------------------------------------- 1 file changed, 123 deletions(-) diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index 56aca1d6..bd243de4 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -67,7 +67,6 @@ all_tests() -> passed = test_log_management(), passed = test_app_management(), passed = test_log_management_during_startup(), - passed = test_memory_pressure(), passed = test_cluster_management(), passed = test_user_management(), passed = test_server_status(), @@ -1032,53 +1031,6 @@ test_hooks() -> end, passed. -test_memory_pressure_receiver(Pid) -> - receive - shutdown -> - ok; - {send_command, Method} -> - ok = case Method of - #'channel.flow'{} -> ok; - #'basic.qos_ok'{} -> ok; - #'channel.open_ok'{} -> ok - end, - Pid ! Method, - test_memory_pressure_receiver(Pid); - sync -> - Pid ! sync, - test_memory_pressure_receiver(Pid) - end. - -test_memory_pressure_receive_flow(Active) -> - receive #'channel.flow'{active = Active} -> ok - after 1000 -> throw(failed_to_receive_channel_flow) - end, - receive #'channel.flow'{} -> - throw(pipelining_sync_commands_detected) - after 0 -> - ok - end. - -test_memory_pressure_sync(Ch, Writer) -> - ok = rabbit_channel:do(Ch, #'basic.qos'{}), - Writer ! sync, - receive sync -> ok after 1000 -> throw(failed_to_receive_writer_sync) end, - receive #'basic.qos_ok'{} -> ok - after 1000 -> throw(failed_to_receive_basic_qos_ok) - end. - -test_memory_pressure_spawn() -> - Me = self(), - Writer = spawn(fun () -> test_memory_pressure_receiver(Me) end), - {ok, Ch} = rabbit_channel:start_link(1, self(), Writer, - <<"user">>, <<"/">>, self()), - ok = rabbit_channel:do(Ch, #'channel.open'{}), - MRef = erlang:monitor(process, Ch), - receive #'channel.open_ok'{} -> ok - after 1000 -> throw(failed_to_receive_channel_open_ok) - end, - {Writer, Ch, MRef}. - expect_normal_channel_termination(MRef, Ch) -> receive {'DOWN', MRef, process, Ch, normal} -> ok after 1000 -> throw(channel_failed_to_exit) @@ -1089,81 +1041,6 @@ gobble_channel_exit() -> after 1000 -> throw(channel_exit_not_received) end. -test_memory_pressure() -> - {Writer0, Ch0, MRef0} = test_memory_pressure_spawn(), - [ok = rabbit_channel:conserve_memory(Ch0, Conserve) || - Conserve <- [false, false, true, false, true, true, false]], - ok = test_memory_pressure_sync(Ch0, Writer0), - receive {'DOWN', MRef0, process, Ch0, Info0} -> - throw({channel_died_early, Info0}) - after 0 -> ok - end, - - %% we should have just 1 active=false waiting for us - ok = test_memory_pressure_receive_flow(false), - - %% if we reply with flow_ok, we should immediately get an - %% active=true back - ok = rabbit_channel:do(Ch0, #'channel.flow_ok'{active = false}), - ok = test_memory_pressure_receive_flow(true), - - %% if we publish at this point, the channel should die - Content = rabbit_basic:build_content(#'P_basic'{}, <<>>), - ok = rabbit_channel:do(Ch0, #'basic.publish'{}, Content), - expect_normal_channel_termination(MRef0, Ch0), - gobble_channel_exit(), - - {Writer1, Ch1, MRef1} = test_memory_pressure_spawn(), - ok = rabbit_channel:conserve_memory(Ch1, true), - ok = test_memory_pressure_receive_flow(false), - ok = rabbit_channel:do(Ch1, #'channel.flow_ok'{active = false}), - ok = test_memory_pressure_sync(Ch1, Writer1), - ok = rabbit_channel:conserve_memory(Ch1, false), - ok = test_memory_pressure_receive_flow(true), - %% send back the wrong flow_ok. Channel should die. - ok = rabbit_channel:do(Ch1, #'channel.flow_ok'{active = false}), - expect_normal_channel_termination(MRef1, Ch1), - gobble_channel_exit(), - - {_Writer2, Ch2, MRef2} = test_memory_pressure_spawn(), - %% just out of the blue, send a flow_ok. Life should end. - ok = rabbit_channel:do(Ch2, #'channel.flow_ok'{active = true}), - expect_normal_channel_termination(MRef2, Ch2), - gobble_channel_exit(), - - {_Writer3, Ch3, MRef3} = test_memory_pressure_spawn(), - ok = rabbit_channel:conserve_memory(Ch3, true), - ok = test_memory_pressure_receive_flow(false), - receive {'DOWN', MRef3, process, Ch3, _} -> - ok - after 12000 -> - throw(channel_failed_to_exit) - end, - gobble_channel_exit(), - - alarm_handler:set_alarm({vm_memory_high_watermark, []}), - Me = self(), - Writer4 = spawn(fun () -> test_memory_pressure_receiver(Me) end), - {ok, Ch4} = rabbit_channel:start_link(1, self(), Writer4, - <<"user">>, <<"/">>, self()), - ok = rabbit_channel:do(Ch4, #'channel.open'{}), - MRef4 = erlang:monitor(process, Ch4), - Writer4 ! sync, - receive sync -> ok after 1000 -> throw(failed_to_receive_writer_sync) end, - receive #'channel.open_ok'{} -> throw(unexpected_channel_open_ok) - after 0 -> ok - end, - alarm_handler:clear_alarm(vm_memory_high_watermark), - Writer4 ! sync, - receive sync -> ok after 1000 -> throw(failed_to_receive_writer_sync) end, - receive #'channel.open_ok'{} -> ok - after 1000 -> throw(failed_to_receive_channel_open_ok) - end, - rabbit_channel:shutdown(Ch4), - expect_normal_channel_termination(MRef4, Ch4), - - passed. - test_delegates_async(SecondaryNode) -> Self = self(), Sender = fun (Pid) -> Pid ! {invoked, Self} end, -- cgit v1.2.1 From 12b389470d9d53a282ff8cf66100b79d9841ade5 Mon Sep 17 00:00:00 2001 From: Alexandru Scvortov Date: Mon, 2 Aug 2010 17:59:24 +0100 Subject: homemade opt parser It's either this or ~400 lines of code from github under a non-standard license. The idea here is that you specify options with tuples like: - {flag, }, or - {option,