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