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