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}