001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.master.balancer; 019 020import static org.junit.jupiter.api.Assertions.assertEquals; 021import static org.junit.jupiter.api.Assertions.assertFalse; 022import static org.junit.jupiter.api.Assertions.assertTrue; 023 024import java.util.ArrayList; 025import java.util.Collections; 026import java.util.HashMap; 027import java.util.HashSet; 028import java.util.Iterator; 029import java.util.List; 030import java.util.Map; 031import java.util.Set; 032import org.apache.commons.lang3.StringUtils; 033import org.apache.hadoop.hbase.HConstants; 034import org.apache.hadoop.hbase.ServerName; 035import org.apache.hadoop.hbase.TableName; 036import org.apache.hadoop.hbase.client.RegionInfo; 037import org.apache.hadoop.hbase.master.LoadBalancer; 038import org.apache.hadoop.hbase.master.RegionPlan; 039import org.apache.hadoop.hbase.net.Address; 040import org.apache.hadoop.hbase.rsgroup.RSGroupBasedLoadBalancer; 041import org.apache.hadoop.hbase.rsgroup.RSGroupInfo; 042import org.apache.hadoop.hbase.testclassification.LargeTests; 043import org.apache.hadoop.net.DNSToSwitchMapping; 044import org.junit.jupiter.api.BeforeAll; 045import org.junit.jupiter.api.Tag; 046import org.junit.jupiter.api.Test; 047import org.slf4j.Logger; 048import org.slf4j.LoggerFactory; 049 050import org.apache.hbase.thirdparty.com.google.common.collect.ArrayListMultimap; 051 052/** 053 * Test RSGroupBasedLoadBalancer with SimpleLoadBalancer as internal balancer 054 */ 055@Tag(LargeTests.TAG) 056public class TestRSGroupBasedLoadBalancer extends RSGroupableBalancerTestBase { 057 private static final Logger LOG = LoggerFactory.getLogger(TestRSGroupBasedLoadBalancer.class); 058 private static RSGroupBasedLoadBalancer loadBalancer; 059 060 @BeforeAll 061 public static void beforeAllTests() throws Exception { 062 servers = generateServers(7); 063 groupMap = constructGroupInfo(servers, groups); 064 tableDescs = constructTableDesc(true); 065 conf.set("hbase.regions.slop", "0"); 066 conf.set("hbase.rsgroup.grouploadbalancer.class", SimpleLoadBalancer.class.getCanonicalName()); 067 conf.setClass("hbase.util.ip.to.rack.determiner", MockMapping.class, DNSToSwitchMapping.class); 068 loadBalancer = new RSGroupBasedLoadBalancer(); 069 loadBalancer.setMasterServices(getMockedMaster()); 070 loadBalancer.initialize(); 071 } 072 073 /** 074 * Test the load balancing algorithm. Invariant is that all servers of the group should be hosting 075 * either floor(average) or ceiling(average) 076 */ 077 @Test 078 public void testBalanceCluster() throws Exception { 079 // Test with/without per table balancer. 080 boolean[] perTableBalancerConfigs = { true, false }; 081 for (boolean isByTable : perTableBalancerConfigs) { 082 conf.setBoolean(HConstants.HBASE_MASTER_LOADBALANCE_BYTABLE, isByTable); 083 loadBalancer.onConfigurationChange(conf); 084 Map<ServerName, List<RegionInfo>> servers = mockClusterServers(); 085 ArrayListMultimap<String, ServerAndLoad> list = convertToGroupBasedMap(servers); 086 LOG.info("Mock Cluster : " + printStats(list)); 087 Map<TableName, Map<ServerName, List<RegionInfo>>> LoadOfAllTable = 088 (Map) mockClusterServersWithTables(servers); 089 List<RegionPlan> plans = loadBalancer.balanceCluster(LoadOfAllTable); 090 ArrayListMultimap<String, ServerAndLoad> balancedCluster = reconcile(list, plans); 091 LOG.info("Mock Balance : " + printStats(balancedCluster)); 092 assertClusterAsBalanced(balancedCluster); 093 } 094 } 095 096 /** 097 * Tests the bulk assignment used during cluster startup. 098 * <p/> 099 * Round-robin. Should yield a balanced cluster so same invariant as the load balancer holds, all 100 * servers holding either floor(avg) or ceiling(avg). 101 */ 102 @Test 103 public void testBulkAssignment() throws Exception { 104 List<RegionInfo> regions = randomRegions(25); 105 Map<ServerName, List<RegionInfo>> assignments = 106 loadBalancer.roundRobinAssignment(regions, servers); 107 // test empty region/servers scenario 108 // this should not throw an NPE 109 loadBalancer.roundRobinAssignment(regions, Collections.emptyList()); 110 // test regular scenario 111 assertTrue(assignments.keySet().size() == servers.size()); 112 for (ServerName sn : assignments.keySet()) { 113 List<RegionInfo> regionAssigned = assignments.get(sn); 114 for (RegionInfo region : regionAssigned) { 115 TableName tableName = region.getTable(); 116 String groupName = 117 tableDescs.get(tableName).getRegionServerGroup().orElse(RSGroupInfo.DEFAULT_GROUP); 118 assertTrue(StringUtils.isNotEmpty(groupName)); 119 RSGroupInfo gInfo = getMockedGroupInfoManager().getRSGroup(groupName); 120 assertTrue(gInfo.containsServer(sn.getAddress()), 121 "Region is not correctly assigned to group servers."); 122 } 123 } 124 ArrayListMultimap<String, ServerAndLoad> loadMap = convertToGroupBasedMap(assignments); 125 assertClusterAsBalanced(loadMap); 126 } 127 128 /** 129 * Test the cluster startup bulk assignment which attempts to retain assignment info. 130 */ 131 @Test 132 public void testRetainAssignment() throws Exception { 133 // Test simple case where all same servers are there 134 Map<ServerName, List<RegionInfo>> currentAssignments = mockClusterServers(); 135 Map<RegionInfo, ServerName> inputForTest = new HashMap<>(); 136 for (ServerName sn : currentAssignments.keySet()) { 137 for (RegionInfo region : currentAssignments.get(sn)) { 138 inputForTest.put(region, sn); 139 } 140 } 141 // verify region->null server assignment is handled 142 inputForTest.put(randomRegions(1).get(0), null); 143 Map<ServerName, List<RegionInfo>> newAssignment = 144 loadBalancer.retainAssignment(inputForTest, servers); 145 assertRetainedAssignment(inputForTest, servers, newAssignment); 146 } 147 148 /** 149 * Test BOGUS_SERVER_NAME among groups do not overwrite each other. 150 */ 151 @Test 152 public void testRoundRobinAssignment() throws Exception { 153 List<ServerName> onlineServers = new ArrayList<ServerName>(servers.size()); 154 onlineServers.addAll(servers); 155 List<RegionInfo> regions = randomRegions(25); 156 int bogusRegion = 0; 157 for (RegionInfo region : regions) { 158 String group = 159 tableDescs.get(region.getTable()).getRegionServerGroup().orElse(RSGroupInfo.DEFAULT_GROUP); 160 if ("dg3".equals(group) || "dg4".equals(group)) { 161 bogusRegion++; 162 } 163 } 164 Set<Address> offlineServers = new HashSet<Address>(); 165 offlineServers.addAll(groupMap.get("dg3").getServers()); 166 offlineServers.addAll(groupMap.get("dg4").getServers()); 167 for (Iterator<ServerName> it = onlineServers.iterator(); it.hasNext();) { 168 ServerName server = it.next(); 169 Address address = server.getAddress(); 170 if (offlineServers.contains(address)) { 171 it.remove(); 172 } 173 } 174 Map<ServerName, List<RegionInfo>> assignments = 175 loadBalancer.roundRobinAssignment(regions, onlineServers); 176 assertEquals(bogusRegion, assignments.get(LoadBalancer.BOGUS_SERVER_NAME).size()); 177 } 178 179 @Test 180 public void testOnConfigurationChange() { 181 // fallbackEnabled default is false 182 assertFalse(loadBalancer.isFallbackEnabled()); 183 184 // change FALLBACK_GROUP_ENABLE_KEY from false to true 185 conf.setBoolean(RSGroupBasedLoadBalancer.FALLBACK_GROUP_ENABLE_KEY, true); 186 loadBalancer.onConfigurationChange(conf); 187 assertTrue(loadBalancer.isFallbackEnabled()); 188 189 // restore 190 conf.setBoolean(RSGroupBasedLoadBalancer.FALLBACK_GROUP_ENABLE_KEY, false); 191 loadBalancer.onConfigurationChange(conf); 192 assertFalse(loadBalancer.isFallbackEnabled()); 193 } 194}