View Javadoc

1   /**
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  package org.apache.hadoop.hbase.util;
20  
21  import java.io.IOException;
22  import java.io.PrintWriter;
23  import java.lang.reflect.Constructor;
24  import java.lang.reflect.InvocationTargetException;
25  import java.util.List;
26  
27  import org.apache.commons.logging.Log;
28  import org.apache.commons.logging.LogFactory;
29  import org.apache.hadoop.classification.InterfaceAudience;
30  import org.apache.hadoop.conf.Configuration;
31  import org.apache.hadoop.fs.FileSystem;
32  import org.apache.hadoop.hbase.master.HMaster;
33  import org.apache.hadoop.hbase.regionserver.HRegionServer;
34  import org.apache.hadoop.hbase.regionserver.ShutdownHook;
35  import org.apache.hadoop.util.ReflectionUtils;
36  
37  /**
38   * Utility used running a cluster all in the one JVM.
39   */
40  @InterfaceAudience.Private
41  public class JVMClusterUtil {
42    private static final Log LOG = LogFactory.getLog(JVMClusterUtil.class);
43  
44    /**
45     * Datastructure to hold RegionServer Thread and RegionServer instance
46     */
47    public static class RegionServerThread extends Thread {
48      private final HRegionServer regionServer;
49  
50      public RegionServerThread(final HRegionServer r, final int index) {
51        super(r, "RS:" + index + ";" + r.getServerName().toShortString());
52        this.regionServer = r;
53      }
54  
55      /** @return the region server */
56      public HRegionServer getRegionServer() {
57        return this.regionServer;
58      }
59  
60      /**
61       * Block until the region server has come online, indicating it is ready
62       * to be used.
63       */
64      public void waitForServerOnline() {
65        // The server is marked online after the init method completes inside of
66        // the HRS#run method.  HRS#init can fail for whatever region.  In those
67        // cases, we'll jump out of the run without setting online flag.  Check
68        // stopRequested so we don't wait here a flag that will never be flipped.
69        regionServer.waitForServerOnline();
70      }
71    }
72  
73    /**
74     * Creates a {@link RegionServerThread}.
75     * Call 'start' on the returned thread to make it run.
76     * @param c Configuration to use.
77     * @param hrsc Class to create.
78     * @param index Used distinguishing the object returned.
79     * @throws IOException
80     * @return Region server added.
81     */
82    public static JVMClusterUtil.RegionServerThread createRegionServerThread(
83        final Configuration c, final Class<? extends HRegionServer> hrsc,
84        final int index)
85    throws IOException {
86      HRegionServer server;
87      try {
88        Constructor<? extends HRegionServer> ctor = hrsc.getConstructor(Configuration.class);
89        ctor.setAccessible(true);
90        server = ctor.newInstance(c);
91      } catch (InvocationTargetException ite) {
92        Throwable target = ite.getTargetException();
93        throw new RuntimeException("Failed construction of RegionServer: " +
94          hrsc.toString() + ((target.getCause() != null)?
95            target.getCause().getMessage(): ""), target);
96      } catch (Exception e) {
97        IOException ioe = new IOException();
98        ioe.initCause(e);
99        throw ioe;
100     }
101     return new JVMClusterUtil.RegionServerThread(server, index);
102   }
103 
104 
105   /**
106    * Datastructure to hold Master Thread and Master instance
107    */
108   public static class MasterThread extends Thread {
109     private final HMaster master;
110 
111     public MasterThread(final HMaster m, final int index) {
112       super(m, "M:" + index + ";" + m.getServerName().toShortString());
113       this.master = m;
114     }
115 
116     /** @return the master */
117     public HMaster getMaster() {
118       return this.master;
119     }
120   }
121 
122   /**
123    * Creates a {@link MasterThread}.
124    * Call 'start' on the returned thread to make it run.
125    * @param c Configuration to use.
126    * @param hmc Class to create.
127    * @param index Used distinguishing the object returned.
128    * @throws IOException
129    * @return Master added.
130    */
131   public static JVMClusterUtil.MasterThread createMasterThread(
132       final Configuration c, final Class<? extends HMaster> hmc,
133       final int index)
134   throws IOException {
135     HMaster server;
136     try {
137       server = hmc.getConstructor(Configuration.class).newInstance(c);
138     } catch (InvocationTargetException ite) {
139       Throwable target = ite.getTargetException();
140       throw new RuntimeException("Failed construction of Master: " +
141         hmc.toString() + ((target.getCause() != null)?
142           target.getCause().getMessage(): ""), target);
143     } catch (Exception e) {
144       IOException ioe = new IOException();
145       ioe.initCause(e);
146       throw ioe;
147     }
148     return new JVMClusterUtil.MasterThread(server, index);
149   }
150 
151   private static JVMClusterUtil.MasterThread findActiveMaster(
152     List<JVMClusterUtil.MasterThread> masters) {
153     for (JVMClusterUtil.MasterThread t : masters) {
154       if (t.master.isActiveMaster()) {
155         return t;
156       }
157     }
158 
159     return null;
160   }
161 
162   /**
163    * Start the cluster.  Waits until there is a primary master initialized
164    * and returns its address.
165    * @param masters
166    * @param regionservers
167    * @return Address to use contacting primary master.
168    */
169   public static String startup(final List<JVMClusterUtil.MasterThread> masters,
170       final List<JVMClusterUtil.RegionServerThread> regionservers) throws IOException {
171 
172     if (masters == null || masters.isEmpty()) {
173       return null;
174     }
175 
176     for (JVMClusterUtil.MasterThread t : masters) {
177       t.start();
178     }
179 
180     // Wait for an active master
181     //  having an active master before starting the region threads allows
182     //  then to succeed on their connection to master
183     long startTime = System.currentTimeMillis();
184     while (findActiveMaster(masters) == null) {
185       try {
186         Thread.sleep(100);
187       } catch (InterruptedException ignored) {
188       }
189       if (System.currentTimeMillis() > startTime + 30000) {
190         throw new RuntimeException("Master not active after 30 seconds");
191       }
192     }
193 
194     if (regionservers != null) {
195       for (JVMClusterUtil.RegionServerThread t: regionservers) {
196         HRegionServer hrs = t.getRegionServer();
197         ShutdownHook.install(hrs.getConfiguration(), FileSystem.get(hrs
198                 .getConfiguration()), hrs, t);
199         t.start();
200       }
201     }
202 
203     // Wait for an active master to be initialized (implies being master)
204     //  with this, when we return the cluster is complete
205     startTime = System.currentTimeMillis();
206     final int maxwait = 200000;
207     while (true) {
208       JVMClusterUtil.MasterThread t = findActiveMaster(masters);
209       if (t != null && t.master.isInitialized()) {
210         return t.master.getServerName().toString();
211       }
212       // REMOVE
213       if (System.currentTimeMillis() > startTime + 10000) {
214 
215         Threads.sleep(1000);
216       }
217       if (System.currentTimeMillis() > startTime + maxwait) {
218         String msg = "Master not initialized after " + maxwait + "ms seconds";
219         ReflectionUtils.printThreadInfo(new PrintWriter(System.out),
220           "Thread dump because: " + msg);
221         throw new RuntimeException(msg);
222       }
223       try {
224         Thread.sleep(100);
225       } catch (InterruptedException ignored) {
226         // Keep waiting
227       }
228     }
229   }
230 
231   /**
232    * @param masters
233    * @param regionservers
234    */
235   public static void shutdown(final List<MasterThread> masters,
236       final List<RegionServerThread> regionservers) {
237     LOG.debug("Shutting down HBase Cluster");
238     if (masters != null) {
239       // Do backups first.
240       JVMClusterUtil.MasterThread activeMaster = null;
241       for (JVMClusterUtil.MasterThread t : masters) {
242         if (!t.master.isActiveMaster()) {
243           t.master.stopMaster();
244         } else {
245           activeMaster = t;
246         }
247       }
248       // Do active after.
249       if (activeMaster != null)
250         activeMaster.master.shutdown();
251 
252     }
253     boolean wasInterrupted = false;
254     final long maxTime = System.currentTimeMillis() + 30 * 1000;
255     if (regionservers != null) {
256       // first try nicely.
257       for (RegionServerThread t : regionservers) {
258         t.getRegionServer().stop("Shutdown requested");
259       }
260       for (RegionServerThread t : regionservers) {
261         long now = System.currentTimeMillis();
262         if (t.isAlive() && !wasInterrupted && now < maxTime) {
263           try {
264             t.join(maxTime - now);
265           } catch (InterruptedException e) {
266             LOG.info("Got InterruptedException on shutdown - " +
267                 "not waiting anymore on region server ends", e);
268             wasInterrupted = true; // someone wants us to speed up.
269           }
270         }
271       }
272 
273       // Let's try to interrupt the remaining threads if any.
274       for (int i = 0; i < 100; ++i) {
275         boolean atLeastOneLiveServer = false;
276         for (RegionServerThread t : regionservers) {
277           if (t.isAlive()) {
278             atLeastOneLiveServer = true;
279             try {
280               LOG.warn("RegionServerThreads remaining, give one more chance before interrupting");
281               t.join(1000);
282             } catch (InterruptedException e) {
283               wasInterrupted = true;
284             }
285           }
286         }
287         if (!atLeastOneLiveServer) break;
288         for (RegionServerThread t : regionservers) {
289           if (t.isAlive()) {
290             LOG.warn("RegionServerThreads taking too long to stop, interrupting");
291             t.interrupt();
292           }
293         }
294       }
295     }
296 
297     if (masters != null) {
298       for (JVMClusterUtil.MasterThread t : masters) {
299         while (t.master.isAlive() && !wasInterrupted) {
300           try {
301             // The below has been replaced to debug sometime hangs on end of
302             // tests.
303             // this.master.join():
304             Threads.threadDumpingIsAlive(t.master.getThread());
305           } catch(InterruptedException e) {
306             LOG.info("Got InterruptedException on shutdown - " +
307                 "not waiting anymore on master ends", e);
308             wasInterrupted = true;
309           }
310         }
311       }
312     }
313     LOG.info("Shutdown of " +
314       ((masters != null) ? masters.size() : "0") + " master(s) and " +
315       ((regionservers != null) ? regionservers.size() : "0") +
316       " regionserver(s) " + (wasInterrupted ? "interrupted" : "complete"));
317 
318     if (wasInterrupted){
319       Thread.currentThread().interrupt();
320     }
321   }
322 }