@@ -72,7 +72,10 @@ groups() ->
7272 bind_destination_line_feed ,
7373 bind_missing_queue ,
7474 exclusive_queue ,
75- purge_stream
75+ purge_stream ,
76+ pipeline ,
77+ multiple_link_pairs ,
78+ link_attach_order
7679 ]},
7780 {cluster_size_3 , [shuffle ],
7881 [classic_queue_stopped ,
@@ -786,6 +789,135 @@ queue_topology(Config) ->
786789 {ok , _ } = rabbitmq_amqp_client :delete_queue (LinkPair2 , SQName ),
787790 ok = cleanup (Init2 ).
788791
792+ % % Even though RabbitMQ processes management requests synchronously (one at a time),
793+ % % the client should be able to send multiple requests at once before receiving a response.
794+ pipeline (Config ) ->
795+ Init = {_ , _ , LinkPair } = init (Config ),
796+ flush (attached ),
797+
798+ % % We should be able to send 8 management requests at once
799+ % % because RabbitMQ grants us 8 link credits initially.
800+ Num = 8 ,
801+ pipeline0 (Num , LinkPair , <<" PUT" >>, {map , []}),
802+ eventually (? _assertEqual (Num , rpc (Config , rabbit_amqqueue , count , [])), 200 , 20 ),
803+ flush (queues_created ),
804+
805+ pipeline0 (Num , LinkPair , <<" DELETE" >>, null ),
806+ eventually (? _assertEqual (0 , rpc (Config , rabbit_amqqueue , count , [])), 200 , 20 ),
807+ flush (queues_deleted ),
808+
809+ ok = cleanup (Init ).
810+
811+ pipeline0 (Num ,
812+ # link_pair {outgoing_link = OutgoingLink ,
813+ incoming_link = IncomingLink },
814+ HttpMethod ,
815+ Body ) ->
816+ ok = amqp10_client :flow_link_credit (IncomingLink , Num , never ),
817+ [begin
818+ Request0 = amqp10_msg :new (<<>>, # 'v1_0.amqp_value' {content = Body }, true ),
819+ Bin = integer_to_binary (N ),
820+ Props = #{subject => HttpMethod ,
821+ to => <<" /queues/q-" , Bin /binary >>,
822+ message_id => {binary , Bin },
823+ reply_to => <<" $me" >>},
824+ Request = amqp10_msg :set_properties (Props , Request0 ),
825+ ok = amqp10_client :send_msg (OutgoingLink , Request )
826+ end || N <- lists :seq (1 , Num )].
827+
828+ % % RabbitMQ allows attaching multiple link pairs.
829+ multiple_link_pairs (Config ) ->
830+ OpnConf = connection_config (Config ),
831+ {ok , Connection } = amqp10_client :open_connection (OpnConf ),
832+ {ok , Session } = amqp10_client :begin_session_sync (Connection ),
833+ {ok , LinkPair1 } = rabbitmq_amqp_client :attach_management_link_pair_sync (Session , <<" link pair 1" >>),
834+ {ok , LinkPair2 } = rabbitmq_amqp_client :attach_management_link_pair_sync (Session , <<" link pair 2" >>),
835+
836+ [SessionPid ] = rpc (Config , rabbit_amqp_session , list_local , []),
837+ #{management_link_pairs := Pairs0 ,
838+ incoming_management_links := Incoming0 ,
839+ outgoing_management_links := Outgoing0 } = gen_server_state (SessionPid ),
840+ ? assertEqual (2 , maps :size (Pairs0 )),
841+ ? assertEqual (2 , maps :size (Incoming0 )),
842+ ? assertEqual (2 , maps :size (Outgoing0 )),
843+
844+ QName = <<" q" >>,
845+ {ok , #{}} = rabbitmq_amqp_client :declare_queue (LinkPair1 , QName , #{}),
846+ {ok , #{}} = rabbitmq_amqp_client :delete_queue (LinkPair2 , QName ),
847+
848+ ok = rabbitmq_amqp_client :detach_management_link_pair_sync (LinkPair1 ),
849+ ok = rabbitmq_amqp_client :detach_management_link_pair_sync (LinkPair2 ),
850+
851+ % % Assert that the server cleaned up its state.
852+ #{management_link_pairs := Pairs ,
853+ incoming_management_links := Incoming ,
854+ outgoing_management_links := Outgoing } = gen_server_state (SessionPid ),
855+ ? assertEqual (0 , maps :size (Pairs )),
856+ ? assertEqual (0 , maps :size (Incoming )),
857+ ? assertEqual (0 , maps :size (Outgoing )),
858+
859+ ok = amqp10_client :end_session (Session ),
860+ ok = amqp10_client :close_connection (Connection ).
861+
862+ % % Attaching (and detaching) either the sender or the receiver link first should both work.
863+ link_attach_order (Config ) ->
864+ PairName1 = <<" link pair 1" >>,
865+ PairName2 = <<" link pair 2" >>,
866+
867+ OpnConf = connection_config (Config ),
868+ {ok , Connection } = amqp10_client :open_connection (OpnConf ),
869+ {ok , Session } = amqp10_client :begin_session_sync (Connection ),
870+
871+ Terminus = #{address => <<" /management" >>,
872+ durable => none },
873+ OutgoingAttachArgs1 = #{name => PairName1 ,
874+ role => {sender , Terminus },
875+ snd_settle_mode => settled ,
876+ rcv_settle_mode => first ,
877+ properties => #{<<" paired" >> => true }},
878+ IncomingAttachArgs1 = OutgoingAttachArgs1 #{role := {receiver , Terminus , self ()},
879+ filter => #{}},
880+ OutgoingAttachArgs2 = OutgoingAttachArgs1 #{name := PairName2 },
881+ IncomingAttachArgs2 = IncomingAttachArgs1 #{name := PairName2 },
882+
883+ % % Attach sender before receiver.
884+ {ok , OutgoingRef1 } = amqp10_client :attach_link (Session , OutgoingAttachArgs1 ),
885+ {ok , IncomingRef1 } = amqp10_client :attach_link (Session , IncomingAttachArgs1 ),
886+ % % Attach receiver before sender.
887+ {ok , IncomingRef2 } = amqp10_client :attach_link (Session , IncomingAttachArgs2 ),
888+ {ok , OutgoingRef2 } = amqp10_client :attach_link (Session , OutgoingAttachArgs2 ),
889+
890+ Refs = [OutgoingRef1 ,
891+ OutgoingRef2 ,
892+ IncomingRef1 ,
893+ IncomingRef2 ],
894+
895+ [ok = wait_for_event (Ref , attached ) || Ref <- Refs ],
896+ flush (attached ),
897+
898+ LinkPair1 = # link_pair {session = Session ,
899+ outgoing_link = OutgoingRef1 ,
900+ incoming_link = IncomingRef1 },
901+ LinkPair2 = # link_pair {session = Session ,
902+ outgoing_link = OutgoingRef2 ,
903+ incoming_link = IncomingRef2 },
904+
905+ QName = <<" test queue" >>,
906+ {ok , #{}} = rabbitmq_amqp_client :declare_queue (LinkPair1 , QName , #{}),
907+ {ok , #{}} = rabbitmq_amqp_client :delete_queue (LinkPair2 , QName ),
908+
909+ % % Detach sender before receiver.
910+ ok = amqp10_client :detach_link (OutgoingRef1 ),
911+ ok = amqp10_client :detach_link (IncomingRef1 ),
912+ % % Detach receiver before sender.
913+ ok = amqp10_client :detach_link (IncomingRef2 ),
914+ ok = amqp10_client :detach_link (OutgoingRef2 ),
915+
916+ [ok = wait_for_event (Ref , {detached , normal }) || Ref <- Refs ],
917+ flush (detached ),
918+ ok = amqp10_client :end_session (Session ),
919+ ok = amqp10_client :close_connection (Connection ).
920+
789921init (Config ) ->
790922 init (Config , 0 ).
791923
@@ -846,3 +978,16 @@ wait_for_settlement(Tag, State) ->
846978 flush (Reason ),
847979 ct :fail (Reason )
848980 end .
981+
982+ wait_for_event (Ref , Event ) ->
983+ receive {amqp10_event , {link , Ref , Event }} -> ok
984+ after 5000 -> ct :fail ({missing_event , Ref , Event })
985+ end .
986+
987+ % % Return the formatted state of a gen_server via sys:get_status/1.
988+ % % (sys:get_state/1 is unformatted)
989+ gen_server_state (Pid ) ->
990+ {status , _ , _ , L0 } = sys :get_status (Pid , 20_000 ),
991+ L1 = lists :last (L0 ),
992+ {data , L2 } = lists :last (L1 ),
993+ proplists :get_value (" State" , L2 ).
0 commit comments