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;
019
020import static org.junit.Assert.assertTrue;
021
022import java.io.IOException;
023import java.util.concurrent.atomic.AtomicBoolean;
024import java.util.concurrent.atomic.AtomicInteger;
025import org.apache.hadoop.hbase.HBaseClassTestRule;
026import org.apache.hadoop.hbase.HBaseTestingUtility;
027import org.apache.hadoop.hbase.HConstants;
028import org.apache.hadoop.hbase.HRegionInfo;
029import org.apache.hadoop.hbase.TableName;
030import org.apache.hadoop.hbase.regionserver.HRegionServer;
031import org.apache.hadoop.hbase.testclassification.MasterTests;
032import org.apache.hadoop.hbase.testclassification.MediumTests;
033import org.apache.hadoop.hbase.util.Bytes;
034import org.junit.After;
035import org.junit.Before;
036import org.junit.ClassRule;
037import org.junit.Ignore;
038import org.junit.Test;
039import org.junit.experimental.categories.Category;
040
041@Ignore // SimpleLoadBalancer seems borked whether AMv2 or not. Disabling till gets attention.
042@Category({MasterTests.class, MediumTests.class})
043public class TestMasterBalanceThrottling {
044
045  @ClassRule
046  public static final HBaseClassTestRule CLASS_RULE =
047      HBaseClassTestRule.forClass(TestMasterBalanceThrottling.class);
048
049  private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
050  private static final byte[] FAMILYNAME = Bytes.toBytes("fam");
051
052  @Before
053  public void setupConfiguration() {
054    TEST_UTIL.getConfiguration().set(HConstants.HBASE_MASTER_LOADBALANCER_CLASS,
055        "org.apache.hadoop.hbase.master.balancer.SimpleLoadBalancer");
056  }
057
058  @After
059  public void shutdown() throws Exception {
060    TEST_UTIL.getConfiguration().setInt(HConstants.HBASE_BALANCER_MAX_BALANCING,
061      HConstants.DEFAULT_HBASE_BALANCER_PERIOD);
062    TEST_UTIL.getConfiguration().setDouble(HConstants.HBASE_MASTER_BALANCER_MAX_RIT_PERCENT,
063      HConstants.DEFAULT_HBASE_MASTER_BALANCER_MAX_RIT_PERCENT);
064    TEST_UTIL.shutdownMiniCluster();
065  }
066
067  @Test
068  public void testThrottlingByBalanceInterval() throws Exception {
069    // Use default config and start a cluster of two regionservers.
070    TEST_UTIL.startMiniCluster(2);
071
072    TableName tableName = createTable("testNoThrottling");
073    final HMaster master = TEST_UTIL.getHBaseCluster().getMaster();
074
075    // Default max balancing time is 300000 ms and there are 50 regions to balance
076    // The balance interval is 6000 ms, much longger than the normal region in transition duration
077    // So the master can balance the region one by one
078    unbalance(master, tableName);
079    AtomicInteger maxCount = new AtomicInteger(0);
080    AtomicBoolean stop = new AtomicBoolean(false);
081    Thread checker = startBalancerChecker(master, maxCount, stop);
082    master.balance();
083    stop.set(true);
084    checker.interrupt();
085    checker.join();
086    assertTrue("max regions in transition: " + maxCount.get(), maxCount.get() == 1);
087
088    TEST_UTIL.deleteTable(tableName);
089  }
090
091  @Test
092  public void testThrottlingByMaxRitPercent() throws Exception {
093    // Set max balancing time to 500 ms and max percent of regions in transition to 0.05
094    TEST_UTIL.getConfiguration().setInt(HConstants.HBASE_BALANCER_MAX_BALANCING, 500);
095    TEST_UTIL.getConfiguration().setDouble(HConstants.HBASE_MASTER_BALANCER_MAX_RIT_PERCENT, 0.05);
096    TEST_UTIL.startMiniCluster(2);
097
098    TableName tableName = createTable("testThrottlingByMaxRitPercent");
099    final HMaster master = TEST_UTIL.getHBaseCluster().getMaster();
100
101    unbalance(master, tableName);
102    AtomicInteger maxCount = new AtomicInteger(0);
103    AtomicBoolean stop = new AtomicBoolean(false);
104    Thread checker = startBalancerChecker(master, maxCount, stop);
105    master.balance();
106    stop.set(true);
107    checker.interrupt();
108    checker.join();
109    // The max number of regions in transition is 100 * 0.05 = 5
110    assertTrue("max regions in transition: " + maxCount.get(), maxCount.get() == 5);
111
112    TEST_UTIL.deleteTable(tableName);
113  }
114
115  private TableName createTable(String table) throws IOException {
116    TableName tableName = TableName.valueOf(table);
117    byte[] startKey = new byte[] { 0x00 };
118    byte[] stopKey = new byte[] { 0x7f };
119    TEST_UTIL.createTable(tableName, new byte[][] { FAMILYNAME }, 1, startKey, stopKey,
120      100);
121    return tableName;
122  }
123
124  private Thread startBalancerChecker(final HMaster master, final AtomicInteger maxCount,
125      final AtomicBoolean stop) {
126    Runnable checker = new Runnable() {
127      @Override
128      public void run() {
129        while (!stop.get()) {
130          maxCount.set(Math.max(maxCount.get(),
131              master.getAssignmentManager().getRegionStates().getRegionsInTransitionCount()));
132          try {
133            Thread.sleep(10);
134          } catch (InterruptedException e) {
135            e.printStackTrace();
136          }
137        }
138      }
139    };
140    Thread thread = new Thread(checker);
141    thread.start();
142    return thread;
143  }
144
145  private void unbalance(HMaster master, TableName tableName) throws Exception {
146    while (master.getAssignmentManager().getRegionStates().getRegionsInTransitionCount() > 0) {
147      Thread.sleep(100);
148    }
149    HRegionServer biasedServer = TEST_UTIL.getMiniHBaseCluster().getRegionServer(0);
150    for (HRegionInfo regionInfo : TEST_UTIL.getAdmin().getTableRegions(tableName)) {
151      master.move(regionInfo.getEncodedNameAsBytes(),
152        Bytes.toBytes(biasedServer.getServerName().getServerName()));
153    }
154    while (master.getAssignmentManager().getRegionStates().getRegionsInTransitionCount() > 0) {
155      Thread.sleep(100);
156    }
157  }
158}