View Javadoc

1   /**
2    * Copyright The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  package org.apache.hadoop.hbase.rsgroup;
21  
22  import static org.junit.Assert.assertEquals;
23  import static org.junit.Assert.assertFalse;
24  import static org.junit.Assert.assertNull;
25  import static org.junit.Assert.assertTrue;
26  import static org.junit.Assert.fail;
27  
28  import java.io.IOException;
29  import java.util.Iterator;
30  import java.util.List;
31  import java.util.Map;
32  import java.util.Set;
33  
34  import org.apache.commons.logging.Log;
35  import org.apache.commons.logging.LogFactory;
36  import org.apache.hadoop.hbase.HColumnDescriptor;
37  import org.apache.hadoop.hbase.HTableDescriptor;
38  import org.apache.hadoop.hbase.NamespaceDescriptor;
39  import org.apache.hadoop.hbase.ServerName;
40  import org.apache.hadoop.hbase.TableName;
41  import org.apache.hadoop.hbase.Waiter;
42  import org.apache.hadoop.hbase.constraint.ConstraintException;
43  import org.apache.hadoop.hbase.master.ServerManager;
44  import org.apache.hadoop.hbase.master.TableNamespaceManager;
45  import org.apache.hadoop.hbase.master.snapshot.SnapshotManager;
46  import org.apache.hadoop.hbase.net.Address;
47  import org.apache.hadoop.hbase.quotas.QuotaUtil;
48  import org.apache.hadoop.hbase.testclassification.MediumTests;
49  import org.apache.hadoop.hbase.util.Bytes;
50  import org.junit.After;
51  import org.junit.AfterClass;
52  import org.junit.Assert;
53  import org.junit.Before;
54  import org.junit.BeforeClass;
55  import org.junit.Test;
56  import org.junit.experimental.categories.Category;
57  
58  import com.google.common.collect.Sets;
59  
60  @Category({MediumTests.class})
61  public class TestRSGroupsAdmin1 extends TestRSGroupsBase {
62    protected static final Log LOG = LogFactory.getLog(TestRSGroupsAdmin1.class);
63  
64    @BeforeClass
65    public static void setUp() throws Exception {
66      setUpTestBeforeClass();
67    }
68  
69    @AfterClass
70    public static void tearDown() throws Exception {
71      tearDownAfterClass();
72    }
73  
74    @Before
75    public void beforeMethod() throws Exception {
76      setUpBeforeMethod();
77    }
78  
79    @After
80    public void afterMethod() throws Exception {
81      tearDownAfterMethod();
82    }
83  
84    @Test
85    public void testValidGroupNames() throws IOException {
86      String[] badNames = {"foo*","foo@","-"};
87      String[] goodNames = {"foo_123"};
88  
89      for(String entry: badNames) {
90        try {
91          rsGroupAdmin.addRSGroup(entry);
92          fail("Expected a constraint exception for: "+entry);
93        } catch(ConstraintException ex) {
94          //expected
95        }
96      }
97  
98      for(String entry: goodNames) {
99        rsGroupAdmin.addRSGroup(entry);
100     }
101   }
102 
103   @Test
104   public void testBogusArgs() throws Exception {
105     assertNull(rsGroupAdmin.getRSGroupInfoOfTable(TableName.valueOf("nonexistent")));
106     assertNull(rsGroupAdmin.getRSGroupOfServer(Address.fromParts("bogus",123)));
107     assertNull(rsGroupAdmin.getRSGroupInfo("bogus"));
108 
109     try {
110       rsGroupAdmin.removeRSGroup("bogus");
111       fail("Expected removing bogus group to fail");
112     } catch(ConstraintException ex) {
113       //expected
114     }
115 
116     try {
117       rsGroupAdmin.moveTables(Sets.newHashSet(TableName.valueOf("bogustable")), "bogus");
118       fail("Expected move with bogus group to fail");
119     } catch(ConstraintException ex) {
120       //expected
121     }
122 
123     try {
124       rsGroupAdmin.moveServers(Sets.newHashSet(Address.fromParts("bogus",123)), "bogus");
125       fail("Expected move with bogus group to fail");
126     } catch(ConstraintException ex) {
127       //expected
128     }
129 
130     try {
131       rsGroupAdmin.balanceRSGroup("bogus");
132       fail("Expected move with bogus group to fail");
133     } catch(ConstraintException ex) {
134       //expected
135     }
136   }
137 
138   @Test
139   public void testNamespaceConstraint() throws Exception {
140     String nsName = tablePrefix+"_foo";
141     String groupName = tablePrefix+"_foo";
142     LOG.info("testNamespaceConstraint");
143     rsGroupAdmin.addRSGroup(groupName);
144     assertTrue(observer.preAddRSGroupCalled);
145     assertTrue(observer.postAddRSGroupCalled);
146     admin.createNamespace(NamespaceDescriptor.create(nsName)
147         .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, groupName)
148         .build());
149     //test removing a referenced group
150     try {
151       rsGroupAdmin.removeRSGroup(groupName);
152       fail("Expected a constraint exception");
153     } catch (IOException ex) {
154     }
155     //test modify group
156     //changing with the same name is fine
157     admin.modifyNamespace(
158         NamespaceDescriptor.create(nsName)
159           .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, groupName)
160           .build());
161     String anotherGroup = tablePrefix+"_anotherGroup";
162     rsGroupAdmin.addRSGroup(anotherGroup);
163     //test add non-existent group
164     admin.deleteNamespace(nsName);
165     rsGroupAdmin.removeRSGroup(groupName);
166     assertTrue(observer.preRemoveRSGroupCalled);
167     assertTrue(observer.postRemoveRSGroupCalled);
168     try {
169       admin.createNamespace(NamespaceDescriptor.create(nsName)
170           .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, "foo")
171           .build());
172       fail("Expected a constraint exception");
173     } catch (IOException ex) {
174     }
175   }
176 
177   @Test
178   public void testGroupInfoMultiAccessing() throws Exception {
179     RSGroupInfoManager manager = RSGroupAdminEndpoint.getGroupInfoManager();
180     final RSGroupInfo defaultGroup = manager.getRSGroup("default");
181     // getRSGroup updates default group's server list
182     // this process must not affect other threads iterating the list
183     Iterator<Address> it = defaultGroup.getServers().iterator();
184     manager.getRSGroup("default");
185     it.next();
186   }
187 
188   @Test
189   public void testFailRemoveGroup() throws IOException, InterruptedException {
190     LOG.info("testFailRemoveGroup");
191 
192     int initNumGroups = rsGroupAdmin.listRSGroups().size();
193     addGroup(rsGroupAdmin, "bar", 3);
194     TableName tableName = TableName.valueOf(tablePrefix+"_my_table");
195     TEST_UTIL.createTable(tableName, Bytes.toBytes("f"));
196     rsGroupAdmin.moveTables(Sets.newHashSet(tableName), "bar");
197     RSGroupInfo barGroup = rsGroupAdmin.getRSGroupInfo("bar");
198     //group is not empty therefore it should fail
199     try {
200       rsGroupAdmin.removeRSGroup(barGroup.getName());
201       fail("Expected remove group to fail");
202     } catch(IOException e) {
203     }
204     //group cannot lose all it's servers therefore it should fail
205     try {
206       rsGroupAdmin.moveServers(barGroup.getServers(), RSGroupInfo.DEFAULT_GROUP);
207       fail("Expected move servers to fail");
208     } catch(IOException e) {
209     }
210 
211     rsGroupAdmin.moveTables(barGroup.getTables(), RSGroupInfo.DEFAULT_GROUP);
212     try {
213       rsGroupAdmin.removeRSGroup(barGroup.getName());
214       fail("Expected move servers to fail");
215     } catch(IOException e) {
216     }
217 
218     rsGroupAdmin.moveServers(barGroup.getServers(), RSGroupInfo.DEFAULT_GROUP);
219     rsGroupAdmin.removeRSGroup(barGroup.getName());
220 
221     Assert.assertEquals(initNumGroups, rsGroupAdmin.listRSGroups().size());
222   }
223 
224   @Test
225   public void testMultiTableMove() throws Exception {
226     LOG.info("testMultiTableMove");
227 
228     final TableName tableNameA = TableName.valueOf(tablePrefix + "_testMultiTableMoveA");
229     final TableName tableNameB = TableName.valueOf(tablePrefix + "_testMultiTableMoveB");
230     final byte[] familyNameBytes = Bytes.toBytes("f");
231     String newGroupName = getGroupName("testMultiTableMove");
232     final RSGroupInfo newGroup = addGroup(rsGroupAdmin, newGroupName, 1);
233 
234     TEST_UTIL.createTable(tableNameA, familyNameBytes);
235     TEST_UTIL.createTable(tableNameB, familyNameBytes);
236     TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
237       @Override
238       public boolean evaluate() throws Exception {
239         List<String> regionsA = getTableRegionMap().get(tableNameA);
240         if (regionsA == null) {
241           return false;
242         }
243         List<String> regionsB = getTableRegionMap().get(tableNameB);
244         if (regionsB == null) {
245           return false;
246         }
247         return getTableRegionMap().get(tableNameA).size() >= 1
248                 && getTableRegionMap().get(tableNameB).size() >= 1;
249       }
250     });
251 
252     RSGroupInfo tableGrpA = rsGroupAdmin.getRSGroupInfoOfTable(tableNameA);
253     assertTrue(tableGrpA.getName().equals(RSGroupInfo.DEFAULT_GROUP));
254 
255     RSGroupInfo tableGrpB = rsGroupAdmin.getRSGroupInfoOfTable(tableNameB);
256     assertTrue(tableGrpB.getName().equals(RSGroupInfo.DEFAULT_GROUP));
257     //change table's group
258     LOG.info("Moving table [" + tableNameA + "," + tableNameB + "] to " + newGroup.getName());
259     rsGroupAdmin.moveTables(Sets.newHashSet(tableNameA, tableNameB), newGroup.getName());
260 
261     //verify group change
262     Assert.assertEquals(newGroup.getName(),
263             rsGroupAdmin.getRSGroupInfoOfTable(tableNameA).getName());
264 
265     Assert.assertEquals(newGroup.getName(),
266             rsGroupAdmin.getRSGroupInfoOfTable(tableNameB).getName());
267 
268     //verify tables' not exist in old group
269     Set<TableName> DefaultTables =
270         rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getTables();
271     assertFalse(DefaultTables.contains(tableNameA));
272     assertFalse(DefaultTables.contains(tableNameB));
273 
274     //verify tables' exist in new group
275     Set<TableName> newGroupTables = rsGroupAdmin.getRSGroupInfo(newGroupName).getTables();
276     assertTrue(newGroupTables.contains(tableNameA));
277     assertTrue(newGroupTables.contains(tableNameB));
278   }
279 
280   @Test
281   public void testTableMoveTruncateAndDrop() throws Exception {
282     LOG.info("testTableMove");
283 
284     final TableName tableName = TableName.valueOf(tablePrefix + "_testTableMoveAndDrop");
285     final byte[] familyNameBytes = Bytes.toBytes("f");
286     String newGroupName = getGroupName("testTableMove");
287     final RSGroupInfo newGroup = addGroup(rsGroupAdmin, newGroupName, 2);
288 
289     TEST_UTIL.createMultiRegionTable(tableName, familyNameBytes, 5);
290     TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
291       @Override
292       public boolean evaluate() throws Exception {
293         List<String> regions = getTableRegionMap().get(tableName);
294         if (regions == null)
295           return false;
296         return getTableRegionMap().get(tableName).size() >= 5;
297       }
298     });
299 
300     RSGroupInfo tableGrp = rsGroupAdmin.getRSGroupInfoOfTable(tableName);
301     assertTrue(tableGrp.getName().equals(RSGroupInfo.DEFAULT_GROUP));
302 
303     //change table's group
304     LOG.info("Moving table "+tableName+" to "+newGroup.getName());
305     rsGroupAdmin.moveTables(Sets.newHashSet(tableName), newGroup.getName());
306 
307     //verify group change
308     Assert.assertEquals(newGroup.getName(),
309         rsGroupAdmin.getRSGroupInfoOfTable(tableName).getName());
310 
311     TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
312       @Override
313       public boolean evaluate() throws Exception {
314         Map<ServerName, List<String>> serverMap = getTableServerRegionMap().get(tableName);
315         int count = 0;
316         if (serverMap != null) {
317           for (ServerName rs : serverMap.keySet()) {
318             if (newGroup.containsServer(rs.getAddress())) {
319               count += serverMap.get(rs).size();
320             }
321           }
322         }
323         return count == 5;
324       }
325     });
326 
327     //test truncate
328     admin.disableTable(tableName);
329     admin.truncateTable(tableName, true);
330     Assert.assertEquals(1, rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getTables().size());
331     Assert.assertEquals(tableName, rsGroupAdmin.getRSGroupInfo(
332         newGroup.getName()).getTables().first());
333 
334     //verify removed table is removed from group
335     TEST_UTIL.deleteTable(tableName);
336     Assert.assertEquals(0, rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getTables().size());
337 
338     assertTrue(observer.preMoveTablesCalled);
339     assertTrue(observer.postMoveTablesCalled);
340   }
341 
342   @Test
343   public void testDisabledTableMove() throws Exception {
344     final TableName tableName = TableName.valueOf(tablePrefix + "_testDisabledTableMove");
345     final byte[] familyNameBytes = Bytes.toBytes("f");
346     String newGroupName = getGroupName("testDisabledTableMove");
347     final RSGroupInfo newGroup = addGroup(rsGroupAdmin, newGroupName, 2);
348 
349     TEST_UTIL.createMultiRegionTable(tableName, familyNameBytes, 5);
350     TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
351       @Override
352       public boolean evaluate() throws Exception {
353         List<String> regions = getTableRegionMap().get(tableName);
354         if (regions == null) {
355           return false;
356         }
357         return getTableRegionMap().get(tableName).size() >= 5;
358       }
359     });
360 
361     RSGroupInfo tableGrp = rsGroupAdmin.getRSGroupInfoOfTable(tableName);
362     assertTrue(tableGrp.getName().equals(RSGroupInfo.DEFAULT_GROUP));
363 
364     //test disable table
365     admin.disableTable(tableName);
366 
367     //change table's group
368     LOG.info("Moving table "+ tableName + " to " + newGroup.getName());
369     rsGroupAdmin.moveTables(Sets.newHashSet(tableName), newGroup.getName());
370 
371     //verify group change
372     Assert.assertEquals(newGroup.getName(),
373         rsGroupAdmin.getRSGroupInfoOfTable(tableName).getName());
374   }
375 
376   @Test
377   public void testRSGroupListDoesNotContainFailedTableCreation() throws Exception {
378     toggleQuotaCheckAndRestartMiniCluster(true);
379     String nsp = "np1";
380     NamespaceDescriptor nspDesc =
381         NamespaceDescriptor.create(nsp).addConfiguration(TableNamespaceManager.KEY_MAX_REGIONS, "5")
382             .addConfiguration(TableNamespaceManager.KEY_MAX_TABLES, "2").build();
383     admin.createNamespace(nspDesc);
384     assertEquals(3, admin.listNamespaceDescriptors().length);
385     HColumnDescriptor fam1 = new HColumnDescriptor("fam1");
386     HTableDescriptor tableDescOne =
387         new HTableDescriptor(TableName.valueOf(nsp + TableName.NAMESPACE_DELIM + "table1"));
388     tableDescOne.addFamily(fam1);
389     admin.createTable(tableDescOne);
390 
391     HTableDescriptor tableDescTwo =
392         new HTableDescriptor(TableName.valueOf(nsp + TableName.NAMESPACE_DELIM + "table2"));
393     tableDescTwo.addFamily(fam1);
394     boolean constraintViolated = false;
395 
396     try {
397       admin.createTable(tableDescTwo, Bytes.toBytes("AAA"), Bytes.toBytes("ZZZ"),
398           6);
399       Assert.fail("Creation table should fail because of quota violation.");
400     } catch (Exception exp) {
401       assertTrue(exp instanceof IOException);
402       constraintViolated = true;
403     } finally {
404       assertTrue("Constraint not violated for table " + tableDescTwo.getTableName(),
405           constraintViolated);
406     }
407     List<RSGroupInfo> rsGroupInfoList = rsGroupAdmin.listRSGroups();
408     boolean foundTable2 = false;
409     boolean foundTable1 = false;
410     for (int i = 0; i < rsGroupInfoList.size(); i++){
411       if(rsGroupInfoList.get(i).getTables().contains(tableDescTwo.getTableName())){
412         foundTable2 = true;
413       }
414       if(rsGroupInfoList.get(i).getTables().contains(tableDescOne.getTableName())){
415         foundTable1 = true;
416       }
417     }
418     assertFalse("Found table2 in rsgroup list.", foundTable2);
419     assertTrue("Did not find table1 in rsgroup list", foundTable1);
420 
421     TEST_UTIL.deleteTable(tableDescOne.getTableName());
422     admin.deleteNamespace(nspDesc.getName());
423     toggleQuotaCheckAndRestartMiniCluster(false);
424 
425   }
426 
427   private void toggleQuotaCheckAndRestartMiniCluster(boolean enable) throws Exception {
428     TEST_UTIL.shutdownMiniCluster();
429     TEST_UTIL.getConfiguration().setBoolean(QuotaUtil.QUOTA_CONF_KEY, enable);
430     TEST_UTIL.startMiniCluster(NUM_SLAVES_BASE - 1);
431     TEST_UTIL.getConfiguration().setInt(
432         ServerManager.WAIT_ON_REGIONSERVERS_MINTOSTART,
433         NUM_SLAVES_BASE - 1);
434     TEST_UTIL.getConfiguration().setBoolean(SnapshotManager.HBASE_SNAPSHOT_ENABLED, true);
435     initialize();
436   }
437 
438 }