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,
074        FavoredNodeLoadBalancer.class, 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 = RegionInfoBuilder.newBuilder(TableName.valueOf(name.getMethodName())).build();
096    regions.add(region);
097    Map<ServerName,List<RegionInfo>> assignmentMap = balancer.roundRobinAssignment(regions,
098        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(ServerName.isSameAddress(serverBefore.iterator().next(),
105        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(),
123                 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 =
138        ((FavoredNodeLoadBalancer)balancer).getFavoredNodes(region);
139    assertTrue(favoredNodesNow.size() == FavoredNodeAssignmentHelper.FAVORED_NODES_NUM);
140    assertTrue(!favoredNodesNow.contains(favoredNodesAfter.get(PRIMARY)) &&
141        !favoredNodesNow.contains(favoredNodesAfter.get(SECONDARY)) &&
142        !favoredNodesNow.contains(favoredNodesAfter.get(TERTIARY)));
143  }
144
145  @Test
146  public void testFavoredNodesPresentForRandomAssignment() throws HBaseIOException {
147    LoadBalancer balancer = LoadBalancerFactory.getLoadBalancer(TEST_UTIL.getConfiguration());
148    balancer.setMasterServices(TEST_UTIL.getMiniHBaseCluster().getMaster());
149    balancer.initialize();
150    List<ServerName> servers = new ArrayList<>();
151    for (int i = 0; i < SLAVES; i++) {
152      ServerName server = TEST_UTIL.getMiniHBaseCluster().getRegionServer(i).getServerName();
153      servers.add(server);
154    }
155    List<RegionInfo> regions = new ArrayList<>(1);
156    RegionInfo region = 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 =
184        ((FavoredNodeLoadBalancer)balancer).getFavoredNodes(region);
185    assertTrue(favoredNodesNow.size() == FavoredNodeAssignmentHelper.FAVORED_NODES_NUM);
186    assertTrue(!favoredNodesNow.contains(favoredNodesAfter.get(PRIMARY)) &&
187        !favoredNodesNow.contains(favoredNodesAfter.get(SECONDARY)) &&
188        !favoredNodesNow.contains(favoredNodesAfter.get(TERTIARY)));
189  }
190
191  private List<ServerName> removeMatchingServers(Collection<ServerName> serversWithoutStartCode,
192      List<ServerName> servers) {
193    List<ServerName> serversToRemove = new ArrayList<>();
194    for (ServerName s : serversWithoutStartCode) {
195      serversToRemove.addAll(removeMatchingServers(s, servers));
196    }
197    return serversToRemove;
198  }
199
200  private List<ServerName> removeMatchingServers(ServerName serverWithoutStartCode,
201      List<ServerName> servers) {
202    List<ServerName> serversToRemove = new ArrayList<>();
203    for (ServerName s : servers) {
204      if (ServerName.isSameAddress(s, serverWithoutStartCode)) {
205        serversToRemove.add(s);
206      }
207    }
208    servers.removeAll(serversToRemove);
209    return serversToRemove;
210  }
211}