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.assertTrue;
25  import static org.junit.Assert.fail;
26  
27  import java.io.IOException;
28  import java.util.Iterator;
29  import java.util.List;
30  import java.util.Set;
31  
32  import org.apache.commons.logging.Log;
33  import org.apache.commons.logging.LogFactory;
34  import org.apache.hadoop.hbase.HBaseTestingUtility;
35  import org.apache.hadoop.hbase.HColumnDescriptor;
36  import org.apache.hadoop.hbase.HConstants;
37  import org.apache.hadoop.hbase.HTableDescriptor;
38  import org.apache.hadoop.hbase.MiniHBaseCluster;
39  import org.apache.hadoop.hbase.NamespaceDescriptor;
40  import org.apache.hadoop.hbase.ServerName;
41  import org.apache.hadoop.hbase.TableName;
42  import org.apache.hadoop.hbase.Waiter;
43  import org.apache.hadoop.hbase.Waiter.Predicate;
44  import org.apache.hadoop.hbase.coprocessor.BaseMasterObserver;
45  import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
46  import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment;
47  import org.apache.hadoop.hbase.coprocessor.MasterObserver;
48  import org.apache.hadoop.hbase.coprocessor.ObserverContext;
49  import org.apache.hadoop.hbase.master.HMaster;
50  import org.apache.hadoop.hbase.master.MasterCoprocessorHost;
51  import org.apache.hadoop.hbase.master.ServerManager;
52  import org.apache.hadoop.hbase.master.TableNamespaceManager;
53  import org.apache.hadoop.hbase.master.snapshot.SnapshotManager;
54  import org.apache.hadoop.hbase.net.Address;
55  import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
56  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos;
57  import org.apache.hadoop.hbase.quotas.QuotaUtil;
58  import org.apache.hadoop.hbase.testclassification.MediumTests;
59  import org.apache.hadoop.hbase.util.Bytes;
60  import org.junit.After;
61  import org.junit.AfterClass;
62  import org.junit.Assert;
63  import org.junit.Before;
64  import org.junit.BeforeClass;
65  import org.junit.Test;
66  import org.junit.experimental.categories.Category;
67  
68  import com.google.common.collect.Sets;
69  
70  @Category({MediumTests.class})
71  public class TestRSGroups extends TestRSGroupsBase {
72    protected static final Log LOG = LogFactory.getLog(TestRSGroups.class);
73    private static HMaster master;
74    private static boolean init = false;
75    private static RSGroupAdminEndpoint RSGroupAdminEndpoint;
76    private static CPMasterObserver observer;
77  
78    @BeforeClass
79    public static void setUp() throws Exception {
80      TEST_UTIL = new HBaseTestingUtility();
81      TEST_UTIL.getConfiguration().setFloat(
82              "hbase.master.balancer.stochastic.tableSkewCost", 6000);
83      TEST_UTIL.getConfiguration().set(
84          HConstants.HBASE_MASTER_LOADBALANCER_CLASS,
85          RSGroupBasedLoadBalancer.class.getName());
86      TEST_UTIL.getConfiguration().set(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY,
87          RSGroupAdminEndpoint.class.getName() + "," + CPMasterObserver.class.getName());
88      TEST_UTIL.getConfiguration().setBoolean(
89          HConstants.ZOOKEEPER_USEMULTI,
90          true);
91      TEST_UTIL.startMiniCluster(NUM_SLAVES_BASE - 1);
92      TEST_UTIL.getConfiguration().setInt(
93          ServerManager.WAIT_ON_REGIONSERVERS_MINTOSTART,
94          NUM_SLAVES_BASE - 1);
95      TEST_UTIL.getConfiguration().setBoolean(SnapshotManager.HBASE_SNAPSHOT_ENABLED, true);
96      initialize();
97    }
98  
99    private static void initialize() throws Exception {
100     admin = TEST_UTIL.getHBaseAdmin();
101     cluster = TEST_UTIL.getHBaseCluster();
102     master = ((MiniHBaseCluster)cluster).getMaster();
103 
104     //wait for balancer to come online
105     TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
106       @Override
107       public boolean evaluate() throws Exception {
108         return master.isInitialized() &&
109             ((RSGroupBasedLoadBalancer) master.getLoadBalancer()).isOnline();
110       }
111     });
112     admin.setBalancerRunning(false,true);
113     rsGroupAdmin = new VerifyingRSGroupAdminClient(new RSGroupAdminClient(TEST_UTIL.getConnection()),
114         TEST_UTIL.getConfiguration());
115     MasterCoprocessorHost host = master.getMasterCoprocessorHost();
116     observer = (CPMasterObserver) host.findCoprocessor(CPMasterObserver.class.getName());
117     RSGroupAdminEndpoint =
118         host.findCoprocessors(RSGroupAdminEndpoint.class).get(0);
119   }
120 
121   @AfterClass
122   public static void tearDown() throws Exception {
123     TEST_UTIL.shutdownMiniCluster();
124   }
125 
126   @Before
127   public void beforeMethod() throws Exception {
128     if(!init) {
129       init = true;
130       afterMethod();
131     }
132     observer.resetFlags();
133   }
134 
135   @After
136   public void afterMethod() throws Exception {
137     deleteTableIfNecessary();
138     deleteNamespaceIfNecessary();
139     deleteGroups();
140 
141     int missing = NUM_SLAVES_BASE - getNumServers();
142     LOG.info("Restoring servers: "+missing);
143     for(int i=0; i<missing; i++) {
144       ((MiniHBaseCluster)cluster).startRegionServer();
145     }
146 
147     rsGroupAdmin.addRSGroup("master");
148     ServerName masterServerName =
149         ((MiniHBaseCluster)cluster).getMaster().getServerName();
150 
151     try {
152       rsGroupAdmin.moveServers(
153           Sets.newHashSet(masterServerName.getAddress()),
154           "master");
155     } catch (Exception ex) {
156       // ignore
157     }
158 
159     TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
160       @Override
161       public boolean evaluate() throws Exception {
162         LOG.info("Waiting for cleanup to finish " + rsGroupAdmin.listRSGroups());
163         //Might be greater since moving servers back to default
164         //is after starting a server
165 
166         return rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getServers().size()
167             == NUM_SLAVES_BASE;
168       }
169     });
170   }
171 
172   @Test
173   public void testBasicStartUp() throws IOException {
174     RSGroupInfo defaultInfo = rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP);
175     assertEquals(4, defaultInfo.getServers().size());
176     // Assignment of root and meta regions.
177     int count = master.getAssignmentManager().getRegionStates().getRegionAssignments().size();
178     //3 meta,namespace, group
179     assertEquals(3, count);
180   }
181 
182   @Test
183   public void testNamespaceCreateAndAssign() throws Exception {
184     LOG.info("testNamespaceCreateAndAssign");
185     String nsName = tablePrefix+"_foo";
186     final TableName tableName = TableName.valueOf(nsName, tablePrefix + "_testCreateAndAssign");
187     RSGroupInfo appInfo = addGroup(rsGroupAdmin, "appInfo", 1);
188     admin.createNamespace(NamespaceDescriptor.create(nsName)
189         .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, "appInfo").build());
190     final HTableDescriptor desc = new HTableDescriptor(tableName);
191     desc.addFamily(new HColumnDescriptor("f"));
192     admin.createTable(desc);
193     //wait for created table to be assigned
194     TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
195       @Override
196       public boolean evaluate() throws Exception {
197         return getTableRegionMap().get(desc.getTableName()) != null;
198       }
199     });
200     ServerName targetServer =
201         ServerName.parseServerName(appInfo.getServers().iterator().next().toString());
202     AdminProtos.AdminService.BlockingInterface rs = admin.getConnection().getAdmin(targetServer);
203     //verify it was assigned to the right group
204     Assert.assertEquals(1, ProtobufUtil.getOnlineRegions(rs).size());
205   }
206 
207   @Test
208   public void testDefaultNamespaceCreateAndAssign() throws Exception {
209     LOG.info("testDefaultNamespaceCreateAndAssign");
210     final byte[] tableName = Bytes.toBytes(tablePrefix + "_testCreateAndAssign");
211     admin.modifyNamespace(NamespaceDescriptor.create("default")
212         .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, "default").build());
213     final HTableDescriptor desc = new HTableDescriptor(tableName);
214     desc.addFamily(new HColumnDescriptor("f"));
215     admin.createTable(desc);
216     //wait for created table to be assigned
217     TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
218       @Override
219       public boolean evaluate() throws Exception {
220         return getTableRegionMap().get(desc.getTableName()) != null;
221       }
222     });
223   }
224 
225   @Test
226   public void testNamespaceConstraint() throws Exception {
227     String nsName = tablePrefix+"_foo";
228     String groupName = tablePrefix+"_foo";
229     LOG.info("testNamespaceConstraint");
230     rsGroupAdmin.addRSGroup(groupName);
231     admin.createNamespace(NamespaceDescriptor.create(nsName)
232         .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, groupName)
233         .build());
234     //test removing a referenced group
235     try {
236       rsGroupAdmin.removeRSGroup(groupName);
237       fail("Expected a constraint exception");
238     } catch (IOException ex) {
239     }
240     //test modify group
241     //changing with the same name is fine
242     admin.modifyNamespace(
243         NamespaceDescriptor.create(nsName)
244           .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, groupName)
245           .build());
246     String anotherGroup = tablePrefix+"_anotherGroup";
247     rsGroupAdmin.addRSGroup(anotherGroup);
248     //test add non-existent group
249     admin.deleteNamespace(nsName);
250     rsGroupAdmin.removeRSGroup(groupName);
251     try {
252       admin.createNamespace(NamespaceDescriptor.create(nsName)
253           .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, "foo")
254           .build());
255       fail("Expected a constraint exception");
256     } catch (IOException ex) {
257     }
258   }
259 
260   @Test
261   public void testGroupInfoMultiAccessing() throws Exception {
262     RSGroupInfoManager manager = RSGroupAdminEndpoint.getGroupInfoManager();
263     final RSGroupInfo defaultGroup = manager.getRSGroup("default");
264     // getRSGroup updates default group's server list
265     // this process must not affect other threads iterating the list
266     Iterator<Address> it = defaultGroup.getServers().iterator();
267     manager.getRSGroup("default");
268     it.next();
269   }
270 
271   public static class CPMasterObserver extends BaseMasterObserver {
272     boolean preBalanceRSGroupCalled = false;
273     boolean postBalanceRSGroupCalled = false;
274     boolean preMoveServersCalled = false;
275     boolean postMoveServersCalled = false;
276     boolean preMoveTablesCalled = false;
277     boolean postMoveTablesCalled = false;
278     boolean preAddRSGroupCalled = false;
279     boolean postAddRSGroupCalled = false;
280     boolean preRemoveRSGroupCalled = false;
281     boolean postRemoveRSGroupCalled = false;
282     boolean preRemoveServersCalled = false;
283     boolean postRemoveServersCalled = false;
284     boolean preMoveServersAndTables = false;
285     boolean postMoveServersAndTables = false;
286 
287     void resetFlags() {
288       preBalanceRSGroupCalled = false;
289       postBalanceRSGroupCalled = false;
290       preMoveServersCalled = false;
291       postMoveServersCalled = false;
292       preMoveTablesCalled = false;
293       postMoveTablesCalled = false;
294       preAddRSGroupCalled = false;
295       postAddRSGroupCalled = false;
296       preRemoveRSGroupCalled = false;
297       postRemoveRSGroupCalled = false;
298       preRemoveServersCalled = false;
299       postRemoveServersCalled = false;
300       preMoveServersAndTables = false;
301       postMoveServersAndTables = false;
302     }
303 
304     @Override
305     public void preMoveServersAndTables(final ObserverContext<MasterCoprocessorEnvironment> ctx,
306         Set<Address> servers, Set<TableName> tables, String targetGroup) throws IOException {
307       preMoveServersAndTables = true;
308     }
309     @Override
310     public void postMoveServersAndTables(final ObserverContext<MasterCoprocessorEnvironment> ctx,
311         Set<Address> servers, Set<TableName> tables, String targetGroup) throws IOException {
312       postMoveServersAndTables = true;
313     }
314     @Override
315     public void preRemoveServers(
316         final ObserverContext<MasterCoprocessorEnvironment> ctx,
317         Set<Address> servers) throws IOException {
318       preRemoveServersCalled = true;
319     }
320     @Override
321     public void postRemoveServers(
322         final ObserverContext<MasterCoprocessorEnvironment> ctx,
323         Set<Address> servers) throws IOException {
324       postRemoveServersCalled = true;
325     }
326     @Override
327     public void preRemoveRSGroup(final ObserverContext<MasterCoprocessorEnvironment> ctx,
328         String name) throws IOException {
329       preRemoveRSGroupCalled = true;
330     }
331     @Override
332     public void postRemoveRSGroup(final ObserverContext<MasterCoprocessorEnvironment> ctx,
333         String name) throws IOException {
334       postRemoveRSGroupCalled = true;
335     }
336     @Override
337     public void preAddRSGroup(final ObserverContext<MasterCoprocessorEnvironment> ctx,
338         String name) throws IOException {
339       preAddRSGroupCalled = true;
340     }
341     @Override
342     public void postAddRSGroup(final ObserverContext<MasterCoprocessorEnvironment> ctx,
343         String name) throws IOException {
344       postAddRSGroupCalled = true;
345     }
346     @Override
347     public void preMoveTables(final ObserverContext<MasterCoprocessorEnvironment> ctx,
348         Set<TableName> tables, String targetGroup) throws IOException {
349       preMoveTablesCalled = true;
350     }
351     @Override
352     public void postMoveTables(final ObserverContext<MasterCoprocessorEnvironment> ctx,
353         Set<TableName> tables, String targetGroup) throws IOException {
354       postMoveTablesCalled = true;
355     }
356     @Override
357     public void preMoveServers(final ObserverContext<MasterCoprocessorEnvironment> ctx,
358         Set<Address> servers, String targetGroup) throws IOException {
359       preMoveServersCalled = true;
360     }
361 
362     @Override
363     public void postMoveServers(final ObserverContext<MasterCoprocessorEnvironment> ctx,
364         Set<Address> servers, String targetGroup) throws IOException {
365       postMoveServersCalled = true;
366     }
367     @Override
368     public void preBalanceRSGroup(final ObserverContext<MasterCoprocessorEnvironment> ctx,
369         String groupName) throws IOException {
370       preBalanceRSGroupCalled = true;
371     }
372     @Override
373     public void postBalanceRSGroup(final ObserverContext<MasterCoprocessorEnvironment> ctx,
374         String groupName, boolean balancerRan) throws IOException {
375       postBalanceRSGroupCalled = true;
376     }
377   }
378   @Test
379   public void testMoveServersAndTables() throws Exception {
380     super.testMoveServersAndTables();
381     assertTrue(observer.preMoveServersAndTables);
382     assertTrue(observer.postMoveServersAndTables);
383   }
384   @Test
385   public void testTableMoveTruncateAndDrop() throws Exception {
386     super.testTableMoveTruncateAndDrop();
387     assertTrue(observer.preMoveTablesCalled);
388     assertTrue(observer.postMoveTablesCalled);
389   }
390 
391   @Test
392   public void testRemoveServers() throws Exception {
393     super.testRemoveServers();
394     assertTrue(observer.preRemoveServersCalled);
395   }
396 
397   @Test
398   public void testGroupBalance() throws Exception {
399     super.testGroupBalance();
400     assertTrue(observer.preBalanceRSGroupCalled);
401     assertTrue(observer.postBalanceRSGroupCalled);
402   }
403 
404   @Test
405   public void testMisplacedRegions() throws Exception {
406     final TableName tableName = TableName.valueOf(tablePrefix+"_testMisplacedRegions");
407     LOG.info("testMisplacedRegions");
408 
409     final RSGroupInfo RSGroupInfo = addGroup(rsGroupAdmin, "testMisplacedRegions", 1);
410 
411     TEST_UTIL.createMultiRegionTable(tableName, new byte[]{'f'}, 15);
412     TEST_UTIL.waitUntilAllRegionsAssigned(tableName);
413 
414     RSGroupAdminEndpoint.getGroupInfoManager()
415         .moveTables(Sets.newHashSet(tableName), RSGroupInfo.getName());
416 
417     assertTrue(rsGroupAdmin.balanceRSGroup(RSGroupInfo.getName()));
418 
419     TEST_UTIL.waitFor(60000, new Predicate<Exception>() {
420       @Override
421       public boolean evaluate() throws Exception {
422         ServerName serverName =
423             ServerName.valueOf(RSGroupInfo.getServers().iterator().next().toString(), 1);
424         return admin.getConnection().getAdmin()
425             .getOnlineRegions(serverName).size() == 15;
426       }
427     });
428   }
429 
430   @Test
431   public void testRSGroupBalancerSwitch() throws IOException {
432     //Balancer is initially off in the test, set to true and check
433     assertFalse(admin.setBalancerRunning(true, true));
434     assertTrue(admin.isBalancerEnabled());
435     //Set balancer off and check if it actually turned off
436     assertTrue(admin.setBalancerRunning(false,true));
437     assertFalse(admin.isBalancerEnabled());
438   }
439 
440   @Test
441   public void testCloneSnapshot() throws Exception {
442     final TableName tableName = TableName.valueOf(tablePrefix+"_testCloneSnapshot");
443     LOG.info("testCloneSnapshot");
444 
445     byte[] FAMILY = Bytes.toBytes("test");
446     String snapshotName = tableName.getNameAsString() + "_snap";
447     TableName clonedTableName = TableName.valueOf(tableName.getNameAsString() + "_clone");
448 
449     // create base table
450     TEST_UTIL.createTable(tableName, FAMILY);
451 
452     // create snapshot
453     admin.snapshot(snapshotName, tableName);
454 
455     // clone
456     admin.cloneSnapshot(snapshotName, clonedTableName);
457   }
458 
459   @Test
460   public void testRSGroupListDoesNotContainFailedTableCreation() throws Exception {
461     toggleQuotaCheckAndRestartMiniCluster(true);
462     String nsp = "np1";
463     NamespaceDescriptor nspDesc =
464         NamespaceDescriptor.create(nsp).addConfiguration(TableNamespaceManager.KEY_MAX_REGIONS, "5")
465             .addConfiguration(TableNamespaceManager.KEY_MAX_TABLES, "2").build();
466     admin.createNamespace(nspDesc);
467     assertEquals(3, admin.listNamespaceDescriptors().length);
468     HColumnDescriptor fam1 = new HColumnDescriptor("fam1");
469     HTableDescriptor tableDescOne =
470         new HTableDescriptor(TableName.valueOf(nsp + TableName.NAMESPACE_DELIM + "table1"));
471     tableDescOne.addFamily(fam1);
472     admin.createTable(tableDescOne);
473 
474     HTableDescriptor tableDescTwo =
475         new HTableDescriptor(TableName.valueOf(nsp + TableName.NAMESPACE_DELIM + "table2"));
476     tableDescTwo.addFamily(fam1);
477     boolean constraintViolated = false;
478 
479     try {
480       admin.createTable(tableDescTwo, Bytes.toBytes("AAA"), Bytes.toBytes("ZZZ"),
481           6);
482       Assert.fail("Creation table should fail because of quota violation.");
483     } catch (Exception exp) {
484       assertTrue(exp instanceof IOException);
485       constraintViolated = true;
486     } finally {
487       assertTrue("Constraint not violated for table " + tableDescTwo.getTableName(),
488           constraintViolated);
489     }
490     List<RSGroupInfo> rsGroupInfoList = rsGroupAdmin.listRSGroups();
491     boolean foundTable2 = false;
492     boolean foundTable1 = false;
493     for (int i = 0; i < rsGroupInfoList.size(); i++){
494       if(rsGroupInfoList.get(i).getTables().contains(tableDescTwo.getTableName())){
495         foundTable2 = true;
496       }
497       if(rsGroupInfoList.get(i).getTables().contains(tableDescOne.getTableName())){
498         foundTable1 = true;
499       }
500     }
501     assertFalse("Found table2 in rsgroup list.", foundTable2);
502     assertTrue("Did not find table1 in rsgroup list", foundTable1);
503 
504     TEST_UTIL.deleteTable(tableDescOne.getTableName());
505     admin.deleteNamespace(nspDesc.getName());
506     toggleQuotaCheckAndRestartMiniCluster(false);
507 
508   }
509 
510   private void toggleQuotaCheckAndRestartMiniCluster(boolean enable) throws Exception {
511     TEST_UTIL.shutdownMiniCluster();
512     TEST_UTIL.getConfiguration().setBoolean(QuotaUtil.QUOTA_CONF_KEY, enable);
513     TEST_UTIL.startMiniCluster(NUM_SLAVES_BASE - 1);
514     TEST_UTIL.getConfiguration().setInt(
515         ServerManager.WAIT_ON_REGIONSERVERS_MINTOSTART,
516         NUM_SLAVES_BASE - 1);
517     TEST_UTIL.getConfiguration().setBoolean(SnapshotManager.HBASE_SNAPSHOT_ENABLED, true);
518     initialize();
519   }
520 
521 }