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