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