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.setRsGroupInfoManager(getMockedGroupInfoManager()); 075 loadBalancer.setMasterServices(getMockedMaster()); 076 loadBalancer.initialize(); 077 } 078 079 /** 080 * Test the load balancing algorithm. Invariant is that all servers of the group should be hosting 081 * either floor(average) or ceiling(average) 082 */ 083 @Test 084 public void testBalanceCluster() throws Exception { 085 // Test with/without per table balancer. 086 boolean[] perTableBalancerConfigs = { true, false }; 087 for (boolean isByTable : perTableBalancerConfigs) { 088 conf.setBoolean(HConstants.HBASE_MASTER_LOADBALANCE_BYTABLE, isByTable); 089 loadBalancer.onConfigurationChange(conf); 090 Map<ServerName, List<RegionInfo>> servers = mockClusterServers(); 091 ArrayListMultimap<String, ServerAndLoad> list = convertToGroupBasedMap(servers); 092 LOG.info("Mock Cluster : " + printStats(list)); 093 Map<TableName, Map<ServerName, List<RegionInfo>>> LoadOfAllTable = 094 (Map) mockClusterServersWithTables(servers); 095 List<RegionPlan> plans = loadBalancer.balanceCluster(LoadOfAllTable); 096 ArrayListMultimap<String, ServerAndLoad> balancedCluster = reconcile(list, plans); 097 LOG.info("Mock Balance : " + printStats(balancedCluster)); 098 assertClusterAsBalanced(balancedCluster); 099 } 100 } 101 102 /** 103 * Tests the bulk assignment used during cluster startup. Round-robin. Should yield a balanced 104 * cluster so same invariant as the load balancer holds, all servers holding either floor(avg) or 105 * 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 = getMockedGroupInfoManager().getRSGroupOfTable(tableName); 122 assertTrue(StringUtils.isNotEmpty(groupName)); 123 RSGroupInfo gInfo = getMockedGroupInfoManager().getRSGroup(groupName); 124 assertTrue("Region is not correctly assigned to group servers.", 125 gInfo.containsServer(sn.getAddress())); 126 } 127 } 128 ArrayListMultimap<String, ServerAndLoad> loadMap = convertToGroupBasedMap(assignments); 129 assertClusterAsBalanced(loadMap); 130 } 131 132 /** 133 * Test the cluster startup bulk assignment which attempts to retain assignment info. 134 */ 135 @Test 136 public void testRetainAssignment() throws Exception { 137 // Test simple case where all same servers are there 138 Map<ServerName, List<RegionInfo>> currentAssignments = mockClusterServers(); 139 Map<RegionInfo, ServerName> inputForTest = new HashMap<>(); 140 for (ServerName sn : currentAssignments.keySet()) { 141 for (RegionInfo region : currentAssignments.get(sn)) { 142 inputForTest.put(region, sn); 143 } 144 } 145 // verify region->null server assignment is handled 146 inputForTest.put(randomRegions(1).get(0), null); 147 Map<ServerName, List<RegionInfo>> newAssignment = 148 loadBalancer.retainAssignment(inputForTest, servers); 149 assertRetainedAssignment(inputForTest, servers, newAssignment); 150 } 151 152 /** 153 * Test BOGUS_SERVER_NAME among groups do not overwrite each other. 154 */ 155 @Test 156 public void testRoundRobinAssignment() throws Exception { 157 List<ServerName> onlineServers = new ArrayList<ServerName>(servers.size()); 158 onlineServers.addAll(servers); 159 List<RegionInfo> regions = randomRegions(25); 160 int bogusRegion = 0; 161 for (RegionInfo region : regions) { 162 String group = tableMap.get(region.getTable()); 163 if ("dg3".equals(group) || "dg4".equals(group)) { 164 bogusRegion++; 165 } 166 } 167 Set<Address> offlineServers = new HashSet<Address>(); 168 offlineServers.addAll(groupMap.get("dg3").getServers()); 169 offlineServers.addAll(groupMap.get("dg4").getServers()); 170 for (Iterator<ServerName> it = onlineServers.iterator(); it.hasNext();) { 171 ServerName server = it.next(); 172 Address address = server.getAddress(); 173 if (offlineServers.contains(address)) { 174 it.remove(); 175 } 176 } 177 Map<ServerName, List<RegionInfo>> assignments = 178 loadBalancer.roundRobinAssignment(regions, onlineServers); 179 assertEquals(bogusRegion, assignments.get(LoadBalancer.BOGUS_SERVER_NAME).size()); 180 } 181 182 @Test 183 public void testOnConfigurationChange() { 184 // fallbackEnabled default is false 185 assertFalse(loadBalancer.isFallbackEnabled()); 186 187 // change FALLBACK_GROUP_ENABLE_KEY from false to true 188 conf.setBoolean(RSGroupBasedLoadBalancer.FALLBACK_GROUP_ENABLE_KEY, true); 189 loadBalancer.onConfigurationChange(conf); 190 assertTrue(loadBalancer.isFallbackEnabled()); 191 192 // restore 193 conf.setBoolean(RSGroupBasedLoadBalancer.FALLBACK_GROUP_ENABLE_KEY, false); 194 loadBalancer.onConfigurationChange(conf); 195 assertFalse(loadBalancer.isFallbackEnabled()); 196 } 197}