/** * 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.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import com.google.common.collect.Maps; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.ClusterStatus; import org.apache.hadoop.hbase.HBaseTestingUtility; import org.apache.hadoop.hbase.HColumnDescriptor; import org.apache.hadoop.hbase.HConstants; import org.apache.hadoop.hbase.HRegionInfo; import org.apache.hadoop.hbase.HTableDescriptor; import org.apache.hadoop.hbase.client.Admin; import org.apache.hadoop.hbase.master.RackManager; import org.apache.hadoop.hbase.testclassification.LargeTests; import org.apache.hadoop.hbase.ServerName; import org.apache.hadoop.hbase.TableName; import org.apache.hadoop.hbase.Waiter; import org.apache.hadoop.hbase.favored.FavoredNodesManager; import org.apache.hadoop.hbase.master.LoadBalancer; import org.apache.hadoop.hbase.master.balancer.BaseLoadBalancer.Cluster; import org.apache.hadoop.hbase.util.Bytes; import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.junit.experimental.categories.Category; import com.google.common.collect.Lists; @Category(LargeTests.class) public class TestFavoredStochasticBalancerPickers extends BalancerTestBase { private static final Log LOG = LogFactory.getLog(TestFavoredStochasticBalancerPickers.class); private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); private static final int SLAVES = 6; private static final int REGIONS = SLAVES * 3; private static Configuration conf; private Admin admin; @BeforeClass public static void setupBeforeClass() throws Exception { conf = TEST_UTIL.getConfiguration(); // Enable favored nodes based load balancer conf.setClass(HConstants.HBASE_MASTER_LOADBALANCER_CLASS, LoadOnlyFavoredStochasticBalancer.class, LoadBalancer.class); conf.setLong("hbase.master.balancer.stochastic.maxRunningTime", 30000); conf.setInt("hbase.master.balancer.stochastic.moveCost", 0); conf.setBoolean("hbase.master.balancer.stochastic.execute.maxSteps", true); conf.set(BaseLoadBalancer.TABLES_ON_MASTER, "none"); } @Before public void startCluster() throws Exception { TEST_UTIL.startMiniCluster(SLAVES); TEST_UTIL.getDFSCluster().waitClusterUp(); TEST_UTIL.getHBaseCluster().waitForActiveAndReadyMaster(120*1000); admin = TEST_UTIL.getAdmin(); admin.setBalancerRunning(false, true); } @After public void stopCluster() throws Exception { TEST_UTIL.cleanupTestDir(); TEST_UTIL.shutdownMiniCluster(); } @Test public void testPickers() throws Exception { TableName tableName = TableName.valueOf("testPickers"); HTableDescriptor desc = new HTableDescriptor(tableName); desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY)); admin.createTable(desc, Bytes.toBytes("aaa"), Bytes.toBytes("zzz"), REGIONS); TEST_UTIL.loadTable(admin.getConnection().getTable(tableName), HConstants.CATALOG_FAMILY); admin.flush(tableName); ServerName masterServerName = TEST_UTIL.getMiniHBaseCluster().getServerHoldingMeta(); final ServerName mostLoadedServer = getRSWithMaxRegions(Lists.newArrayList(masterServerName)); assertNotNull(mostLoadedServer); int numRegions = admin.getOnlineRegions(mostLoadedServer).size(); ServerName source = getRSWithMaxRegions(Lists.newArrayList(masterServerName, mostLoadedServer)); assertNotNull(source); int regionsToMove = admin.getOnlineRegions(source).size()/2; List<HRegionInfo> hris = admin.getOnlineRegions(source); for (int i = 0; i < regionsToMove; i++) { admin.move(hris.get(i).getEncodedNameAsBytes(), Bytes.toBytes(mostLoadedServer.getServerName())); LOG.info("Moving region: " + hris.get(i).getRegionNameAsString() + " to " + mostLoadedServer); } final int finalRegions = numRegions + regionsToMove; TEST_UTIL.waitUntilNoRegionsInTransition(60000); TEST_UTIL.waitFor(60000, new Waiter.Predicate<Exception>() { @Override public boolean evaluate() throws Exception { int numRegions = TEST_UTIL.getAdmin().getOnlineRegions(mostLoadedServer).size(); return (numRegions == finalRegions); } }); TEST_UTIL.getHBaseCluster().startRegionServerAndWait(60000); Map<ServerName, List<HRegionInfo>> serverAssignments = Maps.newHashMap(); ClusterStatus status = admin.getClusterStatus(); for (ServerName sn : status.getServers()) { if (!ServerName.isSameHostnameAndPort(sn, masterServerName)) { serverAssignments.put(sn, admin.getOnlineRegions(sn)); } } RegionLocationFinder regionFinder = new RegionLocationFinder(); regionFinder.setClusterStatus(admin.getClusterStatus()); regionFinder.setConf(conf); regionFinder.setServices(TEST_UTIL.getMiniHBaseCluster().getMaster()); Cluster cluster = new Cluster(serverAssignments, null, regionFinder, new RackManager(conf)); LoadOnlyFavoredStochasticBalancer balancer = (LoadOnlyFavoredStochasticBalancer) TEST_UTIL .getMiniHBaseCluster().getMaster().getLoadBalancer(); FavoredNodesManager fnm = TEST_UTIL.getMiniHBaseCluster().getMaster().getFavoredNodesManager(); cluster.sortServersByRegionCount(); Integer[] servers = cluster.serverIndicesSortedByRegionCount; LOG.info("Servers sorted by region count:" + Arrays.toString(servers)); LOG.info("Cluster dump: " + cluster); if (!mostLoadedServer.equals(cluster.servers[servers[servers.length -1]])) { LOG.error("Most loaded server: " + mostLoadedServer + " does not match: " + cluster.servers[servers[servers.length -1]]); } assertEquals(mostLoadedServer, cluster.servers[servers[servers.length -1]]); FavoredStochasticBalancer.FavoredNodeLoadPicker loadPicker = balancer.new FavoredNodeLoadPicker(); boolean userRegionPicked = false; for (int i = 0; i < 100; i++) { if (userRegionPicked) { break; } else { Cluster.Action action = loadPicker.generate(cluster); if (action.type == Cluster.Action.Type.MOVE_REGION) { Cluster.MoveRegionAction moveRegionAction = (Cluster.MoveRegionAction) action; HRegionInfo region = cluster.regions[moveRegionAction.region]; assertNotEquals(-1, moveRegionAction.toServer); ServerName destinationServer = cluster.servers[moveRegionAction.toServer]; assertEquals(cluster.servers[moveRegionAction.fromServer], mostLoadedServer); if (!region.getTable().isSystemTable()) { List<ServerName> favNodes = fnm.getFavoredNodes(region); assertTrue(favNodes.contains(ServerName.valueOf(destinationServer.getHostAndPort(), -1))); userRegionPicked = true; } } } } assertTrue("load picker did not pick expected regions in 100 iterations.", userRegionPicked); } private ServerName getRSWithMaxRegions(ArrayList<ServerName> excludeNodes) throws IOException { int maxRegions = 0; ServerName maxLoadedServer = null; for (ServerName sn : admin.getClusterStatus().getServers()) { if (admin.getOnlineRegions(sn).size() > maxRegions) { if (excludeNodes == null || !doesMatchExcludeNodes(excludeNodes, sn)) { maxRegions = admin.getOnlineRegions(sn).size(); maxLoadedServer = sn; } } } return maxLoadedServer; } private boolean doesMatchExcludeNodes(ArrayList<ServerName> excludeNodes, ServerName sn) { for (ServerName excludeSN : excludeNodes) { if (ServerName.isSameHostnameAndPort(sn, excludeSN)) { return true; } } return false; } }