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.mockito.Mockito.mock;
021import static org.mockito.Mockito.when;
022
023import java.util.Arrays;
024import java.util.List;
025import java.util.Map;
026import org.apache.hadoop.hbase.HBaseClassTestRule;
027import org.apache.hadoop.hbase.HConstants;
028import org.apache.hadoop.hbase.ServerName;
029import org.apache.hadoop.hbase.TableName;
030import org.apache.hadoop.hbase.client.LogEntry;
031import org.apache.hadoop.hbase.client.RegionInfo;
032import org.apache.hadoop.hbase.master.MasterServices;
033import org.apache.hadoop.hbase.master.RegionPlan;
034import org.apache.hadoop.hbase.namequeues.BalancerDecisionDetails;
035import org.apache.hadoop.hbase.namequeues.request.NamedQueueGetRequest;
036import org.apache.hadoop.hbase.namequeues.response.NamedQueueGetResponse;
037import org.apache.hadoop.hbase.testclassification.MasterTests;
038import org.apache.hadoop.hbase.testclassification.MediumTests;
039import org.junit.Assert;
040import org.junit.ClassRule;
041import org.junit.Test;
042import org.junit.experimental.categories.Category;
043
044import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
045import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos;
046import org.apache.hadoop.hbase.shaded.protobuf.generated.RecentLogs;
047
048/**
049 * Test BalancerDecision ring buffer using namedQueue interface
050 */
051@Category({ MasterTests.class, MediumTests.class })
052public class TestBalancerDecision extends StochasticBalancerTestBase {
053
054  @ClassRule
055  public static final HBaseClassTestRule CLASS_RULE =
056    HBaseClassTestRule.forClass(TestBalancerDecision.class);
057
058  @Test
059  public void testBalancerDecisions() {
060    conf.setBoolean("hbase.master.balancer.decision.buffer.enabled", true);
061    MasterServices services = mock(MasterServices.class);
062    when(services.getConfiguration()).thenReturn(conf);
063    MasterClusterInfoProvider provider = new MasterClusterInfoProvider(services);
064    loadBalancer.setClusterInfoProvider(provider);
065    loadBalancer.onConfigurationChange(conf);
066    float minCost = conf.getFloat("hbase.master.balancer.stochastic.minCostNeedBalance", 0.05f);
067    float slop = conf.getFloat(HConstants.LOAD_BALANCER_SLOP_KEY, 0.2f);
068    conf.setFloat("hbase.master.balancer.stochastic.minCostNeedBalance", 1.0f);
069    conf.setFloat(HConstants.LOAD_BALANCER_SLOP_KEY, -1f);
070    try {
071      // Test with/without per table balancer.
072      boolean[] perTableBalancerConfigs = { true, false };
073      for (boolean isByTable : perTableBalancerConfigs) {
074        conf.setBoolean(HConstants.HBASE_MASTER_LOADBALANCE_BYTABLE, isByTable);
075        loadBalancer.onConfigurationChange(conf);
076        for (int[] mockCluster : clusterStateMocks) {
077          Map<ServerName, List<RegionInfo>> servers = mockClusterServers(mockCluster);
078          Map<TableName, Map<ServerName, List<RegionInfo>>> LoadOfAllTable =
079            (Map) mockClusterServersWithTables(servers);
080          List<RegionPlan> plans = loadBalancer.balanceCluster(LoadOfAllTable);
081          boolean emptyPlans = plans == null || plans.isEmpty();
082          Assert.assertTrue(emptyPlans || needsBalanceIdleRegion(mockCluster));
083        }
084      }
085      final NamedQueueGetRequest namedQueueGetRequest = new NamedQueueGetRequest();
086      namedQueueGetRequest.setNamedQueueEvent(BalancerDecisionDetails.BALANCER_DECISION_EVENT);
087      namedQueueGetRequest
088        .setBalancerDecisionsRequest(MasterProtos.BalancerDecisionsRequest.getDefaultInstance());
089      NamedQueueGetResponse namedQueueGetResponse =
090        provider.getNamedQueueRecorder().getNamedQueueRecords(namedQueueGetRequest);
091      List<RecentLogs.BalancerDecision> balancerDecisions =
092        namedQueueGetResponse.getBalancerDecisions();
093      MasterProtos.BalancerDecisionsResponse response = MasterProtos.BalancerDecisionsResponse
094        .newBuilder().addAllBalancerDecision(balancerDecisions).build();
095      List<LogEntry> balancerDecisionRecords = ProtobufUtil.getBalancerDecisionEntries(response);
096      Assert.assertTrue(balancerDecisionRecords.size() > 160);
097    } finally {
098      // reset config
099      conf.unset(HConstants.HBASE_MASTER_LOADBALANCE_BYTABLE);
100      conf.setFloat("hbase.master.balancer.stochastic.minCostNeedBalance", minCost);
101      conf.setFloat(HConstants.LOAD_BALANCER_SLOP_KEY, slop);
102      loadBalancer.onConfigurationChange(conf);
103    }
104  }
105
106  private static boolean needsBalanceIdleRegion(int[] cluster) {
107    return (Arrays.stream(cluster).anyMatch(x -> x > 1))
108      && (Arrays.stream(cluster).anyMatch(x -> x < 1));
109  }
110}