/** * 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.favored; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; 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.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.HConstants; import org.apache.hadoop.hbase.TableName; import org.apache.hadoop.hbase.HRegionInfo; import org.apache.hadoop.hbase.ServerName; import org.apache.hadoop.hbase.master.RackManager; import org.apache.hadoop.hbase.testclassification.MasterTests; import org.apache.hadoop.hbase.testclassification.SmallTests; import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.hbase.util.Triple; import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.rules.TestName; import org.mockito.Mockito; import com.google.common.collect.Lists; import com.google.common.collect.Sets; @Category({MasterTests.class, SmallTests.class}) public class TestFavoredNodeAssignmentHelper { private static List<ServerName> servers = new ArrayList<>(); private static Map<String, List<ServerName>> rackToServers = new HashMap<>(); private static RackManager rackManager = Mockito.mock(RackManager.class); // Some tests have randomness, so we run them multiple times private static final int MAX_ATTEMPTS = 100; @Rule public TestName name = new TestName(); @BeforeClass public static void setupBeforeClass() throws Exception { // Set up some server -> rack mappings // Have three racks in the cluster with 10 hosts each. for (int i = 0; i < 40; i++) { ServerName server = ServerName.valueOf("foo" + i + ":1234", -1); if (i < 10) { Mockito.when(rackManager.getRack(server)).thenReturn("rack1"); if (rackToServers.get("rack1") == null) { List<ServerName> servers = new ArrayList<>(); rackToServers.put("rack1", servers); } rackToServers.get("rack1").add(server); } if (i >= 10 && i < 20) { Mockito.when(rackManager.getRack(server)).thenReturn("rack2"); if (rackToServers.get("rack2") == null) { List<ServerName> servers = new ArrayList<>(); rackToServers.put("rack2", servers); } rackToServers.get("rack2").add(server); } if (i >= 20 && i < 30) { Mockito.when(rackManager.getRack(server)).thenReturn("rack3"); if (rackToServers.get("rack3") == null) { List<ServerName> servers = new ArrayList<>(); rackToServers.put("rack3", servers); } rackToServers.get("rack3").add(server); } servers.add(server); } } // The tests decide which racks to work with, and how many machines to // work with from any given rack // Return a rondom 'count' number of servers from 'rack' private static List<ServerName> getServersFromRack(Map<String, Integer> rackToServerCount) { List<ServerName> chosenServers = new ArrayList<>(); for (Map.Entry<String, Integer> entry : rackToServerCount.entrySet()) { List<ServerName> servers = rackToServers.get(entry.getKey()); for (int i = 0; i < entry.getValue(); i++) { chosenServers.add(servers.get(i)); } } return chosenServers; } @Test public void testSmallCluster() { // Test the case where we cannot assign favored nodes (because the number // of nodes in the cluster is too less) Map<String,Integer> rackToServerCount = new HashMap<>(); rackToServerCount.put("rack1", 2); List<ServerName> servers = getServersFromRack(rackToServerCount); FavoredNodeAssignmentHelper helper = new FavoredNodeAssignmentHelper(servers, new Configuration()); helper.initialize(); assertFalse(helper.canPlaceFavoredNodes()); } @Test public void testPlacePrimaryRSAsRoundRobin() { // Test the regular case where there are many servers in different racks // Test once for few regions and once for many regions primaryRSPlacement(6, null, 10, 10, 10); // now create lots of regions and try to place them on the limited number of machines primaryRSPlacement(600, null, 10, 10, 10); } @Test public void testRoundRobinAssignmentsWithUnevenSizedRacks() { //In the case of uneven racks, the regions should be distributed //proportionately to the rack sizes primaryRSPlacement(6, null, 10, 10, 10); primaryRSPlacement(600, null, 10, 10, 5); primaryRSPlacement(600, null, 10, 5, 10); primaryRSPlacement(600, null, 5, 10, 10); primaryRSPlacement(500, null, 10, 10, 5); primaryRSPlacement(500, null, 10, 5, 10); primaryRSPlacement(500, null, 5, 10, 10); primaryRSPlacement(500, null, 9, 7, 8); primaryRSPlacement(500, null, 8, 7, 9); primaryRSPlacement(500, null, 7, 9, 8); primaryRSPlacement(459, null, 7, 9, 8); } @Test public void testSecondaryAndTertiaryPlacementWithSingleRack() { // Test the case where there is a single rack and we need to choose // Primary/Secondary/Tertiary from a single rack. Map<String,Integer> rackToServerCount = new HashMap<>(); rackToServerCount.put("rack1", 10); // have lots of regions to test with Triple<Map<HRegionInfo, ServerName>, FavoredNodeAssignmentHelper, List<HRegionInfo>> primaryRSMapAndHelper = secondaryAndTertiaryRSPlacementHelper(60000, rackToServerCount); FavoredNodeAssignmentHelper helper = primaryRSMapAndHelper.getSecond(); Map<HRegionInfo, ServerName> primaryRSMap = primaryRSMapAndHelper.getFirst(); List<HRegionInfo> regions = primaryRSMapAndHelper.getThird(); Map<HRegionInfo, ServerName[]> secondaryAndTertiaryMap = helper.placeSecondaryAndTertiaryRS(primaryRSMap); // although we created lots of regions we should have no overlap on the // primary/secondary/tertiary for any given region for (HRegionInfo region : regions) { ServerName[] secondaryAndTertiaryServers = secondaryAndTertiaryMap.get(region); assertNotNull(secondaryAndTertiaryServers); assertTrue(primaryRSMap.containsKey(region)); assertTrue(!secondaryAndTertiaryServers[0].equals(primaryRSMap.get(region))); assertTrue(!secondaryAndTertiaryServers[1].equals(primaryRSMap.get(region))); assertTrue(!secondaryAndTertiaryServers[0].equals(secondaryAndTertiaryServers[1])); } } @Test public void testSecondaryAndTertiaryPlacementWithSingleServer() { // Test the case where we have a single node in the cluster. In this case // the primary can be assigned but the secondary/tertiary would be null Map<String,Integer> rackToServerCount = new HashMap<>(); rackToServerCount.put("rack1", 1); Triple<Map<HRegionInfo, ServerName>, FavoredNodeAssignmentHelper, List<HRegionInfo>> primaryRSMapAndHelper = secondaryAndTertiaryRSPlacementHelper(1, rackToServerCount); FavoredNodeAssignmentHelper helper = primaryRSMapAndHelper.getSecond(); Map<HRegionInfo, ServerName> primaryRSMap = primaryRSMapAndHelper.getFirst(); List<HRegionInfo> regions = primaryRSMapAndHelper.getThird(); Map<HRegionInfo, ServerName[]> secondaryAndTertiaryMap = helper.placeSecondaryAndTertiaryRS(primaryRSMap); // no secondary/tertiary placement in case of a single RegionServer assertTrue(secondaryAndTertiaryMap.get(regions.get(0)) == null); } @Test public void testSecondaryAndTertiaryPlacementWithMultipleRacks() { // Test the case where we have multiple racks and the region servers // belong to multiple racks Map<String,Integer> rackToServerCount = new HashMap<>(); rackToServerCount.put("rack1", 10); rackToServerCount.put("rack2", 10); Triple<Map<HRegionInfo, ServerName>, FavoredNodeAssignmentHelper, List<HRegionInfo>> primaryRSMapAndHelper = secondaryAndTertiaryRSPlacementHelper(60000, rackToServerCount); FavoredNodeAssignmentHelper helper = primaryRSMapAndHelper.getSecond(); Map<HRegionInfo, ServerName> primaryRSMap = primaryRSMapAndHelper.getFirst(); assertTrue(primaryRSMap.size() == 60000); Map<HRegionInfo, ServerName[]> secondaryAndTertiaryMap = helper.placeSecondaryAndTertiaryRS(primaryRSMap); assertTrue(secondaryAndTertiaryMap.size() == 60000); // for every region, the primary should be on one rack and the secondary/tertiary // on another (we create a lot of regions just to increase probability of failure) for (Map.Entry<HRegionInfo, ServerName[]> entry : secondaryAndTertiaryMap.entrySet()) { ServerName[] allServersForRegion = entry.getValue(); String primaryRSRack = rackManager.getRack(primaryRSMap.get(entry.getKey())); String secondaryRSRack = rackManager.getRack(allServersForRegion[0]); String tertiaryRSRack = rackManager.getRack(allServersForRegion[1]); Set<String> racks = Sets.newHashSet(primaryRSRack); racks.add(secondaryRSRack); racks.add(tertiaryRSRack); assertTrue(racks.size() >= 2); } } @Test public void testSecondaryAndTertiaryPlacementWithLessThanTwoServersInRacks() { // Test the case where we have two racks but with less than two servers in each // We will not have enough machines to select secondary/tertiary Map<String,Integer> rackToServerCount = new HashMap<>(); rackToServerCount.put("rack1", 1); rackToServerCount.put("rack2", 1); Triple<Map<HRegionInfo, ServerName>, FavoredNodeAssignmentHelper, List<HRegionInfo>> primaryRSMapAndHelper = secondaryAndTertiaryRSPlacementHelper(6, rackToServerCount); FavoredNodeAssignmentHelper helper = primaryRSMapAndHelper.getSecond(); Map<HRegionInfo, ServerName> primaryRSMap = primaryRSMapAndHelper.getFirst(); List<HRegionInfo> regions = primaryRSMapAndHelper.getThird(); assertTrue(primaryRSMap.size() == 6); Map<HRegionInfo, ServerName[]> secondaryAndTertiaryMap = helper.placeSecondaryAndTertiaryRS(primaryRSMap); for (HRegionInfo region : regions) { // not enough secondary/tertiary room to place the regions assertTrue(secondaryAndTertiaryMap.get(region) == null); } } @Test public void testSecondaryAndTertiaryPlacementWithMoreThanOneServerInPrimaryRack() { // Test the case where there is only one server in one rack and another rack // has more servers. We try to choose secondary/tertiary on different // racks than what the primary is on. But if the other rack doesn't have // enough nodes to have both secondary/tertiary RSs, the tertiary is placed // on the same rack as the primary server is on Map<String,Integer> rackToServerCount = new HashMap<>(); rackToServerCount.put("rack1", 2); rackToServerCount.put("rack2", 1); Triple<Map<HRegionInfo, ServerName>, FavoredNodeAssignmentHelper, List<HRegionInfo>> primaryRSMapAndHelper = secondaryAndTertiaryRSPlacementHelper(6, rackToServerCount); FavoredNodeAssignmentHelper helper = primaryRSMapAndHelper.getSecond(); Map<HRegionInfo, ServerName> primaryRSMap = primaryRSMapAndHelper.getFirst(); List<HRegionInfo> regions = primaryRSMapAndHelper.getThird(); assertTrue(primaryRSMap.size() == 6); Map<HRegionInfo, ServerName[]> secondaryAndTertiaryMap = helper.placeSecondaryAndTertiaryRS(primaryRSMap); assertTrue(secondaryAndTertiaryMap.size() == regions.size()); for (HRegionInfo region : regions) { ServerName s = primaryRSMap.get(region); ServerName secondaryRS = secondaryAndTertiaryMap.get(region)[0]; ServerName tertiaryRS = secondaryAndTertiaryMap.get(region)[1]; Set<String> racks = Sets.newHashSet(rackManager.getRack(s)); racks.add(rackManager.getRack(secondaryRS)); racks.add(rackManager.getRack(tertiaryRS)); assertTrue(racks.size() >= 2); } } private Triple<Map<HRegionInfo, ServerName>, FavoredNodeAssignmentHelper, List<HRegionInfo>> secondaryAndTertiaryRSPlacementHelper( int regionCount, Map<String, Integer> rackToServerCount) { Map<HRegionInfo, ServerName> primaryRSMap = new HashMap<HRegionInfo, ServerName>(); List<ServerName> servers = getServersFromRack(rackToServerCount); FavoredNodeAssignmentHelper helper = new FavoredNodeAssignmentHelper(servers, rackManager); Map<ServerName, List<HRegionInfo>> assignmentMap = new HashMap<ServerName, List<HRegionInfo>>(); helper.initialize(); // create regions List<HRegionInfo> regions = new ArrayList<>(regionCount); for (int i = 0; i < regionCount; i++) { HRegionInfo region = new HRegionInfo(TableName.valueOf(name.getMethodName()), Bytes.toBytes(i), Bytes.toBytes(i + 1)); regions.add(region); } // place the regions helper.placePrimaryRSAsRoundRobin(assignmentMap, primaryRSMap, regions); return new Triple<>(primaryRSMap, helper, regions); } private void primaryRSPlacement(int regionCount, Map<HRegionInfo, ServerName> primaryRSMap, int firstRackSize, int secondRackSize, int thirdRackSize) { Map<String,Integer> rackToServerCount = new HashMap<>(); rackToServerCount.put("rack1", firstRackSize); rackToServerCount.put("rack2", secondRackSize); rackToServerCount.put("rack3", thirdRackSize); List<ServerName> servers = getServersFromRack(rackToServerCount); FavoredNodeAssignmentHelper helper = new FavoredNodeAssignmentHelper(servers, rackManager); helper.initialize(); assertTrue(helper.canPlaceFavoredNodes()); Map<ServerName, List<HRegionInfo>> assignmentMap = new HashMap<>(); if (primaryRSMap == null) primaryRSMap = new HashMap<>(); // create some regions List<HRegionInfo> regions = new ArrayList<>(regionCount); for (int i = 0; i < regionCount; i++) { HRegionInfo region = new HRegionInfo(TableName.valueOf("foobar"), Bytes.toBytes(i), Bytes.toBytes(i + 1)); regions.add(region); } // place those regions in primary RSs helper.placePrimaryRSAsRoundRobin(assignmentMap, primaryRSMap, regions); // we should have all the regions nicely spread across the racks int regionsOnRack1 = 0; int regionsOnRack2 = 0; int regionsOnRack3 = 0; for (HRegionInfo region : regions) { if (rackManager.getRack(primaryRSMap.get(region)).equals("rack1")) { regionsOnRack1++; } else if (rackManager.getRack(primaryRSMap.get(region)).equals("rack2")) { regionsOnRack2++; } else if (rackManager.getRack(primaryRSMap.get(region)).equals("rack3")) { regionsOnRack3++; } } // Verify that the regions got placed in the way we expect (documented in // FavoredNodeAssignmentHelper#placePrimaryRSAsRoundRobin) checkNumRegions(regionCount, firstRackSize, secondRackSize, thirdRackSize, regionsOnRack1, regionsOnRack2, regionsOnRack3, assignmentMap); } private void checkNumRegions(int regionCount, int firstRackSize, int secondRackSize, int thirdRackSize, int regionsOnRack1, int regionsOnRack2, int regionsOnRack3, Map<ServerName, List<HRegionInfo>> assignmentMap) { //The regions should be distributed proportionately to the racksizes //Verify the ordering was as expected by inserting the racks and regions //in sorted maps. The keys being the racksize and numregions; values are //the relative positions of the racksizes and numregions respectively SortedMap<Integer, Integer> rackMap = new TreeMap<>(); rackMap.put(firstRackSize, 1); rackMap.put(secondRackSize, 2); rackMap.put(thirdRackSize, 3); SortedMap<Integer, Integer> regionMap = new TreeMap<>(); regionMap.put(regionsOnRack1, 1); regionMap.put(regionsOnRack2, 2); regionMap.put(regionsOnRack3, 3); assertTrue(printProportions(firstRackSize, secondRackSize, thirdRackSize, regionsOnRack1, regionsOnRack2, regionsOnRack3), rackMap.get(firstRackSize) == regionMap.get(regionsOnRack1)); assertTrue(printProportions(firstRackSize, secondRackSize, thirdRackSize, regionsOnRack1, regionsOnRack2, regionsOnRack3), rackMap.get(secondRackSize) == regionMap.get(regionsOnRack2)); assertTrue(printProportions(firstRackSize, secondRackSize, thirdRackSize, regionsOnRack1, regionsOnRack2, regionsOnRack3), rackMap.get(thirdRackSize) == regionMap.get(regionsOnRack3)); } private String printProportions(int firstRackSize, int secondRackSize, int thirdRackSize, int regionsOnRack1, int regionsOnRack2, int regionsOnRack3) { return "The rack sizes " + firstRackSize + " " + secondRackSize + " " + thirdRackSize + " " + regionsOnRack1 + " " + regionsOnRack2 + " " + regionsOnRack3; } @Test public void testConstrainedPlacement() throws Exception { List<ServerName> servers = Lists.newArrayList(); servers.add(ServerName.valueOf("foo" + 1 + ":1234", -1)); servers.add(ServerName.valueOf("foo" + 2 + ":1234", -1)); servers.add(ServerName.valueOf("foo" + 15 + ":1234", -1)); FavoredNodeAssignmentHelper helper = new FavoredNodeAssignmentHelper(servers, rackManager); helper.initialize(); assertTrue(helper.canPlaceFavoredNodes()); List<HRegionInfo> regions = new ArrayList<>(20); for (int i = 0; i < 20; i++) { HRegionInfo region = new HRegionInfo(TableName.valueOf(name.getMethodName()), Bytes.toBytes(i), Bytes.toBytes(i + 1)); regions.add(region); } Map<ServerName, List<HRegionInfo>> assignmentMap = new HashMap<ServerName, List<HRegionInfo>>(); Map<HRegionInfo, ServerName> primaryRSMap = new HashMap<HRegionInfo, ServerName>(); helper.placePrimaryRSAsRoundRobin(assignmentMap, primaryRSMap, regions); assertTrue(primaryRSMap.size() == regions.size()); Map<HRegionInfo, ServerName[]> secondaryAndTertiary = helper.placeSecondaryAndTertiaryRS(primaryRSMap); assertEquals(regions.size(), secondaryAndTertiary.size()); } @Test public void testGetOneRandomRack() throws IOException { Map<String,Integer> rackToServerCount = new HashMap<>(); Set<String> rackList = Sets.newHashSet("rack1", "rack2", "rack3"); for (String rack : rackList) { rackToServerCount.put(rack, 2); } List<ServerName> servers = getServersFromRack(rackToServerCount); FavoredNodeAssignmentHelper helper = new FavoredNodeAssignmentHelper(servers, rackManager); helper.initialize(); assertTrue(helper.canPlaceFavoredNodes()); // Check we don't get a bad rack on any number of attempts for (int attempts = 0 ; attempts < MAX_ATTEMPTS; attempts++) { assertTrue(rackList.contains(helper.getOneRandomRack(Sets.newHashSet()))); } // Check skipRack multiple times when an invalid rack is specified Set<String> skipRacks = Sets.newHashSet("rack"); for (int attempts = 0 ; attempts < MAX_ATTEMPTS; attempts++) { assertTrue(rackList.contains(helper.getOneRandomRack(skipRacks))); } // Check skipRack multiple times when an valid rack is specified skipRacks = Sets.newHashSet("rack1"); Set<String> validRacks = Sets.newHashSet("rack2", "rack3"); for (int attempts = 0 ; attempts < MAX_ATTEMPTS; attempts++) { assertTrue(validRacks.contains(helper.getOneRandomRack(skipRacks))); } } @Test public void testGetRandomServerSingleRack() throws IOException { Map<String,Integer> rackToServerCount = new HashMap<>(); final String rack = "rack1"; rackToServerCount.put(rack, 4); List<ServerName> servers = getServersFromRack(rackToServerCount); FavoredNodeAssignmentHelper helper = new FavoredNodeAssignmentHelper(servers, rackManager); helper.initialize(); assertTrue(helper.canPlaceFavoredNodes()); // Check we don't get a bad node on any number of attempts for (int attempts = 0 ; attempts < MAX_ATTEMPTS; attempts++) { ServerName sn = helper.getOneRandomServer(rack, Sets.newHashSet()); assertTrue("Server:" + sn + " does not belong to list: " + servers, servers.contains(sn)); } // Check skipServers multiple times when an invalid server is specified Set<ServerName> skipServers = Sets.newHashSet(ServerName.valueOf("invalidnode:1234", ServerName.NON_STARTCODE)); for (int attempts = 0 ; attempts < MAX_ATTEMPTS; attempts++) { ServerName sn = helper.getOneRandomServer(rack, skipServers); assertTrue("Server:" + sn + " does not belong to list: " + servers, servers.contains(sn)); } // Check skipRack multiple times when an valid servers are specified ServerName skipSN = ServerName.valueOf("foo1:1234", ServerName.NON_STARTCODE); skipServers = Sets.newHashSet(skipSN); for (int attempts = 0 ; attempts < MAX_ATTEMPTS; attempts++) { ServerName sn = helper.getOneRandomServer(rack, skipServers); assertNotEquals("Skip server should not be selected ", skipSN.getHostAndPort(), sn.getHostAndPort()); assertTrue("Server:" + sn + " does not belong to list: " + servers, servers.contains(sn)); } } @Test public void testGetRandomServerMultiRack() throws IOException { Map<String,Integer> rackToServerCount = new HashMap<>(); Set<String> rackList = Sets.newHashSet("rack1", "rack2", "rack3"); for (String rack : rackList) { rackToServerCount.put(rack, 4); } List<ServerName> servers = getServersFromRack(rackToServerCount); FavoredNodeAssignmentHelper helper = new FavoredNodeAssignmentHelper(servers, rackManager); helper.initialize(); assertTrue(helper.canPlaceFavoredNodes()); // Check we don't get a bad node on any number of attempts for (int attempts = 0 ; attempts < MAX_ATTEMPTS; attempts++) { for (String rack : rackList) { ServerName sn = helper.getOneRandomServer(rack, Sets.newHashSet()); assertTrue("Server:" + sn + " does not belong to rack servers: " + rackToServers.get(rack), rackToServers.get(rack).contains(sn)); } } // Check skipServers multiple times when an invalid server is specified Set<ServerName> skipServers = Sets.newHashSet(ServerName.valueOf("invalidnode:1234", ServerName.NON_STARTCODE)); for (int attempts = 0 ; attempts < MAX_ATTEMPTS; attempts++) { for (String rack : rackList) { ServerName sn = helper.getOneRandomServer(rack, skipServers); assertTrue("Server:" + sn + " does not belong to rack servers: " + rackToServers.get(rack), rackToServers.get(rack).contains(sn)); } } // Check skipRack multiple times when an valid servers are specified ServerName skipSN1 = ServerName.valueOf("foo1:1234", ServerName.NON_STARTCODE); ServerName skipSN2 = ServerName.valueOf("foo10:1234", ServerName.NON_STARTCODE); ServerName skipSN3 = ServerName.valueOf("foo20:1234", ServerName.NON_STARTCODE); skipServers = Sets.newHashSet(skipSN1, skipSN2, skipSN3); for (int attempts = 0 ; attempts < MAX_ATTEMPTS; attempts++) { for (String rack : rackList) { ServerName sn = helper.getOneRandomServer(rack, skipServers); assertFalse("Skip server should not be selected ", skipServers.contains(sn)); assertTrue("Server:" + sn + " does not belong to rack servers: " + rackToServers.get(rack), rackToServers.get(rack).contains(sn)); } } } @Test public void testGetFavoredNodes() throws IOException { Map<String,Integer> rackToServerCount = new HashMap<>(); Set<String> rackList = Sets.newHashSet("rack1", "rack2", "rack3"); for (String rack : rackList) { rackToServerCount.put(rack, 4); } List<ServerName> servers = getServersFromRack(rackToServerCount); FavoredNodeAssignmentHelper helper = new FavoredNodeAssignmentHelper(servers, rackManager); helper.initialize(); assertTrue(helper.canPlaceFavoredNodes()); HRegionInfo region = new HRegionInfo(TableName.valueOf(name.getMethodName()), HConstants.EMPTY_START_ROW, HConstants.EMPTY_END_ROW); for (int maxattempts = 0; maxattempts < MAX_ATTEMPTS; maxattempts++) { List<ServerName> fn = helper.generateFavoredNodes(region); checkDuplicateFN(fn); checkFNRacks(fn); } } @Test public void testGenMissingFavoredNodeOneRack() throws IOException { Map<String, Integer> rackToServerCount = new HashMap<>(); final String rack = "rack1"; rackToServerCount.put(rack, 6); List<ServerName> servers = getServersFromRack(rackToServerCount); FavoredNodeAssignmentHelper helper = new FavoredNodeAssignmentHelper(servers, rackManager); helper.initialize(); assertTrue(helper.canPlaceFavoredNodes()); ServerName snRack1SN1 = ServerName.valueOf("foo1:1234", ServerName.NON_STARTCODE); ServerName snRack1SN2 = ServerName.valueOf("foo2:1234", ServerName.NON_STARTCODE); ServerName snRack1SN3 = ServerName.valueOf("foo3:1234", ServerName.NON_STARTCODE); List<ServerName> fn = Lists.newArrayList(snRack1SN1, snRack1SN2); for (int attempts = 0; attempts < MAX_ATTEMPTS; attempts++) { checkDuplicateFN(fn, helper.generateMissingFavoredNode(fn)); } fn = Lists.newArrayList(snRack1SN1, snRack1SN2); List<ServerName> skipServers = Lists.newArrayList(snRack1SN3); for (int attempts = 0; attempts < MAX_ATTEMPTS; attempts++) { ServerName genSN = helper.generateMissingFavoredNode(fn, skipServers); checkDuplicateFN(fn, genSN); assertNotEquals("Generated FN should not match excluded one", snRack1SN3, genSN); } } @Test public void testGenMissingFavoredNodeMultiRack() throws IOException { ServerName snRack1SN1 = ServerName.valueOf("foo1:1234", ServerName.NON_STARTCODE); ServerName snRack1SN2 = ServerName.valueOf("foo2:1234", ServerName.NON_STARTCODE); ServerName snRack2SN1 = ServerName.valueOf("foo10:1234", ServerName.NON_STARTCODE); ServerName snRack2SN2 = ServerName.valueOf("foo11:1234", ServerName.NON_STARTCODE); Map<String,Integer> rackToServerCount = new HashMap<>(); Set<String> rackList = Sets.newHashSet("rack1", "rack2"); for (String rack : rackList) { rackToServerCount.put(rack, 4); } List<ServerName> servers = getServersFromRack(rackToServerCount); FavoredNodeAssignmentHelper helper = new FavoredNodeAssignmentHelper(servers, rackManager); helper.initialize(); assertTrue(helper.canPlaceFavoredNodes()); List<ServerName> fn = Lists.newArrayList(snRack1SN1, snRack1SN2); for (int attempts = 0; attempts < MAX_ATTEMPTS; attempts++) { ServerName genSN = helper.generateMissingFavoredNode(fn); checkDuplicateFN(fn, genSN); checkFNRacks(fn, genSN); } fn = Lists.newArrayList(snRack1SN1, snRack2SN1); for (int attempts = 0; attempts < MAX_ATTEMPTS; attempts++) { ServerName genSN = helper.generateMissingFavoredNode(fn); checkDuplicateFN(fn, genSN); checkFNRacks(fn, genSN); } fn = Lists.newArrayList(snRack1SN1, snRack2SN1); List<ServerName> skipServers = Lists.newArrayList(snRack2SN2); for (int attempts = 0; attempts < MAX_ATTEMPTS; attempts++) { ServerName genSN = helper.generateMissingFavoredNode(fn, skipServers); checkDuplicateFN(fn, genSN); checkFNRacks(fn, genSN); assertNotEquals("Generated FN should not match excluded one", snRack2SN2, genSN); } } private void checkDuplicateFN(List<ServerName> fnList, ServerName genFN) { Set<ServerName> favoredNodes = Sets.newHashSet(fnList); assertNotNull("Generated FN can't be null", genFN); favoredNodes.add(genFN); assertEquals("Did not find expected number of favored nodes", FavoredNodeAssignmentHelper.FAVORED_NODES_NUM, favoredNodes.size()); } private void checkDuplicateFN(List<ServerName> fnList) { Set<ServerName> favoredNodes = Sets.newHashSet(fnList); assertEquals("Did not find expected number of favored nodes", FavoredNodeAssignmentHelper.FAVORED_NODES_NUM, favoredNodes.size()); } private void checkFNRacks(List<ServerName> fnList, ServerName genFN) { Set<ServerName> favoredNodes = Sets.newHashSet(fnList); favoredNodes.add(genFN); Set<String> racks = Sets.newHashSet(); for (ServerName sn : favoredNodes) { racks.add(rackManager.getRack(sn)); } assertTrue("FN should be spread atleast across 2 racks", racks.size() >= 2); } private void checkFNRacks(List<ServerName> fnList) { Set<ServerName> favoredNodes = Sets.newHashSet(fnList); Set<String> racks = Sets.newHashSet(); for (ServerName sn : favoredNodes) { racks.add(rackManager.getRack(sn)); } assertTrue("FN should be spread atleast across 2 racks", racks.size() >= 2); } }