| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.hadoop.hbase.master.balancer; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertTrue; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.TreeMap; |
| import org.apache.hadoop.conf.Configuration; |
| import org.apache.hadoop.hbase.HBaseClassTestRule; |
| import org.apache.hadoop.hbase.HBaseConfiguration; |
| import org.apache.hadoop.hbase.ServerName; |
| import org.apache.hadoop.hbase.TableName; |
| import org.apache.hadoop.hbase.client.RegionInfo; |
| import org.apache.hadoop.hbase.master.RegionPlan; |
| import org.apache.hadoop.hbase.testclassification.MasterTests; |
| import org.apache.hadoop.hbase.testclassification.SmallTests; |
| import org.apache.hadoop.net.DNSToSwitchMapping; |
| import org.junit.BeforeClass; |
| import org.junit.ClassRule; |
| import org.junit.Rule; |
| import org.junit.Test; |
| import org.junit.experimental.categories.Category; |
| import org.junit.rules.TestName; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| /** |
| * Test the load balancer that is created by default. |
| */ |
| @Category({MasterTests.class, SmallTests.class}) |
| public class TestSimpleLoadBalancer extends BalancerTestBase { |
| |
| @ClassRule |
| public static final HBaseClassTestRule CLASS_RULE = |
| HBaseClassTestRule.forClass(TestSimpleLoadBalancer.class); |
| |
| private static final Logger LOG = LoggerFactory.getLogger(TestSimpleLoadBalancer.class); |
| |
| private static SimpleLoadBalancer loadBalancer; |
| |
| @BeforeClass |
| public static void beforeAllTests() throws Exception { |
| Configuration conf = HBaseConfiguration.create(); |
| conf.setClass("hbase.util.ip.to.rack.determiner", MockMapping.class, DNSToSwitchMapping.class); |
| conf.set("hbase.regions.slop", "0"); |
| loadBalancer = new SimpleLoadBalancer(); |
| loadBalancer.setConf(conf); |
| } |
| |
| int[] mockUniformCluster = new int[] { 5, 5, 5, 5, 5, 0 }; |
| |
| @Rule |
| public TestName name = new TestName(); |
| |
| /** |
| * Test the load balancing algorithm. |
| * |
| * Invariant is that all servers should be hosting either floor(average) or |
| * ceiling(average) at both table level and cluster level |
| */ |
| @Test |
| public void testBalanceClusterOverall() throws Exception { |
| Map<TableName, Map<ServerName, List<RegionInfo>>> clusterLoad = new TreeMap<>(); |
| for (int[] mockCluster : clusterStateMocks) { |
| Map<ServerName, List<RegionInfo>> clusterServers = mockClusterServers(mockCluster, 30); |
| List<ServerAndLoad> clusterList = convertToList(clusterServers); |
| clusterLoad.put(TableName.valueOf(name.getMethodName()), clusterServers); |
| HashMap<TableName, TreeMap<ServerName, List<RegionInfo>>> result = |
| mockClusterServersWithTables(clusterServers); |
| loadBalancer.setClusterLoad(clusterLoad); |
| List<RegionPlan> clusterplans = new ArrayList<>(); |
| for (Map.Entry<TableName, TreeMap<ServerName, List<RegionInfo>>> mapEntry : result |
| .entrySet()) { |
| TableName tableName = mapEntry.getKey(); |
| TreeMap<ServerName, List<RegionInfo>> servers = mapEntry.getValue(); |
| List<ServerAndLoad> list = convertToList(servers); |
| LOG.info("Mock Cluster : " + printMock(list) + " " + printStats(list)); |
| List<RegionPlan> partialplans = loadBalancer.balanceTable(tableName, servers); |
| if(partialplans != null) clusterplans.addAll(partialplans); |
| List<ServerAndLoad> balancedClusterPerTable = reconcile(list, partialplans, servers); |
| LOG.info("Mock Balance : " + printMock(balancedClusterPerTable)); |
| assertClusterAsBalanced(balancedClusterPerTable); |
| for (Map.Entry<ServerName, List<RegionInfo>> entry : servers.entrySet()) { |
| returnRegions(entry.getValue()); |
| returnServer(entry.getKey()); |
| } |
| } |
| List<ServerAndLoad> balancedCluster = reconcile(clusterList, clusterplans, clusterServers); |
| assertTrue(assertClusterOverallAsBalanced(balancedCluster, result.keySet().size())); |
| } |
| } |
| |
| /** |
| * Test the load balancing algorithm. |
| * |
| * Invariant is that all servers should be hosting either floor(average) or |
| * ceiling(average) at both table level and cluster level |
| * Deliberately generate a special case to show the overall strategy can achieve cluster |
| * level balance while the bytable strategy cannot |
| */ |
| @Test |
| public void testImpactOfBalanceClusterOverall() throws Exception { |
| testImpactOfBalanceClusterOverall(false); |
| } |
| |
| @Test |
| public void testImpactOfBalanceClusterOverallWithLoadOfAllTable() throws Exception { |
| testImpactOfBalanceClusterOverall(true); |
| } |
| |
| private void testImpactOfBalanceClusterOverall(boolean useLoadOfAllTable) throws Exception { |
| Map<TableName, Map<ServerName, List<RegionInfo>>> clusterLoad = new TreeMap<>(); |
| Map<ServerName, List<RegionInfo>> clusterServers = |
| mockUniformClusterServers(mockUniformCluster); |
| List<ServerAndLoad> clusterList = convertToList(clusterServers); |
| clusterLoad.put(TableName.valueOf(name.getMethodName()), clusterServers); |
| // use overall can achieve both table and cluster level balance |
| HashMap<TableName, TreeMap<ServerName, List<RegionInfo>>> LoadOfAllTable = |
| mockClusterServersWithTables(clusterServers); |
| if (useLoadOfAllTable) { |
| loadBalancer.setClusterLoad((Map) LoadOfAllTable); |
| } else { |
| loadBalancer.setClusterLoad(clusterLoad); |
| } |
| List<RegionPlan> clusterplans1 = new ArrayList<RegionPlan>(); |
| for (Map.Entry<TableName, TreeMap<ServerName, List<RegionInfo>>> mapEntry : LoadOfAllTable |
| .entrySet()) { |
| TableName tableName = mapEntry.getKey(); |
| TreeMap<ServerName, List<RegionInfo>> servers = mapEntry.getValue(); |
| List<ServerAndLoad> list = convertToList(servers); |
| LOG.info("Mock Cluster : " + printMock(list) + " " + printStats(list)); |
| List<RegionPlan> partialplans = loadBalancer.balanceTable(tableName, servers); |
| if (partialplans != null) clusterplans1.addAll(partialplans); |
| List<ServerAndLoad> balancedClusterPerTable = reconcile(list, partialplans, servers); |
| LOG.info("Mock Balance : " + printMock(balancedClusterPerTable)); |
| assertClusterAsBalanced(balancedClusterPerTable); |
| for (Map.Entry<ServerName, List<RegionInfo>> entry : servers.entrySet()) { |
| returnRegions(entry.getValue()); |
| returnServer(entry.getKey()); |
| } |
| } |
| List<ServerAndLoad> balancedCluster1 = reconcile(clusterList, clusterplans1, clusterServers); |
| assertTrue(assertClusterOverallAsBalanced(balancedCluster1, LoadOfAllTable.keySet().size())); |
| } |
| |
| @Test |
| public void testBalanceClusterOverallStrictly() throws Exception { |
| int[] regionNumOfTable1PerServer = { 3, 3, 4, 4, 4, 4, 5, 5, 5 }; |
| int[] regionNumOfTable2PerServer = { 2, 2, 2, 2, 2, 2, 2, 2, 1 }; |
| TreeMap<ServerName, List<RegionInfo>> serverRegionInfo = new TreeMap<>(); |
| List<ServerAndLoad> serverAndLoads = new ArrayList<>(); |
| for (int i = 0; i < regionNumOfTable1PerServer.length; i++) { |
| ServerName serverName = ServerName.valueOf("server" + i, 1000, -1); |
| List<RegionInfo> regions1 = |
| createRegions(regionNumOfTable1PerServer[i], TableName.valueOf("table1")); |
| List<RegionInfo> regions2 = |
| createRegions(regionNumOfTable2PerServer[i], TableName.valueOf("table2")); |
| regions1.addAll(regions2); |
| serverRegionInfo.put(serverName, regions1); |
| ServerAndLoad serverAndLoad = new ServerAndLoad(serverName, |
| regionNumOfTable1PerServer[i] + regionNumOfTable2PerServer[i]); |
| serverAndLoads.add(serverAndLoad); |
| } |
| HashMap<TableName, TreeMap<ServerName, List<RegionInfo>>> LoadOfAllTable = |
| mockClusterServersWithTables(serverRegionInfo); |
| loadBalancer.setClusterLoad((Map) LoadOfAllTable); |
| List<RegionPlan> partialplans = loadBalancer.balanceTable(TableName.valueOf("table1"), |
| LoadOfAllTable.get(TableName.valueOf("table1"))); |
| List<ServerAndLoad> balancedServerLoads = |
| reconcile(serverAndLoads, partialplans, serverRegionInfo); |
| for (ServerAndLoad serverAndLoad : balancedServerLoads) { |
| assertEquals(6, serverAndLoad.getLoad()); |
| } |
| } |
| |
| } |