001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.zookeeper;
019
020import java.util.ArrayList;
021import java.util.Collections;
022import java.util.List;
023import org.apache.hadoop.conf.Configuration;
024import org.apache.hadoop.hbase.HConstants;
025import org.apache.hadoop.hbase.NotAllMetaRegionsOnlineException;
026import org.apache.hadoop.hbase.ServerName;
027import org.apache.hadoop.hbase.client.RegionInfo;
028import org.apache.hadoop.hbase.client.RegionInfoBuilder;
029import org.apache.hadoop.hbase.client.RegionReplicaUtil;
030import org.apache.hadoop.hbase.exceptions.DeserializationException;
031import org.apache.hadoop.hbase.master.RegionState;
032import org.apache.hadoop.hbase.util.Pair;
033import org.apache.yetus.audience.InterfaceAudience;
034import org.apache.zookeeper.KeeperException;
035import org.slf4j.Logger;
036import org.slf4j.LoggerFactory;
037
038import org.apache.hbase.thirdparty.com.google.protobuf.InvalidProtocolBufferException;
039
040import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
041import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos;
042import org.apache.hadoop.hbase.shaded.protobuf.generated.ZooKeeperProtos;
043import org.apache.hadoop.hbase.shaded.protobuf.generated.ZooKeeperProtos.MetaRegionServer;
044
045/**
046 * Utility class to perform operation (get/wait for/verify/set/delete) on znode in ZooKeeper which
047 * keeps hbase:meta region server location.
048 * <p/>
049 * Stateless class with a bunch of static methods. Doesn't manage resources passed in (e.g.
050 * Connection, ZKWatcher etc).
051 * <p/>
052 * Meta region location is set by <code>RegionServerServices</code>. This class doesn't use ZK
053 * watchers, rather accesses ZK directly.
054 * <p/>
055 * TODO: rewrite using RPC calls to master to find out about hbase:meta.
056 */
057@InterfaceAudience.Private
058public final class MetaTableLocator {
059  private static final Logger LOG = LoggerFactory.getLogger(MetaTableLocator.class);
060
061  private MetaTableLocator() {
062  }
063
064  /**
065   * Checks if the meta region location is available.
066   * @return true if meta region location is available, false if not
067   */
068  public static boolean isLocationAvailable(ZKWatcher zkw) {
069    return getMetaRegionLocation(zkw) != null;
070  }
071
072  /**
073   * @param zkw ZooKeeper watcher to be used
074   * @return meta table regions and their locations.
075   */
076  public static List<Pair<RegionInfo, ServerName>> getMetaRegionsAndLocations(ZKWatcher zkw) {
077    return getMetaRegionsAndLocations(zkw, RegionInfo.DEFAULT_REPLICA_ID);
078  }
079
080  /**
081   * Gets the meta regions and their locations for the given path and replica ID.
082   *
083   * @param zkw reference to the {@link ZKWatcher} which also contains configuration and operation
084   * @param replicaId the ID of the replica
085   * @return meta table regions and their locations.
086   */
087  public static List<Pair<RegionInfo, ServerName>> getMetaRegionsAndLocations(ZKWatcher zkw,
088      int replicaId) {
089    ServerName serverName = getMetaRegionLocation(zkw, replicaId);
090    List<Pair<RegionInfo, ServerName>> list = new ArrayList<>(1);
091    list.add(new Pair<>(RegionReplicaUtil.getRegionInfoForReplica(
092        RegionInfoBuilder.FIRST_META_REGIONINFO, replicaId), serverName));
093    return list;
094  }
095
096  /**
097   * Gets the meta regions for the given path with the default replica ID.
098   *
099   * @param zkw ZooKeeper watcher to be used
100   * @return List of meta regions
101   */
102  public static List<RegionInfo> getMetaRegions(ZKWatcher zkw) {
103    return getMetaRegions(zkw, RegionInfo.DEFAULT_REPLICA_ID);
104  }
105
106  /**
107   * Gets the meta regions for the given path and replica ID.
108   * @param zkw reference to the {@link ZKWatcher} which also contains configuration and operation
109   * @param replicaId the ID of the replica
110   * @return List of meta regions
111   */
112  public static List<RegionInfo> getMetaRegions(ZKWatcher zkw, int replicaId) {
113    List<Pair<RegionInfo, ServerName>> result;
114    result = getMetaRegionsAndLocations(zkw, replicaId);
115    return getListOfRegionInfos(result);
116  }
117
118  private static List<RegionInfo> getListOfRegionInfos(
119      final List<Pair<RegionInfo, ServerName>> pairs) {
120    if (pairs == null || pairs.isEmpty()) {
121      return Collections.emptyList();
122    }
123
124    List<RegionInfo> result = new ArrayList<>(pairs.size());
125    for (Pair<RegionInfo, ServerName> pair : pairs) {
126      result.add(pair.getFirst());
127    }
128    return result;
129  }
130
131  /**
132   * Gets the meta region location, if available.  Does not block.
133   * @param zkw zookeeper connection to use
134   * @return server name or null if we failed to get the data.
135   */
136  public static ServerName getMetaRegionLocation(final ZKWatcher zkw) {
137    try {
138      RegionState state = getMetaRegionState(zkw);
139      return state.isOpened() ? state.getServerName() : null;
140    } catch (KeeperException ke) {
141      return null;
142    }
143  }
144
145  /**
146   * Gets the meta region location, if available.  Does not block.
147   * @param zkw reference to the {@link ZKWatcher} which also contains configuration and operation
148   * @param replicaId the ID of the replica
149   * @return server name
150   */
151  public static ServerName getMetaRegionLocation(final ZKWatcher zkw, int replicaId) {
152    try {
153      RegionState state = getMetaRegionState(zkw, replicaId);
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, and waits for up to the specified timeout if not
162   * immediately available. Given the zookeeper notification could be delayed, we will try to get
163   * the latest data.
164   * @param zkw reference to the {@link ZKWatcher} which also contains configuration and operation
165   * @param timeout maximum time to wait, in millis
166   * @return server name for server hosting meta region formatted as per {@link ServerName}, or null
167   *         if none available
168   * @throws InterruptedException if interrupted while waiting
169   * @throws NotAllMetaRegionsOnlineException if a meta or root region is not online
170   */
171  public static ServerName waitMetaRegionLocation(ZKWatcher zkw, long timeout)
172      throws InterruptedException, NotAllMetaRegionsOnlineException {
173    return waitMetaRegionLocation(zkw, RegionInfo.DEFAULT_REPLICA_ID, timeout);
174  }
175
176  /**
177   * Gets the meta region location, if available, and waits for up to the specified timeout if not
178   * immediately available. Given the zookeeper notification could be delayed, we will try to get
179   * the latest data.
180   * @param zkw reference to the {@link ZKWatcher} which also contains configuration and operation
181   * @param replicaId the ID of the replica
182   * @param timeout maximum time to wait, in millis
183   * @return server name for server hosting meta region formatted as per {@link ServerName}, or null
184   *         if none available
185   * @throws InterruptedException if waiting for the socket operation fails
186   * @throws NotAllMetaRegionsOnlineException if a meta or root region is not online
187   */
188  public static ServerName waitMetaRegionLocation(ZKWatcher zkw, int replicaId, long timeout)
189      throws InterruptedException, NotAllMetaRegionsOnlineException {
190    try {
191      if (ZKUtil.checkExists(zkw, zkw.getZNodePaths().baseZNode) == -1) {
192        String errorMsg = "Check the value configured in 'zookeeper.znode.parent'. " +
193          "There could be a mismatch with the one configured in the master.";
194        LOG.error(errorMsg);
195        throw new IllegalArgumentException(errorMsg);
196      }
197    } catch (KeeperException e) {
198      throw new IllegalStateException("KeeperException while trying to check baseZNode:", e);
199    }
200    ServerName sn = blockUntilAvailable(zkw, replicaId, timeout);
201
202    if (sn == null) {
203      throw new NotAllMetaRegionsOnlineException("Timed out; " + timeout + "ms");
204    }
205
206    return sn;
207  }
208
209  /**
210   * Sets the location of <code>hbase:meta</code> in ZooKeeper to the
211   * specified server address.
212   * @param zookeeper zookeeper reference
213   * @param serverName The server hosting <code>hbase:meta</code>
214   * @param state The region transition state
215   * @throws KeeperException unexpected zookeeper exception
216   */
217  public static void setMetaLocation(ZKWatcher zookeeper,
218      ServerName serverName, RegionState.State state) throws KeeperException {
219    setMetaLocation(zookeeper, serverName, RegionInfo.DEFAULT_REPLICA_ID, state);
220  }
221
222  /**
223   * Sets the location of <code>hbase:meta</code> in ZooKeeper to the specified server address.
224   * @param zookeeper reference to the {@link ZKWatcher} which also contains configuration and
225   *                  operation
226   * @param serverName the name of the server
227   * @param replicaId the ID of the replica
228   * @param state the state of the region
229   * @throws KeeperException if a ZooKeeper operation fails
230   */
231  public static void setMetaLocation(ZKWatcher zookeeper, ServerName serverName, int replicaId,
232      RegionState.State state) throws KeeperException {
233    if (serverName == null) {
234      LOG.warn("Tried to set null ServerName in hbase:meta; skipping -- ServerName required");
235      return;
236    }
237    LOG.info("Setting hbase:meta (replicaId={}) location in ZooKeeper as {}", replicaId,
238      serverName);
239    // Make the MetaRegionServer pb and then get its bytes and save this as
240    // the znode content.
241    MetaRegionServer pbrsr = MetaRegionServer.newBuilder()
242      .setServer(ProtobufUtil.toServerName(serverName))
243      .setRpcVersion(HConstants.RPC_CURRENT_VERSION)
244      .setState(state.convert()).build();
245    byte[] data = ProtobufUtil.prependPBMagic(pbrsr.toByteArray());
246    try {
247      ZKUtil.setData(zookeeper,
248          zookeeper.getZNodePaths().getZNodeForReplica(replicaId), data);
249    } catch(KeeperException.NoNodeException nne) {
250      if (replicaId == RegionInfo.DEFAULT_REPLICA_ID) {
251        LOG.debug("META region location doesn't exist, create it");
252      } else {
253        LOG.debug("META region location doesn't exist for replicaId=" + replicaId +
254            ", create it");
255      }
256      ZKUtil.createAndWatch(zookeeper, zookeeper.getZNodePaths().getZNodeForReplica(replicaId),
257              data);
258    }
259  }
260
261  /**
262   * Load the meta region state from the meta server ZNode.
263   */
264  public static RegionState getMetaRegionState(ZKWatcher zkw) throws KeeperException {
265    return getMetaRegionState(zkw, RegionInfo.DEFAULT_REPLICA_ID);
266  }
267
268  /**
269   * Load the meta region state from the meta server ZNode.
270   *
271   * @param zkw reference to the {@link ZKWatcher} which also contains configuration and operation
272   * @param replicaId the ID of the replica
273   * @return regionstate
274   * @throws KeeperException if a ZooKeeper operation fails
275   */
276  public static RegionState getMetaRegionState(ZKWatcher zkw, int replicaId)
277          throws KeeperException {
278    RegionState.State state = RegionState.State.OPEN;
279    ServerName serverName = null;
280    try {
281      byte[] data = ZKUtil.getData(zkw, zkw.getZNodePaths().getZNodeForReplica(replicaId));
282      if (data != null && data.length > 0 && ProtobufUtil.isPBMagicPrefix(data)) {
283        try {
284          int prefixLen = ProtobufUtil.lengthOfPBMagic();
285          ZooKeeperProtos.MetaRegionServer rl =
286            ZooKeeperProtos.MetaRegionServer.parser().parseFrom(data, prefixLen,
287                    data.length - prefixLen);
288          if (rl.hasState()) {
289            state = RegionState.State.convert(rl.getState());
290          }
291          HBaseProtos.ServerName sn = rl.getServer();
292          serverName = ServerName.valueOf(
293            sn.getHostName(), sn.getPort(), sn.getStartCode());
294        } catch (InvalidProtocolBufferException e) {
295          throw new DeserializationException("Unable to parse meta region location");
296        }
297      } else {
298        // old style of meta region location?
299        serverName = ProtobufUtil.parseServerNameFrom(data);
300      }
301    } catch (DeserializationException e) {
302      throw ZKUtil.convert(e);
303    } catch (InterruptedException e) {
304      Thread.currentThread().interrupt();
305    }
306    if (serverName == null) {
307      state = RegionState.State.OFFLINE;
308    }
309    return new RegionState(
310        RegionReplicaUtil.getRegionInfoForReplica(
311            RegionInfoBuilder.FIRST_META_REGIONINFO, replicaId),
312        state, serverName);
313  }
314
315  /**
316   * Deletes the location of <code>hbase:meta</code> in ZooKeeper.
317   * @param zookeeper zookeeper reference
318   * @throws KeeperException unexpected zookeeper exception
319   */
320  public static void deleteMetaLocation(ZKWatcher zookeeper)
321    throws KeeperException {
322    deleteMetaLocation(zookeeper, RegionInfo.DEFAULT_REPLICA_ID);
323  }
324
325  public static void deleteMetaLocation(ZKWatcher zookeeper, int replicaId)
326    throws KeeperException {
327    if (replicaId == RegionInfo.DEFAULT_REPLICA_ID) {
328      LOG.info("Deleting hbase:meta region location in ZooKeeper");
329    } else {
330      LOG.info("Deleting hbase:meta for {} region location in ZooKeeper", replicaId);
331    }
332    try {
333      // Just delete the node.  Don't need any watches.
334      ZKUtil.deleteNode(zookeeper, zookeeper.getZNodePaths().getZNodeForReplica(replicaId));
335    } catch(KeeperException.NoNodeException nne) {
336      // Has already been deleted
337    }
338  }
339  /**
340   * Wait until the primary meta region is available. Get the secondary locations as well but don't
341   * block for those.
342   *
343   * @param zkw reference to the {@link ZKWatcher} which also contains configuration and operation
344   * @param timeout maximum time to wait in millis
345   * @param conf the {@link Configuration} to use
346   * @return ServerName or null if we timed out.
347   * @throws InterruptedException if waiting for the socket operation fails
348   */
349  public static List<ServerName> blockUntilAvailable(final ZKWatcher zkw, final long timeout,
350      Configuration conf) throws InterruptedException {
351    int numReplicasConfigured = 1;
352
353    List<ServerName> servers = new ArrayList<>();
354    // Make the blocking call first so that we do the wait to know
355    // the znodes are all in place or timeout.
356    ServerName server = blockUntilAvailable(zkw, timeout);
357
358    if (server == null) {
359      return null;
360    }
361
362    servers.add(server);
363
364    try {
365      List<String> metaReplicaNodes = zkw.getMetaReplicaNodes();
366      numReplicasConfigured = metaReplicaNodes.size();
367    } catch (KeeperException e) {
368      LOG.warn("Got ZK exception {}", e);
369    }
370    for (int replicaId = 1; replicaId < numReplicasConfigured; replicaId++) {
371      // return all replica locations for the meta
372      servers.add(getMetaRegionLocation(zkw, replicaId));
373    }
374    return servers;
375  }
376
377  /**
378   * Wait until the meta region is available and is not in transition.
379   * @param zkw zookeeper connection to use
380   * @param timeout maximum time to wait, in millis
381   * @return ServerName or null if we timed out.
382   * @throws InterruptedException if waiting for the socket operation fails
383   */
384  public static ServerName blockUntilAvailable(final ZKWatcher zkw, final long timeout)
385      throws InterruptedException {
386    return blockUntilAvailable(zkw, RegionInfo.DEFAULT_REPLICA_ID, timeout);
387  }
388
389  /**
390   * Wait until the meta region is available and is not in transition.
391   * @param zkw reference to the {@link ZKWatcher} which also contains configuration and constants
392   * @param replicaId the ID of the replica
393   * @param timeout maximum time to wait in millis
394   * @return ServerName or null if we timed out.
395   * @throws InterruptedException if waiting for the socket operation fails
396   */
397  public static ServerName blockUntilAvailable(final ZKWatcher zkw, int replicaId,
398      final long timeout) throws InterruptedException {
399    if (timeout < 0) {
400      throw new IllegalArgumentException();
401    }
402
403    if (zkw == null) {
404      throw new IllegalArgumentException();
405    }
406
407    long startTime = System.currentTimeMillis();
408    ServerName sn = null;
409    while (true) {
410      sn = getMetaRegionLocation(zkw, replicaId);
411      if (sn != null ||
412        (System.currentTimeMillis() - startTime) > timeout - HConstants.SOCKET_RETRY_WAIT_MS) {
413        break;
414      }
415      Thread.sleep(HConstants.SOCKET_RETRY_WAIT_MS);
416    }
417    return sn;
418  }
419}