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}