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.Assert.assertEquals; 021import static org.junit.Assert.assertFalse; 022import static org.junit.Assert.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.HBaseClassTestRule; 034import org.apache.hadoop.hbase.HConstants; 035import org.apache.hadoop.hbase.ServerName; 036import org.apache.hadoop.hbase.TableName; 037import org.apache.hadoop.hbase.client.RegionInfo; 038import org.apache.hadoop.hbase.master.LoadBalancer; 039import org.apache.hadoop.hbase.master.RegionPlan; 040import org.apache.hadoop.hbase.net.Address; 041import org.apache.hadoop.hbase.rsgroup.RSGroupBasedLoadBalancer; 042import org.apache.hadoop.hbase.rsgroup.RSGroupInfo; 043import org.apache.hadoop.hbase.testclassification.LargeTests; 044import org.apache.hadoop.net.DNSToSwitchMapping; 045import org.junit.BeforeClass; 046import org.junit.ClassRule; 047import org.junit.Test; 048import org.junit.experimental.categories.Category; 049import org.slf4j.Logger; 050import org.slf4j.LoggerFactory; 051 052import org.apache.hbase.thirdparty.com.google.common.collect.ArrayListMultimap; 053 054/** 055 * Test RSGroupBasedLoadBalancer with SimpleLoadBalancer as internal balancer 056 */ 057@Category(LargeTests.class) 058public class TestRSGroupBasedLoadBalancer extends RSGroupableBalancerTestBase { 059 @ClassRule 060 public static final HBaseClassTestRule CLASS_RULE = 061 HBaseClassTestRule.forClass(TestRSGroupBasedLoadBalancer.class); 062 private static final Logger LOG = LoggerFactory.getLogger(TestRSGroupBasedLoadBalancer.class); 063 private static RSGroupBasedLoadBalancer loadBalancer; 064 065 @BeforeClass 066 public static void beforeAllTests() throws Exception { 067 servers = generateServers(7); 068 groupMap = constructGroupInfo(servers, groups); 069 tableDescs = constructTableDesc(true); 070 conf.set("hbase.regions.slop", "0"); 071 conf.set("hbase.rsgroup.grouploadbalancer.class", SimpleLoadBalancer.class.getCanonicalName()); 072 conf.setClass("hbase.util.ip.to.rack.determiner", MockMapping.class, DNSToSwitchMapping.class); 073 loadBalancer = new RSGroupBasedLoadBalancer(); 074 loadBalancer.setMasterServices(getMockedMaster()); 075 loadBalancer.initialize(); 076 } 077 078 /** 079 * Test the load balancing algorithm. Invariant is that all servers of the group should be hosting 080 * either floor(average) or ceiling(average) 081 */ 082 @Test 083 public void testBalanceCluster() throws Exception { 084 // Test with/without per table balancer. 085 boolean[] perTableBalancerConfigs = { true, false }; 086 for (boolean isByTable : perTableBalancerConfigs) { 087 conf.setBoolean(HConstants.HBASE_MASTER_LOADBALANCE_BYTABLE, isByTable); 088 loadBalancer.onConfigurationChange(conf); 089 Map<ServerName, List<RegionInfo>> servers = mockClusterServers(); 090 ArrayListMultimap<String, ServerAndLoad> list = convertToGroupBasedMap(servers); 091 LOG.info("Mock Cluster : " + printStats(list)); 092 Map<TableName, Map<ServerName, List<RegionInfo>>> LoadOfAllTable = 093 (Map) mockClusterServersWithTables(servers); 094 List<RegionPlan> plans = loadBalancer.balanceCluster(LoadOfAllTable); 095 ArrayListMultimap<String, ServerAndLoad> balancedCluster = reconcile(list, plans); 096 LOG.info("Mock Balance : " + printStats(balancedCluster)); 097 assertClusterAsBalanced(balancedCluster); 098 } 099 } 100 101 /** 102 * Tests the bulk assignment used during cluster startup. 103 * <p/> 104 * Round-robin. Should yield a balanced cluster so same invariant as the load balancer holds, all 105 * servers holding either floor(avg) or ceiling(avg). 106 */ 107 @Test 108 public void testBulkAssignment() throws Exception { 109 List<RegionInfo> regions = randomRegions(25); 110 Map<ServerName, List<RegionInfo>> assignments = 111 loadBalancer.roundRobinAssignment(regions, servers); 112 // test empty region/servers scenario 113 // this should not throw an NPE 114 loadBalancer.roundRobinAssignment(regions, Collections.emptyList()); 115 // test regular scenario 116 assertTrue(assignments.keySet().size() == servers.size()); 117 for (ServerName sn : assignments.keySet()) { 118 List<RegionInfo> regionAssigned = assignments.get(sn); 119 for (RegionInfo region : regionAssigned) { 120 TableName tableName = region.getTable(); 121 String groupName = 122 tableDescs.get(tableName).getRegionServerGroup().orElse(RSGroupInfo.DEFAULT_GROUP); 123 assertTrue(StringUtils.isNotEmpty(groupName)); 124 RSGroupInfo gInfo = getMockedGroupInfoManager().getRSGroup(groupName); 125 assertTrue("Region is not correctly assigned to group servers.", 126 gInfo.containsServer(sn.getAddress())); 127 } 128 } 129 ArrayListMultimap<String, ServerAndLoad> loadMap = convertToGroupBasedMap(assignments); 130 assertClusterAsBalanced(loadMap); 131 } 132 133 /** 134 * Test the cluster startup bulk assignment which attempts to retain assignment info. 135 */ 136 @Test 137 public void testRetainAssignment() throws Exception { 138 // Test simple case where all same servers are there 139 Map<ServerName, List<RegionInfo>> currentAssignments = mockClusterServers(); 140 Map<RegionInfo, ServerName> inputForTest = new HashMap<>(); 141 for (ServerName sn : currentAssignments.keySet()) { 142 for (RegionInfo region : currentAssignments.get(sn)) { 143 inputForTest.put(region, sn); 144 } 145 } 146 // verify region->null server assignment is handled 147 inputForTest.put(randomRegions(1).get(0), null); 148 Map<ServerName, List<RegionInfo>> newAssignment = 149 loadBalancer.retainAssignment(inputForTest, servers); 150 assertRetainedAssignment(inputForTest, servers, newAssignment); 151 } 152 153 /** 154 * Test BOGUS_SERVER_NAME among groups do not overwrite each other. 155 */ 156 @Test 157 public void testRoundRobinAssignment() throws Exception { 158 List<ServerName> onlineServers = new ArrayList<ServerName>(servers.size()); 159 onlineServers.addAll(servers); 160 List<RegionInfo> regions = randomRegions(25); 161 int bogusRegion = 0; 162 for (RegionInfo region : regions) { 163 String group = 164 tableDescs.get(region.getTable()).getRegionServerGroup().orElse(RSGroupInfo.DEFAULT_GROUP); 165 if ("dg3".equals(group) || "dg4".equals(group)) { 166 bogusRegion++; 167 } 168 } 169 Set<Address> offlineServers = new HashSet<Address>(); 170 offlineServers.addAll(groupMap.get("dg3").getServers()); 171 offlineServers.addAll(groupMap.get("dg4").getServers()); 172 for (Iterator<ServerName> it = onlineServers.iterator(); it.hasNext();) { 173 ServerName server = it.next(); 174 Address address = server.getAddress(); 175 if (offlineServers.contains(address)) { 176 it.remove(); 177 } 178 } 179 Map<ServerName, List<RegionInfo>> assignments = 180 loadBalancer.roundRobinAssignment(regions, onlineServers); 181 assertEquals(bogusRegion, assignments.get(LoadBalancer.BOGUS_SERVER_NAME).size()); 182 } 183 184 @Test 185 public void testOnConfigurationChange() { 186 // fallbackEnabled default is false 187 assertFalse(loadBalancer.isFallbackEnabled()); 188 189 // change FALLBACK_GROUP_ENABLE_KEY from false to true 190 conf.setBoolean(RSGroupBasedLoadBalancer.FALLBACK_GROUP_ENABLE_KEY, true); 191 loadBalancer.onConfigurationChange(conf); 192 assertTrue(loadBalancer.isFallbackEnabled()); 193 194 // restore 195 conf.setBoolean(RSGroupBasedLoadBalancer.FALLBACK_GROUP_ENABLE_KEY, false); 196 loadBalancer.onConfigurationChange(conf); 197 assertFalse(loadBalancer.isFallbackEnabled()); 198 } 199}