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.assertTrue; 021 022import java.util.ArrayList; 023import java.util.HashMap; 024import java.util.List; 025import java.util.Map; 026import java.util.TreeMap; 027import org.apache.hadoop.conf.Configuration; 028import org.apache.hadoop.hbase.HBaseClassTestRule; 029import org.apache.hadoop.hbase.HBaseConfiguration; 030import org.apache.hadoop.hbase.ServerName; 031import org.apache.hadoop.hbase.TableName; 032import org.apache.hadoop.hbase.client.RegionInfo; 033import org.apache.hadoop.hbase.master.LoadBalancer; 034import org.apache.hadoop.hbase.master.RegionPlan; 035import org.apache.hadoop.hbase.testclassification.MasterTests; 036import org.apache.hadoop.hbase.testclassification.MediumTests; 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, MediumTests.class}) 052public class TestDefaultLoadBalancer extends BalancerTestBase { 053 054 @ClassRule 055 public static final HBaseClassTestRule CLASS_RULE = 056 HBaseClassTestRule.forClass(TestDefaultLoadBalancer.class); 057 058 private static final Logger LOG = LoggerFactory.getLogger(TestDefaultLoadBalancer.class); 059 060 private static LoadBalancer 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 * @throws Exception 133 */ 134 @Test 135 public void testBalanceClusterOverall() throws Exception { 136 Map<TableName, Map<ServerName, List<RegionInfo>>> clusterLoad = new TreeMap<>(); 137 for (int[] mockCluster : clusterStateMocks) { 138 Map<ServerName, List<RegionInfo>> clusterServers = mockClusterServers(mockCluster, 50); 139 List<ServerAndLoad> clusterList = convertToList(clusterServers); 140 clusterLoad.put(TableName.valueOf(name.getMethodName()), clusterServers); 141 HashMap<TableName, TreeMap<ServerName, List<RegionInfo>>> result = mockClusterServersWithTables(clusterServers); 142 loadBalancer.setClusterLoad(clusterLoad); 143 List<RegionPlan> clusterplans = new ArrayList<>(); 144 List<Pair<TableName, Integer>> regionAmountList = new ArrayList<>(); 145 for(TreeMap<ServerName, List<RegionInfo>> servers : result.values()){ 146 List<ServerAndLoad> list = convertToList(servers); 147 LOG.info("Mock Cluster : " + printMock(list) + " " + printStats(list)); 148 List<RegionPlan> partialplans = loadBalancer.balanceCluster(servers); 149 if(partialplans != null) clusterplans.addAll(partialplans); 150 List<ServerAndLoad> balancedClusterPerTable = reconcile(list, partialplans, servers); 151 LOG.info("Mock Balance : " + printMock(balancedClusterPerTable)); 152 assertClusterAsBalanced(balancedClusterPerTable); 153 for (Map.Entry<ServerName, List<RegionInfo>> entry : servers.entrySet()) { 154 returnRegions(entry.getValue()); 155 returnServer(entry.getKey()); 156 } 157 } 158 List<ServerAndLoad> balancedCluster = reconcile(clusterList, clusterplans, clusterServers); 159 assertTrue(assertClusterOverallAsBalanced(balancedCluster, result.keySet().size())); 160 } 161 } 162 163 /** 164 * Test the load balancing algorithm. 165 * 166 * Invariant is that all servers should be hosting either floor(average) or 167 * ceiling(average) at both table level and cluster level 168 * Deliberately generate a special case to show the overall strategy can achieve cluster 169 * level balance while the bytable strategy cannot 170 * @throws Exception 171 */ 172 @Test 173 public void testImpactOfBalanceClusterOverall() throws Exception { 174 Map<TableName, Map<ServerName, List<RegionInfo>>> clusterLoad = new TreeMap<>(); 175 Map<ServerName, List<RegionInfo>> clusterServers = mockUniformClusterServers(mockUniformCluster); 176 List<ServerAndLoad> clusterList = convertToList(clusterServers); 177 clusterLoad.put(TableName.valueOf(name.getMethodName()), clusterServers); 178 // use overall can achieve both table and cluster level balance 179 HashMap<TableName, TreeMap<ServerName, List<RegionInfo>>> result1 = mockClusterServersWithTables(clusterServers); 180 loadBalancer.setClusterLoad(clusterLoad); 181 List<RegionPlan> clusterplans1 = new ArrayList<RegionPlan>(); 182 List<Pair<TableName, Integer>> regionAmountList = new ArrayList<Pair<TableName, Integer>>(); 183 for(TreeMap<ServerName, List<RegionInfo>> servers : result1.values()){ 184 List<ServerAndLoad> list = convertToList(servers); 185 LOG.info("Mock Cluster : " + printMock(list) + " " + printStats(list)); 186 List<RegionPlan> partialplans = loadBalancer.balanceCluster(servers); 187 if(partialplans != null) clusterplans1.addAll(partialplans); 188 List<ServerAndLoad> balancedClusterPerTable = reconcile(list, partialplans, servers); 189 LOG.info("Mock Balance : " + printMock(balancedClusterPerTable)); 190 assertClusterAsBalanced(balancedClusterPerTable); 191 for (Map.Entry<ServerName, List<RegionInfo>> entry : servers.entrySet()) { 192 returnRegions(entry.getValue()); 193 returnServer(entry.getKey()); 194 } 195 } 196 List<ServerAndLoad> balancedCluster1 = reconcile(clusterList, clusterplans1, clusterServers); 197 assertTrue(assertClusterOverallAsBalanced(balancedCluster1, result1.keySet().size())); 198 } 199}