| 
									
										
										
										
											2020-07-10 21:31:17 +08:00
										 |  |  | %% This Source Code Form is subject to the terms of the Mozilla Public
 | 
					
						
							|  |  |  | %% License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
					
						
							|  |  |  | %% file, You can obtain one at https://mozilla.org/MPL/2.0/.
 | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  | %%
 | 
					
						
							| 
									
										
										
										
											2021-01-22 14:00:14 +08:00
										 |  |  | %% Copyright (c) 2011-2021 VMware, Inc. or its affiliates.  All rights reserved.
 | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  | %%
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  | -module(backing_queue_SUITE). | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | -include_lib("common_test/include/ct.hrl"). | 
					
						
							|  |  |  | -include_lib("amqp_client/include/amqp_client.hrl"). | 
					
						
							| 
									
										
										
										
											2018-10-11 18:12:39 +08:00
										 |  |  | -include("amqqueue.hrl"). | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | -compile(export_all). | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-23 02:30:08 +08:00
										 |  |  | -define(PERSISTENT_MSG_STORE, msg_store_persistent). | 
					
						
							|  |  |  | -define(TRANSIENT_MSG_STORE,  msg_store_transient). | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-28 17:11:36 +08:00
										 |  |  | -define(INDEX, rabbit_classic_queue_index_v2). | 
					
						
							| 
									
										
										
										
											2021-04-09 21:01:28 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  | -define(TIMEOUT, 30000). | 
					
						
							| 
									
										
										
										
											2017-03-23 02:30:08 +08:00
										 |  |  | -define(VHOST, <<"/">>). | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | -define(VARIABLE_QUEUE_TESTCASES, [ | 
					
						
							|  |  |  |     variable_queue_dynamic_duration_change, | 
					
						
							|  |  |  |     variable_queue_partial_segments_delta_thing, | 
					
						
							|  |  |  |     variable_queue_all_the_bits_not_covered_elsewhere_A, | 
					
						
							|  |  |  |     variable_queue_all_the_bits_not_covered_elsewhere_B, | 
					
						
							|  |  |  |     variable_queue_drop, | 
					
						
							|  |  |  |     variable_queue_fold_msg_on_disk, | 
					
						
							|  |  |  |     variable_queue_dropfetchwhile, | 
					
						
							|  |  |  |     variable_queue_dropwhile_varying_ram_duration, | 
					
						
							| 
									
										
										
										
											2021-08-10 23:10:00 +08:00
										 |  |  |     variable_queue_dropwhile_restart, | 
					
						
							|  |  |  |     variable_queue_dropwhile_sync_restart, | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |     variable_queue_fetchwhile_varying_ram_duration, | 
					
						
							|  |  |  |     variable_queue_ack_limiting, | 
					
						
							|  |  |  |     variable_queue_purge, | 
					
						
							|  |  |  |     variable_queue_requeue, | 
					
						
							|  |  |  |     variable_queue_requeue_ram_beta, | 
					
						
							| 
									
										
										
										
											2020-04-28 03:38:58 +08:00
										 |  |  |     variable_queue_fold, | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |     variable_queue_batch_publish, | 
					
						
							|  |  |  |     variable_queue_batch_publish_delivered | 
					
						
							|  |  |  |   ]). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | -define(BACKING_QUEUE_TESTCASES, [ | 
					
						
							| 
									
										
										
										
											2021-05-19 21:03:45 +08:00
										 |  |  |     msg_store, | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |     bq_queue_index, | 
					
						
							|  |  |  |     bq_queue_index_props, | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  |     {variable_queue_default, [parallel], ?VARIABLE_QUEUE_TESTCASES}, | 
					
						
							|  |  |  |     {variable_queue_lazy, [parallel], ?VARIABLE_QUEUE_TESTCASES ++ | 
					
						
							| 
									
										
										
										
											2017-03-09 16:55:15 +08:00
										 |  |  |                                       [variable_queue_mode_change]}, | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |     bq_variable_queue_delete_msg_store_files_callback, | 
					
						
							|  |  |  |     bq_queue_recover | 
					
						
							|  |  |  |   ]). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | all() -> | 
					
						
							|  |  |  |     [ | 
					
						
							|  |  |  |       {group, backing_queue_tests} | 
					
						
							|  |  |  |     ]. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | groups() -> | 
					
						
							|  |  |  |     [ | 
					
						
							| 
									
										
										
										
											2021-05-19 21:03:45 +08:00
										 |  |  |      {backing_queue_tests, [], ?BACKING_QUEUE_TESTCASES} | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |     ]. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | %% -------------------------------------------------------------------
 | 
					
						
							|  |  |  | %% Testsuite setup/teardown.
 | 
					
						
							|  |  |  | %% -------------------------------------------------------------------
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | init_per_suite(Config) -> | 
					
						
							|  |  |  |     rabbit_ct_helpers:log_environment(), | 
					
						
							|  |  |  |     rabbit_ct_helpers:run_setup_steps(Config). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | end_per_suite(Config) -> | 
					
						
							|  |  |  |     rabbit_ct_helpers:run_teardown_steps(Config). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | init_per_group(Group, Config) -> | 
					
						
							|  |  |  |     case lists:member({group, Group}, all()) of | 
					
						
							|  |  |  |         true -> | 
					
						
							|  |  |  |             ClusterSize = 2, | 
					
						
							|  |  |  |             Config1 = rabbit_ct_helpers:set_config(Config, [ | 
					
						
							|  |  |  |                 {rmq_nodename_suffix, Group}, | 
					
						
							|  |  |  |                 {rmq_nodes_count, ClusterSize} | 
					
						
							|  |  |  |               ]), | 
					
						
							|  |  |  |             rabbit_ct_helpers:run_steps(Config1, | 
					
						
							|  |  |  |               rabbit_ct_broker_helpers:setup_steps() ++ | 
					
						
							|  |  |  |               rabbit_ct_client_helpers:setup_steps() ++ [ | 
					
						
							|  |  |  |                 fun(C) -> init_per_group1(Group, C) end, | 
					
						
							|  |  |  |                 fun setup_file_handle_cache/1 | 
					
						
							|  |  |  |               ]); | 
					
						
							|  |  |  |         false -> | 
					
						
							|  |  |  |             rabbit_ct_helpers:run_steps(Config, [ | 
					
						
							|  |  |  |                 fun(C) -> init_per_group1(Group, C) end | 
					
						
							|  |  |  |               ]) | 
					
						
							|  |  |  |     end. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | init_per_group1(backing_queue_tests, Config) -> | 
					
						
							|  |  |  |     Module = rabbit_ct_broker_helpers:rpc(Config, 0, | 
					
						
							|  |  |  |       application, get_env, [rabbit, backing_queue_module]), | 
					
						
							|  |  |  |     case Module of | 
					
						
							|  |  |  |         {ok, rabbit_priority_queue} -> | 
					
						
							|  |  |  |             rabbit_ct_broker_helpers:rpc(Config, 0, | 
					
						
							|  |  |  |               ?MODULE, setup_backing_queue_test_group, [Config]); | 
					
						
							|  |  |  |         _ -> | 
					
						
							|  |  |  |             {skip, rabbit_misc:format( | 
					
						
							|  |  |  |                "Backing queue module not supported by this test group: ~p~n", | 
					
						
							|  |  |  |                [Module])} | 
					
						
							|  |  |  |     end; | 
					
						
							|  |  |  | init_per_group1(variable_queue_default, Config) -> | 
					
						
							|  |  |  |     rabbit_ct_helpers:set_config(Config, {variable_queue_type, default}); | 
					
						
							|  |  |  | init_per_group1(variable_queue_lazy, Config) -> | 
					
						
							|  |  |  |     rabbit_ct_helpers:set_config(Config, {variable_queue_type, lazy}); | 
					
						
							|  |  |  | init_per_group1(from_cluster_node1, Config) -> | 
					
						
							|  |  |  |     rabbit_ct_helpers:set_config(Config, {test_direction, {0, 1}}); | 
					
						
							|  |  |  | init_per_group1(from_cluster_node2, Config) -> | 
					
						
							|  |  |  |     rabbit_ct_helpers:set_config(Config, {test_direction, {1, 0}}); | 
					
						
							|  |  |  | init_per_group1(_, Config) -> | 
					
						
							|  |  |  |     Config. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | setup_file_handle_cache(Config) -> | 
					
						
							|  |  |  |     ok = rabbit_ct_broker_helpers:rpc(Config, 0, | 
					
						
							|  |  |  |       ?MODULE, setup_file_handle_cache1, []), | 
					
						
							|  |  |  |     Config. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | setup_file_handle_cache1() -> | 
					
						
							|  |  |  |     %% FIXME: Why are we doing this?
 | 
					
						
							| 
									
										
										
										
											2021-06-04 17:01:23 +08:00
										 |  |  |     application:set_env(rabbit, file_handles_high_watermark, 100), | 
					
						
							|  |  |  |     ok = file_handle_cache:set_limit(100), | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |     ok. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | end_per_group(Group, Config) -> | 
					
						
							|  |  |  |     case lists:member({group, Group}, all()) of | 
					
						
							|  |  |  |         true -> | 
					
						
							|  |  |  |             rabbit_ct_helpers:run_steps(Config, | 
					
						
							|  |  |  |               [fun(C) -> end_per_group1(Group, C) end] ++ | 
					
						
							|  |  |  |               rabbit_ct_client_helpers:teardown_steps() ++ | 
					
						
							|  |  |  |               rabbit_ct_broker_helpers:teardown_steps()); | 
					
						
							|  |  |  |         false -> | 
					
						
							|  |  |  |             Config | 
					
						
							|  |  |  |     end. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | end_per_group1(backing_queue_tests, Config) -> | 
					
						
							|  |  |  |     rabbit_ct_broker_helpers:rpc(Config, 0, | 
					
						
							|  |  |  |       ?MODULE, teardown_backing_queue_test_group, [Config]); | 
					
						
							|  |  |  | end_per_group1(_, Config) -> | 
					
						
							|  |  |  |     Config. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-28 03:38:58 +08:00
										 |  |  | init_per_testcase(Testcase, Config) when Testcase == variable_queue_requeue; | 
					
						
							|  |  |  |                                          Testcase == variable_queue_fold -> | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |     ok = rabbit_ct_broker_helpers:rpc( | 
					
						
							|  |  |  |            Config, 0, application, set_env, | 
					
						
							|  |  |  |            [rabbit, queue_explicit_gc_run_operation_threshold, 0]), | 
					
						
							|  |  |  |     rabbit_ct_helpers:testcase_started(Config, Testcase); | 
					
						
							|  |  |  | init_per_testcase(Testcase, Config) -> | 
					
						
							|  |  |  |     rabbit_ct_helpers:testcase_started(Config, Testcase). | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-28 03:38:58 +08:00
										 |  |  | end_per_testcase(Testcase, Config) when Testcase == variable_queue_requeue; | 
					
						
							|  |  |  |                                         Testcase == variable_queue_fold -> | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |     ok = rabbit_ct_broker_helpers:rpc( | 
					
						
							|  |  |  |            Config, 0, application, set_env, | 
					
						
							|  |  |  |            [rabbit, queue_explicit_gc_run_operation_threshold, 1000]), | 
					
						
							|  |  |  |     rabbit_ct_helpers:testcase_finished(Config, Testcase); | 
					
						
							|  |  |  | end_per_testcase(Testcase, Config) -> | 
					
						
							|  |  |  |     rabbit_ct_helpers:testcase_finished(Config, Testcase). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | %% -------------------------------------------------------------------
 | 
					
						
							|  |  |  | %% Message store.
 | 
					
						
							|  |  |  | %% -------------------------------------------------------------------
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | msg_store(Config) -> | 
					
						
							|  |  |  |     passed = rabbit_ct_broker_helpers:rpc(Config, 0, | 
					
						
							|  |  |  |       ?MODULE, msg_store1, [Config]). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | msg_store1(_Config) -> | 
					
						
							|  |  |  |     restart_msg_store_empty(), | 
					
						
							|  |  |  |     MsgIds = [msg_id_bin(M) || M <- lists:seq(1,100)], | 
					
						
							|  |  |  |     {MsgIds1stHalf, MsgIds2ndHalf} = lists:split(length(MsgIds) div 2, MsgIds), | 
					
						
							|  |  |  |     Ref = rabbit_guid:gen(), | 
					
						
							|  |  |  |     {Cap, MSCState} = msg_store_client_init_capture( | 
					
						
							|  |  |  |                         ?PERSISTENT_MSG_STORE, Ref), | 
					
						
							|  |  |  |     Ref2 = rabbit_guid:gen(), | 
					
						
							|  |  |  |     {Cap2, MSC2State} = msg_store_client_init_capture( | 
					
						
							|  |  |  |                           ?PERSISTENT_MSG_STORE, Ref2), | 
					
						
							|  |  |  |     %% check we don't contain any of the msgs we're about to publish
 | 
					
						
							|  |  |  |     false = msg_store_contains(false, MsgIds, MSCState), | 
					
						
							|  |  |  |     %% test confirm logic
 | 
					
						
							|  |  |  |     passed = test_msg_store_confirms([hd(MsgIds)], Cap, MSCState), | 
					
						
							|  |  |  |     %% check we don't contain any of the msgs we're about to publish
 | 
					
						
							|  |  |  |     false = msg_store_contains(false, MsgIds, MSCState), | 
					
						
							|  |  |  |     %% publish the first half
 | 
					
						
							|  |  |  |     ok = msg_store_write(MsgIds1stHalf, MSCState), | 
					
						
							|  |  |  |     %% sync on the first half
 | 
					
						
							|  |  |  |     ok = on_disk_await(Cap, MsgIds1stHalf), | 
					
						
							|  |  |  |     %% publish the second half
 | 
					
						
							|  |  |  |     ok = msg_store_write(MsgIds2ndHalf, MSCState), | 
					
						
							|  |  |  |     %% check they're all in there
 | 
					
						
							|  |  |  |     true = msg_store_contains(true, MsgIds, MSCState), | 
					
						
							|  |  |  |     %% publish the latter half twice so we hit the caching and ref
 | 
					
						
							|  |  |  |     %% count code. We need to do this through a 2nd client since a
 | 
					
						
							|  |  |  |     %% single client is not supposed to write the same message more
 | 
					
						
							|  |  |  |     %% than once without first removing it.
 | 
					
						
							|  |  |  |     ok = msg_store_write(MsgIds2ndHalf, MSC2State), | 
					
						
							|  |  |  |     %% check they're still all in there
 | 
					
						
							|  |  |  |     true = msg_store_contains(true, MsgIds, MSCState), | 
					
						
							|  |  |  |     %% sync on the 2nd half
 | 
					
						
							|  |  |  |     ok = on_disk_await(Cap2, MsgIds2ndHalf), | 
					
						
							|  |  |  |     %% cleanup
 | 
					
						
							|  |  |  |     ok = on_disk_stop(Cap2), | 
					
						
							|  |  |  |     ok = rabbit_msg_store:client_delete_and_terminate(MSC2State), | 
					
						
							|  |  |  |     ok = on_disk_stop(Cap), | 
					
						
							|  |  |  |     %% read them all
 | 
					
						
							|  |  |  |     MSCState1 = msg_store_read(MsgIds, MSCState), | 
					
						
							|  |  |  |     %% read them all again - this will hit the cache, not disk
 | 
					
						
							|  |  |  |     MSCState2 = msg_store_read(MsgIds, MSCState1), | 
					
						
							|  |  |  |     %% remove them all
 | 
					
						
							|  |  |  |     ok = msg_store_remove(MsgIds, MSCState2), | 
					
						
							|  |  |  |     %% check first half doesn't exist
 | 
					
						
							|  |  |  |     false = msg_store_contains(false, MsgIds1stHalf, MSCState2), | 
					
						
							|  |  |  |     %% check second half does exist
 | 
					
						
							|  |  |  |     true = msg_store_contains(true, MsgIds2ndHalf, MSCState2), | 
					
						
							|  |  |  |     %% read the second half again
 | 
					
						
							|  |  |  |     MSCState3 = msg_store_read(MsgIds2ndHalf, MSCState2), | 
					
						
							|  |  |  |     %% read the second half again, just for fun (aka code coverage)
 | 
					
						
							|  |  |  |     MSCState4 = msg_store_read(MsgIds2ndHalf, MSCState3), | 
					
						
							|  |  |  |     ok = rabbit_msg_store:client_terminate(MSCState4), | 
					
						
							|  |  |  |     %% stop and restart, preserving every other msg in 2nd half
 | 
					
						
							| 
									
										
										
										
											2017-03-23 02:30:08 +08:00
										 |  |  |     ok = rabbit_variable_queue:stop_msg_store(?VHOST), | 
					
						
							|  |  |  |     ok = rabbit_variable_queue:start_msg_store(?VHOST, | 
					
						
							|  |  |  |            [], {fun ([]) -> finished; | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |                     ([MsgId|MsgIdsTail]) | 
					
						
							|  |  |  |                       when length(MsgIdsTail) rem 2 == 0 -> | 
					
						
							|  |  |  |                         {MsgId, 1, MsgIdsTail}; | 
					
						
							|  |  |  |                     ([MsgId|MsgIdsTail]) -> | 
					
						
							|  |  |  |                         {MsgId, 0, MsgIdsTail} | 
					
						
							|  |  |  |                 end, MsgIds2ndHalf}), | 
					
						
							|  |  |  |     MSCState5 = msg_store_client_init(?PERSISTENT_MSG_STORE, Ref), | 
					
						
							|  |  |  |     %% check we have the right msgs left
 | 
					
						
							|  |  |  |     lists:foldl( | 
					
						
							|  |  |  |       fun (MsgId, Bool) -> | 
					
						
							|  |  |  |               not(Bool = rabbit_msg_store:contains(MsgId, MSCState5)) | 
					
						
							|  |  |  |       end, false, MsgIds2ndHalf), | 
					
						
							|  |  |  |     ok = rabbit_msg_store:client_terminate(MSCState5), | 
					
						
							|  |  |  |     %% restart empty
 | 
					
						
							|  |  |  |     restart_msg_store_empty(), | 
					
						
							|  |  |  |     MSCState6 = msg_store_client_init(?PERSISTENT_MSG_STORE, Ref), | 
					
						
							|  |  |  |     %% check we don't contain any of the msgs
 | 
					
						
							|  |  |  |     false = msg_store_contains(false, MsgIds, MSCState6), | 
					
						
							|  |  |  |     %% publish the first half again
 | 
					
						
							|  |  |  |     ok = msg_store_write(MsgIds1stHalf, MSCState6), | 
					
						
							|  |  |  |     %% this should force some sort of sync internally otherwise misread
 | 
					
						
							|  |  |  |     ok = rabbit_msg_store:client_terminate( | 
					
						
							|  |  |  |            msg_store_read(MsgIds1stHalf, MSCState6)), | 
					
						
							|  |  |  |     MSCState7 = msg_store_client_init(?PERSISTENT_MSG_STORE, Ref), | 
					
						
							|  |  |  |     ok = msg_store_remove(MsgIds1stHalf, MSCState7), | 
					
						
							|  |  |  |     ok = rabbit_msg_store:client_terminate(MSCState7), | 
					
						
							|  |  |  |     %% restart empty
 | 
					
						
							|  |  |  |     restart_msg_store_empty(), %% now safe to reuse msg_ids
 | 
					
						
							|  |  |  |     %% push a lot of msgs in... at least 100 files worth
 | 
					
						
							|  |  |  |     {ok, FileSize} = application:get_env(rabbit, msg_store_file_size_limit), | 
					
						
							|  |  |  |     PayloadSizeBits = 65536, | 
					
						
							|  |  |  |     BigCount = trunc(100 * FileSize / (PayloadSizeBits div 8)), | 
					
						
							|  |  |  |     MsgIdsBig = [msg_id_bin(X) || X <- lists:seq(1, BigCount)], | 
					
						
							|  |  |  |     Payload = << 0:PayloadSizeBits >>, | 
					
						
							|  |  |  |     ok = with_msg_store_client( | 
					
						
							|  |  |  |            ?PERSISTENT_MSG_STORE, Ref, | 
					
						
							|  |  |  |            fun (MSCStateM) -> | 
					
						
							|  |  |  |                    [ok = rabbit_msg_store:write(MsgId, Payload, MSCStateM) || | 
					
						
							|  |  |  |                        MsgId <- MsgIdsBig], | 
					
						
							|  |  |  |                    MSCStateM | 
					
						
							|  |  |  |            end), | 
					
						
							|  |  |  |     %% now read them to ensure we hit the fast client-side reading
 | 
					
						
							|  |  |  |     ok = foreach_with_msg_store_client( | 
					
						
							|  |  |  |            ?PERSISTENT_MSG_STORE, Ref, | 
					
						
							|  |  |  |            fun (MsgId, MSCStateM) -> | 
					
						
							|  |  |  |                    {{ok, Payload}, MSCStateN} = rabbit_msg_store:read( | 
					
						
							|  |  |  |                                                   MsgId, MSCStateM), | 
					
						
							|  |  |  |                    MSCStateN | 
					
						
							|  |  |  |            end, MsgIdsBig), | 
					
						
							|  |  |  |     %% .., then 3s by 1...
 | 
					
						
							|  |  |  |     ok = msg_store_remove(?PERSISTENT_MSG_STORE, Ref, | 
					
						
							|  |  |  |                           [msg_id_bin(X) || X <- lists:seq(BigCount, 1, -3)]), | 
					
						
							|  |  |  |     %% .., then remove 3s by 2, from the young end first. This hits
 | 
					
						
							|  |  |  |     %% GC (under 50% good data left, but no empty files. Must GC).
 | 
					
						
							|  |  |  |     ok = msg_store_remove(?PERSISTENT_MSG_STORE, Ref, | 
					
						
							|  |  |  |                           [msg_id_bin(X) || X <- lists:seq(BigCount-1, 1, -3)]), | 
					
						
							|  |  |  |     %% .., then remove 3s by 3, from the young end first. This hits
 | 
					
						
							|  |  |  |     %% GC...
 | 
					
						
							|  |  |  |     ok = msg_store_remove(?PERSISTENT_MSG_STORE, Ref, | 
					
						
							|  |  |  |                           [msg_id_bin(X) || X <- lists:seq(BigCount-2, 1, -3)]), | 
					
						
							|  |  |  |     %% ensure empty
 | 
					
						
							|  |  |  |     ok = with_msg_store_client( | 
					
						
							|  |  |  |            ?PERSISTENT_MSG_STORE, Ref, | 
					
						
							|  |  |  |            fun (MSCStateM) -> | 
					
						
							|  |  |  |                    false = msg_store_contains(false, MsgIdsBig, MSCStateM), | 
					
						
							|  |  |  |                    MSCStateM | 
					
						
							|  |  |  |            end), | 
					
						
							|  |  |  |     %%
 | 
					
						
							|  |  |  |     passed = test_msg_store_client_delete_and_terminate(), | 
					
						
							|  |  |  |     %% restart empty
 | 
					
						
							|  |  |  |     restart_msg_store_empty(), | 
					
						
							|  |  |  |     passed. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | restart_msg_store_empty() -> | 
					
						
							| 
									
										
										
										
											2017-03-23 02:30:08 +08:00
										 |  |  |     ok = rabbit_variable_queue:stop_msg_store(?VHOST), | 
					
						
							|  |  |  |     ok = rabbit_variable_queue:start_msg_store(?VHOST, | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |            undefined, {fun (ok) -> finished end, ok}). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | msg_id_bin(X) -> | 
					
						
							|  |  |  |     erlang:md5(term_to_binary(X)). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | on_disk_capture() -> | 
					
						
							|  |  |  |     receive | 
					
						
							|  |  |  |         {await, MsgIds, Pid} -> on_disk_capture([], MsgIds, Pid); | 
					
						
							|  |  |  |         stop                 -> done | 
					
						
							|  |  |  |     end. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | on_disk_capture([_|_], _Awaiting, Pid) -> | 
					
						
							|  |  |  |     Pid ! {self(), surplus}; | 
					
						
							|  |  |  | on_disk_capture(OnDisk, Awaiting, Pid) -> | 
					
						
							|  |  |  |     receive | 
					
						
							|  |  |  |         {on_disk, MsgIdsS} -> | 
					
						
							|  |  |  |             MsgIds = gb_sets:to_list(MsgIdsS), | 
					
						
							|  |  |  |             on_disk_capture(OnDisk ++ (MsgIds -- Awaiting), Awaiting -- MsgIds, | 
					
						
							|  |  |  |                             Pid); | 
					
						
							|  |  |  |         stop -> | 
					
						
							|  |  |  |             done | 
					
						
							|  |  |  |     after (case Awaiting of [] -> 200; _ -> ?TIMEOUT end) -> | 
					
						
							|  |  |  |             case Awaiting of | 
					
						
							|  |  |  |                 [] -> Pid ! {self(), arrived}, on_disk_capture(); | 
					
						
							|  |  |  |                 _  -> Pid ! {self(), timeout} | 
					
						
							|  |  |  |             end | 
					
						
							|  |  |  |     end. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | on_disk_await(Pid, MsgIds) when is_list(MsgIds) -> | 
					
						
							|  |  |  |     Pid ! {await, MsgIds, self()}, | 
					
						
							|  |  |  |     receive | 
					
						
							|  |  |  |         {Pid, arrived} -> ok; | 
					
						
							|  |  |  |         {Pid, Error}   -> Error | 
					
						
							|  |  |  |     end. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | on_disk_stop(Pid) -> | 
					
						
							|  |  |  |     MRef = erlang:monitor(process, Pid), | 
					
						
							|  |  |  |     Pid ! stop, | 
					
						
							|  |  |  |     receive {'DOWN', MRef, process, Pid, _Reason} -> | 
					
						
							|  |  |  |             ok | 
					
						
							|  |  |  |     end. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | msg_store_client_init_capture(MsgStore, Ref) -> | 
					
						
							|  |  |  |     Pid = spawn(fun on_disk_capture/0), | 
					
						
							| 
									
										
										
										
											2017-03-23 02:30:08 +08:00
										 |  |  |     {Pid, rabbit_vhost_msg_store:client_init(?VHOST, MsgStore, Ref, | 
					
						
							|  |  |  |                                              fun (MsgIds, _ActionTaken) -> | 
					
						
							|  |  |  |                                                  Pid ! {on_disk, MsgIds} | 
					
						
							|  |  |  |                                              end, undefined)}. | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | msg_store_contains(Atom, MsgIds, MSCState) -> | 
					
						
							|  |  |  |     Atom = lists:foldl( | 
					
						
							|  |  |  |              fun (MsgId, Atom1) when Atom1 =:= Atom -> | 
					
						
							|  |  |  |                      rabbit_msg_store:contains(MsgId, MSCState) end, | 
					
						
							|  |  |  |              Atom, MsgIds). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | msg_store_read(MsgIds, MSCState) -> | 
					
						
							|  |  |  |     lists:foldl(fun (MsgId, MSCStateM) -> | 
					
						
							|  |  |  |                         {{ok, MsgId}, MSCStateN} = rabbit_msg_store:read( | 
					
						
							|  |  |  |                                                      MsgId, MSCStateM), | 
					
						
							|  |  |  |                         MSCStateN | 
					
						
							|  |  |  |                 end, MSCState, MsgIds). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | msg_store_write(MsgIds, MSCState) -> | 
					
						
							|  |  |  |     ok = lists:foldl(fun (MsgId, ok) -> | 
					
						
							|  |  |  |                              rabbit_msg_store:write(MsgId, MsgId, MSCState) | 
					
						
							|  |  |  |                      end, ok, MsgIds). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | msg_store_write_flow(MsgIds, MSCState) -> | 
					
						
							|  |  |  |     ok = lists:foldl(fun (MsgId, ok) -> | 
					
						
							|  |  |  |                              rabbit_msg_store:write_flow(MsgId, MsgId, MSCState) | 
					
						
							|  |  |  |                      end, ok, MsgIds). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | msg_store_remove(MsgIds, MSCState) -> | 
					
						
							|  |  |  |     rabbit_msg_store:remove(MsgIds, MSCState). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | msg_store_remove(MsgStore, Ref, MsgIds) -> | 
					
						
							|  |  |  |     with_msg_store_client(MsgStore, Ref, | 
					
						
							|  |  |  |                           fun (MSCStateM) -> | 
					
						
							|  |  |  |                                   ok = msg_store_remove(MsgIds, MSCStateM), | 
					
						
							|  |  |  |                                   MSCStateM | 
					
						
							|  |  |  |                           end). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | with_msg_store_client(MsgStore, Ref, Fun) -> | 
					
						
							|  |  |  |     rabbit_msg_store:client_terminate( | 
					
						
							|  |  |  |       Fun(msg_store_client_init(MsgStore, Ref))). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | foreach_with_msg_store_client(MsgStore, Ref, Fun, L) -> | 
					
						
							|  |  |  |     rabbit_msg_store:client_terminate( | 
					
						
							|  |  |  |       lists:foldl(fun (MsgId, MSCState) -> Fun(MsgId, MSCState) end, | 
					
						
							|  |  |  |                   msg_store_client_init(MsgStore, Ref), L)). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | test_msg_store_confirms(MsgIds, Cap, MSCState) -> | 
					
						
							|  |  |  |     %% write -> confirmed
 | 
					
						
							|  |  |  |     ok = msg_store_write(MsgIds, MSCState), | 
					
						
							|  |  |  |     ok = on_disk_await(Cap, MsgIds), | 
					
						
							|  |  |  |     %% remove -> _
 | 
					
						
							|  |  |  |     ok = msg_store_remove(MsgIds, MSCState), | 
					
						
							|  |  |  |     ok = on_disk_await(Cap, []), | 
					
						
							|  |  |  |     %% write, remove -> confirmed
 | 
					
						
							|  |  |  |     ok = msg_store_write(MsgIds, MSCState), | 
					
						
							|  |  |  |     ok = msg_store_remove(MsgIds, MSCState), | 
					
						
							|  |  |  |     ok = on_disk_await(Cap, MsgIds), | 
					
						
							|  |  |  |     %% write, remove, write -> confirmed, confirmed
 | 
					
						
							|  |  |  |     ok = msg_store_write(MsgIds, MSCState), | 
					
						
							|  |  |  |     ok = msg_store_remove(MsgIds, MSCState), | 
					
						
							|  |  |  |     ok = msg_store_write(MsgIds, MSCState), | 
					
						
							|  |  |  |     ok = on_disk_await(Cap, MsgIds ++ MsgIds), | 
					
						
							|  |  |  |     %% remove, write -> confirmed
 | 
					
						
							|  |  |  |     ok = msg_store_remove(MsgIds, MSCState), | 
					
						
							|  |  |  |     ok = msg_store_write(MsgIds, MSCState), | 
					
						
							|  |  |  |     ok = on_disk_await(Cap, MsgIds), | 
					
						
							|  |  |  |     %% remove, write, remove -> confirmed
 | 
					
						
							|  |  |  |     ok = msg_store_remove(MsgIds, MSCState), | 
					
						
							|  |  |  |     ok = msg_store_write(MsgIds, MSCState), | 
					
						
							|  |  |  |     ok = msg_store_remove(MsgIds, MSCState), | 
					
						
							|  |  |  |     ok = on_disk_await(Cap, MsgIds), | 
					
						
							|  |  |  |     %% confirmation on timer-based sync
 | 
					
						
							|  |  |  |     passed = test_msg_store_confirm_timer(), | 
					
						
							|  |  |  |     passed. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | test_msg_store_confirm_timer() -> | 
					
						
							|  |  |  |     Ref = rabbit_guid:gen(), | 
					
						
							|  |  |  |     MsgId  = msg_id_bin(1), | 
					
						
							|  |  |  |     Self = self(), | 
					
						
							| 
									
										
										
										
											2017-03-23 02:30:08 +08:00
										 |  |  |     MSCState = rabbit_vhost_msg_store:client_init( | 
					
						
							|  |  |  |         ?VHOST, | 
					
						
							|  |  |  |         ?PERSISTENT_MSG_STORE, | 
					
						
							|  |  |  |         Ref, | 
					
						
							|  |  |  |         fun (MsgIds, _ActionTaken) -> | 
					
						
							|  |  |  |             case gb_sets:is_member(MsgId, MsgIds) of | 
					
						
							|  |  |  |                 true  -> Self ! on_disk; | 
					
						
							|  |  |  |                 false -> ok | 
					
						
							|  |  |  |             end | 
					
						
							|  |  |  |         end, undefined), | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |     ok = msg_store_write([MsgId], MSCState), | 
					
						
							|  |  |  |     ok = msg_store_keep_busy_until_confirm([msg_id_bin(2)], MSCState, false), | 
					
						
							|  |  |  |     ok = msg_store_remove([MsgId], MSCState), | 
					
						
							|  |  |  |     ok = rabbit_msg_store:client_delete_and_terminate(MSCState), | 
					
						
							|  |  |  |     passed. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | msg_store_keep_busy_until_confirm(MsgIds, MSCState, Blocked) -> | 
					
						
							|  |  |  |     After = case Blocked of | 
					
						
							|  |  |  |                 false -> 0; | 
					
						
							|  |  |  |                 true  -> ?MAX_WAIT | 
					
						
							|  |  |  |             end, | 
					
						
							|  |  |  |     Recurse = fun () -> msg_store_keep_busy_until_confirm( | 
					
						
							|  |  |  |                           MsgIds, MSCState, credit_flow:blocked()) end, | 
					
						
							|  |  |  |     receive | 
					
						
							|  |  |  |         on_disk            -> ok; | 
					
						
							|  |  |  |         {bump_credit, Msg} -> credit_flow:handle_bump_msg(Msg), | 
					
						
							|  |  |  |                               Recurse() | 
					
						
							|  |  |  |     after After -> | 
					
						
							|  |  |  |             ok = msg_store_write_flow(MsgIds, MSCState), | 
					
						
							|  |  |  |             ok = msg_store_remove(MsgIds, MSCState), | 
					
						
							|  |  |  |             Recurse() | 
					
						
							|  |  |  |     end. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | test_msg_store_client_delete_and_terminate() -> | 
					
						
							|  |  |  |     restart_msg_store_empty(), | 
					
						
							|  |  |  |     MsgIds = [msg_id_bin(M) || M <- lists:seq(1, 10)], | 
					
						
							|  |  |  |     Ref = rabbit_guid:gen(), | 
					
						
							|  |  |  |     MSCState = msg_store_client_init(?PERSISTENT_MSG_STORE, Ref), | 
					
						
							|  |  |  |     ok = msg_store_write(MsgIds, MSCState), | 
					
						
							|  |  |  |     %% test the 'dying client' fast path for writes
 | 
					
						
							|  |  |  |     ok = rabbit_msg_store:client_delete_and_terminate(MSCState), | 
					
						
							|  |  |  |     passed. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | %% -------------------------------------------------------------------
 | 
					
						
							|  |  |  | %% Backing queue.
 | 
					
						
							|  |  |  | %% -------------------------------------------------------------------
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | setup_backing_queue_test_group(Config) -> | 
					
						
							|  |  |  |     {ok, FileSizeLimit} = | 
					
						
							|  |  |  |         application:get_env(rabbit, msg_store_file_size_limit), | 
					
						
							|  |  |  |     application:set_env(rabbit, msg_store_file_size_limit, 512), | 
					
						
							|  |  |  |     application:set_env(rabbit, msg_store_file_size_limit, | 
					
						
							|  |  |  |                         FileSizeLimit), | 
					
						
							| 
									
										
										
										
											2021-05-19 21:03:45 +08:00
										 |  |  |     Config. | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | teardown_backing_queue_test_group(Config) -> | 
					
						
							|  |  |  |     %% FIXME: Undo all the setup function did.
 | 
					
						
							|  |  |  |     %% We will have restarted the message store, and thus changed
 | 
					
						
							|  |  |  |     %% the order of the children of rabbit_sup. This will cause
 | 
					
						
							|  |  |  |     %% problems if there are subsequent failures - see bug 24262.
 | 
					
						
							|  |  |  |     ok = restart_app(), | 
					
						
							|  |  |  |     Config. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bq_queue_index(Config) -> | 
					
						
							|  |  |  |     passed = rabbit_ct_broker_helpers:rpc(Config, 0, | 
					
						
							|  |  |  |       ?MODULE, bq_queue_index1, [Config]). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bq_queue_index1(_Config) -> | 
					
						
							| 
									
										
										
										
											2021-04-07 20:45:43 +08:00
										 |  |  |     init_queue_index(), | 
					
						
							| 
									
										
										
										
											2021-04-09 21:01:28 +08:00
										 |  |  |     SegmentSize = ?INDEX:next_segment_boundary(0), | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |     TwoSegs = SegmentSize + SegmentSize, | 
					
						
							|  |  |  |     MostOfASegment = trunc(SegmentSize*0.75), | 
					
						
							|  |  |  |     SeqIdsA = lists:seq(0, MostOfASegment-1), | 
					
						
							|  |  |  |     SeqIdsB = lists:seq(MostOfASegment, 2*MostOfASegment), | 
					
						
							|  |  |  |     SeqIdsC = lists:seq(0, trunc(SegmentSize/2)), | 
					
						
							|  |  |  |     SeqIdsD = lists:seq(0, SegmentSize*4), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     with_empty_test_queue( | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  |       fun (Qi0, QName) -> | 
					
						
							| 
									
										
										
										
											2021-04-09 21:01:28 +08:00
										 |  |  |               {0, 0, Qi1} = ?INDEX:bounds(Qi0), | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |               {Qi2, SeqIdsMsgIdsA} = queue_index_publish(SeqIdsA, false, Qi1), | 
					
						
							| 
									
										
										
										
											2021-04-09 21:01:28 +08:00
										 |  |  |               {0, SegmentSize, Qi3} = ?INDEX:bounds(Qi2), | 
					
						
							|  |  |  |               {ReadA, Qi4} = ?INDEX:read(0, SegmentSize, Qi3), | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |               ok = verify_read_with_published(false, false, ReadA, | 
					
						
							|  |  |  |                                               lists:reverse(SeqIdsMsgIdsA)), | 
					
						
							|  |  |  |               %% should get length back as 0, as all the msgs were transient
 | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  |               {0, 0, Qi6} = restart_test_queue(Qi4, QName), | 
					
						
							| 
									
										
										
										
											2021-04-09 21:01:28 +08:00
										 |  |  |               {0, 0, Qi7} = ?INDEX:bounds(Qi6), | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |               {Qi8, SeqIdsMsgIdsB} = queue_index_publish(SeqIdsB, true, Qi7), | 
					
						
							| 
									
										
										
										
											2021-04-09 21:01:28 +08:00
										 |  |  |               {0, TwoSegs, Qi9} = ?INDEX:bounds(Qi8), | 
					
						
							|  |  |  |               {ReadB, Qi10} = ?INDEX:read(0, SegmentSize, Qi9), | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |               ok = verify_read_with_published(false, true, ReadB, | 
					
						
							|  |  |  |                                               lists:reverse(SeqIdsMsgIdsB)), | 
					
						
							|  |  |  |               %% should get length back as MostOfASegment
 | 
					
						
							|  |  |  |               LenB = length(SeqIdsB), | 
					
						
							|  |  |  |               BytesB = LenB * 10, | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  |               {LenB, BytesB, Qi12} = restart_test_queue(Qi10, QName), | 
					
						
							| 
									
										
										
										
											2021-04-09 21:01:28 +08:00
										 |  |  |               {0, TwoSegs, Qi13} = ?INDEX:bounds(Qi12), | 
					
						
							|  |  |  |               Qi14 = ?INDEX:deliver(SeqIdsB, Qi13), | 
					
						
							|  |  |  |               {ReadC, Qi15} = ?INDEX:read(0, SegmentSize, Qi14), | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |               ok = verify_read_with_published(true, true, ReadC, | 
					
						
							|  |  |  |                                               lists:reverse(SeqIdsMsgIdsB)), | 
					
						
							| 
									
										
										
										
											2021-04-09 21:01:28 +08:00
										 |  |  |               Qi16 = ?INDEX:ack(SeqIdsB, Qi15), | 
					
						
							|  |  |  |               Qi17 = ?INDEX:flush(Qi16), | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |               %% Everything will have gone now because #pubs == #acks
 | 
					
						
							| 
									
										
										
										
											2021-04-09 21:01:28 +08:00
										 |  |  |               {0, 0, Qi18} = ?INDEX:bounds(Qi17), | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |               %% should get length back as 0 because all persistent
 | 
					
						
							|  |  |  |               %% msgs have been acked
 | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  |               {0, 0, Qi19} = restart_test_queue(Qi18, QName), | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |               Qi19 | 
					
						
							|  |  |  |       end), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     %% These next bits are just to hit the auto deletion of segment files.
 | 
					
						
							|  |  |  |     %% First, partials:
 | 
					
						
							|  |  |  |     %% a) partial pub+del+ack, then move to new segment
 | 
					
						
							|  |  |  |     with_empty_test_queue( | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  |       fun (Qi0, _QName) -> | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |               {Qi1, _SeqIdsMsgIdsC} = queue_index_publish(SeqIdsC, | 
					
						
							|  |  |  |                                                           false, Qi0), | 
					
						
							| 
									
										
										
										
											2021-04-09 21:01:28 +08:00
										 |  |  |               Qi2 = ?INDEX:deliver(SeqIdsC, Qi1), | 
					
						
							|  |  |  |               Qi3 = ?INDEX:ack(SeqIdsC, Qi2), | 
					
						
							|  |  |  |               Qi4 = ?INDEX:flush(Qi3), | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |               {Qi5, _SeqIdsMsgIdsC1} = queue_index_publish([SegmentSize], | 
					
						
							|  |  |  |                                                            false, Qi4), | 
					
						
							|  |  |  |               Qi5 | 
					
						
							|  |  |  |       end), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     %% b) partial pub+del, then move to new segment, then ack all in old segment
 | 
					
						
							|  |  |  |     with_empty_test_queue( | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  |       fun (Qi0, _QName) -> | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |               {Qi1, _SeqIdsMsgIdsC2} = queue_index_publish(SeqIdsC, | 
					
						
							|  |  |  |                                                            false, Qi0), | 
					
						
							| 
									
										
										
										
											2021-04-09 21:01:28 +08:00
										 |  |  |               Qi2 = ?INDEX:deliver(SeqIdsC, Qi1), | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |               {Qi3, _SeqIdsMsgIdsC3} = queue_index_publish([SegmentSize], | 
					
						
							|  |  |  |                                                            false, Qi2), | 
					
						
							| 
									
										
										
										
											2021-04-09 21:01:28 +08:00
										 |  |  |               Qi4 = ?INDEX:ack(SeqIdsC, Qi3), | 
					
						
							|  |  |  |               ?INDEX:flush(Qi4) | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |       end), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     %% c) just fill up several segments of all pubs, then +dels, then +acks
 | 
					
						
							|  |  |  |     with_empty_test_queue( | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  |       fun (Qi0, _QName) -> | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |               {Qi1, _SeqIdsMsgIdsD} = queue_index_publish(SeqIdsD, | 
					
						
							|  |  |  |                                                           false, Qi0), | 
					
						
							| 
									
										
										
										
											2021-04-09 21:01:28 +08:00
										 |  |  |               Qi2 = ?INDEX:deliver(SeqIdsD, Qi1), | 
					
						
							|  |  |  |               Qi3 = ?INDEX:ack(SeqIdsD, Qi2), | 
					
						
							|  |  |  |               ?INDEX:flush(Qi3) | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |       end), | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-09 21:01:28 +08:00
										 |  |  | %% @todo This is commented out because the modern index expects
 | 
					
						
							|  |  |  | %%       publishes to be ordered by seq_id(). It also does not
 | 
					
						
							|  |  |  | %%       have a journal so the tests are not doing much anyway.
 | 
					
						
							|  |  |  | %%
 | 
					
						
							|  |  |  | %    %% d) get messages in all states to a segment, then flush, then do
 | 
					
						
							|  |  |  | %    %% the same again, don't flush and read. This will hit all
 | 
					
						
							|  |  |  | %    %% possibilities in combining the segment with the journal.
 | 
					
						
							|  |  |  | %    with_empty_test_queue(
 | 
					
						
							|  |  |  | %      fun (Qi0, _QName) ->
 | 
					
						
							|  |  |  | %              {Qi1, [Seven,Five,Four|_]} = queue_index_publish([0,1,2,4,5,7],
 | 
					
						
							|  |  |  | %                                                               false, Qi0),
 | 
					
						
							|  |  |  | %              Qi2 = ?INDEX:deliver([0,1,4], Qi1),
 | 
					
						
							|  |  |  | %              Qi3 = ?INDEX:ack([0], Qi2),
 | 
					
						
							|  |  |  | %              Qi4 = ?INDEX:flush(Qi3),
 | 
					
						
							|  |  |  | %              {Qi5, [Eight,Six|_]} = queue_index_publish([3,6,8], false, Qi4),
 | 
					
						
							|  |  |  | %              Qi6 = ?INDEX:deliver([2,3,5,6], Qi5),
 | 
					
						
							|  |  |  | %              Qi7 = ?INDEX:ack([1,2,3], Qi6),
 | 
					
						
							|  |  |  | %              {[], Qi8} = ?INDEX:read(0, 4, Qi7),
 | 
					
						
							|  |  |  | %              {ReadD, Qi9} = ?INDEX:read(4, 7, Qi8),
 | 
					
						
							|  |  |  | %              ok = verify_read_with_published(true, false, ReadD,
 | 
					
						
							|  |  |  | %                                              [Four, Five, Six]),
 | 
					
						
							|  |  |  | %              {ReadE, Qi10} = ?INDEX:read(7, 9, Qi9),
 | 
					
						
							|  |  |  | %              ok = verify_read_with_published(false, false, ReadE,
 | 
					
						
							|  |  |  | %                                              [Seven, Eight]),
 | 
					
						
							|  |  |  | %              Qi10
 | 
					
						
							|  |  |  | %      end),
 | 
					
						
							|  |  |  | %
 | 
					
						
							|  |  |  | %    %% e) as for (d), but use terminate instead of read, which will
 | 
					
						
							|  |  |  | %    %% exercise journal_minus_segment, not segment_plus_journal.
 | 
					
						
							|  |  |  | %    with_empty_test_queue(
 | 
					
						
							|  |  |  | %      fun (Qi0, QName) ->
 | 
					
						
							|  |  |  | %              {Qi1, _SeqIdsMsgIdsE} = queue_index_publish([0,1,2,4,5,7],
 | 
					
						
							|  |  |  | %                                                          true, Qi0),
 | 
					
						
							|  |  |  | %              Qi2 = ?INDEX:deliver([0,1,4], Qi1),
 | 
					
						
							|  |  |  | %              Qi3 = ?INDEX:ack([0], Qi2),
 | 
					
						
							|  |  |  | %              {5, 50, Qi4} = restart_test_queue(Qi3, QName),
 | 
					
						
							|  |  |  | %              {Qi5, _SeqIdsMsgIdsF} = queue_index_publish([3,6,8], true, Qi4),
 | 
					
						
							|  |  |  | %              Qi6 = ?INDEX:deliver([2,3,5,6], Qi5),
 | 
					
						
							|  |  |  | %              Qi7 = ?INDEX:ack([1,2,3], Qi6),
 | 
					
						
							|  |  |  | %              {5, 50, Qi8} = restart_test_queue(Qi7, QName),
 | 
					
						
							|  |  |  | %              Qi8
 | 
					
						
							|  |  |  | %      end),
 | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-23 02:30:08 +08:00
										 |  |  |     ok = rabbit_variable_queue:stop(?VHOST), | 
					
						
							|  |  |  |     {ok, _} = rabbit_variable_queue:start(?VHOST, []), | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     passed. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bq_queue_index_props(Config) -> | 
					
						
							|  |  |  |     passed = rabbit_ct_broker_helpers:rpc(Config, 0, | 
					
						
							|  |  |  |       ?MODULE, bq_queue_index_props1, [Config]). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bq_queue_index_props1(_Config) -> | 
					
						
							|  |  |  |     with_empty_test_queue( | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  |       fun(Qi0, _QName) -> | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |               MsgId = rabbit_guid:gen(), | 
					
						
							|  |  |  |               Props = #message_properties{expiry=12345, size = 10}, | 
					
						
							| 
									
										
										
										
											2021-04-09 21:01:28 +08:00
										 |  |  |               Qi1 = ?INDEX:publish( | 
					
						
							| 
									
										
										
										
											2021-07-28 19:43:31 +08:00
										 |  |  |                       MsgId, 0, memory, Props, true, false, infinity, Qi0), | 
					
						
							| 
									
										
										
										
											2021-04-09 21:01:28 +08:00
										 |  |  |               {[{MsgId, 0, Props, _, _}], Qi2} = | 
					
						
							|  |  |  |                   ?INDEX:read(0, 1, Qi1), | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |               Qi2 | 
					
						
							|  |  |  |       end), | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-23 02:30:08 +08:00
										 |  |  |     ok = rabbit_variable_queue:stop(?VHOST), | 
					
						
							|  |  |  |     {ok, _} = rabbit_variable_queue:start(?VHOST, []), | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     passed. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bq_variable_queue_delete_msg_store_files_callback(Config) -> | 
					
						
							|  |  |  |     passed = rabbit_ct_broker_helpers:rpc(Config, 0, | 
					
						
							|  |  |  |       ?MODULE, bq_variable_queue_delete_msg_store_files_callback1, [Config]). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bq_variable_queue_delete_msg_store_files_callback1(Config) -> | 
					
						
							|  |  |  |     ok = restart_msg_store_empty(), | 
					
						
							| 
									
										
										
										
											2018-10-11 18:12:39 +08:00
										 |  |  |     QName0 = queue_name(Config, <<"bq_variable_queue_delete_msg_store_files_callback-q">>), | 
					
						
							|  |  |  |     {new, Q} = rabbit_amqqueue:declare(QName0, true, false, [], none, <<"acting-user">>), | 
					
						
							|  |  |  |     QName = amqqueue:get_name(Q), | 
					
						
							|  |  |  |     QPid = amqqueue:get_pid(Q), | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |     Payload = <<0:8388608>>, %% 1MB
 | 
					
						
							|  |  |  |     Count = 30, | 
					
						
							| 
									
										
										
										
											2020-09-29 18:43:24 +08:00
										 |  |  |     QTState = publish_and_confirm(Q, Payload, Count), | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     rabbit_amqqueue:set_ram_duration_target(QPid, 0), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     {ok, Limiter} = rabbit_limiter:start_link(no_id), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     CountMinusOne = Count - 1, | 
					
						
							| 
									
										
										
										
											2020-09-29 18:43:24 +08:00
										 |  |  |     {ok, CountMinusOne, {QName, QPid, _AckTag, false, _Msg}, _} = | 
					
						
							|  |  |  |         rabbit_amqqueue:basic_get(Q, true, Limiter, | 
					
						
							| 
									
										
											  
											
												Quorum queues (#1706)
* Test queue.declare method with quorum type
[#154472130]
* Cosmetics
[#154472130]
* Start quorum queue
Includes ra as a rabbit dependency
[#154472152]
* Update info and list operations to use quorum queues
Basic implementation. Might need an update when more functionality
is added to the quorum queues.
[#154472152]
* Stop quorum queue
[#154472158]
* Restart quorum queue
[#154472164]
* Introduce UId in ra config to support newer version of ra
Improved ra stop
[#154472158]
* Put data inside VHost specific subdirs
[#154472164]
* Include ra in rabbit deps to support stop_app/start_app command
[#154472164]
* Stop quorum queues in `rabbit_amqqueue:stop/1`
[#154472158]
* Revert creation of fifo ets table inside rabbit
Now supported by ra
[#154472158]
* Filter quorum queues
[#154472158]
* Test restart node with quorum queues
[#154472164]
* Publish to quorum queues
[#154472174]
* Use `ra:restart_node/1`
[#154472164]
* Wait for stats to be published when querying quorum queues
[#154472174]
* Test publish and queue length after restart
[#154472174]
* Consume messages from quorum queues with basic.get
[#154472211]
* Autoack messages from quorum queues on basic.get
[#154472211]
* Fix no_ack meaning
no_ack = true is equivalent to autoack
[#154472211]
* Use data_dir as provided in the config
If we modify the data_dir, ra is not able to delete the data
when a queue is deleted
[#154472158]
* Remove unused code/variables
[#154472158]
* Subscribe to a quorum queue
Supports auto-ack
[#154472215]
* Ack messages consumed from quorum queues
[#154472221]
* Nack messages consumed from quorum queues
[#154804608]
* Use delivery tag as consumer tag for basic.get in quorum queues
[#154472221]
* Support for publisher confirms in quorum queues
[#154472198]
* Integrate with ra_fifo_client
* Clear queue state on queue.delete
[#154472158]
* Fix quorum nack
[#154804608]
* Test redelivery after nack
[#154804608]
* Nack without requeueing
[#154472225]
* Test multiple acks
[#154804208]
* Test multiple nacks
[#154804314]
* Configure dead letter exchange with queue declare
[#155076661]
* Use a per-vhost process to handle dead-lettering
Needs to hold state for quorum queues
[#155401802]
* Implement dead-lettering on nack'ed messages
[#154804620]
* Use queue name as a resource on message delivery
Fixes a previously introduced bug
[#154804608]
* Handle ra events on dead letter process
[#155401802]
* Pass empty queue states to queue delete
Queue deletion on vhost deletion calls directly to rabbit_amqqueue.
Queue states are not available, but we can provide an empty map as
in deletion the states are only needed for cleanup.
* Generate quorum queue stats and events
Consumer delete events are still pending, as depend on basic.cancel
(not implemented yet), ra terminating or ra detecting channel down
[#154472241]
* Ensure quorum mapping entries are available before metric emission
[#154472241]
* Configure data_dir, uses new RABBITMQ_QUORUM_BASE env var
[#154472152]
* Use untracked enqueues when sending wihtout channel
Updated several other calls missed during the quorum implementation
* Revert "Configure data_dir, uses new RABBITMQ_QUORUM_BASE env var"
This reverts commit f2261212410affecb238fcbd1fb451381aee4036.
* Configure data_dir, uses new RABBITMQ_QUORUM_DIR based on mnesia dir
[#154472152]
* Fix get_quorum_state
* Fix calculation of quorum pids
* Move all quorum queues code to its own module
[#154472241]
* Return an error when declaring a quorum queue with an incompatible argument
[#154521696]
* Cleanup of quorum queue state after queue delete
Also fixes some existing problems where the state wasn't properly
stored
[#155458625]
* Revert Revert "Declare a quorum queue using the queue.declare method"
* Remove duplicated state info
[#154472241]
* Start/stop multi-node quorum queue
[#154472231]
[#154472236]
* Restart nodes in a multi-node quorum cluster
[#154472238]
* Test restart and leadership takeover on multiple nodes
[#154472238]
* Wait for leader down after deleting a quorum cluster
It ensures an smooth delete-declare sequence without race
conditions. The test included here detected the situation before
the fix.
[#154472236]
* Populate quorum_mapping from mnesia when not available
Ensures that leader nodes that don't have direct requests can get
the mapping ra name -> queue name
* Cosmetics
* Do not emit core metrics if queue has just been deleted
* Use rabbit_mnesia:is_process_alive
Fixes bug introduced by cac9583e1bb2705be7f06c2ab7f416a75d11c875
[#154472231]
* Only try to report stats if quorum process is alive
* Implement cancel consumer callback
Deletes metrics and sends consumer deleted event
* Remove unnecessary trigger election call
ra:restart_node has already been called during the recovery
* Apply cancellation callback on node hosting the channel
* Cosmetics
* Read new fifo metrics which store directly total, ready and unack
* Implement basic.cancel for quorum queues
* Store leader in amqqueue record, report all in stats
[#154472407]
* Declare quorum queue in mnesia before starting the ra cluster
Record needs to be stored first to update the leader on ra effects
* Revert
* Purge quorum queues
[#154472182]
* Improve use of untracked_enqueue
Choose the persisted leader id instead of just using the id of the
leader at point of creation.
* Store quorum leader in the pid field of amqqueue record
Same as mirrored queues, no real need for an additional field
* Improve recovery
When a ra node has never been started on a rabbit node ensure it doesn't
fail but instead rebuilds the config and starts the node as a new node.
Also fix issue when a quorum queue is declared when one of it's rabbit
nodes are unavailable.
[#157054606]
* Cleanup core metrics after leader change
[#157054473]
* Return an error on sync_queue on quorum queues
[#154472334]
* Return an error on cancel_sync_queue on quorum queues
[#154472337]
* Fix basic_cancel and basic_consume return values
Ensure the quorum queue state is always returned by these functions.
* Restore arity of amqqeueu delete and purge functions.
This avoids some breaking changes in the cli.
* Fix bug returning consumers.
* remove rogue debug log
* Integrate ingress flow control with quorum queues
[#157000583]
* Configure commands soft limit
[#157000583]
* Support quorum pids on rabbit_mnesia:is_process_alive
* Publish consumers metric for quorum queues
* Whitelist quorum directory in is_virgin_node
Allow the quorum directoy to exist without affecting the status of the
Rabbit node.
* Delete queue_metrics on leader change.
Also run the become_leader handler in a separate process to avoid
blocking.
[#157424225]
* Report cluster status in quorum queue infos. New per node status command.
Related to
[#157146500]
* Remove quorum_mapping table
As we can store the full queue name resource as the cluster id of the
ra_fifo_client state we can avoid needed the quorum_mapping table.
* Fix xref issue
* Provide quorum members information in stats
[#157146500]
* fix unused variable
* quorum queue multiple declare handling
Extend rabbit_amqqueue:internal_declare/2 to indicate if the queue
record was created or exisiting. From this we can then provide a code
path that should handle concurrent queue declares of the same quorum
queue.
* Return an error when declaring exclusive/auto-delete quorum queue
[#157472160]
* Restore lost changes
from 79c9bd201e1eac006a42bd162e7c86df96496629
* recover another part of commit
* fixup cherry pick
* Ra io/file metrics handler and stats publishing
[#157193081]
* Revert "Ra io/file metrics handler and stats publishing"
This reverts commit 05d15c786540322583fc655709825db215b70952.
* Do not issue confirms on node down for quorum queues.
Only a ra_event should be used to issue positive confirms for a quorum
queue.
* Ra stats publishing
[#157193081]
* Pick consumer utilisation from ra data
[#155402726]
* Handle error when deleting a quorum queue and all nodes are already down
This is in fact a successful deletion as all raft nodes are already 'stopped'
[#158656366]
* Return an error when declaring non-durable quorum queues
[#158656454]
* Rename dirty_query to committed_query
* Delete stats on leader node
[#158661152]
* Give full list of nodes to fifo client
* Handle timeout in quorum basic_get
* Fix unused variable error
* Handle timeout in basic get
[#158656366]
* Force GC after purge
[#158789389]
* Increase `ra:delete_cluster` timeout to 120s
* Revert "Force GC after purge"
This reverts commit 5c98bf22994eb39004760799d3a2c5041d16e9d4.
* Add quorum member command
[#157481599]
* Delete quorum member command
[#157481599]
* Implement basic.recover for quorum queues
[#157597411]
* Change concumer utilisation
to use the new ra_fifo table and api.
* Set max quorum queue size limit
Defaults to 7, can be configured per queue on queue.declare
Nodes are selected randomly from the list of nodes, but the one
that is executing the queue.declare command
[#159338081]
* remove potentially unrelated changes to rabbit_networking
* Move ra_fifo to rabbit
Copied ra_fifo to rabbit and renamed it rabbit_fifo.
[#159338031]
* rabbit_fifo tidy up
* rabbit_fifo tidy up
* rabbit_fifo: customer -> consumer rename
* Move ra_fifo tests
[#159338031]
* Tweak quorum_queue defaults
* quorum_queue test reliability
* Optimise quorum_queue test suite.
By only starting a rabbit cluster per group rather than test.
[#160612638]
* Renamings in line with ra API changes
* rabbit_fifo fixes
* Update with ra API changes
Ra has consolidated and simplified it's api. These changes update to
confirm to that.
* Update rabbit_fifo with latest ra changes
* Clean up out of date comment
* Return map of states
* Add test case for basic.get on an empty queue
Before the previous patch, any subsequent basic.get would crash as
the map of states had been replaced by a single state.
* Clarify use of deliver tags on record_sent
* Clean up queues after testcase
* Remove erlang monitor of quorum queues in rabbit_channel
The eol event can be used instead
* Use macros to make clearer distinctions between quorum/classic queues
Cosmetic only
* Erase queue stats on 'eol' event
* Update to follow Ra's cluster_id -> cluster_name rename.
* Rename qourum-cluster-size
To quorum-initial-group-size
* Issue confirms on quorum queue eol
Also avoid creating quorum queue session state on queue operation
methods.
* Only classic queues should be notified on channel down
* Quorum queues do not support global qos
Exit with protocol error of a basic.consume for a quorum queue is issued
on a channel with global qos enabled.
* unused variable name
* Refactoring
Strictly enfornce that channels do not monitor quorum queues.
* Refactor foreach_per_queue in the channel.
To make it call classic and quorum queues the same way.
[#161314899]
* rename function
* Query classic and quorum queues separately
during recovery as they should not be marked as stopped during failed
vhost recovery.
* Remove force_event_refresh function
As the only user of this function, the management API no longer requires
it.
* fix errors
* Remove created_at from amqqueue record
[#161343680]
* rabbit_fifo: support AMQP 1.0 consumer credit
This change implements an alternative consumer credit mechanism similar
to AMQP 1.0 link credit where the credit (prefetch) isn't automatically
topped up as deliveries are settled and instead needs to be manually
increased using a credit command. This is to be integrated with the AMQP
1.0 plugin.
[#161256187]
* Add basic.credit support for quorum queues.
Added support for AMQP 1.0 transfer flow control.
[#161256187]
* Make quorum queue recover idempotent
So that if a vhost crashes and runs the recover steps it doesn't fail
because ra servers are still running.
[#161343651]
* Add tests for vhost deletion
To ensure quorum queues are cleaned up on vhost removal.
Also fix xref issue.
[#161343673]
* remove unused clause
* always return latest value of queue
* Add rabbitmq-queues scripts. Remove ra config from .bat scripts.
* Return error if trying to get quorum status of a classic queue.
											
										 
											2018-10-29 17:47:29 +08:00
										 |  |  |                                   <<"bq_variable_queue_delete_msg_store_files_callback1">>, | 
					
						
							| 
									
										
										
										
											2020-09-29 18:43:24 +08:00
										 |  |  |                                   QTState), | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |     {ok, CountMinusOne} = rabbit_amqqueue:purge(Q), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     %% give the queue a second to receive the close_fds callback msg
 | 
					
						
							|  |  |  |     timer:sleep(1000), | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-09 23:05:37 +08:00
										 |  |  |     rabbit_amqqueue:delete(Q, false, false, <<"acting-user">>), | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |     passed. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bq_queue_recover(Config) -> | 
					
						
							|  |  |  |     passed = rabbit_ct_broker_helpers:rpc(Config, 0, | 
					
						
							|  |  |  |       ?MODULE, bq_queue_recover1, [Config]). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bq_queue_recover1(Config) -> | 
					
						
							| 
									
										
										
										
											2021-04-07 20:45:43 +08:00
										 |  |  |     init_queue_index(), | 
					
						
							| 
									
										
										
										
											2021-04-09 21:01:28 +08:00
										 |  |  |     Count = 2 * ?INDEX:next_segment_boundary(0), | 
					
						
							| 
									
										
										
										
											2018-10-11 18:12:39 +08:00
										 |  |  |     QName0 = queue_name(Config, <<"bq_queue_recover-q">>), | 
					
						
							|  |  |  |     {new, Q} = rabbit_amqqueue:declare(QName0, true, false, [], none, <<"acting-user">>), | 
					
						
							|  |  |  |     QName = amqqueue:get_name(Q), | 
					
						
							|  |  |  |     QPid = amqqueue:get_pid(Q), | 
					
						
							| 
									
										
										
										
											2020-09-29 18:43:24 +08:00
										 |  |  |     QT = publish_and_confirm(Q, <<>>, Count), | 
					
						
							| 
									
										
										
										
											2019-01-24 21:04:42 +08:00
										 |  |  |     SupPid = get_queue_sup_pid(Q), | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |     true = is_pid(SupPid), | 
					
						
							|  |  |  |     exit(SupPid, kill), | 
					
						
							|  |  |  |     exit(QPid, kill), | 
					
						
							|  |  |  |     MRef = erlang:monitor(process, QPid), | 
					
						
							|  |  |  |     receive {'DOWN', MRef, process, QPid, _Info} -> ok | 
					
						
							|  |  |  |     after 10000 -> exit(timeout_waiting_for_queue_death) | 
					
						
							|  |  |  |     end, | 
					
						
							| 
									
										
										
										
											2017-03-23 02:30:08 +08:00
										 |  |  |     rabbit_amqqueue:stop(?VHOST), | 
					
						
							| 
									
										
										
										
											2020-09-29 18:43:24 +08:00
										 |  |  |     {Recovered, []} = rabbit_amqqueue:recover(?VHOST), | 
					
						
							| 
									
										
										
										
											2019-02-13 06:26:47 +08:00
										 |  |  |     rabbit_amqqueue:start(Recovered), | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |     {ok, Limiter} = rabbit_limiter:start_link(no_id), | 
					
						
							|  |  |  |     rabbit_amqqueue:with_or_die( | 
					
						
							|  |  |  |       QName, | 
					
						
							| 
									
										
										
										
											2018-10-11 18:12:39 +08:00
										 |  |  |       fun (Q1) when ?is_amqqueue(Q1) -> | 
					
						
							|  |  |  |               QPid1 = amqqueue:get_pid(Q1), | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |               CountMinusOne = Count - 1, | 
					
						
							| 
									
										
										
										
											2020-09-29 18:43:24 +08:00
										 |  |  |               {ok, CountMinusOne, {QName, QPid1, _AckTag, true, _Msg}, _} = | 
					
						
							|  |  |  |                   rabbit_amqqueue:basic_get(Q1, false, Limiter, | 
					
						
							|  |  |  |                                             <<"bq_queue_recover1">>, QT), | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |               exit(QPid1, shutdown), | 
					
						
							|  |  |  |               VQ1 = variable_queue_init(Q, true), | 
					
						
							|  |  |  |               {{_Msg1, true, _AckTag1}, VQ2} = | 
					
						
							|  |  |  |                   rabbit_variable_queue:fetch(true, VQ1), | 
					
						
							|  |  |  |               CountMinusOne = rabbit_variable_queue:len(VQ2), | 
					
						
							|  |  |  |               _VQ3 = rabbit_variable_queue:delete_and_terminate(shutdown, VQ2), | 
					
						
							| 
									
										
										
										
											2017-03-09 23:05:37 +08:00
										 |  |  |               ok = rabbit_amqqueue:internal_delete(QName, <<"acting-user">>) | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |       end), | 
					
						
							|  |  |  |     passed. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-24 21:04:42 +08:00
										 |  |  | %% Return the PID of the given queue's supervisor.
 | 
					
						
							| 
									
										
										
										
											2018-10-11 18:12:39 +08:00
										 |  |  | get_queue_sup_pid(Q) when ?is_amqqueue(Q) -> | 
					
						
							|  |  |  |     QName = amqqueue:get_name(Q), | 
					
						
							|  |  |  |     QPid = amqqueue:get_pid(Q), | 
					
						
							| 
									
										
										
										
											2019-01-24 21:04:42 +08:00
										 |  |  |     VHost = QName#resource.virtual_host, | 
					
						
							|  |  |  |     {ok, AmqSup} = rabbit_amqqueue_sup_sup:find_for_vhost(VHost, node(QPid)), | 
					
						
							|  |  |  |     Sups = supervisor:which_children(AmqSup), | 
					
						
							|  |  |  |     get_queue_sup_pid(Sups, QPid). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | get_queue_sup_pid([{_, SupPid, _, _} | Rest], QueuePid) -> | 
					
						
							|  |  |  |     WorkerPids = [Pid || {_, Pid, _, _} <- supervisor:which_children(SupPid)], | 
					
						
							|  |  |  |     case lists:member(QueuePid, WorkerPids) of | 
					
						
							|  |  |  |         true  -> SupPid; | 
					
						
							|  |  |  |         false -> get_queue_sup_pid(Rest, QueuePid) | 
					
						
							|  |  |  |     end; | 
					
						
							|  |  |  | get_queue_sup_pid([], _QueuePid) -> | 
					
						
							|  |  |  |     undefined. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  | variable_queue_dynamic_duration_change(Config) -> | 
					
						
							|  |  |  |     passed = rabbit_ct_broker_helpers:rpc(Config, 0, | 
					
						
							|  |  |  |       ?MODULE, variable_queue_dynamic_duration_change1, [Config]). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | variable_queue_dynamic_duration_change1(Config) -> | 
					
						
							|  |  |  |     with_fresh_variable_queue( | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  |       fun variable_queue_dynamic_duration_change2/2, | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |       ?config(variable_queue_type, Config)). | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  | variable_queue_dynamic_duration_change2(VQ0, _QName) -> | 
					
						
							| 
									
										
										
										
											2021-04-09 21:01:28 +08:00
										 |  |  |     SegmentSize = ?INDEX:next_segment_boundary(0), | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     %% start by sending in a couple of segments worth
 | 
					
						
							|  |  |  |     Len = 2*SegmentSize, | 
					
						
							|  |  |  |     VQ1 = variable_queue_publish(false, Len, VQ0), | 
					
						
							|  |  |  |     %% squeeze and relax queue
 | 
					
						
							|  |  |  |     Churn = Len div 32, | 
					
						
							|  |  |  |     VQ2 = publish_fetch_and_ack(Churn, Len, VQ1), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     {Duration, VQ3} = rabbit_variable_queue:ram_duration(VQ2), | 
					
						
							|  |  |  |     VQ7 = lists:foldl( | 
					
						
							|  |  |  |             fun (Duration1, VQ4) -> | 
					
						
							|  |  |  |                     {_Duration, VQ5} = rabbit_variable_queue:ram_duration(VQ4), | 
					
						
							|  |  |  |                     VQ6 = variable_queue_set_ram_duration_target( | 
					
						
							|  |  |  |                             Duration1, VQ5), | 
					
						
							|  |  |  |                     publish_fetch_and_ack(Churn, Len, VQ6) | 
					
						
							|  |  |  |             end, VQ3, [Duration / 4, 0, Duration / 4, infinity]), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     %% drain
 | 
					
						
							|  |  |  |     {VQ8, AckTags} = variable_queue_fetch(Len, false, false, Len, VQ7), | 
					
						
							|  |  |  |     {_Guids, VQ9} = rabbit_variable_queue:ack(AckTags, VQ8), | 
					
						
							|  |  |  |     {empty, VQ10} = rabbit_variable_queue:fetch(true, VQ9), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     VQ10. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | variable_queue_partial_segments_delta_thing(Config) -> | 
					
						
							|  |  |  |     passed = rabbit_ct_broker_helpers:rpc(Config, 0, | 
					
						
							|  |  |  |       ?MODULE, variable_queue_partial_segments_delta_thing1, [Config]). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | variable_queue_partial_segments_delta_thing1(Config) -> | 
					
						
							|  |  |  |     with_fresh_variable_queue( | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  |       fun variable_queue_partial_segments_delta_thing2/2, | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |       ?config(variable_queue_type, Config)). | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  | variable_queue_partial_segments_delta_thing2(VQ0, _QName) -> | 
					
						
							| 
									
										
										
										
											2021-04-09 21:01:28 +08:00
										 |  |  |     SegmentSize = ?INDEX:next_segment_boundary(0), | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |     HalfSegment = SegmentSize div 2, | 
					
						
							|  |  |  |     OneAndAHalfSegment = SegmentSize + HalfSegment, | 
					
						
							|  |  |  |     VQ1 = variable_queue_publish(true, OneAndAHalfSegment, VQ0), | 
					
						
							|  |  |  |     {_Duration, VQ2} = rabbit_variable_queue:ram_duration(VQ1), | 
					
						
							|  |  |  |     VQ3 = check_variable_queue_status( | 
					
						
							|  |  |  |             variable_queue_set_ram_duration_target(0, VQ2), | 
					
						
							|  |  |  |             %% one segment in q3, and half a segment in delta
 | 
					
						
							|  |  |  |             [{delta, {delta, SegmentSize, HalfSegment, OneAndAHalfSegment}}, | 
					
						
							|  |  |  |              {q3, SegmentSize}, | 
					
						
							|  |  |  |              {len, SegmentSize + HalfSegment}]), | 
					
						
							|  |  |  |     VQ4 = variable_queue_set_ram_duration_target(infinity, VQ3), | 
					
						
							|  |  |  |     VQ5 = check_variable_queue_status( | 
					
						
							|  |  |  |             variable_queue_publish(true, 1, VQ4), | 
					
						
							|  |  |  |             %% one alpha, but it's in the same segment as the deltas
 | 
					
						
							|  |  |  |             [{q1, 1}, | 
					
						
							|  |  |  |              {delta, {delta, SegmentSize, HalfSegment, OneAndAHalfSegment}}, | 
					
						
							|  |  |  |              {q3, SegmentSize}, | 
					
						
							|  |  |  |              {len, SegmentSize + HalfSegment + 1}]), | 
					
						
							|  |  |  |     {VQ6, AckTags} = variable_queue_fetch(SegmentSize, true, false, | 
					
						
							|  |  |  |                                           SegmentSize + HalfSegment + 1, VQ5), | 
					
						
							|  |  |  |     VQ7 = check_variable_queue_status( | 
					
						
							|  |  |  |             VQ6, | 
					
						
							|  |  |  |             %% the half segment should now be in q3
 | 
					
						
							|  |  |  |             [{q1, 1}, | 
					
						
							|  |  |  |              {delta, {delta, undefined, 0, undefined}}, | 
					
						
							|  |  |  |              {q3, HalfSegment}, | 
					
						
							|  |  |  |              {len, HalfSegment + 1}]), | 
					
						
							|  |  |  |     {VQ8, AckTags1} = variable_queue_fetch(HalfSegment + 1, true, false, | 
					
						
							|  |  |  |                                            HalfSegment + 1, VQ7), | 
					
						
							|  |  |  |     {_Guids, VQ9} = rabbit_variable_queue:ack(AckTags ++ AckTags1, VQ8), | 
					
						
							|  |  |  |     %% should be empty now
 | 
					
						
							|  |  |  |     {empty, VQ10} = rabbit_variable_queue:fetch(true, VQ9), | 
					
						
							|  |  |  |     VQ10. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | variable_queue_all_the_bits_not_covered_elsewhere_A(Config) -> | 
					
						
							|  |  |  |     passed = rabbit_ct_broker_helpers:rpc(Config, 0, | 
					
						
							|  |  |  |       ?MODULE, variable_queue_all_the_bits_not_covered_elsewhere_A1, [Config]). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | variable_queue_all_the_bits_not_covered_elsewhere_A1(Config) -> | 
					
						
							|  |  |  |     with_fresh_variable_queue( | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  |       fun variable_queue_all_the_bits_not_covered_elsewhere_A2/2, | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |       ?config(variable_queue_type, Config)). | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  | variable_queue_all_the_bits_not_covered_elsewhere_A2(VQ0, QName) -> | 
					
						
							| 
									
										
										
										
											2021-04-09 21:01:28 +08:00
										 |  |  |     Count = 2 * ?INDEX:next_segment_boundary(0), | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |     VQ1 = variable_queue_publish(true, Count, VQ0), | 
					
						
							|  |  |  |     VQ2 = variable_queue_publish(false, Count, VQ1), | 
					
						
							|  |  |  |     VQ3 = variable_queue_set_ram_duration_target(0, VQ2), | 
					
						
							|  |  |  |     {VQ4, _AckTags}  = variable_queue_fetch(Count, true, false, | 
					
						
							|  |  |  |                                             Count + Count, VQ3), | 
					
						
							|  |  |  |     {VQ5, _AckTags1} = variable_queue_fetch(Count, false, false, | 
					
						
							|  |  |  |                                             Count, VQ4), | 
					
						
							|  |  |  |     _VQ6 = rabbit_variable_queue:terminate(shutdown, VQ5), | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  |     VQ7 = variable_queue_init(test_amqqueue(QName, true), true), | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |     {{_Msg1, true, _AckTag1}, VQ8} = rabbit_variable_queue:fetch(true, VQ7), | 
					
						
							|  |  |  |     Count1 = rabbit_variable_queue:len(VQ8), | 
					
						
							|  |  |  |     VQ9 = variable_queue_publish(false, 1, VQ8), | 
					
						
							|  |  |  |     VQ10 = variable_queue_set_ram_duration_target(0, VQ9), | 
					
						
							|  |  |  |     {VQ11, _AckTags2} = variable_queue_fetch(Count1, true, true, Count, VQ10), | 
					
						
							|  |  |  |     {VQ12, _AckTags3} = variable_queue_fetch(1, false, false, 1, VQ11), | 
					
						
							|  |  |  |     VQ12. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | variable_queue_all_the_bits_not_covered_elsewhere_B(Config) -> | 
					
						
							|  |  |  |     passed = rabbit_ct_broker_helpers:rpc(Config, 0, | 
					
						
							|  |  |  |       ?MODULE, variable_queue_all_the_bits_not_covered_elsewhere_B1, [Config]). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | variable_queue_all_the_bits_not_covered_elsewhere_B1(Config) -> | 
					
						
							|  |  |  |     with_fresh_variable_queue( | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  |       fun variable_queue_all_the_bits_not_covered_elsewhere_B2/2, | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |       ?config(variable_queue_type, Config)). | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  | variable_queue_all_the_bits_not_covered_elsewhere_B2(VQ0, QName) -> | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |     VQ1 = variable_queue_set_ram_duration_target(0, VQ0), | 
					
						
							|  |  |  |     VQ2 = variable_queue_publish(false, 4, VQ1), | 
					
						
							|  |  |  |     {VQ3, AckTags} = variable_queue_fetch(2, false, false, 4, VQ2), | 
					
						
							|  |  |  |     {_Guids, VQ4} = | 
					
						
							|  |  |  |         rabbit_variable_queue:requeue(AckTags, VQ3), | 
					
						
							|  |  |  |     VQ5 = rabbit_variable_queue:timeout(VQ4), | 
					
						
							|  |  |  |     _VQ6 = rabbit_variable_queue:terminate(shutdown, VQ5), | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  |     VQ7 = variable_queue_init(test_amqqueue(QName, true), true), | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |     {empty, VQ8} = rabbit_variable_queue:fetch(false, VQ7), | 
					
						
							|  |  |  |     VQ8. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | variable_queue_drop(Config) -> | 
					
						
							|  |  |  |     passed = rabbit_ct_broker_helpers:rpc(Config, 0, | 
					
						
							|  |  |  |       ?MODULE, variable_queue_drop1, [Config]). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | variable_queue_drop1(Config) -> | 
					
						
							|  |  |  |     with_fresh_variable_queue( | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  |       fun variable_queue_drop2/2, | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |       ?config(variable_queue_type, Config)). | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  | variable_queue_drop2(VQ0, _QName) -> | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |     %% start by sending a messages
 | 
					
						
							|  |  |  |     VQ1 = variable_queue_publish(false, 1, VQ0), | 
					
						
							|  |  |  |     %% drop message with AckRequired = true
 | 
					
						
							|  |  |  |     {{MsgId, AckTag}, VQ2} = rabbit_variable_queue:drop(true, VQ1), | 
					
						
							|  |  |  |     true = rabbit_variable_queue:is_empty(VQ2), | 
					
						
							|  |  |  |     true = AckTag =/= undefinded, | 
					
						
							|  |  |  |     %% drop again -> empty
 | 
					
						
							|  |  |  |     {empty, VQ3} = rabbit_variable_queue:drop(false, VQ2), | 
					
						
							|  |  |  |     %% requeue
 | 
					
						
							|  |  |  |     {[MsgId], VQ4} = rabbit_variable_queue:requeue([AckTag], VQ3), | 
					
						
							|  |  |  |     %% drop message with AckRequired = false
 | 
					
						
							|  |  |  |     {{MsgId, undefined}, VQ5} = rabbit_variable_queue:drop(false, VQ4), | 
					
						
							|  |  |  |     true = rabbit_variable_queue:is_empty(VQ5), | 
					
						
							|  |  |  |     VQ5. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | variable_queue_fold_msg_on_disk(Config) -> | 
					
						
							|  |  |  |     passed = rabbit_ct_broker_helpers:rpc(Config, 0, | 
					
						
							|  |  |  |       ?MODULE, variable_queue_fold_msg_on_disk1, [Config]). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | variable_queue_fold_msg_on_disk1(Config) -> | 
					
						
							|  |  |  |     with_fresh_variable_queue( | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  |       fun variable_queue_fold_msg_on_disk2/2, | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |       ?config(variable_queue_type, Config)). | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  | variable_queue_fold_msg_on_disk2(VQ0, _QName) -> | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |     VQ1 = variable_queue_publish(true, 1, VQ0), | 
					
						
							|  |  |  |     {VQ2, AckTags} = variable_queue_fetch(1, true, false, 1, VQ1), | 
					
						
							|  |  |  |     {ok, VQ3} = rabbit_variable_queue:ackfold(fun (_M, _A, ok) -> ok end, | 
					
						
							|  |  |  |                                               ok, VQ2, AckTags), | 
					
						
							|  |  |  |     VQ3. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | variable_queue_dropfetchwhile(Config) -> | 
					
						
							|  |  |  |     passed = rabbit_ct_broker_helpers:rpc(Config, 0, | 
					
						
							|  |  |  |       ?MODULE, variable_queue_dropfetchwhile1, [Config]). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | variable_queue_dropfetchwhile1(Config) -> | 
					
						
							|  |  |  |     with_fresh_variable_queue( | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  |       fun variable_queue_dropfetchwhile2/2, | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |       ?config(variable_queue_type, Config)). | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  | variable_queue_dropfetchwhile2(VQ0, _QName) -> | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |     Count = 10, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     %% add messages with sequential expiry
 | 
					
						
							|  |  |  |     VQ1 = variable_queue_publish( | 
					
						
							|  |  |  |             false, 1, Count, | 
					
						
							|  |  |  |             fun (N, Props) -> Props#message_properties{expiry = N} end, | 
					
						
							|  |  |  |             fun erlang:term_to_binary/1, VQ0), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     %% fetch the first 5 messages
 | 
					
						
							|  |  |  |     {#message_properties{expiry = 6}, {Msgs, AckTags}, VQ2} = | 
					
						
							|  |  |  |         rabbit_variable_queue:fetchwhile( | 
					
						
							|  |  |  |           fun (#message_properties{expiry = Expiry}) -> Expiry =< 5 end, | 
					
						
							|  |  |  |           fun (Msg, AckTag, {MsgAcc, AckAcc}) -> | 
					
						
							|  |  |  |                   {[Msg | MsgAcc], [AckTag | AckAcc]} | 
					
						
							|  |  |  |           end, {[], []}, VQ1), | 
					
						
							|  |  |  |     true = lists:seq(1, 5) == [msg2int(M) || M <- lists:reverse(Msgs)], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     %% requeue them
 | 
					
						
							|  |  |  |     {_MsgIds, VQ3} = rabbit_variable_queue:requeue(AckTags, VQ2), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     %% drop the first 5 messages
 | 
					
						
							|  |  |  |     {#message_properties{expiry = 6}, VQ4} = | 
					
						
							|  |  |  |         rabbit_variable_queue:dropwhile( | 
					
						
							|  |  |  |           fun (#message_properties {expiry = Expiry}) -> Expiry =< 5 end, VQ3), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     %% fetch 5
 | 
					
						
							|  |  |  |     VQ5 = lists:foldl(fun (N, VQN) -> | 
					
						
							|  |  |  |                               {{Msg, _, _}, VQM} = | 
					
						
							|  |  |  |                                   rabbit_variable_queue:fetch(false, VQN), | 
					
						
							|  |  |  |                               true = msg2int(Msg) == N, | 
					
						
							|  |  |  |                               VQM | 
					
						
							|  |  |  |                       end, VQ4, lists:seq(6, Count)), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     %% should be empty now
 | 
					
						
							|  |  |  |     true = rabbit_variable_queue:is_empty(VQ5), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     VQ5. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-10 23:10:00 +08:00
										 |  |  | variable_queue_dropwhile_restart(Config) -> | 
					
						
							|  |  |  |     passed = rabbit_ct_broker_helpers:rpc(Config, 0, | 
					
						
							|  |  |  |       ?MODULE, variable_queue_dropwhile_restart1, [Config]). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | variable_queue_dropwhile_restart1(Config) -> | 
					
						
							|  |  |  |     with_fresh_variable_queue( | 
					
						
							|  |  |  |       fun variable_queue_dropwhile_restart2/2, | 
					
						
							|  |  |  |       ?config(variable_queue_type, Config)). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | variable_queue_dropwhile_restart2(VQ0, QName) -> | 
					
						
							|  |  |  |     Count = 10000, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     %% add messages with sequential expiry
 | 
					
						
							|  |  |  |     VQ1 = variable_queue_publish( | 
					
						
							|  |  |  |             true, 1, Count, | 
					
						
							|  |  |  |             fun (N, Props) -> Props#message_properties{expiry = N} end, | 
					
						
							|  |  |  |             fun erlang:term_to_binary/1, VQ0), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     %% drop the first 5 messages
 | 
					
						
							|  |  |  |     {#message_properties{expiry = 6}, VQ2} = | 
					
						
							|  |  |  |         rabbit_variable_queue:dropwhile( | 
					
						
							|  |  |  |           fun (#message_properties {expiry = Expiry}) -> Expiry =< 5 end, VQ1), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     _VQ3 = rabbit_variable_queue:terminate(shutdown, VQ2), | 
					
						
							|  |  |  |     Terms = variable_queue_read_terms(QName), | 
					
						
							|  |  |  |     VQ4 = variable_queue_init(test_amqqueue(QName, true), Terms), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     %% fetch 5
 | 
					
						
							|  |  |  |     VQ5 = lists:foldl(fun (_, VQN) -> | 
					
						
							|  |  |  |                               {{_, _, _}, VQM} = | 
					
						
							|  |  |  |                                   rabbit_variable_queue:fetch(false, VQN), | 
					
						
							|  |  |  |                               VQM | 
					
						
							|  |  |  |                       end, VQ4, lists:seq(6, Count)), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     %% should be empty now
 | 
					
						
							|  |  |  |     true = rabbit_variable_queue:is_empty(VQ5), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     VQ5. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | variable_queue_dropwhile_sync_restart(Config) -> | 
					
						
							|  |  |  |     passed = rabbit_ct_broker_helpers:rpc(Config, 0, | 
					
						
							|  |  |  |       ?MODULE, variable_queue_dropwhile_sync_restart1, [Config]). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | variable_queue_dropwhile_sync_restart1(Config) -> | 
					
						
							|  |  |  |     with_fresh_variable_queue( | 
					
						
							|  |  |  |       fun variable_queue_dropwhile_sync_restart2/2, | 
					
						
							|  |  |  |       ?config(variable_queue_type, Config)). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | variable_queue_dropwhile_sync_restart2(VQ0, QName) -> | 
					
						
							|  |  |  |     Count = 10000, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     %% add messages with sequential expiry
 | 
					
						
							|  |  |  |     VQ1 = variable_queue_publish( | 
					
						
							|  |  |  |             true, 1, Count, | 
					
						
							|  |  |  |             fun (N, Props) -> Props#message_properties{expiry = N} end, | 
					
						
							|  |  |  |             fun erlang:term_to_binary/1, VQ0), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     %% drop the first 5 messages
 | 
					
						
							|  |  |  |     {#message_properties{expiry = 6}, VQ2} = | 
					
						
							|  |  |  |         rabbit_variable_queue:dropwhile( | 
					
						
							|  |  |  |           fun (#message_properties {expiry = Expiry}) -> Expiry =< 5 end, VQ1), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     %% Queue index sync.
 | 
					
						
							|  |  |  |     VQ2b = rabbit_variable_queue:handle_pre_hibernate(VQ2), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     _VQ3 = rabbit_variable_queue:terminate(shutdown, VQ2b), | 
					
						
							|  |  |  |     Terms = variable_queue_read_terms(QName), | 
					
						
							|  |  |  |     VQ4 = variable_queue_init(test_amqqueue(QName, true), Terms), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     %% fetch 5
 | 
					
						
							|  |  |  |     VQ5 = lists:foldl(fun (_, VQN) -> | 
					
						
							|  |  |  |                               {{_, _, _}, VQM} = | 
					
						
							|  |  |  |                                   rabbit_variable_queue:fetch(false, VQN), | 
					
						
							|  |  |  |                               VQM | 
					
						
							|  |  |  |                       end, VQ4, lists:seq(6, Count)), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     %% should be empty now
 | 
					
						
							|  |  |  |     true = rabbit_variable_queue:is_empty(VQ5), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     VQ5. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  | variable_queue_dropwhile_varying_ram_duration(Config) -> | 
					
						
							|  |  |  |     passed = rabbit_ct_broker_helpers:rpc(Config, 0, | 
					
						
							|  |  |  |       ?MODULE, variable_queue_dropwhile_varying_ram_duration1, [Config]). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | variable_queue_dropwhile_varying_ram_duration1(Config) -> | 
					
						
							|  |  |  |     with_fresh_variable_queue( | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  |       fun variable_queue_dropwhile_varying_ram_duration2/2, | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |       ?config(variable_queue_type, Config)). | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  | variable_queue_dropwhile_varying_ram_duration2(VQ0, _QName) -> | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |     test_dropfetchwhile_varying_ram_duration( | 
					
						
							|  |  |  |       fun (VQ1) -> | 
					
						
							|  |  |  |               {_, VQ2} = rabbit_variable_queue:dropwhile( | 
					
						
							|  |  |  |                            fun (_) -> false end, VQ1), | 
					
						
							|  |  |  |               VQ2 | 
					
						
							|  |  |  |       end, VQ0). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | variable_queue_fetchwhile_varying_ram_duration(Config) -> | 
					
						
							|  |  |  |     passed = rabbit_ct_broker_helpers:rpc(Config, 0, | 
					
						
							|  |  |  |       ?MODULE, variable_queue_fetchwhile_varying_ram_duration1, [Config]). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | variable_queue_fetchwhile_varying_ram_duration1(Config) -> | 
					
						
							|  |  |  |     with_fresh_variable_queue( | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  |       fun variable_queue_fetchwhile_varying_ram_duration2/2, | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |       ?config(variable_queue_type, Config)). | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  | variable_queue_fetchwhile_varying_ram_duration2(VQ0, _QName) -> | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |     test_dropfetchwhile_varying_ram_duration( | 
					
						
							|  |  |  |       fun (VQ1) -> | 
					
						
							|  |  |  |               {_, ok, VQ2} = rabbit_variable_queue:fetchwhile( | 
					
						
							|  |  |  |                                fun (_) -> false end, | 
					
						
							|  |  |  |                                fun (_, _, A) -> A end, | 
					
						
							|  |  |  |                                ok, VQ1), | 
					
						
							|  |  |  |               VQ2 | 
					
						
							|  |  |  |       end, VQ0). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | test_dropfetchwhile_varying_ram_duration(Fun, VQ0) -> | 
					
						
							|  |  |  |     VQ1 = variable_queue_publish(false, 1, VQ0), | 
					
						
							|  |  |  |     VQ2 = variable_queue_set_ram_duration_target(0, VQ1), | 
					
						
							|  |  |  |     VQ3 = Fun(VQ2), | 
					
						
							|  |  |  |     VQ4 = variable_queue_set_ram_duration_target(infinity, VQ3), | 
					
						
							|  |  |  |     VQ5 = variable_queue_publish(false, 1, VQ4), | 
					
						
							|  |  |  |     VQ6 = Fun(VQ5), | 
					
						
							|  |  |  |     VQ6. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | variable_queue_ack_limiting(Config) -> | 
					
						
							|  |  |  |     passed = rabbit_ct_broker_helpers:rpc(Config, 0, | 
					
						
							|  |  |  |       ?MODULE, variable_queue_ack_limiting1, [Config]). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | variable_queue_ack_limiting1(Config) -> | 
					
						
							|  |  |  |     with_fresh_variable_queue( | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  |       fun variable_queue_ack_limiting2/2, | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |       ?config(variable_queue_type, Config)). | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  | variable_queue_ack_limiting2(VQ0, _Config) -> | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |     %% start by sending in a bunch of messages
 | 
					
						
							|  |  |  |     Len = 1024, | 
					
						
							|  |  |  |     VQ1 = variable_queue_publish(false, Len, VQ0), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     %% squeeze and relax queue
 | 
					
						
							|  |  |  |     Churn = Len div 32, | 
					
						
							|  |  |  |     VQ2 = publish_fetch_and_ack(Churn, Len, VQ1), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     %% update stats for duration
 | 
					
						
							|  |  |  |     {_Duration, VQ3} = rabbit_variable_queue:ram_duration(VQ2), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     %% fetch half the messages
 | 
					
						
							|  |  |  |     {VQ4, _AckTags} = variable_queue_fetch(Len div 2, false, false, Len, VQ3), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     VQ5 = check_variable_queue_status( | 
					
						
							|  |  |  |             VQ4, [{len,                         Len div 2}, | 
					
						
							|  |  |  |                   {messages_unacknowledged_ram, Len div 2}, | 
					
						
							|  |  |  |                   {messages_ready_ram,          Len div 2}, | 
					
						
							|  |  |  |                   {messages_ram,                Len}]), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     %% ensure all acks go to disk on 0 duration target
 | 
					
						
							|  |  |  |     VQ6 = check_variable_queue_status( | 
					
						
							|  |  |  |             variable_queue_set_ram_duration_target(0, VQ5), | 
					
						
							|  |  |  |             [{len,                         Len div 2}, | 
					
						
							|  |  |  |              {target_ram_count,            0}, | 
					
						
							|  |  |  |              {messages_unacknowledged_ram, 0}, | 
					
						
							|  |  |  |              {messages_ready_ram,          0}, | 
					
						
							|  |  |  |              {messages_ram,                0}]), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     VQ6. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | variable_queue_purge(Config) -> | 
					
						
							|  |  |  |     passed = rabbit_ct_broker_helpers:rpc(Config, 0, | 
					
						
							|  |  |  |       ?MODULE, variable_queue_purge1, [Config]). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | variable_queue_purge1(Config) -> | 
					
						
							|  |  |  |     with_fresh_variable_queue( | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  |       fun variable_queue_purge2/2, | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |       ?config(variable_queue_type, Config)). | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  | variable_queue_purge2(VQ0, _Config) -> | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |     LenDepth = fun (VQ) -> | 
					
						
							|  |  |  |                        {rabbit_variable_queue:len(VQ), | 
					
						
							|  |  |  |                         rabbit_variable_queue:depth(VQ)} | 
					
						
							|  |  |  |                end, | 
					
						
							|  |  |  |     VQ1         = variable_queue_publish(false, 10, VQ0), | 
					
						
							|  |  |  |     {VQ2, Acks} = variable_queue_fetch(6, false, false, 10, VQ1), | 
					
						
							|  |  |  |     {4, VQ3}    = rabbit_variable_queue:purge(VQ2), | 
					
						
							|  |  |  |     {0, 6}      = LenDepth(VQ3), | 
					
						
							|  |  |  |     {_, VQ4}    = rabbit_variable_queue:requeue(lists:sublist(Acks, 2), VQ3), | 
					
						
							|  |  |  |     {2, 6}      = LenDepth(VQ4), | 
					
						
							|  |  |  |     VQ5         = rabbit_variable_queue:purge_acks(VQ4), | 
					
						
							|  |  |  |     {2, 2}      = LenDepth(VQ5), | 
					
						
							|  |  |  |     VQ5. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | variable_queue_requeue(Config) -> | 
					
						
							|  |  |  |     passed = rabbit_ct_broker_helpers:rpc(Config, 0, | 
					
						
							|  |  |  |       ?MODULE, variable_queue_requeue1, [Config]). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | variable_queue_requeue1(Config) -> | 
					
						
							|  |  |  |     with_fresh_variable_queue( | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  |       fun variable_queue_requeue2/2, | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |       ?config(variable_queue_type, Config)). | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  | variable_queue_requeue2(VQ0, _Config) -> | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |     {_PendingMsgs, RequeuedMsgs, FreshMsgs, VQ1} = | 
					
						
							|  |  |  |         variable_queue_with_holes(VQ0), | 
					
						
							|  |  |  |     Msgs = | 
					
						
							|  |  |  |         lists:zip(RequeuedMsgs, | 
					
						
							|  |  |  |                   lists:duplicate(length(RequeuedMsgs), true)) ++ | 
					
						
							|  |  |  |         lists:zip(FreshMsgs, | 
					
						
							|  |  |  |                   lists:duplicate(length(FreshMsgs), false)), | 
					
						
							|  |  |  |     VQ2 = lists:foldl(fun ({I, Requeued}, VQa) -> | 
					
						
							|  |  |  |                               {{M, MRequeued, _}, VQb} = | 
					
						
							|  |  |  |                                   rabbit_variable_queue:fetch(true, VQa), | 
					
						
							|  |  |  |                               Requeued = MRequeued, %% assertion
 | 
					
						
							|  |  |  |                               I = msg2int(M),       %% assertion
 | 
					
						
							|  |  |  |                               VQb | 
					
						
							|  |  |  |                       end, VQ1, Msgs), | 
					
						
							|  |  |  |     {empty, VQ3} = rabbit_variable_queue:fetch(true, VQ2), | 
					
						
							|  |  |  |     VQ3. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | %% requeue from ram_pending_ack into q3, move to delta and then empty queue
 | 
					
						
							|  |  |  | variable_queue_requeue_ram_beta(Config) -> | 
					
						
							|  |  |  |     passed = rabbit_ct_broker_helpers:rpc(Config, 0, | 
					
						
							|  |  |  |       ?MODULE, variable_queue_requeue_ram_beta1, [Config]). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | variable_queue_requeue_ram_beta1(Config) -> | 
					
						
							|  |  |  |     with_fresh_variable_queue( | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  |       fun variable_queue_requeue_ram_beta2/2, | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |       ?config(variable_queue_type, Config)). | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  | variable_queue_requeue_ram_beta2(VQ0, _Config) -> | 
					
						
							| 
									
										
										
										
											2021-04-09 21:01:28 +08:00
										 |  |  |     Count = ?INDEX:next_segment_boundary(0)*2 + 2, | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |     VQ1 = variable_queue_publish(false, Count, VQ0), | 
					
						
							|  |  |  |     {VQ2, AcksR} = variable_queue_fetch(Count, false, false, Count, VQ1), | 
					
						
							|  |  |  |     {Back, Front} = lists:split(Count div 2, AcksR), | 
					
						
							|  |  |  |     {_, VQ3} = rabbit_variable_queue:requeue(erlang:tl(Back), VQ2), | 
					
						
							|  |  |  |     VQ4 = variable_queue_set_ram_duration_target(0, VQ3), | 
					
						
							|  |  |  |     {_, VQ5} = rabbit_variable_queue:requeue([erlang:hd(Back)], VQ4), | 
					
						
							|  |  |  |     VQ6 = requeue_one_by_one(Front, VQ5), | 
					
						
							|  |  |  |     {VQ7, AcksAll} = variable_queue_fetch(Count, false, true, Count, VQ6), | 
					
						
							|  |  |  |     {_, VQ8} = rabbit_variable_queue:ack(AcksAll, VQ7), | 
					
						
							|  |  |  |     VQ8. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-28 03:38:58 +08:00
										 |  |  | variable_queue_fold(Config) -> | 
					
						
							|  |  |  |     passed = rabbit_ct_broker_helpers:rpc(Config, 0, | 
					
						
							|  |  |  |       ?MODULE, variable_queue_fold1, [Config]). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | variable_queue_fold1(Config) -> | 
					
						
							|  |  |  |     with_fresh_variable_queue( | 
					
						
							|  |  |  |       fun variable_queue_fold2/2, | 
					
						
							|  |  |  |       ?config(variable_queue_type, Config)). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | variable_queue_fold2(VQ0, _Config) -> | 
					
						
							|  |  |  |     {PendingMsgs, RequeuedMsgs, FreshMsgs, VQ1} = | 
					
						
							|  |  |  |         variable_queue_with_holes(VQ0), | 
					
						
							|  |  |  |     Count = rabbit_variable_queue:depth(VQ1), | 
					
						
							|  |  |  |     Msgs = lists:sort(PendingMsgs ++ RequeuedMsgs ++ FreshMsgs), | 
					
						
							|  |  |  |     lists:foldl(fun (Cut, VQ2) -> | 
					
						
							|  |  |  |                         test_variable_queue_fold(Cut, Msgs, PendingMsgs, VQ2) | 
					
						
							|  |  |  |                 end, VQ1, [0, 1, 2, Count div 2, | 
					
						
							|  |  |  |                            Count - 1, Count, Count + 1, Count * 2]). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | test_variable_queue_fold(Cut, Msgs, PendingMsgs, VQ0) -> | 
					
						
							|  |  |  |     {Acc, VQ1} = rabbit_variable_queue:fold( | 
					
						
							|  |  |  |                    fun (M, _, Pending, A) -> | 
					
						
							|  |  |  |                            MInt = msg2int(M), | 
					
						
							|  |  |  |                            Pending = lists:member(MInt, PendingMsgs), %% assert
 | 
					
						
							|  |  |  |                            case MInt =< Cut of | 
					
						
							|  |  |  |                                true  -> {cont, [MInt | A]}; | 
					
						
							|  |  |  |                                false -> {stop, A} | 
					
						
							|  |  |  |                            end | 
					
						
							|  |  |  |                    end, [], VQ0), | 
					
						
							|  |  |  |     Expected = lists:takewhile(fun (I) -> I =< Cut end, Msgs), | 
					
						
							|  |  |  |     Expected = lists:reverse(Acc), %% assertion
 | 
					
						
							|  |  |  |     VQ1. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  | variable_queue_batch_publish(Config) -> | 
					
						
							|  |  |  |     passed = rabbit_ct_broker_helpers:rpc(Config, 0, | 
					
						
							|  |  |  |       ?MODULE, variable_queue_batch_publish1, [Config]). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | variable_queue_batch_publish1(Config) -> | 
					
						
							|  |  |  |     with_fresh_variable_queue( | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  |       fun variable_queue_batch_publish2/2, | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |       ?config(variable_queue_type, Config)). | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  | variable_queue_batch_publish2(VQ, _Config) -> | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |     Count = 10, | 
					
						
							|  |  |  |     VQ1 = variable_queue_batch_publish(true, Count, VQ), | 
					
						
							|  |  |  |     Count = rabbit_variable_queue:len(VQ1), | 
					
						
							|  |  |  |     VQ1. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | variable_queue_batch_publish_delivered(Config) -> | 
					
						
							|  |  |  |     passed = rabbit_ct_broker_helpers:rpc(Config, 0, | 
					
						
							|  |  |  |       ?MODULE, variable_queue_batch_publish_delivered1, [Config]). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | variable_queue_batch_publish_delivered1(Config) -> | 
					
						
							|  |  |  |     with_fresh_variable_queue( | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  |       fun variable_queue_batch_publish_delivered2/2, | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |       ?config(variable_queue_type, Config)). | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  | variable_queue_batch_publish_delivered2(VQ, _Config) -> | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |     Count = 10, | 
					
						
							|  |  |  |     VQ1 = variable_queue_batch_publish_delivered(true, Count, VQ), | 
					
						
							|  |  |  |     Count = rabbit_variable_queue:depth(VQ1), | 
					
						
							|  |  |  |     VQ1. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | %% same as test_variable_queue_requeue_ram_beta but randomly changing
 | 
					
						
							|  |  |  | %% the queue mode after every step.
 | 
					
						
							|  |  |  | variable_queue_mode_change(Config) -> | 
					
						
							|  |  |  |     passed = rabbit_ct_broker_helpers:rpc(Config, 0, | 
					
						
							|  |  |  |       ?MODULE, variable_queue_mode_change1, [Config]). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | variable_queue_mode_change1(Config) -> | 
					
						
							|  |  |  |     with_fresh_variable_queue( | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  |       fun variable_queue_mode_change2/2, | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |       ?config(variable_queue_type, Config)). | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  | variable_queue_mode_change2(VQ0, _Config) -> | 
					
						
							| 
									
										
										
										
											2021-04-09 21:01:28 +08:00
										 |  |  |     Count = ?INDEX:next_segment_boundary(0)*2 + 2, | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |     VQ1 = variable_queue_publish(false, Count, VQ0), | 
					
						
							|  |  |  |     VQ2 = maybe_switch_queue_mode(VQ1), | 
					
						
							|  |  |  |     {VQ3, AcksR} = variable_queue_fetch(Count, false, false, Count, VQ2), | 
					
						
							|  |  |  |     VQ4 = maybe_switch_queue_mode(VQ3), | 
					
						
							|  |  |  |     {Back, Front} = lists:split(Count div 2, AcksR), | 
					
						
							|  |  |  |     {_, VQ5} = rabbit_variable_queue:requeue(erlang:tl(Back), VQ4), | 
					
						
							|  |  |  |     VQ6 = maybe_switch_queue_mode(VQ5), | 
					
						
							|  |  |  |     VQ7 = variable_queue_set_ram_duration_target(0, VQ6), | 
					
						
							|  |  |  |     VQ8 = maybe_switch_queue_mode(VQ7), | 
					
						
							|  |  |  |     {_, VQ9} = rabbit_variable_queue:requeue([erlang:hd(Back)], VQ8), | 
					
						
							|  |  |  |     VQ10 = maybe_switch_queue_mode(VQ9), | 
					
						
							|  |  |  |     VQ11 = requeue_one_by_one(Front, VQ10), | 
					
						
							|  |  |  |     VQ12 = maybe_switch_queue_mode(VQ11), | 
					
						
							|  |  |  |     {VQ13, AcksAll} = variable_queue_fetch(Count, false, true, Count, VQ12), | 
					
						
							|  |  |  |     VQ14 = maybe_switch_queue_mode(VQ13), | 
					
						
							|  |  |  |     {_, VQ15} = rabbit_variable_queue:ack(AcksAll, VQ14), | 
					
						
							|  |  |  |     VQ16 = maybe_switch_queue_mode(VQ15), | 
					
						
							|  |  |  |     VQ16. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | maybe_switch_queue_mode(VQ) -> | 
					
						
							|  |  |  |     Mode = random_queue_mode(), | 
					
						
							|  |  |  |     set_queue_mode(Mode, VQ). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | random_queue_mode() -> | 
					
						
							|  |  |  |     Modes = [lazy, default], | 
					
						
							| 
									
										
										
										
											2017-03-09 23:05:37 +08:00
										 |  |  |     lists:nth(rand:uniform(length(Modes)), Modes). | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | pub_res({_, VQS}) -> | 
					
						
							|  |  |  |     VQS; | 
					
						
							|  |  |  | pub_res(VQS) -> | 
					
						
							|  |  |  |     VQS. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | make_publish(IsPersistent, PayloadFun, PropFun, N) -> | 
					
						
							|  |  |  |     {rabbit_basic:message( | 
					
						
							|  |  |  |        rabbit_misc:r(<<>>, exchange, <<>>), | 
					
						
							|  |  |  |        <<>>, #'P_basic'{delivery_mode = case IsPersistent of | 
					
						
							|  |  |  |                                             true  -> 2; | 
					
						
							|  |  |  |                                             false -> 1 | 
					
						
							|  |  |  |                                         end}, | 
					
						
							|  |  |  |        PayloadFun(N)), | 
					
						
							|  |  |  |      PropFun(N, #message_properties{size = 10}), | 
					
						
							|  |  |  |      false}. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | make_publish_delivered(IsPersistent, PayloadFun, PropFun, N) -> | 
					
						
							|  |  |  |     {rabbit_basic:message( | 
					
						
							|  |  |  |        rabbit_misc:r(<<>>, exchange, <<>>), | 
					
						
							|  |  |  |        <<>>, #'P_basic'{delivery_mode = case IsPersistent of | 
					
						
							|  |  |  |                                             true  -> 2; | 
					
						
							|  |  |  |                                             false -> 1 | 
					
						
							|  |  |  |                                         end}, | 
					
						
							|  |  |  |        PayloadFun(N)), | 
					
						
							|  |  |  |      PropFun(N, #message_properties{size = 10})}. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | queue_name(Config, Name) -> | 
					
						
							| 
									
										
										
										
											2017-06-06 22:27:37 +08:00
										 |  |  |     Name1 = iolist_to_binary(rabbit_ct_helpers:config_to_testcase_name(Config, Name)), | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |     queue_name(Name1). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | queue_name(Name) -> | 
					
						
							|  |  |  |     rabbit_misc:r(<<"/">>, queue, Name). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | test_queue() -> | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  |     queue_name(rabbit_guid:gen()). | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  | init_test_queue(QName) -> | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |     PRef = rabbit_guid:gen(), | 
					
						
							|  |  |  |     PersistentClient = msg_store_client_init(?PERSISTENT_MSG_STORE, PRef), | 
					
						
							| 
									
										
										
										
											2021-04-09 21:01:28 +08:00
										 |  |  |     Res = ?INDEX:recover( | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  |             QName, [], false, | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |             fun (MsgId) -> | 
					
						
							|  |  |  |                     rabbit_msg_store:contains(MsgId, PersistentClient) | 
					
						
							|  |  |  |             end, | 
					
						
							|  |  |  |             fun nop/1, fun nop/1), | 
					
						
							|  |  |  |     ok = rabbit_msg_store:client_delete_and_terminate(PersistentClient), | 
					
						
							|  |  |  |     Res. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  | restart_test_queue(Qi, QName) -> | 
					
						
							| 
									
										
										
										
											2021-04-09 21:01:28 +08:00
										 |  |  |     _ = ?INDEX:terminate(?VHOST, [], Qi), | 
					
						
							| 
									
										
										
										
											2017-03-23 02:30:08 +08:00
										 |  |  |     ok = rabbit_variable_queue:stop(?VHOST), | 
					
						
							|  |  |  |     {ok, _} = rabbit_variable_queue:start(?VHOST, [QName]), | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  |     init_test_queue(QName). | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  | empty_test_queue(QName) -> | 
					
						
							| 
									
										
										
										
											2017-03-23 02:30:08 +08:00
										 |  |  |     ok = rabbit_variable_queue:stop(?VHOST), | 
					
						
							|  |  |  |     {ok, _} = rabbit_variable_queue:start(?VHOST, []), | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  |     {0, 0, Qi} = init_test_queue(QName), | 
					
						
							| 
									
										
										
										
											2021-04-09 21:01:28 +08:00
										 |  |  |     _ = ?INDEX:delete_and_terminate(Qi), | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  |     ok. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | unin_empty_test_queue(QName) -> | 
					
						
							|  |  |  |     {0, 0, Qi} = init_test_queue(QName), | 
					
						
							| 
									
										
										
										
											2021-04-09 21:01:28 +08:00
										 |  |  |     _ = ?INDEX:delete_and_terminate(Qi), | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |     ok. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | with_empty_test_queue(Fun) -> | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  |     QName = test_queue(), | 
					
						
							|  |  |  |     ok = empty_test_queue(QName), | 
					
						
							|  |  |  |     {0, 0, Qi} = init_test_queue(QName), | 
					
						
							| 
									
										
										
										
											2021-04-09 21:01:28 +08:00
										 |  |  |     ?INDEX:delete_and_terminate(Fun(Qi, QName)). | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-07 20:45:43 +08:00
										 |  |  | init_queue_index() -> | 
					
						
							|  |  |  |     %% We must set the segment entry count in the process dictionary
 | 
					
						
							|  |  |  |     %% for tests that call the queue index directly to have a correct
 | 
					
						
							|  |  |  |     %% value.
 | 
					
						
							|  |  |  |     put(segment_entry_count, 2048), | 
					
						
							|  |  |  |     ok. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  | restart_app() -> | 
					
						
							|  |  |  |     rabbit:stop(), | 
					
						
							|  |  |  |     rabbit:start(). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | queue_index_publish(SeqIds, Persistent, Qi) -> | 
					
						
							|  |  |  |     Ref = rabbit_guid:gen(), | 
					
						
							|  |  |  |     MsgStore = case Persistent of | 
					
						
							|  |  |  |                    true  -> ?PERSISTENT_MSG_STORE; | 
					
						
							|  |  |  |                    false -> ?TRANSIENT_MSG_STORE | 
					
						
							|  |  |  |                end, | 
					
						
							|  |  |  |     MSCState = msg_store_client_init(MsgStore, Ref), | 
					
						
							|  |  |  |     {A, B = [{_SeqId, LastMsgIdWritten} | _]} = | 
					
						
							|  |  |  |         lists:foldl( | 
					
						
							|  |  |  |           fun (SeqId, {QiN, SeqIdsMsgIdsAcc}) -> | 
					
						
							|  |  |  |                   MsgId = rabbit_guid:gen(), | 
					
						
							| 
									
										
										
										
											2021-04-09 21:01:28 +08:00
										 |  |  |                   QiM = ?INDEX:publish( | 
					
						
							| 
									
										
										
										
											2021-07-28 19:43:31 +08:00
										 |  |  |                           MsgId, SeqId, memory, #message_properties{size = 10}, | 
					
						
							| 
									
										
										
										
											2021-04-09 21:01:28 +08:00
										 |  |  |                           Persistent, false, infinity, QiN), | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |                   ok = rabbit_msg_store:write(MsgId, MsgId, MSCState), | 
					
						
							|  |  |  |                   {QiM, [{SeqId, MsgId} | SeqIdsMsgIdsAcc]} | 
					
						
							|  |  |  |           end, {Qi, []}, SeqIds), | 
					
						
							|  |  |  |     %% do this just to force all of the publishes through to the msg_store:
 | 
					
						
							|  |  |  |     true = rabbit_msg_store:contains(LastMsgIdWritten, MSCState), | 
					
						
							|  |  |  |     ok = rabbit_msg_store:client_delete_and_terminate(MSCState), | 
					
						
							|  |  |  |     {A, B}. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-28 19:43:31 +08:00
										 |  |  | %% @todo Check for Delivered intentionally removed since it's now per-queue not per-message.
 | 
					
						
							| 
									
										
										
										
											2021-08-24 21:48:28 +08:00
										 |  |  | %% @todo Check for MsgId intentionally removed since it's no longer stored if not necessary.
 | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  | verify_read_with_published(_Delivered, _Persistent, [], _) -> | 
					
						
							|  |  |  |     ok; | 
					
						
							|  |  |  | verify_read_with_published(Delivered, Persistent, | 
					
						
							| 
									
										
										
										
											2021-08-24 21:48:28 +08:00
										 |  |  |                            [{_MsgId1, SeqId, _Location, _Props, Persistent, _IgnoreDelivered}|Read], | 
					
						
							|  |  |  |                            [{SeqId, _MsgId2}|Published]) -> | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |     verify_read_with_published(Delivered, Persistent, Read, Published); | 
					
						
							|  |  |  | verify_read_with_published(_Delivered, _Persistent, _Read, _Published) -> | 
					
						
							|  |  |  |     ko. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | nop(_) -> ok. | 
					
						
							|  |  |  | nop(_, _) -> ok. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | msg_store_client_init(MsgStore, Ref) -> | 
					
						
							| 
									
										
										
										
											2017-03-23 02:30:08 +08:00
										 |  |  |     rabbit_vhost_msg_store:client_init(?VHOST, MsgStore, Ref,  undefined, undefined). | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | variable_queue_init(Q, Recover) -> | 
					
						
							|  |  |  |     rabbit_variable_queue:init( | 
					
						
							|  |  |  |       Q, case Recover of | 
					
						
							|  |  |  |              true  -> non_clean_shutdown; | 
					
						
							| 
									
										
										
										
											2021-08-10 23:10:00 +08:00
										 |  |  |              false -> new; | 
					
						
							|  |  |  |              Terms -> Terms | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |          end, fun nop/2, fun nop/2, fun nop/1, fun nop/1). | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-10 23:10:00 +08:00
										 |  |  | variable_queue_read_terms(QName) -> | 
					
						
							|  |  |  |     #resource { kind = queue, | 
					
						
							|  |  |  |                 virtual_host = VHost, | 
					
						
							|  |  |  |                 name = Name } = QName, | 
					
						
							|  |  |  |     <<Num:128>> = erlang:md5(<<"queue", VHost/binary, Name/binary>>), | 
					
						
							|  |  |  |     DirName = rabbit_misc:format("~.36B", [Num]), | 
					
						
							|  |  |  |     {ok, Terms} = rabbit_recovery_terms:read(VHost, DirName), | 
					
						
							|  |  |  |     Terms. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  | publish_and_confirm(Q, Payload, Count) -> | 
					
						
							|  |  |  |     Seqs = lists:seq(1, Count), | 
					
						
							| 
									
										
										
										
											2020-09-29 18:43:24 +08:00
										 |  |  |     QTState0 = rabbit_queue_type:new(Q, rabbit_queue_type:init()), | 
					
						
							|  |  |  |     QTState = | 
					
						
							|  |  |  |     lists:foldl( | 
					
						
							|  |  |  |       fun (Seq, Acc0) -> | 
					
						
							|  |  |  |               Msg = rabbit_basic:message(rabbit_misc:r(<<>>, exchange, <<>>), | 
					
						
							|  |  |  |                                          <<>>, #'P_basic'{delivery_mode = 2}, | 
					
						
							|  |  |  |                                          Payload), | 
					
						
							|  |  |  |               Delivery = #delivery{mandatory = false, sender = self(), | 
					
						
							|  |  |  |                                    confirm = true, message = Msg, msg_seq_no = Seq, | 
					
						
							|  |  |  |                                    flow = noflow}, | 
					
						
							|  |  |  |               {ok, Acc, _Actions} = rabbit_queue_type:deliver([Q], Delivery, Acc0), | 
					
						
							|  |  |  |               Acc | 
					
						
							|  |  |  |       end, QTState0, Seqs), | 
					
						
							|  |  |  |     wait_for_confirms(gb_sets:from_list(Seqs)), | 
					
						
							|  |  |  |     QTState. | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | wait_for_confirms(Unconfirmed) -> | 
					
						
							|  |  |  |     case gb_sets:is_empty(Unconfirmed) of | 
					
						
							|  |  |  |         true  -> ok; | 
					
						
							| 
									
										
										
										
											2020-10-07 20:05:25 +08:00
										 |  |  |         false -> | 
					
						
							|  |  |  |             receive | 
					
						
							|  |  |  |                 {'$gen_cast', | 
					
						
							|  |  |  |                  {queue_event, _QName, {confirm, Confirmed, _}}} -> | 
					
						
							|  |  |  |                     wait_for_confirms( | 
					
						
							|  |  |  |                       rabbit_misc:gb_sets_difference( | 
					
						
							|  |  |  |                         Unconfirmed, gb_sets:from_list(Confirmed))); | 
					
						
							|  |  |  |                 {'$gen_cast', {confirm, Confirmed, _}} -> | 
					
						
							|  |  |  |                     wait_for_confirms( | 
					
						
							|  |  |  |                       rabbit_misc:gb_sets_difference( | 
					
						
							|  |  |  |                         Unconfirmed, gb_sets:from_list(Confirmed))) | 
					
						
							|  |  |  |             after ?TIMEOUT -> | 
					
						
							|  |  |  |                       flush(), | 
					
						
							|  |  |  |                       exit(timeout_waiting_for_confirm) | 
					
						
							|  |  |  |             end | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |     end. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | with_fresh_variable_queue(Fun, Mode) -> | 
					
						
							|  |  |  |     Ref = make_ref(), | 
					
						
							|  |  |  |     Me = self(), | 
					
						
							|  |  |  |     %% Run in a separate process since rabbit_msg_store will send
 | 
					
						
							|  |  |  |     %% bump_credit messages and we want to ignore them
 | 
					
						
							|  |  |  |     spawn_link(fun() -> | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  |                        QName = test_queue(), | 
					
						
							|  |  |  |                        ok = unin_empty_test_queue(QName), | 
					
						
							|  |  |  |                        VQ = variable_queue_init(test_amqqueue(QName, true), false), | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |                        S0 = variable_queue_status(VQ), | 
					
						
							|  |  |  |                        assert_props(S0, [{q1, 0}, {q2, 0}, | 
					
						
							|  |  |  |                                          {delta, | 
					
						
							|  |  |  |                                           {delta, undefined, 0, undefined}}, | 
					
						
							|  |  |  |                                          {q3, 0}, {q4, 0}, | 
					
						
							|  |  |  |                                          {len, 0}]), | 
					
						
							|  |  |  |                        VQ1 = set_queue_mode(Mode, VQ), | 
					
						
							|  |  |  |                        try | 
					
						
							|  |  |  |                            _ = rabbit_variable_queue:delete_and_terminate( | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  |                                  shutdown, Fun(VQ1, QName)), | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |                            Me ! Ref | 
					
						
							|  |  |  |                        catch | 
					
						
							| 
									
										
										
										
											2018-12-20 23:31:15 +08:00
										 |  |  |                            Type:Error:Stacktrace -> | 
					
						
							|  |  |  |                                Me ! {Ref, Type, Error, Stacktrace} | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |                        end | 
					
						
							|  |  |  |                end), | 
					
						
							|  |  |  |     receive | 
					
						
							|  |  |  |         Ref                    -> ok; | 
					
						
							|  |  |  |         {Ref, Type, Error, ST} -> exit({Type, Error, ST}) | 
					
						
							|  |  |  |     end, | 
					
						
							|  |  |  |     passed. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | set_queue_mode(Mode, VQ) -> | 
					
						
							|  |  |  |     VQ1 = rabbit_variable_queue:set_queue_mode(Mode, VQ), | 
					
						
							|  |  |  |     S1 = variable_queue_status(VQ1), | 
					
						
							|  |  |  |     assert_props(S1, [{mode, Mode}]), | 
					
						
							|  |  |  |     VQ1. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | variable_queue_publish(IsPersistent, Count, VQ) -> | 
					
						
							|  |  |  |     variable_queue_publish(IsPersistent, Count, fun (_N, P) -> P end, VQ). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | variable_queue_publish(IsPersistent, Count, PropFun, VQ) -> | 
					
						
							|  |  |  |     variable_queue_publish(IsPersistent, 1, Count, PropFun, | 
					
						
							|  |  |  |                            fun (_N) -> <<>> end, VQ). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | variable_queue_publish(IsPersistent, Start, Count, PropFun, PayloadFun, VQ) -> | 
					
						
							|  |  |  |     variable_queue_wait_for_shuffling_end( | 
					
						
							|  |  |  |       lists:foldl( | 
					
						
							|  |  |  |         fun (N, VQN) -> | 
					
						
							| 
									
										
										
										
											2020-09-29 18:43:24 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |                 rabbit_variable_queue:publish( | 
					
						
							|  |  |  |                   rabbit_basic:message( | 
					
						
							|  |  |  |                     rabbit_misc:r(<<>>, exchange, <<>>), | 
					
						
							|  |  |  |                     <<>>, #'P_basic'{delivery_mode = case IsPersistent of | 
					
						
							|  |  |  |                                                          true  -> 2; | 
					
						
							|  |  |  |                                                          false -> 1 | 
					
						
							|  |  |  |                                                      end}, | 
					
						
							|  |  |  |                     PayloadFun(N)), | 
					
						
							|  |  |  |                   PropFun(N, #message_properties{size = 10}), | 
					
						
							|  |  |  |                   false, self(), noflow, VQN) | 
					
						
							|  |  |  |         end, VQ, lists:seq(Start, Start + Count - 1))). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | variable_queue_batch_publish(IsPersistent, Count, VQ) -> | 
					
						
							|  |  |  |     variable_queue_batch_publish(IsPersistent, Count, fun (_N, P) -> P end, VQ). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | variable_queue_batch_publish(IsPersistent, Count, PropFun, VQ) -> | 
					
						
							|  |  |  |     variable_queue_batch_publish(IsPersistent, 1, Count, PropFun, | 
					
						
							|  |  |  |                                  fun (_N) -> <<>> end, VQ). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | variable_queue_batch_publish(IsPersistent, Start, Count, PropFun, PayloadFun, VQ) -> | 
					
						
							|  |  |  |     variable_queue_batch_publish0(IsPersistent, Start, Count, PropFun, | 
					
						
							|  |  |  |                                   PayloadFun, fun make_publish/4, | 
					
						
							|  |  |  |                                   fun rabbit_variable_queue:batch_publish/4, | 
					
						
							|  |  |  |                                   VQ). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | variable_queue_batch_publish_delivered(IsPersistent, Count, VQ) -> | 
					
						
							|  |  |  |     variable_queue_batch_publish_delivered(IsPersistent, Count, fun (_N, P) -> P end, VQ). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | variable_queue_batch_publish_delivered(IsPersistent, Count, PropFun, VQ) -> | 
					
						
							|  |  |  |     variable_queue_batch_publish_delivered(IsPersistent, 1, Count, PropFun, | 
					
						
							|  |  |  |                                            fun (_N) -> <<>> end, VQ). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | variable_queue_batch_publish_delivered(IsPersistent, Start, Count, PropFun, PayloadFun, VQ) -> | 
					
						
							|  |  |  |     variable_queue_batch_publish0(IsPersistent, Start, Count, PropFun, | 
					
						
							|  |  |  |                                   PayloadFun, fun make_publish_delivered/4, | 
					
						
							|  |  |  |                                   fun rabbit_variable_queue:batch_publish_delivered/4, | 
					
						
							|  |  |  |                                   VQ). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | variable_queue_batch_publish0(IsPersistent, Start, Count, PropFun, PayloadFun, | 
					
						
							|  |  |  |                               MakePubFun, PubFun, VQ) -> | 
					
						
							|  |  |  |     Publishes = | 
					
						
							|  |  |  |         [MakePubFun(IsPersistent, PayloadFun, PropFun, N) | 
					
						
							|  |  |  |          || N <- lists:seq(Start, Start + Count - 1)], | 
					
						
							|  |  |  |     Res = PubFun(Publishes, self(), noflow, VQ), | 
					
						
							|  |  |  |     VQ1 = pub_res(Res), | 
					
						
							|  |  |  |     variable_queue_wait_for_shuffling_end(VQ1). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | variable_queue_fetch(Count, IsPersistent, IsDelivered, Len, VQ) -> | 
					
						
							|  |  |  |     lists:foldl(fun (N, {VQN, AckTagsAcc}) -> | 
					
						
							|  |  |  |                         Rem = Len - N, | 
					
						
							|  |  |  |                         {{#basic_message { is_persistent = IsPersistent }, | 
					
						
							|  |  |  |                           IsDelivered, AckTagN}, VQM} = | 
					
						
							|  |  |  |                             rabbit_variable_queue:fetch(true, VQN), | 
					
						
							|  |  |  |                         Rem = rabbit_variable_queue:len(VQM), | 
					
						
							|  |  |  |                         {VQM, [AckTagN | AckTagsAcc]} | 
					
						
							|  |  |  |                 end, {VQ, []}, lists:seq(1, Count)). | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-08 22:46:25 +08:00
										 |  |  | test_amqqueue(QName, Durable) -> | 
					
						
							| 
									
										
										
										
											2018-10-11 18:12:39 +08:00
										 |  |  |     rabbit_amqqueue:pseudo_queue(QName, self(), Durable). | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | assert_prop(List, Prop, Value) -> | 
					
						
							|  |  |  |     case proplists:get_value(Prop, List)of | 
					
						
							|  |  |  |         Value -> ok; | 
					
						
							|  |  |  |         _     -> {exit, Prop, exp, Value, List} | 
					
						
							|  |  |  |     end. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | assert_props(List, PropVals) -> | 
					
						
							|  |  |  |     [assert_prop(List, Prop, Value) || {Prop, Value} <- PropVals]. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | variable_queue_set_ram_duration_target(Duration, VQ) -> | 
					
						
							|  |  |  |     variable_queue_wait_for_shuffling_end( | 
					
						
							|  |  |  |       rabbit_variable_queue:set_ram_duration_target(Duration, VQ)). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | publish_fetch_and_ack(0, _Len, VQ0) -> | 
					
						
							|  |  |  |     VQ0; | 
					
						
							|  |  |  | publish_fetch_and_ack(N, Len, VQ0) -> | 
					
						
							|  |  |  |     VQ1 = variable_queue_publish(false, 1, VQ0), | 
					
						
							|  |  |  |     {{_Msg, false, AckTag}, VQ2} = rabbit_variable_queue:fetch(true, VQ1), | 
					
						
							|  |  |  |     Len = rabbit_variable_queue:len(VQ2), | 
					
						
							|  |  |  |     {_Guids, VQ3} = rabbit_variable_queue:ack([AckTag], VQ2), | 
					
						
							|  |  |  |     publish_fetch_and_ack(N-1, Len, VQ3). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | variable_queue_status(VQ) -> | 
					
						
							|  |  |  |     Keys = rabbit_backing_queue:info_keys() -- [backing_queue_status], | 
					
						
							|  |  |  |     [{K, rabbit_variable_queue:info(K, VQ)} || K <- Keys] ++ | 
					
						
							|  |  |  |         rabbit_variable_queue:info(backing_queue_status, VQ). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | variable_queue_wait_for_shuffling_end(VQ) -> | 
					
						
							|  |  |  |     case credit_flow:blocked() of | 
					
						
							|  |  |  |         false -> VQ; | 
					
						
							| 
									
										
										
										
											2020-09-29 18:43:24 +08:00
										 |  |  |         true  -> | 
					
						
							|  |  |  |             receive | 
					
						
							|  |  |  |                 {bump_credit, Msg} -> | 
					
						
							|  |  |  |                     credit_flow:handle_bump_msg(Msg), | 
					
						
							|  |  |  |                     variable_queue_wait_for_shuffling_end( | 
					
						
							|  |  |  |                       rabbit_variable_queue:resume(VQ)) | 
					
						
							|  |  |  |             end | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |     end. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | msg2int(#basic_message{content = #content{ payload_fragments_rev = P}}) -> | 
					
						
							|  |  |  |     binary_to_term(list_to_binary(lists:reverse(P))). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ack_subset(AckSeqs, Interval, Rem) -> | 
					
						
							|  |  |  |     lists:filter(fun ({_Ack, N}) -> (N + Rem) rem Interval == 0 end, AckSeqs). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | requeue_one_by_one(Acks, VQ) -> | 
					
						
							|  |  |  |     lists:foldl(fun (AckTag, VQN) -> | 
					
						
							|  |  |  |                         {_MsgId, VQM} = rabbit_variable_queue:requeue( | 
					
						
							|  |  |  |                                           [AckTag], VQN), | 
					
						
							|  |  |  |                         VQM | 
					
						
							|  |  |  |                 end, VQ, Acks). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | %% Create a vq with messages in q1, delta, and q3, and holes (in the
 | 
					
						
							|  |  |  | %% form of pending acks) in the latter two.
 | 
					
						
							|  |  |  | variable_queue_with_holes(VQ0) -> | 
					
						
							|  |  |  |     Interval = 2048, %% should match vq:IO_BATCH_SIZE
 | 
					
						
							| 
									
										
										
										
											2021-04-09 21:01:28 +08:00
										 |  |  |     Count = ?INDEX:next_segment_boundary(0)*2 + 2 * Interval, | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |     Seq = lists:seq(1, Count), | 
					
						
							|  |  |  |     VQ1 = variable_queue_set_ram_duration_target(0, VQ0), | 
					
						
							|  |  |  |     VQ2 = variable_queue_publish( | 
					
						
							|  |  |  |             false, 1, Count, | 
					
						
							|  |  |  |             fun (_, P) -> P end, fun erlang:term_to_binary/1, VQ1), | 
					
						
							|  |  |  |     {VQ3, AcksR} = variable_queue_fetch(Count, false, false, Count, VQ2), | 
					
						
							|  |  |  |     Acks = lists:reverse(AcksR), | 
					
						
							|  |  |  |     AckSeqs = lists:zip(Acks, Seq), | 
					
						
							|  |  |  |     [{Subset1, _Seq1}, {Subset2, _Seq2}, {Subset3, Seq3}] = | 
					
						
							|  |  |  |         [lists:unzip(ack_subset(AckSeqs, Interval, I)) || I <- [0, 1, 2]], | 
					
						
							|  |  |  |     %% we requeue in three phases in order to exercise requeuing logic
 | 
					
						
							|  |  |  |     %% in various vq states
 | 
					
						
							|  |  |  |     {_MsgIds, VQ4} = rabbit_variable_queue:requeue( | 
					
						
							|  |  |  |                        Acks -- (Subset1 ++ Subset2 ++ Subset3), VQ3), | 
					
						
							|  |  |  |     VQ5 = requeue_one_by_one(Subset1, VQ4), | 
					
						
							|  |  |  |     %% by now we have some messages (and holes) in delta
 | 
					
						
							|  |  |  |     VQ6 = requeue_one_by_one(Subset2, VQ5), | 
					
						
							|  |  |  |     VQ7 = variable_queue_set_ram_duration_target(infinity, VQ6), | 
					
						
							|  |  |  |     %% add the q1 tail
 | 
					
						
							|  |  |  |     VQ8 = variable_queue_publish( | 
					
						
							|  |  |  |             true, Count + 1, Interval, | 
					
						
							|  |  |  |             fun (_, P) -> P end, fun erlang:term_to_binary/1, VQ7), | 
					
						
							|  |  |  |     %% assertions
 | 
					
						
							|  |  |  |     Status = variable_queue_status(VQ8), | 
					
						
							| 
									
										
										
										
											2020-09-29 18:43:24 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |     vq_with_holes_assertions(VQ8, proplists:get_value(mode, Status)), | 
					
						
							|  |  |  |     Depth = Count + Interval, | 
					
						
							|  |  |  |     Depth = rabbit_variable_queue:depth(VQ8), | 
					
						
							|  |  |  |     Len = Depth - length(Subset3), | 
					
						
							|  |  |  |     Len = rabbit_variable_queue:len(VQ8), | 
					
						
							| 
									
										
										
										
											2020-09-29 18:43:24 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-07 00:13:57 +08:00
										 |  |  |     {Seq3, Seq -- Seq3, lists:seq(Count + 1, Count + Interval), VQ8}. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | vq_with_holes_assertions(VQ, default) -> | 
					
						
							|  |  |  |     [false = | 
					
						
							|  |  |  |          case V of | 
					
						
							|  |  |  |              {delta, _, 0, _} -> true; | 
					
						
							|  |  |  |              0                -> true; | 
					
						
							|  |  |  |              _                -> false | 
					
						
							|  |  |  |          end || {K, V} <- variable_queue_status(VQ), | 
					
						
							|  |  |  |                 lists:member(K, [q1, delta, q3])]; | 
					
						
							|  |  |  | vq_with_holes_assertions(VQ, lazy) -> | 
					
						
							|  |  |  |     [false = | 
					
						
							|  |  |  |          case V of | 
					
						
							|  |  |  |              {delta, _, 0, _} -> true; | 
					
						
							|  |  |  |              _                -> false | 
					
						
							|  |  |  |          end || {K, V} <- variable_queue_status(VQ), | 
					
						
							|  |  |  |                 lists:member(K, [delta])]. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | check_variable_queue_status(VQ0, Props) -> | 
					
						
							|  |  |  |     VQ1 = variable_queue_wait_for_shuffling_end(VQ0), | 
					
						
							|  |  |  |     S = variable_queue_status(VQ1), | 
					
						
							|  |  |  |     assert_props(S, Props), | 
					
						
							|  |  |  |     VQ1. | 
					
						
							| 
									
										
										
										
											2020-09-29 18:43:24 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | flush() -> | 
					
						
							|  |  |  |     receive | 
					
						
							|  |  |  |         Any -> | 
					
						
							|  |  |  |             ct:pal("flush ~p", [Any]), | 
					
						
							|  |  |  |             flush() | 
					
						
							|  |  |  |     after 0 -> | 
					
						
							|  |  |  |               ok | 
					
						
							|  |  |  |     end. |