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.net.DNSToSwitchMapping; 038import org.junit.BeforeClass; 039import org.junit.ClassRule; 040import org.junit.Rule; 041import org.junit.Test; 042import org.junit.experimental.categories.Category; 043import org.junit.rules.TestName; 044import org.slf4j.Logger; 045import org.slf4j.LoggerFactory; 046 047/** 048 * Test the load balancer that is created by default. 049 */ 050@Category({ MasterTests.class, SmallTests.class }) 051public class TestSimpleLoadBalancer extends BalancerTestBase { 052 053 @ClassRule 054 public static final HBaseClassTestRule CLASS_RULE = 055 HBaseClassTestRule.forClass(TestSimpleLoadBalancer.class); 056 057 private static final Logger LOG = LoggerFactory.getLogger(TestSimpleLoadBalancer.class); 058 059 private static SimpleLoadBalancer loadBalancer; 060 061 @BeforeClass 062 public static void beforeAllTests() throws Exception { 063 Configuration conf = HBaseConfiguration.create(); 064 conf.setClass("hbase.util.ip.to.rack.determiner", MockMapping.class, DNSToSwitchMapping.class); 065 conf.set("hbase.regions.slop", "0"); 066 loadBalancer = new SimpleLoadBalancer(); 067 loadBalancer.setClusterInfoProvider(new DummyClusterInfoProvider(conf)); 068 loadBalancer.initialize(); 069 } 070 071 int[] mockUniformCluster = new int[] { 5, 5, 5, 5, 5, 0 }; 072 073 @Rule 074 public TestName name = new TestName(); 075 076 /** 077 * Test the load balancing algorithm. Invariant is that all servers should be hosting either 078 * floor(average) or ceiling(average) at both table level and cluster level 079 */ 080 @Test 081 public void testBalanceClusterOverall() throws Exception { 082 Map<TableName, Map<ServerName, List<RegionInfo>>> clusterLoad = new TreeMap<>(); 083 for (int[] mockCluster : clusterStateMocks) { 084 Map<ServerName, List<RegionInfo>> clusterServers = mockClusterServers(mockCluster, 30); 085 List<ServerAndLoad> clusterList = convertToList(clusterServers); 086 clusterLoad.put(TableName.valueOf(name.getMethodName()), clusterServers); 087 HashMap<TableName, TreeMap<ServerName, List<RegionInfo>>> result = 088 mockClusterServersWithTables(clusterServers); 089 loadBalancer.setClusterLoad(clusterLoad); 090 List<RegionPlan> clusterplans = new ArrayList<>(); 091 for (Map.Entry<TableName, TreeMap<ServerName, List<RegionInfo>>> mapEntry : result 092 .entrySet()) { 093 TableName tableName = mapEntry.getKey(); 094 TreeMap<ServerName, List<RegionInfo>> servers = mapEntry.getValue(); 095 List<ServerAndLoad> list = convertToList(servers); 096 LOG.info("Mock Cluster : " + printMock(list) + " " + printStats(list)); 097 List<RegionPlan> partialplans = loadBalancer.balanceTable(tableName, servers); 098 if (partialplans != null) clusterplans.addAll(partialplans); 099 List<ServerAndLoad> balancedClusterPerTable = reconcile(list, partialplans, servers); 100 LOG.info("Mock Balance : " + printMock(balancedClusterPerTable)); 101 assertClusterAsBalanced(balancedClusterPerTable); 102 for (Map.Entry<ServerName, List<RegionInfo>> entry : servers.entrySet()) { 103 returnRegions(entry.getValue()); 104 returnServer(entry.getKey()); 105 } 106 } 107 List<ServerAndLoad> balancedCluster = reconcile(clusterList, clusterplans, clusterServers); 108 assertTrue(assertClusterOverallAsBalanced(balancedCluster, result.keySet().size())); 109 } 110 } 111 112 /** 113 * Test the load balancing algorithm. Invariant is that all servers should be hosting either 114 * floor(average) or ceiling(average) at both table level and cluster level Deliberately generate 115 * a special case to show the overall strategy can achieve cluster level balance while the bytable 116 * strategy cannot 117 */ 118 @Test 119 public void testImpactOfBalanceClusterOverall() throws Exception { 120 testImpactOfBalanceClusterOverall(false); 121 } 122 123 @Test 124 public void testImpactOfBalanceClusterOverallWithLoadOfAllTable() throws Exception { 125 testImpactOfBalanceClusterOverall(true); 126 } 127 128 private void testImpactOfBalanceClusterOverall(boolean useLoadOfAllTable) throws Exception { 129 Map<TableName, Map<ServerName, List<RegionInfo>>> clusterLoad = new TreeMap<>(); 130 Map<ServerName, List<RegionInfo>> clusterServers = 131 mockUniformClusterServers(mockUniformCluster); 132 List<ServerAndLoad> clusterList = convertToList(clusterServers); 133 clusterLoad.put(TableName.valueOf(name.getMethodName()), clusterServers); 134 // use overall can achieve both table and cluster level balance 135 HashMap<TableName, TreeMap<ServerName, List<RegionInfo>>> LoadOfAllTable = 136 mockClusterServersWithTables(clusterServers); 137 if (useLoadOfAllTable) { 138 loadBalancer.setClusterLoad((Map) LoadOfAllTable); 139 } else { 140 loadBalancer.setClusterLoad(clusterLoad); 141 } 142 List<RegionPlan> clusterplans1 = new ArrayList<RegionPlan>(); 143 for (Map.Entry<TableName, TreeMap<ServerName, List<RegionInfo>>> mapEntry : LoadOfAllTable 144 .entrySet()) { 145 TableName tableName = mapEntry.getKey(); 146 TreeMap<ServerName, List<RegionInfo>> servers = mapEntry.getValue(); 147 List<ServerAndLoad> list = convertToList(servers); 148 LOG.info("Mock Cluster : " + printMock(list) + " " + printStats(list)); 149 List<RegionPlan> partialplans = loadBalancer.balanceTable(tableName, servers); 150 if (partialplans != null) clusterplans1.addAll(partialplans); 151 List<ServerAndLoad> balancedClusterPerTable = reconcile(list, partialplans, servers); 152 LOG.info("Mock Balance : " + printMock(balancedClusterPerTable)); 153 assertClusterAsBalanced(balancedClusterPerTable); 154 for (Map.Entry<ServerName, List<RegionInfo>> entry : servers.entrySet()) { 155 returnRegions(entry.getValue()); 156 returnServer(entry.getKey()); 157 } 158 } 159 List<ServerAndLoad> balancedCluster1 = reconcile(clusterList, clusterplans1, clusterServers); 160 assertTrue(assertClusterOverallAsBalanced(balancedCluster1, LoadOfAllTable.keySet().size())); 161 } 162 163 @Test 164 public void testBalanceClusterOverallStrictly() { 165 int[][] regionsPerServerPerTable = new int[][] { new int[] { 3, 3, 4, 4, 4, 4, 5, 5, 5 }, 166 new int[] { 2, 2, 2, 2, 2, 2, 2, 2, 1 }, }; 167 TreeMap<ServerName, List<RegionInfo>> serverRegionInfo = 168 mockClusterServers(regionsPerServerPerTable); 169 List<ServerAndLoad> serverAndLoads = convertToList(serverRegionInfo); 170 Map<TableName, TreeMap<ServerName, List<RegionInfo>>> loadOfAllTable = 171 mockClusterServersWithTables(serverRegionInfo); 172 loadBalancer.setClusterLoad((Map) loadOfAllTable); 173 List<RegionPlan> partialplans = loadBalancer.balanceTable(TableName.valueOf("table0"), 174 loadOfAllTable.get(TableName.valueOf("table0"))); 175 List<ServerAndLoad> balancedServerLoads = 176 reconcile(serverAndLoads, partialplans, serverRegionInfo); 177 for (ServerAndLoad serverAndLoad : balancedServerLoads) { 178 assertEquals(6, serverAndLoad.getLoad()); 179 } 180 } 181 182}