View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.hadoop.hbase.zookeeper;
19  
20  import java.io.EOFException;
21  import java.io.IOException;
22  import java.net.ConnectException;
23  import java.net.NoRouteToHostException;
24  import java.net.SocketException;
25  import java.net.SocketTimeoutException;
26  import java.net.UnknownHostException;
27  import java.util.ArrayList;
28  import java.util.List;
29  
30  import org.apache.commons.logging.Log;
31  import org.apache.commons.logging.LogFactory;
32  import org.apache.hadoop.conf.Configuration;
33  import org.apache.hadoop.hbase.HConstants;
34  import org.apache.hadoop.hbase.HRegionInfo;
35  import org.apache.hadoop.hbase.NotAllMetaRegionsOnlineException;
36  import org.apache.hadoop.hbase.ServerName;
37  import org.apache.hadoop.hbase.classification.InterfaceAudience;
38  import org.apache.hadoop.hbase.client.ClusterConnection;
39  import org.apache.hadoop.hbase.client.Connection;
40  import org.apache.hadoop.hbase.client.HConnection;
41  import org.apache.hadoop.hbase.client.RegionReplicaUtil;
42  import org.apache.hadoop.hbase.client.RetriesExhaustedException;
43  import org.apache.hadoop.hbase.exceptions.DeserializationException;
44  import org.apache.hadoop.hbase.ipc.FailedServerException;
45  import org.apache.hadoop.hbase.ipc.PayloadCarryingRpcController;
46  import org.apache.hadoop.hbase.ipc.ServerNotRunningYetException;
47  import org.apache.hadoop.hbase.master.RegionState;
48  import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
49  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos;
50  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.AdminService;
51  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos;
52  import org.apache.hadoop.hbase.protobuf.generated.ZooKeeperProtos;
53  import org.apache.hadoop.hbase.protobuf.generated.ZooKeeperProtos.MetaRegionServer;
54  import org.apache.hadoop.hbase.regionserver.RegionServerStoppedException;
55  import org.apache.hadoop.hbase.util.Bytes;
56  import org.apache.hadoop.hbase.util.Pair;
57  import org.apache.hadoop.ipc.RemoteException;
58  import org.apache.zookeeper.KeeperException;
59  
60  import com.google.protobuf.InvalidProtocolBufferException;
61  
62  /**
63   * Utility class to perform operation (get/wait for/verify/set/delete) on znode in ZooKeeper
64   * which keeps hbase:meta region server location.
65   *
66   * Stateless class with a bunch of static methods. Doesn't manage resources passed in
67   * (e.g. HConnection, ZooKeeperWatcher etc).
68   *
69   * Meta region location is set by <code>RegionServerServices</code>.
70   * This class doesn't use ZK watchers, rather accesses ZK directly.
71   *
72   * This class it stateless. The only reason it's not made a non-instantiable util class
73   * with a collection of static methods is that it'd be rather hard to mock properly in tests.
74   *
75   * TODO: rewrite using RPC calls to master to find out about hbase:meta.
76   */
77  @InterfaceAudience.Private
78  public class MetaTableLocator {
79    private static final Log LOG = LogFactory.getLog(MetaTableLocator.class);
80  
81    // only needed to allow non-timeout infinite waits to stop when cluster shuts down
82    private volatile boolean stopped = false;
83  
84    /**
85     * Checks if the meta region location is available.
86     * @return true if meta region location is available, false if not
87     */
88    public boolean isLocationAvailable(ZooKeeperWatcher zkw) {
89      return getMetaRegionLocation(zkw) != null;
90    }
91  
92    /**
93     * @param zkw ZooKeeper watcher to be used
94     * @return meta table regions and their locations.
95     */
96    public List<Pair<HRegionInfo, ServerName>> getMetaRegionsAndLocations(ZooKeeperWatcher zkw) {
97      return getMetaRegionsAndLocations(zkw, HRegionInfo.DEFAULT_REPLICA_ID);
98    }
99  
100   /**
101    * 
102    * @param zkw
103    * @param replicaId
104    * @return meta table regions and their locations.
105    */
106   public List<Pair<HRegionInfo, ServerName>> getMetaRegionsAndLocations(ZooKeeperWatcher zkw,
107       int replicaId) {
108     ServerName serverName = getMetaRegionLocation(zkw, replicaId);
109     List<Pair<HRegionInfo, ServerName>> list = new ArrayList<Pair<HRegionInfo, ServerName>>();
110     list.add(new Pair<HRegionInfo, ServerName>(RegionReplicaUtil.getRegionInfoForReplica(
111         HRegionInfo.FIRST_META_REGIONINFO, replicaId), serverName));
112     return list;
113   }
114 
115   /**
116    * @param zkw ZooKeeper watcher to be used
117    * @return List of meta regions
118    */
119   public List<HRegionInfo> getMetaRegions(ZooKeeperWatcher zkw) {
120     return getMetaRegions(zkw, HRegionInfo.DEFAULT_REPLICA_ID);
121   }
122 
123   /**
124    * 
125    * @param zkw
126    * @param replicaId
127    * @return List of meta regions
128    */
129   public List<HRegionInfo> getMetaRegions(ZooKeeperWatcher zkw, int replicaId) {
130     List<Pair<HRegionInfo, ServerName>> result;
131     result = getMetaRegionsAndLocations(zkw, replicaId);
132     return getListOfHRegionInfos(result);
133   }
134 
135   private List<HRegionInfo> getListOfHRegionInfos(
136       final List<Pair<HRegionInfo, ServerName>> pairs) {
137     if (pairs == null || pairs.isEmpty()) return null;
138     List<HRegionInfo> result = new ArrayList<HRegionInfo>(pairs.size());
139     for (Pair<HRegionInfo, ServerName> pair: pairs) {
140       result.add(pair.getFirst());
141     }
142     return result;
143   }
144 
145   /**
146    * Gets the meta region location, if available.  Does not block.
147    * @param zkw zookeeper connection to use
148    * @return server name or null if we failed to get the data.
149    */
150   public ServerName getMetaRegionLocation(final ZooKeeperWatcher zkw) {
151     try {
152       RegionState state = getMetaRegionState(zkw);
153       return state.isOpened() ? state.getServerName() : null;
154     } catch (KeeperException ke) {
155       return null;
156     }
157   }
158 
159   /**
160    * Gets the meta region location, if available.  Does not block.
161    * @param zkw
162    * @param replicaId
163    * @return server name
164    */
165   public ServerName getMetaRegionLocation(final ZooKeeperWatcher zkw, int replicaId) {
166     try {
167       RegionState state = getMetaRegionState(zkw, replicaId);
168       return state.isOpened() ? state.getServerName() : null;
169     } catch (KeeperException ke) {
170       return null;
171     }
172   }
173 
174   /**
175    * Gets the meta region location, if available, and waits for up to the
176    * specified timeout if not immediately available.
177    * Given the zookeeper notification could be delayed, we will try to
178    * get the latest data.
179    * @param zkw
180    * @param timeout maximum time to wait, in millis
181    * @return server name for server hosting meta region formatted as per
182    * {@link ServerName}, or null if none available
183    * @throws InterruptedException if interrupted while waiting
184    * @throws NotAllMetaRegionsOnlineException
185    */
186   public ServerName waitMetaRegionLocation(ZooKeeperWatcher zkw, long timeout)
187   throws InterruptedException, NotAllMetaRegionsOnlineException {
188     return waitMetaRegionLocation(zkw, HRegionInfo.DEFAULT_REPLICA_ID, timeout);
189   }
190 
191   /**
192    * Gets the meta region location, if available, and waits for up to the
193    * specified timeout if not immediately available.
194    * Given the zookeeper notification could be delayed, we will try to
195    * get the latest data.
196    * @param zkw
197    * @param replicaId
198    * @param timeout maximum time to wait, in millis
199    * @return server name for server hosting meta region formatted as per
200    * {@link ServerName}, or null if none available
201    * @throws InterruptedException
202    * @throws NotAllMetaRegionsOnlineException
203    */
204   public ServerName waitMetaRegionLocation(ZooKeeperWatcher zkw, int replicaId, long timeout)
205   throws InterruptedException, NotAllMetaRegionsOnlineException {
206     try {
207       if (ZKUtil.checkExists(zkw, zkw.baseZNode) == -1) {
208         String errorMsg = "Check the value configured in 'zookeeper.znode.parent'. "
209             + "There could be a mismatch with the one configured in the master.";
210         LOG.error(errorMsg);
211         throw new IllegalArgumentException(errorMsg);
212       }
213     } catch (KeeperException e) {
214       throw new IllegalStateException("KeeperException while trying to check baseZNode:", e);
215     }
216     ServerName sn = blockUntilAvailable(zkw, replicaId, timeout);
217 
218     if (sn == null) {
219       throw new NotAllMetaRegionsOnlineException("Timed out; " + timeout + "ms");
220     }
221 
222     return sn;
223   }
224 
225   /**
226    * Waits indefinitely for availability of <code>hbase:meta</code>.  Used during
227    * cluster startup.  Does not verify meta, just that something has been
228    * set up in zk.
229    * @see #waitMetaRegionLocation(org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher, long)
230    * @throws InterruptedException if interrupted while waiting
231    */
232   public void waitMetaRegionLocation(ZooKeeperWatcher zkw) throws InterruptedException {
233     long startTime = System.currentTimeMillis();
234     while (!stopped) {
235       try {
236         if (waitMetaRegionLocation(zkw, 100) != null) break;
237         long sleepTime = System.currentTimeMillis() - startTime;
238         // +1 in case sleepTime=0
239         if ((sleepTime + 1) % 10000 == 0) {
240           LOG.warn("Have been waiting for meta to be assigned for " + sleepTime + "ms");
241         }
242       } catch (NotAllMetaRegionsOnlineException e) {
243         if (LOG.isTraceEnabled()) {
244           LOG.trace("hbase:meta still not available, sleeping and retrying." +
245             " Reason: " + e.getMessage());
246         }
247       }
248     }
249   }
250 
251   /**
252    * Verify <code>hbase:meta</code> is deployed and accessible.
253    * @param hConnection
254    * @param zkw
255    * @param timeout How long to wait on zk for meta address (passed through to
256    * the internal call to {@link #getMetaServerConnection}.
257    * @return True if the <code>hbase:meta</code> location is healthy.
258    * @throws java.io.IOException
259    * @throws InterruptedException
260    */
261   public boolean verifyMetaRegionLocation(HConnection hConnection,
262       ZooKeeperWatcher zkw, final long timeout)
263   throws InterruptedException, IOException {
264     return verifyMetaRegionLocation(hConnection, zkw, timeout, HRegionInfo.DEFAULT_REPLICA_ID);
265   }
266 
267   /**
268    * Verify <code>hbase:meta</code> is deployed and accessible.
269    * @param hConnection
270    * @param zkw
271    * @param timeout How long to wait on zk for meta address (passed through to
272    * @param replicaId
273    * @return True if the <code>hbase:meta</code> location is healthy.
274    * @throws InterruptedException
275    * @throws IOException
276    */
277   public boolean verifyMetaRegionLocation(HConnection hConnection,
278       ZooKeeperWatcher zkw, final long timeout, int replicaId)
279   throws InterruptedException, IOException {
280     AdminProtos.AdminService.BlockingInterface service = null;
281     try {
282       service = getMetaServerConnection(hConnection, zkw, timeout, replicaId);
283     } catch (NotAllMetaRegionsOnlineException e) {
284       // Pass
285     } catch (ServerNotRunningYetException e) {
286       // Pass -- remote server is not up so can't be carrying root
287     } catch (UnknownHostException e) {
288       // Pass -- server name doesn't resolve so it can't be assigned anything.
289     } catch (RegionServerStoppedException e) {
290       // Pass -- server name sends us to a server that is dying or already dead.
291     }
292     return (service != null) && verifyRegionLocation(hConnection, service,
293             getMetaRegionLocation(zkw, replicaId), RegionReplicaUtil.getRegionInfoForReplica(
294                 HRegionInfo.FIRST_META_REGIONINFO, replicaId).getRegionName());
295   }
296 
297   /**
298    * Verify we can connect to <code>hostingServer</code> and that its carrying
299    * <code>regionName</code>.
300    * @param hostingServer Interface to the server hosting <code>regionName</code>
301    * @param address The servername that goes with the <code>metaServer</code>
302    * Interface.  Used logging.
303    * @param regionName The regionname we are interested in.
304    * @return True if we were able to verify the region located at other side of
305    * the Interface.
306    * @throws IOException
307    */
308   // TODO: We should be able to get the ServerName from the AdminProtocol
309   // rather than have to pass it in.  Its made awkward by the fact that the
310   // HRI is likely a proxy against remote server so the getServerName needs
311   // to be fixed to go to a local method or to a cache before we can do this.
312   private boolean verifyRegionLocation(final Connection connection,
313       AdminService.BlockingInterface hostingServer, final ServerName address,
314       final byte [] regionName)
315   throws IOException {
316     if (hostingServer == null) {
317       LOG.info("Passed hostingServer is null");
318       return false;
319     }
320     Throwable t;
321     PayloadCarryingRpcController controller = null;
322     if (connection instanceof ClusterConnection) {
323       controller = ((ClusterConnection) connection).getRpcControllerFactory().newController();
324     }
325     try {
326       // Try and get regioninfo from the hosting server.
327       return ProtobufUtil.getRegionInfo(controller, hostingServer, regionName) != null;
328     } catch (ConnectException e) {
329       t = e;
330     } catch (RetriesExhaustedException e) {
331       t = e;
332     } catch (RemoteException e) {
333       IOException ioe = e.unwrapRemoteException();
334       t = ioe;
335     } catch (IOException e) {
336       Throwable cause = e.getCause();
337       if (cause != null && cause instanceof EOFException) {
338         t = cause;
339       } else if (cause != null && cause.getMessage() != null
340           && cause.getMessage().contains("Connection reset")) {
341         t = cause;
342       } else {
343         t = e;
344       }
345     }
346     LOG.info("Failed verification of " + Bytes.toStringBinary(regionName) +
347       " at address=" + address + ", exception=" + t.getMessage());
348     return false;
349   }
350 
351   /**
352    * Gets a connection to the server hosting meta, as reported by ZooKeeper,
353    * waiting up to the specified timeout for availability.
354    * <p>WARNING: Does not retry.  Use an {@link org.apache.hadoop.hbase.client.HTable} instead.
355    * @param hConnection
356    * @param zkw
357    * @param timeout How long to wait on meta location
358    * @param replicaId
359    * @return connection to server hosting meta
360    * @throws InterruptedException
361    * @throws NotAllMetaRegionsOnlineException if timed out waiting
362    * @throws IOException
363    */
364   private AdminService.BlockingInterface getMetaServerConnection(HConnection hConnection,
365       ZooKeeperWatcher zkw, long timeout, int replicaId)
366   throws InterruptedException, NotAllMetaRegionsOnlineException, IOException {
367     return getCachedConnection(hConnection, waitMetaRegionLocation(zkw, replicaId, timeout));
368   }
369 
370   /**
371    * @param sn ServerName to get a connection against.
372    * @return The AdminProtocol we got when we connected to <code>sn</code>
373    * May have come from cache, may not be good, may have been setup by this
374    * invocation, or may be null.
375    * @throws IOException
376    */
377   @SuppressWarnings("deprecation")
378   private static AdminService.BlockingInterface getCachedConnection(HConnection hConnection,
379     ServerName sn)
380   throws IOException {
381     if (sn == null) {
382       return null;
383     }
384     AdminService.BlockingInterface service = null;
385     try {
386       service = hConnection.getAdmin(sn);
387     } catch (RetriesExhaustedException e) {
388       if (e.getCause() != null && e.getCause() instanceof ConnectException) {
389         // Catch this; presume it means the cached connection has gone bad.
390       } else {
391         throw e;
392       }
393     } catch (SocketTimeoutException e) {
394       LOG.debug("Timed out connecting to " + sn);
395     } catch (NoRouteToHostException e) {
396       LOG.debug("Connecting to " + sn, e);
397     } catch (SocketException e) {
398       LOG.debug("Exception connecting to " + sn);
399     } catch (UnknownHostException e) {
400       LOG.debug("Unknown host exception connecting to  " + sn);
401     } catch (FailedServerException e) {
402       if (LOG.isDebugEnabled()) {
403         LOG.debug("Server " + sn + " is in failed server list.");
404       }
405     } catch (IOException ioe) {
406       Throwable cause = ioe.getCause();
407       if (ioe instanceof ConnectException) {
408         // Catch. Connect refused.
409       } else if (cause != null && cause instanceof EOFException) {
410         // Catch. Other end disconnected us.
411       } else if (cause != null && cause.getMessage() != null &&
412         cause.getMessage().toLowerCase().contains("connection reset")) {
413         // Catch. Connection reset.
414       } else {
415         throw ioe;
416       }
417 
418     }
419     return service;
420   }
421 
422   /**
423    * Sets the location of <code>hbase:meta</code> in ZooKeeper to the
424    * specified server address.
425    * @param zookeeper zookeeper reference
426    * @param serverName The server hosting <code>hbase:meta</code>
427    * @param state The region transition state
428    * @throws KeeperException unexpected zookeeper exception
429    */
430   public static void setMetaLocation(ZooKeeperWatcher zookeeper,
431       ServerName serverName, RegionState.State state) throws KeeperException {
432     setMetaLocation(zookeeper, serverName, HRegionInfo.DEFAULT_REPLICA_ID, state);
433   }
434 
435   /**
436    * Sets the location of <code>hbase:meta</code> in ZooKeeper to the
437    * specified server address.
438    * @param zookeeper
439    * @param serverName
440    * @param replicaId
441    * @param state
442    * @throws KeeperException
443    */
444   public static void setMetaLocation(ZooKeeperWatcher zookeeper,
445       ServerName serverName, int replicaId, RegionState.State state) throws KeeperException {
446     LOG.info("Setting hbase:meta region location in ZooKeeper as " + serverName);
447     // Make the MetaRegionServer pb and then get its bytes and save this as
448     // the znode content.
449     MetaRegionServer pbrsr = MetaRegionServer.newBuilder()
450       .setServer(ProtobufUtil.toServerName(serverName))
451       .setRpcVersion(HConstants.RPC_CURRENT_VERSION)
452       .setState(state.convert()).build();
453     byte[] data = ProtobufUtil.prependPBMagic(pbrsr.toByteArray());
454     try {
455       ZKUtil.setData(zookeeper, zookeeper.getZNodeForReplica(replicaId), data);
456     } catch(KeeperException.NoNodeException nne) {
457       if (replicaId == HRegionInfo.DEFAULT_REPLICA_ID) {
458         LOG.debug("META region location doesn't exist, create it");
459       } else {
460         LOG.debug("META region location doesn't exist for replicaId " + replicaId +
461             ", create it");
462       }
463       ZKUtil.createAndWatch(zookeeper, zookeeper.getZNodeForReplica(replicaId), data);
464     }
465   }
466 
467   /**
468    * Load the meta region state from the meta server ZNode.
469    */
470   public static RegionState getMetaRegionState(ZooKeeperWatcher zkw) throws KeeperException {
471     return getMetaRegionState(zkw, HRegionInfo.DEFAULT_REPLICA_ID);
472   }
473 
474   /**
475    * Load the meta region state from the meta server ZNode.
476    * @param zkw
477    * @param replicaId
478    * @return regionstate
479    * @throws KeeperException
480    */
481   public static RegionState getMetaRegionState(ZooKeeperWatcher zkw, int replicaId)
482       throws KeeperException {
483     RegionState.State state = RegionState.State.OPEN;
484     ServerName serverName = null;
485     try {
486       byte[] data = ZKUtil.getData(zkw, zkw.getZNodeForReplica(replicaId));
487       if (data != null && data.length > 0 && ProtobufUtil.isPBMagicPrefix(data)) {
488         try {
489           int prefixLen = ProtobufUtil.lengthOfPBMagic();
490           ZooKeeperProtos.MetaRegionServer rl =
491             ZooKeeperProtos.MetaRegionServer.PARSER.parseFrom
492               (data, prefixLen, data.length - prefixLen);
493           if (rl.hasState()) {
494             state = RegionState.State.convert(rl.getState());
495           }
496           HBaseProtos.ServerName sn = rl.getServer();
497           serverName = ServerName.valueOf(
498             sn.getHostName(), sn.getPort(), sn.getStartCode());
499         } catch (InvalidProtocolBufferException e) {
500           throw new DeserializationException("Unable to parse meta region location");
501         }
502       } else {
503         // old style of meta region location?
504         serverName = ServerName.parseFrom(data);
505       }
506     } catch (DeserializationException e) {
507       throw ZKUtil.convert(e);
508     } catch (InterruptedException e) {
509       Thread.currentThread().interrupt();
510     }
511     if (serverName == null) {
512       state = RegionState.State.OFFLINE;
513     }
514     return new RegionState(
515         RegionReplicaUtil.getRegionInfoForReplica(HRegionInfo.FIRST_META_REGIONINFO, replicaId),
516       state, serverName);
517   }
518 
519   /**
520    * Deletes the location of <code>hbase:meta</code> in ZooKeeper.
521    * @param zookeeper zookeeper reference
522    * @throws KeeperException unexpected zookeeper exception
523    */
524   public void deleteMetaLocation(ZooKeeperWatcher zookeeper)
525   throws KeeperException {
526     deleteMetaLocation(zookeeper, HRegionInfo.DEFAULT_REPLICA_ID);
527   }
528 
529   public void deleteMetaLocation(ZooKeeperWatcher zookeeper, int replicaId)
530   throws KeeperException {
531     if (replicaId == HRegionInfo.DEFAULT_REPLICA_ID) {
532       LOG.info("Deleting hbase:meta region location in ZooKeeper");
533     } else {
534       LOG.info("Deleting hbase:meta for " + replicaId + " region location in ZooKeeper");
535     }
536     try {
537       // Just delete the node.  Don't need any watches.
538       ZKUtil.deleteNode(zookeeper, zookeeper.getZNodeForReplica(replicaId));
539     } catch(KeeperException.NoNodeException nne) {
540       // Has already been deleted
541     }
542   }
543   /**
544    * Wait until the primary meta region is available. Get the secondary
545    * locations as well but don't block for those.
546    * @param zkw
547    * @param timeout
548    * @param conf
549    * @return ServerName or null if we timed out.
550    * @throws InterruptedException
551    */
552   public List<ServerName> blockUntilAvailable(final ZooKeeperWatcher zkw,
553       final long timeout, Configuration conf)
554           throws InterruptedException {
555     int numReplicasConfigured = 1;
556 
557     List<ServerName> servers = new ArrayList<ServerName>();
558     // Make the blocking call first so that we do the wait to know
559     // the znodes are all in place or timeout.
560     ServerName server = blockUntilAvailable(zkw, timeout);
561     if (server == null) return null;
562     servers.add(server);
563 
564     try {
565       List<String> metaReplicaNodes = zkw.getMetaReplicaNodes();
566       numReplicasConfigured = metaReplicaNodes.size();
567     } catch (KeeperException e) {
568       LOG.warn("Got ZK exception " + e);
569     }
570     for (int replicaId = 1; replicaId < numReplicasConfigured; replicaId++) {
571       // return all replica locations for the meta
572       servers.add(getMetaRegionLocation(zkw, replicaId));
573     }
574     return servers;
575   }
576 
577   /**
578    * Wait until the meta region is available and is not in transition.
579    * @param zkw zookeeper connection to use
580    * @param timeout maximum time to wait, in millis
581    * @return ServerName or null if we timed out.
582    * @throws InterruptedException
583    */
584   public ServerName blockUntilAvailable(final ZooKeeperWatcher zkw,
585       final long timeout)
586   throws InterruptedException {
587     return blockUntilAvailable(zkw, HRegionInfo.DEFAULT_REPLICA_ID, timeout);
588   }
589 
590   /**
591    * Wait until the meta region is available and is not in transition.
592    * @param zkw
593    * @param replicaId
594    * @param timeout
595    * @return ServerName or null if we timed out.
596    * @throws InterruptedException
597    */
598   public ServerName blockUntilAvailable(final ZooKeeperWatcher zkw, int replicaId,
599       final long timeout)
600   throws InterruptedException {
601     if (timeout < 0) throw new IllegalArgumentException();
602     if (zkw == null) throw new IllegalArgumentException();
603     long startTime = System.currentTimeMillis();
604     ServerName sn = null;
605     while (true) {
606       sn = getMetaRegionLocation(zkw, replicaId);
607       if (sn != null || (System.currentTimeMillis() - startTime)
608           > timeout - HConstants.SOCKET_RETRY_WAIT_MS) {
609         break;
610       }
611       Thread.sleep(HConstants.SOCKET_RETRY_WAIT_MS);
612     }
613     return sn;
614   }
615 
616   /**
617    * Stop working.
618    * Interrupts any ongoing waits.
619    */
620   public void stop() {
621     if (!stopped) {
622       LOG.debug("Stopping MetaTableLocator");
623       stopped = true;
624     }
625   }
626 }