From b745b2aa685040a7f9dc2293a2608e5e6462fbf7 Mon Sep 17 00:00:00 2001 From: liuxiaocs7 Date: Wed, 25 Feb 2026 01:23:06 +0800 Subject: [PATCH] HBASE-28913 LoadBalancerPerformanceEvaluation fails with NPE --- .../balancer/StochasticLoadBalancer.java | 9 +- .../LoadBalancerPerformanceEvaluation.java | 25 ++++- ...TestLoadBalancerPerformanceEvaluation.java | 103 ++++++++++++++++++ 3 files changed, 127 insertions(+), 10 deletions(-) create mode 100644 hbase-server/src/test/java/org/apache/hadoop/hbase/master/balancer/TestLoadBalancerPerformanceEvaluation.java diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/StochasticLoadBalancer.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/StochasticLoadBalancer.java index 689c65fd6ca4..821825ce0cef 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/StochasticLoadBalancer.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/StochasticLoadBalancer.java @@ -322,10 +322,11 @@ protected void loadConf(Configuration conf) { this.namedQueueRecorder = NamedQueueRecorder.getInstance(conf); } - LOG.info("Loaded config; maxSteps=" + maxSteps + ", runMaxSteps=" + runMaxSteps - + ", stepsPerRegion=" + stepsPerRegion + ", maxRunningTime=" + maxRunningTime + ", isByTable=" - + isByTable + ", CostFunctions=" + Arrays.toString(getCostFunctionNames()) - + " , sum of multiplier of cost functions = " + sumMultiplier + " etc."); + LOG.info( + "Loaded config: maxSteps={}, runMaxSteps={}, stepsPerRegion={}, maxRunningTime={}, " + + "isByTable={}, CostFunctions={}, sum of multiplier of cost functions = {} etc.", + maxSteps, runMaxSteps, stepsPerRegion, maxRunningTime, isByTable, + Arrays.toString(getCostFunctionNames()), sumMultiplier); } @Override diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/balancer/LoadBalancerPerformanceEvaluation.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/balancer/LoadBalancerPerformanceEvaluation.java index d5c66828bb2e..0058fff02f7c 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/balancer/LoadBalancerPerformanceEvaluation.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/balancer/LoadBalancerPerformanceEvaluation.java @@ -17,6 +17,9 @@ */ package org.apache.hadoop.hbase.master.balancer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + import java.io.IOException; import java.util.ArrayList; import java.util.Collections; @@ -32,6 +35,7 @@ import org.apache.hadoop.hbase.client.RegionInfo; import org.apache.hadoop.hbase.client.RegionInfoBuilder; import org.apache.hadoop.hbase.master.LoadBalancer; +import org.apache.hadoop.hbase.master.MasterServices; import org.apache.hadoop.hbase.util.AbstractHBaseTool; import org.apache.hadoop.hbase.util.Bytes; import org.apache.yetus.audience.InterfaceAudience; @@ -57,16 +61,16 @@ public class LoadBalancerPerformanceEvaluation extends AbstractHBaseTool { protected static final HBaseCommonTestingUtility UTIL = new HBaseCommonTestingUtility(); private static final int DEFAULT_NUM_REGIONS = 1000000; - private static Option NUM_REGIONS_OPT = new Option("regions", true, + private static final Option NUM_REGIONS_OPT = new Option("regions", true, "Number of regions to consider by load balancer. Default: " + DEFAULT_NUM_REGIONS); private static final int DEFAULT_NUM_SERVERS = 1000; - private static Option NUM_SERVERS_OPT = new Option("servers", true, + private static final Option NUM_SERVERS_OPT = new Option("servers", true, "Number of servers to consider by load balancer. Default: " + DEFAULT_NUM_SERVERS); private static final String DEFAULT_LOAD_BALANCER = "org.apache.hadoop.hbase.master.balancer.StochasticLoadBalancer"; - private static Option LOAD_BALANCER_OPT = new Option("load_balancer", true, + private static final Option LOAD_BALANCER_OPT = new Option("load_balancer", true, "Type of Load Balancer to use. Default: " + DEFAULT_LOAD_BALANCER); private int numRegions; @@ -87,6 +91,15 @@ private void setupConf() { conf.setClass(HConstants.HBASE_MASTER_LOADBALANCER_CLASS, loadBalancerClazz, LoadBalancer.class); loadBalancer = LoadBalancerFactory.getLoadBalancer(conf); + MasterServices services = mock(MasterServices.class); + when(services.getConfiguration()).thenReturn(conf); + loadBalancer.setMasterServices(services); + try { + loadBalancer.initialize(); + } catch (IOException e) { + LOG.error("Failed to initialize load balancer", e); + throw new RuntimeException("Failed to initialize load balancer", e); + } } private void generateRegionsAndServers() { @@ -154,19 +167,19 @@ protected int doWork() throws Exception { generateRegionsAndServers(); String methodName = "roundRobinAssignment"; - LOG.info("Calling " + methodName); + LOG.info("Calling {}", methodName); Stopwatch watch = Stopwatch.createStarted(); loadBalancer.roundRobinAssignment(regions, servers); System.out.print(formatResults(methodName, watch.elapsed(TimeUnit.MILLISECONDS))); methodName = "retainAssignment"; - LOG.info("Calling " + methodName); + LOG.info("Calling {}", methodName); watch.reset().start(); loadBalancer.retainAssignment(regionServerMap, servers); System.out.print(formatResults(methodName, watch.elapsed(TimeUnit.MILLISECONDS))); methodName = "balanceCluster"; - LOG.info("Calling " + methodName); + LOG.info("Calling {}", methodName); watch.reset().start(); loadBalancer.balanceCluster(tableServerRegionMap); diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/balancer/TestLoadBalancerPerformanceEvaluation.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/balancer/TestLoadBalancerPerformanceEvaluation.java new file mode 100644 index 000000000000..044eb4104337 --- /dev/null +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/balancer/TestLoadBalancerPerformanceEvaluation.java @@ -0,0 +1,103 @@ +/* + * 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 static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.io.IOException; +import org.apache.hadoop.hbase.HBaseConfiguration; +import org.apache.hadoop.hbase.master.LoadBalancer; +import org.apache.hadoop.hbase.testclassification.LargeTests; +import org.apache.hadoop.hbase.testclassification.MasterTests; +import org.apache.hadoop.hbase.util.AbstractHBaseTool; +import org.apache.hadoop.metrics2.lib.DefaultMetricsSystem; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; + +@Tag(MasterTests.TAG) +@Tag(LargeTests.TAG) +public class TestLoadBalancerPerformanceEvaluation { + + private static final LoadBalancerPerformanceEvaluation tool = + new LoadBalancerPerformanceEvaluation(); + + @BeforeEach + public void setUpBeforeEach() { + tool.setConf(HBaseConfiguration.create()); + } + + @AfterEach + public void tearDownAfterEach() { + DefaultMetricsSystem.shutdown(); + } + + @Test + public void testLoadBalancerWithDefaultParams() throws IOException { + int ret = tool.run(new String[0]); + assertEquals(AbstractHBaseTool.EXIT_SUCCESS, ret); + } + + @Test + public void testStochasticLoadBalancer() throws Exception { + testLoadBalancer(StochasticLoadBalancer.class); + } + + @Test + public void testSimpleLoadBalancer() throws Exception { + testLoadBalancer(SimpleLoadBalancer.class); + } + + @Test + public void testCacheAwareLoadBalancer() throws Exception { + testLoadBalancer(CacheAwareLoadBalancer.class); + } + + private void testLoadBalancer(Class loadBalancerClass) throws Exception { + String[] args = + { "-regions", "1000", "-servers", "100", "-load_balancer", loadBalancerClass.getName() }; + int ret = tool.run(args); + assertEquals(AbstractHBaseTool.EXIT_SUCCESS, ret); + } + + @Test + public void testInvalidRegions() { + String[] args = { "-regions", "-100" }; + IllegalArgumentException exception = + assertThrows(IllegalArgumentException.class, () -> tool.run(args)); + assertEquals("Invalid number of regions!", exception.getMessage()); + } + + @Test + public void testInvalidServers() { + String[] args = { "-servers", "0" }; + IllegalArgumentException exception = + assertThrows(IllegalArgumentException.class, () -> tool.run(args)); + assertEquals("Invalid number of servers!", exception.getMessage()); + } + + @Test + public void testEmptyLoadBalancer() { + String[] args = { "-load_balancer", "" }; + IllegalArgumentException exception = + assertThrows(IllegalArgumentException.class, () -> tool.run(args)); + assertEquals("Invalid load balancer type!", exception.getMessage()); + } +}