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