@@ -72,7 +72,10 @@ groups() ->
72
72
bind_destination_line_feed ,
73
73
bind_missing_queue ,
74
74
exclusive_queue ,
75
- purge_stream
75
+ purge_stream ,
76
+ pipeline ,
77
+ multiple_link_pairs ,
78
+ link_attach_order
76
79
]},
77
80
{cluster_size_3 , [shuffle ],
78
81
[classic_queue_stopped ,
@@ -786,6 +789,135 @@ queue_topology(Config) ->
786
789
{ok , _ } = rabbitmq_amqp_client :delete_queue (LinkPair2 , SQName ),
787
790
ok = cleanup (Init2 ).
788
791
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
+
789
921
init (Config ) ->
790
922
init (Config , 0 ).
791
923
@@ -846,3 +978,16 @@ wait_for_settlement(Tag, State) ->
846
978
flush (Reason ),
847
979
ct :fail (Reason )
848
980
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