@@ -81,7 +81,8 @@ groups() ->
81
81
resource_alarm_after_session_begin ,
82
82
max_message_size_client_to_server ,
83
83
max_message_size_server_to_client ,
84
- receive_transfer_flow_order
84
+ receive_transfer_flow_order ,
85
+ stream_filtering
85
86
]},
86
87
87
88
{cluster_size_3 , [shuffle ],
@@ -2430,6 +2431,154 @@ queue_and_client_different_nodes(QueueLeaderNode, ClientNode, QueueType, Config)
2430
2431
ok = rabbit_ct_client_helpers :close_channel (Ch ),
2431
2432
ok = amqp10_client :close_connection (Connection ).
2432
2433
2434
+
2435
+ stream_filtering (Config ) ->
2436
+ Host = ? config (rmq_hostname , Config ),
2437
+ Port = rabbit_ct_broker_helpers :get_node_config (Config , 0 , tcp_port_amqp ),
2438
+ Stream = list_to_binary (atom_to_list (? FUNCTION_NAME ) ++ " -" ++ integer_to_list (rand :uniform (10000 ))),
2439
+ Address = <<" /amq/queue/" , Stream /binary >>,
2440
+ Ch = rabbit_ct_client_helpers :open_channel (Config , 0 ),
2441
+ Args = [{<<" x-queue-type" >>, longstr , <<" stream" >>}],
2442
+ amqp_channel :call (Ch , # 'queue.declare' {queue = Stream ,
2443
+ durable = true ,
2444
+ arguments = Args }),
2445
+
2446
+ % % we are going to publish several waves of messages with and without filter values.
2447
+ % % we will then create subscriptions with various filter options
2448
+ % % and make sure we receive only what we asked for and not all the messages.
2449
+
2450
+ WaveCount = 1000 ,
2451
+ OpnConf = #{address => Host ,
2452
+ port => Port ,
2453
+ transfer_limit_margin => 10 * WaveCount ,
2454
+ container_id => atom_to_binary (? FUNCTION_NAME , utf8 ),
2455
+ sasl => {plain , <<" guest" >>, <<" guest" >>}},
2456
+
2457
+ {ok , Connection } = amqp10_client :open_connection (OpnConf ),
2458
+ {ok , Session } = amqp10_client :begin_session (Connection ),
2459
+ SenderLinkName = <<" test-sender" >>,
2460
+ {ok , Sender } = amqp10_client :attach_sender_link (Session ,
2461
+ SenderLinkName ,
2462
+ Address ),
2463
+
2464
+ wait_for_credit (Sender ),
2465
+
2466
+ % % logic to publish a wave of messages with or without a filter value
2467
+ Publish = fun (FilterValue ) ->
2468
+ lists :foreach (fun (Seq ) ->
2469
+ {AppProps , Anns } =
2470
+ case FilterValue of
2471
+ undefined ->
2472
+ {#{}, #{}};
2473
+ _ ->
2474
+ {#{<<" filter" >> => FilterValue },
2475
+ #{<<" x-stream-filter-value" >> => FilterValue }}
2476
+ end ,
2477
+ FilterBin = rabbit_data_coercion :to_binary (FilterValue ),
2478
+ SeqBin = rabbit_data_coercion :to_binary (Seq ),
2479
+ DTag = <<FilterBin /binary , SeqBin /binary >>,
2480
+ Msg0 = amqp10_msg :new (DTag , <<" my-body" >>, false ),
2481
+ Msg1 = amqp10_msg :set_application_properties (AppProps , Msg0 ),
2482
+ Msg2 = amqp10_msg :set_message_annotations (Anns , Msg1 ),
2483
+ ok = amqp10_client :send_msg (Sender , Msg2 ),
2484
+ ok = wait_for_settlement (DTag )
2485
+ end , lists :seq (1 , WaveCount ))
2486
+ end ,
2487
+
2488
+ % % publishing messages with the "apple" filter value
2489
+ Publish (" apple" ),
2490
+ % % publishing messages with no filter value
2491
+ Publish (undefined ),
2492
+ % % publishing messages with the "orange" filter value
2493
+ Publish (" orange" ),
2494
+ ok = amqp10_client :detach_link (Sender ),
2495
+
2496
+ % filtering on "apple"
2497
+ TerminusDurability = none ,
2498
+ Properties = #{},
2499
+ {ok , AppleReceiver } =
2500
+ amqp10_client :attach_receiver_link (Session , <<" test-receiver" >>,
2501
+ Address , settled ,
2502
+ TerminusDurability ,
2503
+ #{<<" rabbitmq:stream-offset-spec" >> => <<" first" >>,
2504
+ <<" rabbitmq:stream-filter" >> => <<" apple" >>},
2505
+ Properties ),
2506
+ ok = amqp10_client :flow_link_credit (AppleReceiver , 100 , 10 ),
2507
+
2508
+ AppleMessages = receive_all_messages (AppleReceiver , []),
2509
+ % % we should get less than all the waves combined
2510
+ ? assert (length (AppleMessages ) < WaveCount * 3 ),
2511
+ % % client-side filtering
2512
+ AppleFilteredMessages =
2513
+ lists :filter (fun (Msg ) ->
2514
+ maps :get (<<" filter" >>, amqp10_msg :application_properties (Msg )) =:= <<" apple" >>
2515
+ end , AppleMessages ),
2516
+ ? assert (length (AppleFilteredMessages ) =:= WaveCount ),
2517
+ ok = amqp10_client :detach_link (AppleReceiver ),
2518
+
2519
+ % % filtering on "apple" and "orange"
2520
+ TerminusDurability = none ,
2521
+ Properties = #{},
2522
+ {ok , AppleOrangeReceiver } =
2523
+ amqp10_client :attach_receiver_link (Session , <<" test-receiver" >>,
2524
+ Address , settled ,
2525
+ TerminusDurability ,
2526
+ #{<<" rabbitmq:stream-offset-spec" >> => <<" first" >>,
2527
+ <<" rabbitmq:stream-filter" >> => [<<" apple" >>, <<" orange" >>]},
2528
+ Properties ),
2529
+ ok = amqp10_client :flow_link_credit (AppleOrangeReceiver , 100 , 10 ),
2530
+
2531
+ AppleOrangeMessages = receive_all_messages (AppleOrangeReceiver , []),
2532
+ % % we should get less than all the waves combined
2533
+ ? assert (length (AppleOrangeMessages ) < WaveCount * 3 ),
2534
+ % % client-side filtering
2535
+ AppleOrangeFilteredMessages =
2536
+ lists :filter (fun (Msg ) ->
2537
+ AP = amqp10_msg :application_properties (Msg ),
2538
+ maps :get (<<" filter" >>, AP ) =:= <<" apple" >> orelse
2539
+ maps :get (<<" filter" >>, AP ) =:= <<" orange" >>
2540
+ end , AppleOrangeMessages ),
2541
+ ? assert (length (AppleOrangeFilteredMessages ) =:= WaveCount * 2 ),
2542
+ ok = amqp10_client :detach_link (AppleOrangeReceiver ),
2543
+
2544
+ % % filtering on "apple" and messages without a filter value
2545
+ {ok , AppleUnfilteredReceiver } =
2546
+ amqp10_client :attach_receiver_link (Session , <<" test-receiver" >>,
2547
+ Address , settled ,
2548
+ TerminusDurability ,
2549
+ #{<<" rabbitmq:stream-offset-spec" >> => <<" first" >>,
2550
+ <<" rabbitmq:stream-filter" >> => <<" apple" >>,
2551
+ <<" rabbitmq:stream-match-unfiltered" >> => {boolean , true }},
2552
+ Properties ),
2553
+ ok = amqp10_client :flow_link_credit (AppleUnfilteredReceiver , 100 , 10 ),
2554
+
2555
+ AppleUnfilteredMessages = receive_all_messages (AppleUnfilteredReceiver , []),
2556
+ % % we should get less than all the waves combined
2557
+ ? assert (length (AppleUnfilteredMessages ) < WaveCount * 3 ),
2558
+ % % client-side filtering
2559
+ AppleUnfilteredFilteredMessages =
2560
+ lists :filter (fun (Msg ) ->
2561
+ AP = amqp10_msg :application_properties (Msg ),
2562
+ maps :is_key (<<" filter" >>, AP ) =:= false orelse
2563
+ maps :get (<<" filter" >>, AP ) =:= <<" apple" >>
2564
+ end , AppleUnfilteredMessages ),
2565
+ ? assert (length (AppleUnfilteredFilteredMessages ) =:= WaveCount * 2 ),
2566
+ ok = amqp10_client :detach_link (AppleUnfilteredReceiver ),
2567
+
2568
+ delete_queue (Config , Stream ),
2569
+ ok = amqp10_client :close_connection (Connection ),
2570
+ ok .
2571
+
2572
+ receive_all_messages (Receiver , Acc ) ->
2573
+ receive
2574
+ {amqp10_msg , Receiver , InMsg } ->
2575
+ ok = amqp10_client :accept_msg (Receiver , InMsg ),
2576
+ receive_all_messages (Receiver , [InMsg ] ++ Acc )
2577
+ after 1000 ->
2578
+ Acc
2579
+ end .
2580
+
2581
+
2433
2582
% % internal
2434
2583
% %
2435
2584
0 commit comments