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 com.google.common.collect.Maps;
23  import com.google.common.collect.Sets;
24  
25  import java.io.IOException;
26  import java.util.HashSet;
27  import java.util.LinkedList;
28  import java.util.List;
29  import java.util.Map;
30  import java.util.Random;
31  import java.util.Set;
32  import java.util.TreeMap;
33  
34  import org.apache.commons.logging.Log;
35  import org.apache.commons.logging.LogFactory;
36  import org.apache.hadoop.hbase.ClusterStatus;
37  import org.apache.hadoop.hbase.HBaseCluster;
38  import org.apache.hadoop.hbase.HBaseTestingUtility;
39  import org.apache.hadoop.hbase.HConstants;
40  import org.apache.hadoop.hbase.HRegionInfo;
41  import org.apache.hadoop.hbase.HTableDescriptor;
42  import org.apache.hadoop.hbase.MiniHBaseCluster;
43  import org.apache.hadoop.hbase.NamespaceDescriptor;
44  import org.apache.hadoop.hbase.RegionLoad;
45  import org.apache.hadoop.hbase.ServerName;
46  import org.apache.hadoop.hbase.TableName;
47  import org.apache.hadoop.hbase.Waiter;
48  import org.apache.hadoop.hbase.client.HBaseAdmin;
49  import org.apache.hadoop.hbase.coprocessor.BaseMasterObserver;
50  import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
51  import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment;
52  import org.apache.hadoop.hbase.coprocessor.ObserverContext;
53  import org.apache.hadoop.hbase.master.HMaster;
54  import org.apache.hadoop.hbase.master.MasterCoprocessorHost;
55  import org.apache.hadoop.hbase.master.ServerManager;
56  import org.apache.hadoop.hbase.master.snapshot.SnapshotManager;
57  import org.apache.hadoop.hbase.net.Address;
58  import org.apache.hadoop.hbase.util.Bytes;
59  import org.apache.hadoop.hbase.util.JVMClusterUtil;
60  
61  public abstract class TestRSGroupsBase {
62    protected static final Log LOG = LogFactory.getLog(TestRSGroupsBase.class);
63  
64    //shared
65    protected final static String groupPrefix = "Group";
66    protected final static String tablePrefix = "Group";
67    protected final static Random rand = new Random();
68  
69    //shared, cluster type specific
70    protected static HBaseTestingUtility TEST_UTIL;
71    protected static HBaseAdmin admin;
72    protected static HBaseCluster cluster;
73    protected static RSGroupAdmin rsGroupAdmin;
74    protected static HMaster master;
75    protected static boolean init = false;
76    protected static RSGroupAdminEndpoint RSGroupAdminEndpoint;
77    protected static CPMasterObserver observer;
78  
79    public final static long WAIT_TIMEOUT = 60000*5;
80    public final static int NUM_SLAVES_BASE = 4; //number of slaves for the smallest cluster
81  
82    public static void setUpTestBeforeClass() throws Exception {
83      TEST_UTIL = new HBaseTestingUtility();
84      TEST_UTIL.getConfiguration().setFloat(
85              "hbase.master.balancer.stochastic.tableSkewCost", 6000);
86      TEST_UTIL.getConfiguration().set(
87          HConstants.HBASE_MASTER_LOADBALANCER_CLASS,
88          RSGroupBasedLoadBalancer.class.getName());
89      TEST_UTIL.getConfiguration().set(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY,
90          RSGroupAdminEndpoint.class.getName() + "," + CPMasterObserver.class.getName());
91      TEST_UTIL.getConfiguration().setBoolean(
92          HConstants.ZOOKEEPER_USEMULTI,
93          true);
94      TEST_UTIL.startMiniCluster(NUM_SLAVES_BASE - 1);
95      TEST_UTIL.getConfiguration().setInt(
96          ServerManager.WAIT_ON_REGIONSERVERS_MINTOSTART,
97          NUM_SLAVES_BASE - 1);
98      TEST_UTIL.getConfiguration().setBoolean(SnapshotManager.HBASE_SNAPSHOT_ENABLED, true);
99      initialize();
100   }
101 
102   protected static void initialize() throws Exception {
103     admin = TEST_UTIL.getHBaseAdmin();
104     cluster = TEST_UTIL.getHBaseCluster();
105     master = ((MiniHBaseCluster)cluster).getMaster();
106     master.balanceSwitch(true);
107 
108     //wait for balancer to come online
109     TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
110       @Override
111       public boolean evaluate() throws Exception {
112         return master.isInitialized() &&
113             ((RSGroupBasedLoadBalancer) master.getLoadBalancer()).isOnline();
114       }
115     });
116     admin.setBalancerRunning(false,true);
117     rsGroupAdmin = new VerifyingRSGroupAdminClient(new RSGroupAdminClient(TEST_UTIL.getConnection()),
118         TEST_UTIL.getConfiguration());
119     MasterCoprocessorHost host = master.getMasterCoprocessorHost();
120     observer = (CPMasterObserver) host.findCoprocessor(CPMasterObserver.class.getName());
121     RSGroupAdminEndpoint =
122         host.findCoprocessors(RSGroupAdminEndpoint.class).get(0);
123   }
124 
125   public static void tearDownAfterClass() throws Exception {
126     TEST_UTIL.shutdownMiniCluster();
127   }
128 
129   public void setUpBeforeMethod() throws Exception {
130     if(!init) {
131       init = true;
132       tearDownAfterMethod();
133     }
134     observer.resetFlags();
135   }
136 
137   public void tearDownAfterMethod() throws Exception {
138     deleteTableIfNecessary();
139     deleteNamespaceIfNecessary();
140     deleteGroups();
141 
142     int missing = NUM_SLAVES_BASE - getNumServers();
143     LOG.info("Restoring servers: "+missing);
144     for(int i=0; i<missing; i++) {
145       ((MiniHBaseCluster)cluster).startRegionServer();
146     }
147 
148     rsGroupAdmin.addRSGroup("master");
149     ServerName masterServerName =
150         ((MiniHBaseCluster)cluster).getMaster().getServerName();
151 
152     try {
153       rsGroupAdmin.moveServers(
154           Sets.newHashSet(masterServerName.getAddress()),
155           "master");
156     } catch (Exception ex) {
157       // ignore
158     }
159 
160     TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
161       @Override
162       public boolean evaluate() throws Exception {
163         LOG.info("Waiting for cleanup to finish " + rsGroupAdmin.listRSGroups());
164         //Might be greater since moving servers back to default
165         //is after starting a server
166 
167         return rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getServers().size()
168             == NUM_SLAVES_BASE;
169       }
170     });
171   }
172 
173   // return the real number of region servers, excluding the master embedded region server in 2.0+
174   protected int getNumServers() throws IOException {
175     ClusterStatus status = admin.getClusterStatus();
176     ServerName master = status.getMaster();
177     int count = 0;
178     for (ServerName sn : status.getServers()) {
179       if (!sn.equals(master)) {
180         count++;
181       }
182     }
183     return count;
184   }
185 
186   protected RSGroupInfo addGroup(RSGroupAdmin gAdmin, String groupName,
187                                  int serverCount) throws IOException, InterruptedException {
188     RSGroupInfo defaultInfo = gAdmin
189         .getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP);
190     gAdmin.addRSGroup(groupName);
191 
192     Set<Address> set = new HashSet<Address>();
193     for(Address server: defaultInfo.getServers()) {
194       if(set.size() == serverCount) {
195         break;
196       }
197       set.add(server);
198     }
199     gAdmin.moveServers(set, groupName);
200     RSGroupInfo result = gAdmin.getRSGroupInfo(groupName);
201     return result;
202   }
203 
204   static void removeGroup(RSGroupAdminClient groupAdmin, String groupName) throws IOException {
205     RSGroupInfo info = groupAdmin.getRSGroupInfo(groupName);
206     groupAdmin.moveTables(info.getTables(), RSGroupInfo.DEFAULT_GROUP);
207     groupAdmin.moveServers(info.getServers(), RSGroupInfo.DEFAULT_GROUP);
208     groupAdmin.removeRSGroup(groupName);
209   }
210 
211   protected void deleteTableIfNecessary() throws IOException {
212     for (HTableDescriptor desc : TEST_UTIL.getHBaseAdmin().listTables(tablePrefix+".*")) {
213       TEST_UTIL.deleteTable(desc.getTableName());
214     }
215   }
216 
217   protected void deleteNamespaceIfNecessary() throws IOException {
218     for (NamespaceDescriptor desc : TEST_UTIL.getHBaseAdmin().listNamespaceDescriptors()) {
219       if(desc.getName().startsWith(tablePrefix)) {
220         admin.deleteNamespace(desc.getName());
221       }
222     }
223   }
224 
225   protected void deleteGroups() throws IOException {
226     try (RSGroupAdmin groupAdmin = new RSGroupAdminClient(TEST_UTIL.getConnection())) {
227       for(RSGroupInfo group: groupAdmin.listRSGroups()) {
228         if(!group.getName().equals(RSGroupInfo.DEFAULT_GROUP)) {
229           groupAdmin.moveTables(group.getTables(), RSGroupInfo.DEFAULT_GROUP);
230           groupAdmin.moveServers(group.getServers(), RSGroupInfo.DEFAULT_GROUP);
231           groupAdmin.removeRSGroup(group.getName());
232         }
233       }
234     }
235   }
236 
237   public Map<TableName, List<String>> getTableRegionMap() throws IOException {
238     Map<TableName, List<String>> map = Maps.newTreeMap();
239     Map<TableName, Map<ServerName, List<String>>> tableServerRegionMap
240         = getTableServerRegionMap();
241     for(TableName tableName : tableServerRegionMap.keySet()) {
242       if(!map.containsKey(tableName)) {
243         map.put(tableName, new LinkedList<String>());
244       }
245       for(List<String> subset: tableServerRegionMap.get(tableName).values()) {
246         map.get(tableName).addAll(subset);
247       }
248     }
249     return map;
250   }
251 
252   public Map<TableName, Map<ServerName, List<String>>> getTableServerRegionMap()
253       throws IOException {
254     Map<TableName, Map<ServerName, List<String>>> map = Maps.newTreeMap();
255     ClusterStatus status = TEST_UTIL.getHBaseClusterInterface().getClusterStatus();
256     for(ServerName serverName : status.getServers()) {
257       for(RegionLoad rl : status.getLoad(serverName).getRegionsLoad().values()) {
258         TableName tableName = null;
259         try {
260           tableName = HRegionInfo.getTable(rl.getName());
261         } catch (IllegalArgumentException e) {
262           LOG.warn("Failed parse a table name from regionname=" +
263               Bytes.toStringBinary(rl.getName()));
264           continue;
265         }
266         if(!map.containsKey(tableName)) {
267           map.put(tableName, new TreeMap<ServerName, List<String>>());
268         }
269         if(!map.get(tableName).containsKey(serverName)) {
270           map.get(tableName).put(serverName, new LinkedList<String>());
271         }
272         map.get(tableName).get(serverName).add(rl.getNameAsString());
273       }
274     }
275     return map;
276   }
277 
278   protected String getGroupName(String baseName) {
279     return groupPrefix+"_"+baseName+"_"+rand.nextInt(Integer.MAX_VALUE);
280   }
281 
282   /**
283    * The server name in group does not contain the start code, this method will find out the start
284    * code and construct the ServerName object.
285    */
286   protected final ServerName getServerName(Address addr) {
287     for (JVMClusterUtil.RegionServerThread rsThread:
288       TEST_UTIL.getMiniHBaseCluster().getRegionServerThreads()) {
289       ServerName sn = rsThread.getRegionServer().getServerName();
290       if (sn.getAddress().equals(addr)) {
291         return sn;
292       }
293     }
294     return null;
295   }
296 
297   public static class CPMasterObserver extends BaseMasterObserver {
298     boolean preBalanceRSGroupCalled = false;
299     boolean postBalanceRSGroupCalled = false;
300     boolean preMoveServersCalled = false;
301     boolean postMoveServersCalled = false;
302     boolean preMoveTablesCalled = false;
303     boolean postMoveTablesCalled = false;
304     boolean preAddRSGroupCalled = false;
305     boolean postAddRSGroupCalled = false;
306     boolean preRemoveRSGroupCalled = false;
307     boolean postRemoveRSGroupCalled = false;
308     boolean preRemoveServersCalled = false;
309     boolean postRemoveServersCalled = false;
310     boolean preMoveServersAndTables = false;
311     boolean postMoveServersAndTables = false;
312     boolean preRenameRSGroupCalled = false;
313     boolean postRenameRSGroupCalled = false;
314 
315     public void resetFlags() {
316       preBalanceRSGroupCalled = false;
317       postBalanceRSGroupCalled = false;
318       preMoveServersCalled = false;
319       postMoveServersCalled = false;
320       preMoveTablesCalled = false;
321       postMoveTablesCalled = false;
322       preAddRSGroupCalled = false;
323       postAddRSGroupCalled = false;
324       preRemoveRSGroupCalled = false;
325       postRemoveRSGroupCalled = false;
326       preRemoveServersCalled = false;
327       postRemoveServersCalled = false;
328       preMoveServersAndTables = false;
329       postMoveServersAndTables = false;
330       preRenameRSGroupCalled = false;
331       postRenameRSGroupCalled = false;
332     }
333 
334     @Override
335     public void preMoveServersAndTables(final ObserverContext<MasterCoprocessorEnvironment> ctx,
336         Set<Address> servers, Set<TableName> tables, String targetGroup) throws IOException {
337       preMoveServersAndTables = true;
338     }
339 
340     @Override
341     public void postMoveServersAndTables(final ObserverContext<MasterCoprocessorEnvironment> ctx,
342         Set<Address> servers, Set<TableName> tables, String targetGroup) throws IOException {
343       postMoveServersAndTables = true;
344     }
345 
346     @Override
347     public void preRemoveServers(
348         final ObserverContext<MasterCoprocessorEnvironment> ctx,
349         Set<Address> servers) throws IOException {
350       preRemoveServersCalled = true;
351     }
352 
353     @Override
354     public void postRemoveServers(
355         final ObserverContext<MasterCoprocessorEnvironment> ctx,
356         Set<Address> servers) throws IOException {
357       postRemoveServersCalled = true;
358     }
359 
360     @Override
361     public void preRemoveRSGroup(final ObserverContext<MasterCoprocessorEnvironment> ctx,
362         String name) throws IOException {
363       preRemoveRSGroupCalled = true;
364     }
365 
366     @Override
367     public void postRemoveRSGroup(final ObserverContext<MasterCoprocessorEnvironment> ctx,
368         String name) throws IOException {
369       postRemoveRSGroupCalled = true;
370     }
371 
372     @Override
373     public void preAddRSGroup(final ObserverContext<MasterCoprocessorEnvironment> ctx,
374         String name) throws IOException {
375       preAddRSGroupCalled = true;
376     }
377 
378     @Override
379     public void postAddRSGroup(final ObserverContext<MasterCoprocessorEnvironment> ctx,
380         String name) throws IOException {
381       postAddRSGroupCalled = true;
382     }
383 
384     @Override
385     public void preMoveTables(final ObserverContext<MasterCoprocessorEnvironment> ctx,
386         Set<TableName> tables, String targetGroup) throws IOException {
387       preMoveTablesCalled = true;
388     }
389 
390     @Override
391     public void postMoveTables(final ObserverContext<MasterCoprocessorEnvironment> ctx,
392         Set<TableName> tables, String targetGroup) throws IOException {
393       postMoveTablesCalled = true;
394     }
395 
396     @Override
397     public void preMoveServers(final ObserverContext<MasterCoprocessorEnvironment> ctx,
398         Set<Address> servers, String targetGroup) throws IOException {
399       preMoveServersCalled = true;
400     }
401 
402     @Override
403     public void postMoveServers(final ObserverContext<MasterCoprocessorEnvironment> ctx,
404         Set<Address> servers, String targetGroup) throws IOException {
405       postMoveServersCalled = true;
406     }
407 
408     @Override
409     public void preBalanceRSGroup(final ObserverContext<MasterCoprocessorEnvironment> ctx,
410         String groupName) throws IOException {
411       preBalanceRSGroupCalled = true;
412     }
413 
414     @Override
415     public void postBalanceRSGroup(final ObserverContext<MasterCoprocessorEnvironment> ctx,
416         String groupName, boolean balancerRan) throws IOException {
417       postBalanceRSGroupCalled = true;
418     }
419 
420     @Override
421     public void preRenameRSGroup(ObserverContext<MasterCoprocessorEnvironment> ctx,
422                                  String oldName, String newName) throws IOException {
423       preRenameRSGroupCalled = true;
424     }
425 
426     @Override
427     public void postRenameRSGroup(ObserverContext<MasterCoprocessorEnvironment> ctx,
428                                   String oldName, String newName) throws IOException {
429       postRenameRSGroupCalled = true;
430     }
431   }
432 }