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