| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.hadoop.hbase.master.balancer; |
| |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.apache.hadoop.conf.Configuration; |
| import org.apache.hadoop.hbase.HBaseClassTestRule; |
| |
| import org.apache.hadoop.hbase.ServerName; |
| import org.apache.hadoop.hbase.TableName; |
| import org.apache.hadoop.hbase.client.LogEntry; |
| import org.apache.hadoop.hbase.client.RegionInfo; |
| |
| import org.apache.hadoop.hbase.namequeues.BalancerRejectionDetails; |
| import org.apache.hadoop.hbase.namequeues.request.NamedQueueGetRequest; |
| import org.apache.hadoop.hbase.namequeues.response.NamedQueueGetResponse; |
| import org.apache.hadoop.hbase.testclassification.MasterTests; |
| import org.apache.hadoop.hbase.testclassification.MediumTests; |
| import org.junit.Assert; |
| import org.junit.ClassRule; |
| import org.junit.Ignore; |
| import org.junit.Test; |
| import org.junit.experimental.categories.Category; |
| |
| import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; |
| import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos; |
| import org.apache.hadoop.hbase.shaded.protobuf.generated.RecentLogs; |
| |
| /** |
| * Test BalancerRejection ring buffer using namedQueue interface |
| */ |
| @Category({ MasterTests.class, MediumTests.class }) |
| public class TestBalancerRejection extends StochasticBalancerTestBase { |
| |
| @ClassRule |
| public static final HBaseClassTestRule CLASS_RULE = |
| HBaseClassTestRule.forClass(TestBalancerRejection.class); |
| |
| static class MockCostFunction extends CostFunction{ |
| public static double mockCost; |
| |
| public MockCostFunction(Configuration c) { |
| } |
| |
| @Override |
| protected double cost() { |
| return mockCost; |
| } |
| |
| @Override |
| boolean isNeeded() { |
| return super.isNeeded(); |
| } |
| |
| @Override |
| float getMultiplier() { |
| return 1; |
| } |
| } |
| |
| @Test |
| public void testBalancerRejections() throws Exception{ |
| try { |
| //enabled balancer rejection recording |
| conf.setBoolean(BaseLoadBalancer.BALANCER_REJECTION_BUFFER_ENABLED, true); |
| conf.set(StochasticLoadBalancer.COST_FUNCTIONS_COST_FUNCTIONS_KEY, MockCostFunction.class.getName()); |
| loadBalancer.setConf(conf); |
| //Simulate 2 servers with 5 regions. |
| Map<ServerName, List<RegionInfo>> servers = mockClusterServers(new int[] { 5, 5 }); |
| Map<TableName, Map<ServerName, List<RegionInfo>>> LoadOfAllTable = (Map) mockClusterServersWithTables(servers); |
| |
| //Reject case 1: Total cost < 0 |
| MockCostFunction.mockCost = -Double.MAX_VALUE; |
| //Since the Balancer was rejected, there should not be any plans |
| Assert.assertNull(loadBalancer.balanceCluster(LoadOfAllTable)); |
| |
| //Reject case 2: Cost < minCostNeedBalance |
| MockCostFunction.mockCost = 1; |
| conf.setFloat("hbase.master.balancer.stochastic.minCostNeedBalance", Float.MAX_VALUE); |
| loadBalancer.setConf(conf); |
| Assert.assertNull(loadBalancer.balanceCluster(LoadOfAllTable)); |
| |
| //NamedQueue is an async Producer-consumer Pattern, waiting here until it completed |
| int maxWaitingCount = 10; |
| while (maxWaitingCount-- > 0 && getBalancerRejectionLogEntries().size() != 2) { |
| Thread.sleep(1000); |
| } |
| //There are two cases, should be 2 logEntries |
| List<LogEntry> logEntries = getBalancerRejectionLogEntries(); |
| Assert.assertEquals(2, logEntries.size()); |
| Assert.assertTrue( |
| logEntries.get(0).toJsonPrettyPrint().contains("minCostNeedBalance")); |
| Assert.assertTrue( |
| logEntries.get(1).toJsonPrettyPrint().contains("cost1*multiplier1")); |
| }finally { |
| conf.unset(StochasticLoadBalancer.COST_FUNCTIONS_COST_FUNCTIONS_KEY); |
| conf.unset(BaseLoadBalancer.BALANCER_REJECTION_BUFFER_ENABLED); |
| loadBalancer.setConf(conf); |
| } |
| } |
| |
| private List<LogEntry> getBalancerRejectionLogEntries(){ |
| NamedQueueGetRequest namedQueueGetRequest = new NamedQueueGetRequest(); |
| namedQueueGetRequest.setNamedQueueEvent(BalancerRejectionDetails.BALANCER_REJECTION_EVENT); |
| namedQueueGetRequest.setBalancerRejectionsRequest(MasterProtos.BalancerRejectionsRequest.getDefaultInstance()); |
| NamedQueueGetResponse namedQueueGetResponse = |
| loadBalancer.namedQueueRecorder.getNamedQueueRecords(namedQueueGetRequest); |
| List<RecentLogs.BalancerRejection> balancerRejections = namedQueueGetResponse.getBalancerRejections(); |
| MasterProtos.BalancerRejectionsResponse response = |
| MasterProtos.BalancerRejectionsResponse.newBuilder() |
| .addAllBalancerRejection(balancerRejections) |
| .build(); |
| List<LogEntry> balancerRejectionRecords = |
| ProtobufUtil.getBalancerRejectionEntries(response); |
| return balancerRejectionRecords; |
| } |
| } |