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}