@@ -41,6 +41,7 @@ az_aware_tests() -> [
4141 az_aware_below_threshold_balanced ,
4242 az_aware_fewer_nodes_than_size ,
4343 az_aware_running_nodes_preferred ,
44+ az_aware_entire_az_down ,
4445 az_aware_node_tags_for_nodes_offline_nodes
4546 ].
4647
@@ -392,6 +393,41 @@ az_aware_running_nodes_preferred(_Config) ->
392393 ? assertNot (lists :member (n2 , Members )),
393394 ok = meck :unload ([rabbit_member_placement_az , rabbit_queue_type ]).
394395
396+ % % An entire AZ can be without any running node (e.g. an outage). Unlike the
397+ % % non-AZ-aware strategies, which simply prefer whichever nodes are running,
398+ % % AZ-aware placement must still assign a member to the down AZ (picking one
399+ % % of its stopped nodes) rather than over-allocating to the AZs that are up.
400+ % % Balance across AZs is favoured over short-term node availability.
401+ az_aware_entire_az_down (_Config ) ->
402+ ok = meck :new (rabbit_member_placement_az , [passthrough ]),
403+ ok = meck :expect (rabbit_member_placement_az , node_tags_for_nodes ,
404+ fun ([n1 , n2 , n3 , n4 , n5 , n6 ], <<" az" >>) ->
405+ #{n1 => <<" az1" >>, n2 => <<" az1" >>,
406+ n3 => <<" az2" >>, n4 => <<" az2" >>,
407+ n5 => <<" az3" >>, n6 => <<" az3" >>}
408+ end ),
409+ ok = meck :new (rabbit_queue_type , [passthrough ]),
410+ ok = meck :expect (rabbit_queue_type , get_nodes , fun ({fake_queue , N }) -> [N ] end ),
411+ AllNodes = [n1 , n2 , n3 , n4 , n5 , n6 ],
412+ % % Both az3 nodes are stopped: the whole AZ is unavailable.
413+ RunningNodes = [n1 , n2 , n3 , n4 ],
414+ QueueType = rabbit_quorum_queue ,
415+ GetQueues = fun () -> [] end ,
416+ QueueCount = 0 ,
417+ QueueCountStartRandom = 1000 ,
418+ {Members , _ } = rabbit_queue_location :select_members (3 , QueueType , AllNodes , RunningNodes ,
419+ QueueCount , QueueCountStartRandom ,
420+ GetQueues , <<" az" >>),
421+ ? assertEqual (3 , length (Members )),
422+ AZMap = #{n1 => <<" az1" >>, n2 => <<" az1" >>,
423+ n3 => <<" az2" >>, n4 => <<" az2" >>,
424+ n5 => <<" az3" >>, n6 => <<" az3" >>},
425+ AZsUsed = lists :sort (lists :uniq ([maps :get (N , AZMap ) || N <- Members ])),
426+ % % az3 must still be represented even though none of its nodes are running.
427+ ? assertEqual ([<<" az1" >>, <<" az2" >>, <<" az3" >>], AZsUsed ),
428+ ? assert (lists :member (n5 , Members ) orelse lists :member (n6 , Members )),
429+ ok = meck :unload ([rabbit_member_placement_az , rabbit_queue_type ]).
430+
395431az_aware_node_tags_for_nodes_offline_nodes (_Config ) ->
396432 ok = meck :new (rabbit_db_node_metadata , [passthrough ]),
397433 ok = meck :expect (rabbit_db_node_metadata , get ,
0 commit comments