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.HashMap; 025import java.util.List; 026import java.util.Map; 027import java.util.TreeMap; 028import org.apache.hadoop.conf.Configuration; 029import org.apache.hadoop.hbase.HBaseClassTestRule; 030import org.apache.hadoop.hbase.HBaseConfiguration; 031import org.apache.hadoop.hbase.ServerName; 032import org.apache.hadoop.hbase.TableName; 033import org.apache.hadoop.hbase.client.RegionInfo; 034import org.apache.hadoop.hbase.master.RegionPlan; 035import org.apache.hadoop.hbase.testclassification.MasterTests; 036import org.apache.hadoop.hbase.testclassification.SmallTests; 037import org.apache.hadoop.hbase.util.Pair; 038import org.apache.hadoop.net.DNSToSwitchMapping; 039import org.junit.BeforeClass; 040import org.junit.ClassRule; 041import org.junit.Rule; 042import org.junit.Test; 043import org.junit.experimental.categories.Category; 044import org.junit.rules.TestName; 045import org.slf4j.Logger; 046import org.slf4j.LoggerFactory; 047 048/** 049 * Test the load balancer that is created by default. 050 */ 051@Category({MasterTests.class, SmallTests.class}) 052public class TestSimpleLoadBalancer extends BalancerTestBase { 053 054 @ClassRule 055 public static final HBaseClassTestRule CLASS_RULE = 056 HBaseClassTestRule.forClass(TestSimpleLoadBalancer.class); 057 058 private static final Logger LOG = LoggerFactory.getLogger(TestSimpleLoadBalancer.class); 059 060 private static SimpleLoadBalancer loadBalancer; 061 062 @BeforeClass 063 public static void beforeAllTests() throws Exception { 064 Configuration conf = HBaseConfiguration.create(); 065 conf.setClass("hbase.util.ip.to.rack.determiner", MockMapping.class, DNSToSwitchMapping.class); 066 conf.set("hbase.regions.slop", "0"); 067 loadBalancer = new SimpleLoadBalancer(); 068 loadBalancer.setConf(conf); 069 } 070 071 // int[testnum][servernumber] -> numregions 072 int[][] clusterStateMocks = new int[][] { 073 // 1 node 074 new int[] { 0 }, 075 new int[] { 1 }, 076 new int[] { 10 }, 077 // 2 node 078 new int[] { 0, 0 }, 079 new int[] { 2, 0 }, 080 new int[] { 2, 1 }, 081 new int[] { 2, 2 }, 082 new int[] { 2, 3 }, 083 new int[] { 2, 4 }, 084 new int[] { 1, 1 }, 085 new int[] { 0, 1 }, 086 new int[] { 10, 1 }, 087 new int[] { 14, 1432 }, 088 new int[] { 47, 53 }, 089 // 3 node 090 new int[] { 0, 1, 2 }, 091 new int[] { 1, 2, 3 }, 092 new int[] { 0, 2, 2 }, 093 new int[] { 0, 3, 0 }, 094 new int[] { 0, 4, 0 }, 095 new int[] { 20, 20, 0 }, 096 // 4 node 097 new int[] { 0, 1, 2, 3 }, 098 new int[] { 4, 0, 0, 0 }, 099 new int[] { 5, 0, 0, 0 }, 100 new int[] { 6, 6, 0, 0 }, 101 new int[] { 6, 2, 0, 0 }, 102 new int[] { 6, 1, 0, 0 }, 103 new int[] { 6, 0, 0, 0 }, 104 new int[] { 4, 4, 4, 7 }, 105 new int[] { 4, 4, 4, 8 }, 106 new int[] { 0, 0, 0, 7 }, 107 // 5 node 108 new int[] { 1, 1, 1, 1, 4 }, 109 // more nodes 110 new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, 111 new int[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 10 }, new int[] { 6, 6, 5, 6, 6, 6, 6, 6, 6, 1 }, 112 new int[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 54 }, new int[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 55 }, 113 new int[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 56 }, new int[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 16 }, 114 new int[] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 8 }, new int[] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 9 }, 115 new int[] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 10 }, new int[] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 123 }, 116 new int[] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 155 }, 117 new int[] { 0, 0, 144, 1, 1, 1, 1, 1123, 133, 138, 12, 1444 }, 118 new int[] { 0, 0, 144, 1, 0, 4, 1, 1123, 133, 138, 12, 1444 }, 119 new int[] { 1538, 1392, 1561, 1557, 1535, 1553, 1385, 1542, 1619 } }; 120 121 int [] mockUniformCluster = new int[] { 5, 5, 5, 5, 5 ,0}; 122 123 @Rule 124 public TestName name = new TestName(); 125 126 /** 127 * Test the load balancing algorithm. 128 * 129 * Invariant is that all servers should be hosting either floor(average) or 130 * ceiling(average) at both table level and cluster level 131 * 132 */ 133 @Test 134 public void testBalanceClusterOverall() throws Exception { 135 Map<TableName, Map<ServerName, List<RegionInfo>>> clusterLoad = new TreeMap<>(); 136 for (int[] mockCluster : clusterStateMocks) { 137 Map<ServerName, List<RegionInfo>> clusterServers = mockClusterServers(mockCluster, 30); 138 List<ServerAndLoad> clusterList = convertToList(clusterServers); 139 clusterLoad.put(TableName.valueOf(name.getMethodName()), clusterServers); 140 HashMap<TableName, TreeMap<ServerName, List<RegionInfo>>> result = 141 mockClusterServersWithTables(clusterServers); 142 loadBalancer.setClusterLoad(clusterLoad); 143 List<RegionPlan> clusterplans = new ArrayList<>(); 144 List<Pair<TableName, Integer>> regionAmountList = new ArrayList<>(); 145 for (Map.Entry<TableName, TreeMap<ServerName, List<RegionInfo>>> mapEntry : result 146 .entrySet()) { 147 TableName tableName = mapEntry.getKey(); 148 TreeMap<ServerName, List<RegionInfo>> servers = mapEntry.getValue(); 149 List<ServerAndLoad> list = convertToList(servers); 150 LOG.info("Mock Cluster : " + printMock(list) + " " + printStats(list)); 151 List<RegionPlan> partialplans = loadBalancer.balanceTable(tableName, servers); 152 if(partialplans != null) clusterplans.addAll(partialplans); 153 List<ServerAndLoad> balancedClusterPerTable = reconcile(list, partialplans, servers); 154 LOG.info("Mock Balance : " + printMock(balancedClusterPerTable)); 155 assertClusterAsBalanced(balancedClusterPerTable); 156 for (Map.Entry<ServerName, List<RegionInfo>> entry : servers.entrySet()) { 157 returnRegions(entry.getValue()); 158 returnServer(entry.getKey()); 159 } 160 } 161 List<ServerAndLoad> balancedCluster = reconcile(clusterList, clusterplans, clusterServers); 162 assertTrue(assertClusterOverallAsBalanced(balancedCluster, result.keySet().size())); 163 } 164 } 165 166 /** 167 * Test the load balancing algorithm. 168 * 169 * Invariant is that all servers should be hosting either floor(average) or 170 * ceiling(average) at both table level and cluster level 171 * Deliberately generate a special case to show the overall strategy can achieve cluster 172 * level balance while the bytable strategy cannot 173 * @throws Exception 174 */ 175 @Test 176 public void testImpactOfBalanceClusterOverall() throws Exception { 177 testImpactOfBalanceClusterOverall(false); 178 } 179 180 @Test 181 public void testImpactOfBalanceClusterOverallWithLoadOfAllTable() throws Exception { 182 testImpactOfBalanceClusterOverall(true); 183 } 184 185 private void testImpactOfBalanceClusterOverall(boolean useLoadOfAllTable) throws Exception { 186 Map<TableName, Map<ServerName, List<RegionInfo>>> clusterLoad = new TreeMap<>(); 187 Map<ServerName, List<RegionInfo>> clusterServers = 188 mockUniformClusterServers(mockUniformCluster); 189 List<ServerAndLoad> clusterList = convertToList(clusterServers); 190 clusterLoad.put(TableName.valueOf(name.getMethodName()), clusterServers); 191 // use overall can achieve both table and cluster level balance 192 HashMap<TableName, TreeMap<ServerName, List<RegionInfo>>> LoadOfAllTable = 193 mockClusterServersWithTables(clusterServers); 194 if (useLoadOfAllTable) { 195 loadBalancer.setClusterLoad((Map) LoadOfAllTable); 196 } else { 197 loadBalancer.setClusterLoad(clusterLoad); 198 } 199 List<RegionPlan> clusterplans1 = new ArrayList<RegionPlan>(); 200 List<Pair<TableName, Integer>> regionAmountList = new ArrayList<Pair<TableName, Integer>>(); 201 for (Map.Entry<TableName, TreeMap<ServerName, List<RegionInfo>>> mapEntry : LoadOfAllTable 202 .entrySet()) { 203 TableName tableName = mapEntry.getKey(); 204 TreeMap<ServerName, List<RegionInfo>> servers = mapEntry.getValue(); 205 List<ServerAndLoad> list = convertToList(servers); 206 LOG.info("Mock Cluster : " + printMock(list) + " " + printStats(list)); 207 List<RegionPlan> partialplans = loadBalancer.balanceTable(tableName, servers); 208 if (partialplans != null) clusterplans1.addAll(partialplans); 209 List<ServerAndLoad> balancedClusterPerTable = reconcile(list, partialplans, servers); 210 LOG.info("Mock Balance : " + printMock(balancedClusterPerTable)); 211 assertClusterAsBalanced(balancedClusterPerTable); 212 for (Map.Entry<ServerName, List<RegionInfo>> entry : servers.entrySet()) { 213 returnRegions(entry.getValue()); 214 returnServer(entry.getKey()); 215 } 216 } 217 List<ServerAndLoad> balancedCluster1 = reconcile(clusterList, clusterplans1, clusterServers); 218 assertTrue(assertClusterOverallAsBalanced(balancedCluster1, LoadOfAllTable.keySet().size())); 219 } 220 221 @Test 222 public void testBalanceClusterOverallStrictly() throws Exception { 223 int[] regionNumOfTable1PerServer = { 3, 3, 4, 4, 4, 4, 5, 5, 5 }; 224 int[] regionNumOfTable2PerServer = { 2, 2, 2, 2, 2, 2, 2, 2, 1 }; 225 TreeMap<ServerName, List<RegionInfo>> serverRegionInfo = new TreeMap<>(); 226 List<ServerAndLoad> serverAndLoads = new ArrayList<>(); 227 for (int i = 0; i < regionNumOfTable1PerServer.length; i++) { 228 ServerName serverName = ServerName.valueOf("server" + i, 1000, -1); 229 List<RegionInfo> regions1 = 230 createRegions(regionNumOfTable1PerServer[i], TableName.valueOf("table1")); 231 List<RegionInfo> regions2 = 232 createRegions(regionNumOfTable2PerServer[i], TableName.valueOf("table2")); 233 regions1.addAll(regions2); 234 serverRegionInfo.put(serverName, regions1); 235 ServerAndLoad serverAndLoad = new ServerAndLoad(serverName, 236 regionNumOfTable1PerServer[i] + regionNumOfTable2PerServer[i]); 237 serverAndLoads.add(serverAndLoad); 238 } 239 HashMap<TableName, TreeMap<ServerName, List<RegionInfo>>> LoadOfAllTable = 240 mockClusterServersWithTables(serverRegionInfo); 241 loadBalancer.setClusterLoad((Map) LoadOfAllTable); 242 List<RegionPlan> partialplans = loadBalancer.balanceTable(TableName.valueOf("table1"), 243 LoadOfAllTable.get(TableName.valueOf("table1"))); 244 List<ServerAndLoad> balancedServerLoads = 245 reconcile(serverAndLoads, partialplans, serverRegionInfo); 246 for (ServerAndLoad serverAndLoad : balancedServerLoads) { 247 assertEquals(6, serverAndLoad.getLoad()); 248 } 249 } 250 251}