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