diff --git hbase-server/src/main/java/org/apache/hadoop/hbase/master/ServerManager.java hbase-server/src/main/java/org/apache/hadoop/hbase/master/ServerManager.java index 278030f..46503ee 100644 --- hbase-server/src/main/java/org/apache/hadoop/hbase/master/ServerManager.java +++ hbase-server/src/main/java/org/apache/hadoop/hbase/master/ServerManager.java @@ -79,6 +79,10 @@ import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher; import org.apache.zookeeper.KeeperException; import com.google.common.annotations.VisibleForTesting; +import java.util.function.BiFunction; +import java.util.function.Predicate; +import java.util.stream.Collector; +import java.util.stream.Collectors; import org.apache.hadoop.hbase.shaded.com.google.protobuf.ServiceException; import org.apache.hadoop.hbase.shaded.com.google.protobuf.UnsafeByteOperations; @@ -1085,6 +1089,27 @@ public class ServerManager { } /** + * @param keys The target server name + * @param predicater The ServerName wil return if the server is matched + * @return A copy of the internal list of online servers matched by the predicater + */ + public List getOnlineServersList(List keys, + BiFunction predicater) { + List names = new ArrayList<>(); + if (keys != null) { + names.forEach(name -> { + ServerLoad load = onlineServers.get(name); + if (load != null && predicater != null) { + if (Boolean.TRUE.equals(predicater.apply(name, load))) { + names.add(name); + } + } + }); + } + return names; + } + + /** * @return A copy of the internal list of draining servers. */ public List getDrainingServersList() { diff --git hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/BaseLoadBalancer.java hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/BaseLoadBalancer.java index f71f8f7..04f2f8a 100644 --- hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/BaseLoadBalancer.java +++ hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/BaseLoadBalancer.java @@ -59,6 +59,8 @@ import com.google.common.base.Joiner; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Sets; +import java.util.function.BiFunction; +import org.apache.hadoop.hbase.ServerLoad; /** * The base class for load balancers. It provides the the functions used to by @@ -73,6 +75,9 @@ public abstract class BaseLoadBalancer implements LoadBalancer { private static final List EMPTY_REGION_LIST = new ArrayList(0); + static final BiFunction predicater + = (name, load) -> load.getNumberOfRegions() == 0; + protected final RegionLocationFinder regionFinder = new RegionLocationFinder(); private static class DefaultRackManager extends RackManager { @@ -1312,6 +1317,10 @@ public abstract class BaseLoadBalancer implements LoadBalancer { rackManager); } + private List findIdleServers(List servers) { + return this.services.getServerManager().getOnlineServersList(servers, predicater); + } + /** * Used to assign a single region to a random server. */ @@ -1326,19 +1335,22 @@ public abstract class BaseLoadBalancer implements LoadBalancer { // Guarantee not to put other regions on master servers.remove(masterServerName); } - - int numServers = servers == null ? 0 : servers.size(); - if (numServers == 0) { + if (servers == null) { LOG.warn("Wanted to do retain assignment but no servers to assign to"); return null; } - if (numServers == 1) { // Only one server, nothing fancy we can do here + if (servers.size() == 1) { // Only one server, nothing fancy we can do here return servers.get(0); } - + List idleServers = findIdleServers(servers); + if (idleServers.size() == 1) { + return idleServers.get(0); + } + final List finalServers = idleServers.isEmpty() ? + servers : idleServers; List regions = Lists.newArrayList(regionInfo); - Cluster cluster = createCluster(servers, regions, false); - return randomAssignment(cluster, regionInfo, servers); + Cluster cluster = createCluster(finalServers, regions, false); + return randomAssignment(cluster, regionInfo, finalServers); } /** diff --git hbase-server/src/test/java/org/apache/hadoop/hbase/master/balancer/TestBaseLoadBalancer.java hbase-server/src/test/java/org/apache/hadoop/hbase/master/balancer/TestBaseLoadBalancer.java index b0b0a2b..4bdb12f 100644 --- hbase-server/src/test/java/org/apache/hadoop/hbase/master/balancer/TestBaseLoadBalancer.java +++ hbase-server/src/test/java/org/apache/hadoop/hbase/master/balancer/TestBaseLoadBalancer.java @@ -56,6 +56,8 @@ import org.junit.experimental.categories.Category; import org.mockito.Mockito; import com.google.common.collect.Lists; +import java.util.Arrays; +import org.apache.hadoop.hbase.master.ServerManager; @Category({MasterTests.class, MediumTests.class}) public class TestBaseLoadBalancer extends BalancerTestBase { @@ -211,6 +213,33 @@ public class TestBaseLoadBalancer extends BalancerTestBase { assertRetainedAssignment(existing, listOfServerNames, assignment); } + @Test (timeout=20000) + public void testRandomAssignment() throws Exception { + List names = new ArrayList<>(); + names.add(ServerName.valueOf("server-1", 1000, 1L)); + names.add(ServerName.valueOf("server-2", 1000, 1L)); + names.add(ServerName.valueOf("server-3", 1000, 1L)); + LoadBalancer balancer = new MockBalancer() { + @Override + public boolean shouldBeOnMaster(HRegionInfo region) { + return false; + } + }; + Configuration conf = HBaseConfiguration.create(); + conf.setClass("hbase.util.ip.to.rack.determiner", MockMapping.class, DNSToSwitchMapping.class); + balancer.setConf(conf); + ServerManager sm = Mockito.mock(ServerManager.class); + Mockito.when(sm.getOnlineServersList(names, BaseLoadBalancer.predicater)).thenReturn(Arrays.asList(names.get(1), names.get(2))); + MasterServices services = Mockito.mock(MasterServices.class); + Mockito.when(services.getServerManager()).thenReturn(sm); + balancer.setMasterServices(services); + HRegionInfo hri1 = new HRegionInfo( + TableName.valueOf("table"), "key1".getBytes(), "key2".getBytes(), + false, 100); + ServerName sn = balancer.randomAssignment(hri1, names); + assertTrue("random server:" + sn, names.get(1).equals(sn) || names.get(2).equals(sn)); + } + @Test (timeout=180000) public void testRegionAvailability() throws Exception { // Create a cluster with a few servers, assign them to specific racks