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.rsgroup;
019
020import static org.junit.Assert.assertEquals;
021import static org.junit.Assert.assertTrue;
022import static org.junit.Assert.fail;
023
024import java.io.IOException;
025import java.util.Iterator;
026import org.apache.hadoop.hbase.HBaseClassTestRule;
027import org.apache.hadoop.hbase.HBaseTestingUtility;
028import org.apache.hadoop.hbase.HColumnDescriptor;
029import org.apache.hadoop.hbase.HConstants;
030import org.apache.hadoop.hbase.HTableDescriptor;
031import org.apache.hadoop.hbase.MiniHBaseCluster;
032import org.apache.hadoop.hbase.NamespaceDescriptor;
033import org.apache.hadoop.hbase.ServerName;
034import org.apache.hadoop.hbase.TableName;
035import org.apache.hadoop.hbase.Waiter;
036import org.apache.hadoop.hbase.Waiter.Predicate;
037import org.apache.hadoop.hbase.client.ClusterConnection;
038import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
039import org.apache.hadoop.hbase.master.ServerManager;
040import org.apache.hadoop.hbase.master.snapshot.SnapshotManager;
041import org.apache.hadoop.hbase.net.Address;
042import org.apache.hadoop.hbase.testclassification.MediumTests;
043import org.apache.hadoop.hbase.util.Bytes;
044import org.junit.After;
045import org.junit.AfterClass;
046import org.junit.Assert;
047import org.junit.Before;
048import org.junit.BeforeClass;
049import org.junit.ClassRule;
050import org.junit.Test;
051import org.junit.experimental.categories.Category;
052import org.slf4j.Logger;
053import org.slf4j.LoggerFactory;
054
055import org.apache.hbase.thirdparty.com.google.common.collect.Sets;
056
057import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
058import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos;
059
060@Category({MediumTests.class})
061public class TestRSGroups extends TestRSGroupsBase {
062
063  @ClassRule
064  public static final HBaseClassTestRule CLASS_RULE =
065      HBaseClassTestRule.forClass(TestRSGroups.class);
066
067  protected static final Logger LOG = LoggerFactory.getLogger(TestRSGroups.class);
068  private static boolean INIT = false;
069  private static RSGroupAdminEndpoint rsGroupAdminEndpoint;
070
071
072  @BeforeClass
073  public static void setUp() throws Exception {
074    TEST_UTIL = new HBaseTestingUtility();
075    TEST_UTIL.getConfiguration().setFloat(
076            "hbase.master.balancer.stochastic.tableSkewCost", 6000);
077    TEST_UTIL.getConfiguration().set(
078        HConstants.HBASE_MASTER_LOADBALANCER_CLASS,
079        RSGroupBasedLoadBalancer.class.getName());
080    TEST_UTIL.getConfiguration().set(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY,
081        RSGroupAdminEndpoint.class.getName());
082    TEST_UTIL.startMiniCluster(NUM_SLAVES_BASE - 1);
083    TEST_UTIL.getConfiguration().setInt(
084        ServerManager.WAIT_ON_REGIONSERVERS_MINTOSTART,
085        NUM_SLAVES_BASE - 1);
086    TEST_UTIL.getConfiguration().setBoolean(SnapshotManager.HBASE_SNAPSHOT_ENABLED, true);
087
088    admin = TEST_UTIL.getAdmin();
089    cluster = TEST_UTIL.getHBaseCluster();
090    master = ((MiniHBaseCluster)cluster).getMaster();
091
092    //wait for balancer to come online
093    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
094      @Override
095      public boolean evaluate() throws Exception {
096        return master.isInitialized() &&
097            ((RSGroupBasedLoadBalancer) master.getLoadBalancer()).isOnline();
098      }
099    });
100    admin.setBalancerRunning(false,true);
101    rsGroupAdmin = new VerifyingRSGroupAdminClient(
102        new RSGroupAdminClient(TEST_UTIL.getConnection()), TEST_UTIL.getConfiguration());
103    rsGroupAdminEndpoint = (RSGroupAdminEndpoint)
104        master.getMasterCoprocessorHost().findCoprocessor(RSGroupAdminEndpoint.class.getName());
105  }
106
107  @AfterClass
108  public static void tearDown() throws Exception {
109    TEST_UTIL.shutdownMiniCluster();
110  }
111
112  @Before
113  public void beforeMethod() throws Exception {
114    if (!INIT) {
115      INIT = true;
116      afterMethod();
117    }
118
119  }
120
121  @After
122  public void afterMethod() throws Exception {
123    deleteTableIfNecessary();
124    deleteNamespaceIfNecessary();
125    deleteGroups();
126
127    for(ServerName sn : admin.listDecommissionedRegionServers()){
128      admin.recommissionRegionServer(sn, null);
129    }
130    assertTrue(admin.listDecommissionedRegionServers().isEmpty());
131
132    int missing = NUM_SLAVES_BASE - getNumServers();
133    LOG.info("Restoring servers: "+missing);
134    for(int i=0; i<missing; i++) {
135      ((MiniHBaseCluster)cluster).startRegionServer();
136    }
137
138    rsGroupAdmin.addRSGroup("master");
139    ServerName masterServerName =
140        ((MiniHBaseCluster)cluster).getMaster().getServerName();
141
142    try {
143      rsGroupAdmin.moveServers(Sets.newHashSet(masterServerName.getAddress()), "master");
144    } catch (Exception ex) {
145      LOG.warn("Got this on setup, FYI", ex);
146    }
147    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
148      @Override
149      public boolean evaluate() throws Exception {
150        LOG.info("Waiting for cleanup to finish " + rsGroupAdmin.listRSGroups());
151        //Might be greater since moving servers back to default
152        //is after starting a server
153
154        return rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getServers().size()
155            == NUM_SLAVES_BASE;
156      }
157    });
158  }
159
160  @Test
161  public void testBasicStartUp() throws IOException {
162    RSGroupInfo defaultInfo = rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP);
163    assertEquals(4, defaultInfo.getServers().size());
164    // Assignment of root and meta regions.
165    int count = master.getAssignmentManager().getRegionStates().getRegionAssignments().size();
166    //3 meta,namespace, group
167    assertEquals(3, count);
168  }
169
170  @Test
171  public void testNamespaceCreateAndAssign() throws Exception {
172    LOG.info("testNamespaceCreateAndAssign");
173    String nsName = tablePrefix+"_foo";
174    final TableName tableName = TableName.valueOf(nsName, tablePrefix + "_testCreateAndAssign");
175    RSGroupInfo appInfo = addGroup("appInfo", 1);
176    admin.createNamespace(NamespaceDescriptor.create(nsName)
177        .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, "appInfo").build());
178    final HTableDescriptor desc = new HTableDescriptor(tableName);
179    desc.addFamily(new HColumnDescriptor("f"));
180    admin.createTable(desc);
181    //wait for created table to be assigned
182    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
183      @Override
184      public boolean evaluate() throws Exception {
185        return getTableRegionMap().get(desc.getTableName()) != null;
186      }
187    });
188    ServerName targetServer =
189        ServerName.parseServerName(appInfo.getServers().iterator().next().toString());
190    AdminProtos.AdminService.BlockingInterface rs =
191      ((ClusterConnection) admin.getConnection()).getAdmin(targetServer);
192    //verify it was assigned to the right group
193    Assert.assertEquals(1, ProtobufUtil.getOnlineRegions(rs).size());
194  }
195
196  @Test
197  public void testDefaultNamespaceCreateAndAssign() throws Exception {
198    LOG.info("testDefaultNamespaceCreateAndAssign");
199    String tableName = tablePrefix + "_testCreateAndAssign";
200    admin.modifyNamespace(NamespaceDescriptor.create("default")
201        .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, "default").build());
202    final HTableDescriptor desc = new HTableDescriptor(TableName.valueOf(tableName));
203    desc.addFamily(new HColumnDescriptor("f"));
204    admin.createTable(desc);
205    //wait for created table to be assigned
206    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
207      @Override
208      public boolean evaluate() throws Exception {
209        return getTableRegionMap().get(desc.getTableName()) != null;
210      }
211    });
212  }
213
214  @Test
215  public void testNamespaceConstraint() throws Exception {
216    String nsName = tablePrefix+"_foo";
217    String groupName = tablePrefix+"_foo";
218    LOG.info("testNamespaceConstraint");
219    rsGroupAdmin.addRSGroup(groupName);
220    admin.createNamespace(NamespaceDescriptor.create(nsName)
221        .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, groupName)
222        .build());
223    //test removing a referenced group
224    try {
225      rsGroupAdmin.removeRSGroup(groupName);
226      fail("Expected a constraint exception");
227    } catch (IOException ex) {
228    }
229    //test modify group
230    //changing with the same name is fine
231    admin.modifyNamespace(
232        NamespaceDescriptor.create(nsName)
233          .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, groupName)
234          .build());
235    String anotherGroup = tablePrefix+"_anotherGroup";
236    rsGroupAdmin.addRSGroup(anotherGroup);
237    //test add non-existent group
238    admin.deleteNamespace(nsName);
239    rsGroupAdmin.removeRSGroup(groupName);
240    try {
241      admin.createNamespace(NamespaceDescriptor.create(nsName)
242          .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, "foo")
243          .build());
244      fail("Expected a constraint exception");
245    } catch (IOException ex) {
246    }
247  }
248
249  @Test
250  public void testGroupInfoMultiAccessing() throws Exception {
251    RSGroupInfoManager manager = rsGroupAdminEndpoint.getGroupInfoManager();
252    RSGroupInfo defaultGroup = manager.getRSGroup("default");
253    // getRSGroup updates default group's server list
254    // this process must not affect other threads iterating the list
255    Iterator<Address> it = defaultGroup.getServers().iterator();
256    manager.getRSGroup("default");
257    it.next();
258  }
259
260  @Test
261  public void testMisplacedRegions() throws Exception {
262    final TableName tableName = TableName.valueOf(tablePrefix+"_testMisplacedRegions");
263    LOG.info("testMisplacedRegions");
264
265    final RSGroupInfo RSGroupInfo = addGroup("testMisplacedRegions", 1);
266
267    TEST_UTIL.createMultiRegionTable(tableName, new byte[]{'f'}, 15);
268    TEST_UTIL.waitUntilAllRegionsAssigned(tableName);
269
270    rsGroupAdminEndpoint.getGroupInfoManager()
271        .moveTables(Sets.newHashSet(tableName), RSGroupInfo.getName());
272
273    admin.setBalancerRunning(true,true);
274    assertTrue(rsGroupAdmin.balanceRSGroup(RSGroupInfo.getName()));
275    admin.setBalancerRunning(false,true);
276
277    TEST_UTIL.waitFor(60000, new Predicate<Exception>() {
278      @Override
279      public boolean evaluate() throws Exception {
280        ServerName serverName =
281            ServerName.valueOf(RSGroupInfo.getServers().iterator().next().toString(), 1);
282        return admin.getConnection().getAdmin()
283            .getOnlineRegions(serverName).size() == 15;
284      }
285    });
286  }
287
288  @Test
289  public void testCloneSnapshot() throws Exception {
290    byte[] FAMILY = Bytes.toBytes("test");
291    String snapshotName = tableName.getNameAsString() + "_snap";
292    TableName clonedTableName = TableName.valueOf(tableName.getNameAsString() + "_clone");
293
294    // create base table
295    TEST_UTIL.createTable(tableName, FAMILY);
296
297    // create snapshot
298    admin.snapshot(snapshotName, tableName);
299
300    // clone
301    admin.cloneSnapshot(snapshotName, clonedTableName);
302  }
303
304}