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