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