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