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; 019 020import static org.junit.Assert.assertTrue; 021 022import java.util.ArrayList; 023import java.util.Collection; 024import java.util.List; 025import java.util.Map; 026import java.util.Set; 027import org.apache.hadoop.conf.Configuration; 028import org.apache.hadoop.hbase.HBaseClassTestRule; 029import org.apache.hadoop.hbase.HBaseIOException; 030import org.apache.hadoop.hbase.HBaseTestingUtility; 031import org.apache.hadoop.hbase.HConstants; 032import org.apache.hadoop.hbase.ServerName; 033import org.apache.hadoop.hbase.TableName; 034import org.apache.hadoop.hbase.client.RegionInfo; 035import org.apache.hadoop.hbase.client.RegionInfoBuilder; 036import org.apache.hadoop.hbase.favored.FavoredNodeAssignmentHelper; 037import org.apache.hadoop.hbase.favored.FavoredNodeLoadBalancer; 038import org.apache.hadoop.hbase.favored.FavoredNodesPlan.Position; 039import org.apache.hadoop.hbase.master.balancer.LoadBalancerFactory; 040import org.apache.hadoop.hbase.testclassification.MasterTests; 041import org.apache.hadoop.hbase.testclassification.MediumTests; 042import org.junit.AfterClass; 043import org.junit.BeforeClass; 044import org.junit.ClassRule; 045import org.junit.Rule; 046import org.junit.Test; 047import org.junit.experimental.categories.Category; 048import org.junit.rules.TestName; 049import org.slf4j.Logger; 050import org.slf4j.LoggerFactory; 051 052@Category({ MasterTests.class, MediumTests.class }) 053public class TestRegionPlacement2 { 054 055 @ClassRule 056 public static final HBaseClassTestRule CLASS_RULE = 057 HBaseClassTestRule.forClass(TestRegionPlacement2.class); 058 059 private static final Logger LOG = LoggerFactory.getLogger(TestRegionPlacement2.class); 060 private final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); 061 private final static int SLAVES = 7; 062 private final static int PRIMARY = Position.PRIMARY.ordinal(); 063 private final static int SECONDARY = Position.SECONDARY.ordinal(); 064 private final static int TERTIARY = Position.TERTIARY.ordinal(); 065 066 @Rule 067 public TestName name = new TestName(); 068 069 @BeforeClass 070 public static void setupBeforeClass() throws Exception { 071 Configuration conf = TEST_UTIL.getConfiguration(); 072 // Enable the favored nodes based load balancer 073 conf.setClass(HConstants.HBASE_MASTER_LOADBALANCER_CLASS, FavoredNodeLoadBalancer.class, 074 LoadBalancer.class); 075 conf.setBoolean("hbase.tests.use.shortcircuit.reads", false); 076 TEST_UTIL.startMiniCluster(SLAVES); 077 } 078 079 @AfterClass 080 public static void tearDownAfterClass() throws Exception { 081 TEST_UTIL.shutdownMiniCluster(); 082 } 083 084 @Test 085 public void testFavoredNodesPresentForRoundRobinAssignment() throws HBaseIOException { 086 LoadBalancer balancer = LoadBalancerFactory.getLoadBalancer(TEST_UTIL.getConfiguration()); 087 balancer.setMasterServices(TEST_UTIL.getMiniHBaseCluster().getMaster()); 088 balancer.initialize(); 089 List<ServerName> servers = new ArrayList<>(); 090 for (int i = 0; i < SLAVES; i++) { 091 ServerName server = TEST_UTIL.getMiniHBaseCluster().getRegionServer(i).getServerName(); 092 servers.add(server); 093 } 094 List<RegionInfo> regions = new ArrayList<>(1); 095 RegionInfo region = 096 RegionInfoBuilder.newBuilder(TableName.valueOf(name.getMethodName())).build(); 097 regions.add(region); 098 Map<ServerName, List<RegionInfo>> assignmentMap = 099 balancer.roundRobinAssignment(regions, servers); 100 Set<ServerName> serverBefore = assignmentMap.keySet(); 101 List<ServerName> favoredNodesBefore = 102 ((FavoredNodeLoadBalancer) balancer).getFavoredNodes(region); 103 assertTrue(favoredNodesBefore.size() == FavoredNodeAssignmentHelper.FAVORED_NODES_NUM); 104 // the primary RS should be the one that the balancer's assignment returns 105 assertTrue( 106 ServerName.isSameAddress(serverBefore.iterator().next(), favoredNodesBefore.get(PRIMARY))); 107 // now remove the primary from the list of available servers 108 List<ServerName> removedServers = removeMatchingServers(serverBefore, servers); 109 // call roundRobinAssignment with the modified servers list 110 assignmentMap = balancer.roundRobinAssignment(regions, servers); 111 List<ServerName> favoredNodesAfter = 112 ((FavoredNodeLoadBalancer) balancer).getFavoredNodes(region); 113 assertTrue(favoredNodesAfter.size() == FavoredNodeAssignmentHelper.FAVORED_NODES_NUM); 114 // We don't expect the favored nodes assignments to change in multiple calls 115 // to the roundRobinAssignment method in the balancer (relevant for AssignmentManager.assign 116 // failures) 117 assertTrue(favoredNodesAfter.containsAll(favoredNodesBefore)); 118 Set<ServerName> serverAfter = assignmentMap.keySet(); 119 // We expect the new RegionServer assignee to be one of the favored nodes 120 // chosen earlier. 121 assertTrue(ServerName.isSameAddress(serverAfter.iterator().next(), 122 favoredNodesBefore.get(SECONDARY)) 123 || ServerName.isSameAddress(serverAfter.iterator().next(), favoredNodesBefore.get(TERTIARY))); 124 125 // put back the primary in the list of available servers 126 servers.addAll(removedServers); 127 // now roundRobinAssignment with the modified servers list should return the primary 128 // as the regionserver assignee 129 assignmentMap = balancer.roundRobinAssignment(regions, servers); 130 Set<ServerName> serverWithPrimary = assignmentMap.keySet(); 131 assertTrue(serverBefore.containsAll(serverWithPrimary)); 132 133 // Make all the favored nodes unavailable for assignment 134 removeMatchingServers(favoredNodesAfter, servers); 135 // call roundRobinAssignment with the modified servers list 136 assignmentMap = balancer.roundRobinAssignment(regions, servers); 137 List<ServerName> favoredNodesNow = ((FavoredNodeLoadBalancer) balancer).getFavoredNodes(region); 138 assertTrue(favoredNodesNow.size() == FavoredNodeAssignmentHelper.FAVORED_NODES_NUM); 139 assertTrue(!favoredNodesNow.contains(favoredNodesAfter.get(PRIMARY)) 140 && !favoredNodesNow.contains(favoredNodesAfter.get(SECONDARY)) 141 && !favoredNodesNow.contains(favoredNodesAfter.get(TERTIARY))); 142 } 143 144 @Test 145 public void testFavoredNodesPresentForRandomAssignment() throws HBaseIOException { 146 LoadBalancer balancer = LoadBalancerFactory.getLoadBalancer(TEST_UTIL.getConfiguration()); 147 balancer.setMasterServices(TEST_UTIL.getMiniHBaseCluster().getMaster()); 148 balancer.initialize(); 149 List<ServerName> servers = new ArrayList<>(); 150 for (int i = 0; i < SLAVES; i++) { 151 ServerName server = TEST_UTIL.getMiniHBaseCluster().getRegionServer(i).getServerName(); 152 servers.add(server); 153 } 154 List<RegionInfo> regions = new ArrayList<>(1); 155 RegionInfo region = 156 RegionInfoBuilder.newBuilder(TableName.valueOf(name.getMethodName())).build(); 157 regions.add(region); 158 ServerName serverBefore = balancer.randomAssignment(region, servers); 159 List<ServerName> favoredNodesBefore = 160 ((FavoredNodeLoadBalancer) balancer).getFavoredNodes(region); 161 assertTrue(favoredNodesBefore.size() == FavoredNodeAssignmentHelper.FAVORED_NODES_NUM); 162 // the primary RS should be the one that the balancer's assignment returns 163 assertTrue(ServerName.isSameAddress(serverBefore, favoredNodesBefore.get(PRIMARY))); 164 // now remove the primary from the list of servers 165 removeMatchingServers(serverBefore, servers); 166 // call randomAssignment with the modified servers list 167 ServerName serverAfter = balancer.randomAssignment(region, servers); 168 List<ServerName> favoredNodesAfter = 169 ((FavoredNodeLoadBalancer) balancer).getFavoredNodes(region); 170 assertTrue(favoredNodesAfter.size() == FavoredNodeAssignmentHelper.FAVORED_NODES_NUM); 171 // We don't expect the favored nodes assignments to change in multiple calls 172 // to the randomAssignment method in the balancer (relevant for AssignmentManager.assign 173 // failures) 174 assertTrue(favoredNodesAfter.containsAll(favoredNodesBefore)); 175 // We expect the new RegionServer assignee to be one of the favored nodes 176 // chosen earlier. 177 assertTrue(ServerName.isSameAddress(serverAfter, favoredNodesBefore.get(SECONDARY)) 178 || ServerName.isSameAddress(serverAfter, favoredNodesBefore.get(TERTIARY))); 179 // Make all the favored nodes unavailable for assignment 180 removeMatchingServers(favoredNodesAfter, servers); 181 // call randomAssignment with the modified servers list 182 balancer.randomAssignment(region, servers); 183 List<ServerName> favoredNodesNow = ((FavoredNodeLoadBalancer) balancer).getFavoredNodes(region); 184 assertTrue(favoredNodesNow.size() == FavoredNodeAssignmentHelper.FAVORED_NODES_NUM); 185 assertTrue(!favoredNodesNow.contains(favoredNodesAfter.get(PRIMARY)) 186 && !favoredNodesNow.contains(favoredNodesAfter.get(SECONDARY)) 187 && !favoredNodesNow.contains(favoredNodesAfter.get(TERTIARY))); 188 } 189 190 private List<ServerName> removeMatchingServers(Collection<ServerName> serversWithoutStartCode, 191 List<ServerName> servers) { 192 List<ServerName> serversToRemove = new ArrayList<>(); 193 for (ServerName s : serversWithoutStartCode) { 194 serversToRemove.addAll(removeMatchingServers(s, servers)); 195 } 196 return serversToRemove; 197 } 198 199 private List<ServerName> removeMatchingServers(ServerName serverWithoutStartCode, 200 List<ServerName> servers) { 201 List<ServerName> serversToRemove = new ArrayList<>(); 202 for (ServerName s : servers) { 203 if (ServerName.isSameAddress(s, serverWithoutStartCode)) { 204 serversToRemove.add(s); 205 } 206 } 207 servers.removeAll(serversToRemove); 208 return serversToRemove; 209 } 210}