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 
296     public void resetFlags() {
297       preBalanceRSGroupCalled = false;
298       postBalanceRSGroupCalled = false;
299       preMoveServersCalled = false;
300       postMoveServersCalled = false;
301       preMoveTablesCalled = false;
302       postMoveTablesCalled = false;
303       preAddRSGroupCalled = false;
304       postAddRSGroupCalled = false;
305       preRemoveRSGroupCalled = false;
306       postRemoveRSGroupCalled = false;
307       preRemoveServersCalled = false;
308       postRemoveServersCalled = false;
309       preMoveServersAndTables = false;
310       postMoveServersAndTables = false;
311     }
312 
313     @Override
314     public void preMoveServersAndTables(final ObserverContext<MasterCoprocessorEnvironment> ctx,
315         Set<Address> servers, Set<TableName> tables, String targetGroup) throws IOException {
316       preMoveServersAndTables = true;
317     }
318 
319     @Override
320     public void postMoveServersAndTables(final ObserverContext<MasterCoprocessorEnvironment> ctx,
321         Set<Address> servers, Set<TableName> tables, String targetGroup) throws IOException {
322       postMoveServersAndTables = true;
323     }
324 
325     @Override
326     public void preRemoveServers(
327         final ObserverContext<MasterCoprocessorEnvironment> ctx,
328         Set<Address> servers) throws IOException {
329       preRemoveServersCalled = true;
330     }
331 
332     @Override
333     public void postRemoveServers(
334         final ObserverContext<MasterCoprocessorEnvironment> ctx,
335         Set<Address> servers) throws IOException {
336       postRemoveServersCalled = true;
337     }
338 
339     @Override
340     public void preRemoveRSGroup(final ObserverContext<MasterCoprocessorEnvironment> ctx,
341         String name) throws IOException {
342       preRemoveRSGroupCalled = true;
343     }
344 
345     @Override
346     public void postRemoveRSGroup(final ObserverContext<MasterCoprocessorEnvironment> ctx,
347         String name) throws IOException {
348       postRemoveRSGroupCalled = true;
349     }
350 
351     @Override
352     public void preAddRSGroup(final ObserverContext<MasterCoprocessorEnvironment> ctx,
353         String name) throws IOException {
354       preAddRSGroupCalled = true;
355     }
356 
357     @Override
358     public void postAddRSGroup(final ObserverContext<MasterCoprocessorEnvironment> ctx,
359         String name) throws IOException {
360       postAddRSGroupCalled = true;
361     }
362 
363     @Override
364     public void preMoveTables(final ObserverContext<MasterCoprocessorEnvironment> ctx,
365         Set<TableName> tables, String targetGroup) throws IOException {
366       preMoveTablesCalled = true;
367     }
368 
369     @Override
370     public void postMoveTables(final ObserverContext<MasterCoprocessorEnvironment> ctx,
371         Set<TableName> tables, String targetGroup) throws IOException {
372       postMoveTablesCalled = true;
373     }
374 
375     @Override
376     public void preMoveServers(final ObserverContext<MasterCoprocessorEnvironment> ctx,
377         Set<Address> servers, String targetGroup) throws IOException {
378       preMoveServersCalled = true;
379     }
380 
381     @Override
382     public void postMoveServers(final ObserverContext<MasterCoprocessorEnvironment> ctx,
383         Set<Address> servers, String targetGroup) throws IOException {
384       postMoveServersCalled = true;
385     }
386 
387     @Override
388     public void preBalanceRSGroup(final ObserverContext<MasterCoprocessorEnvironment> ctx,
389         String groupName) throws IOException {
390       preBalanceRSGroupCalled = true;
391     }
392 
393     @Override
394     public void postBalanceRSGroup(final ObserverContext<MasterCoprocessorEnvironment> ctx,
395         String groupName, boolean balancerRan) throws IOException {
396       postBalanceRSGroupCalled = true;
397     }
398   }
399 }