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 com.google.errorprone.annotations.RestrictedApi; 021import org.apache.hadoop.hbase.HConstants; 022import org.apache.hadoop.hbase.NotAllMetaRegionsOnlineException; 023import org.apache.hadoop.hbase.ServerName; 024import org.apache.hadoop.hbase.TableName; 025import org.apache.hadoop.hbase.client.RegionInfo; 026import org.apache.hadoop.hbase.exceptions.DeserializationException; 027import org.apache.hadoop.hbase.master.RegionState; 028import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; 029import org.apache.yetus.audience.InterfaceAudience; 030import org.apache.zookeeper.KeeperException; 031import org.slf4j.Logger; 032import org.slf4j.LoggerFactory; 033 034import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; 035import org.apache.hadoop.hbase.shaded.protobuf.generated.ZooKeeperProtos.MetaRegionServer; 036 037/** 038 * Utility class to perform operation (get/wait for/verify/set/delete) on znode in ZooKeeper which 039 * keeps hbase:meta region server location. 040 * <p/> 041 * Stateless class with a bunch of static methods. Doesn't manage resources passed in (e.g. 042 * Connection, ZKWatcher etc). 043 * <p/> 044 * Meta region location is set by <code>RegionServerServices</code>. This class doesn't use ZK 045 * watchers, rather accesses ZK directly. 046 * <p/> 047 * TODO: rewrite using RPC calls to master to find out about hbase:meta. 048 */ 049@InterfaceAudience.Private 050public final class MetaTableLocator { 051 private static final Logger LOG = LoggerFactory.getLogger(MetaTableLocator.class); 052 053 private MetaTableLocator() { 054 } 055 056 /** 057 * Gets the meta region location, if available. Does not block. 058 * @param zkw zookeeper connection to use 059 * @return server name or null if we failed to get the data. 060 */ 061 @RestrictedApi(explanation = "Should only be called in tests or ZKUtil", link = "", 062 allowedOnPath = ".*/src/test/.*|.*/ZKDump\\.java") 063 public static ServerName getMetaRegionLocation(final ZKWatcher zkw) { 064 try { 065 RegionState state = getMetaRegionState(zkw); 066 return state.isOpened() ? state.getServerName() : null; 067 } catch (KeeperException ke) { 068 return null; 069 } 070 } 071 072 /** 073 * Gets the meta region location, if available. Does not block. 074 * @param zkw reference to the {@link ZKWatcher} which also contains configuration and 075 * operation 076 * @param replicaId the ID of the replica 077 * @return server name 078 */ 079 @RestrictedApi(explanation = "Should only be called in self or ZKUtil", link = "", 080 allowedOnPath = ".*(MetaTableLocator|ZKDump)\\.java") 081 public static ServerName getMetaRegionLocation(final ZKWatcher zkw, int replicaId) { 082 try { 083 RegionState state = getMetaRegionState(zkw, replicaId); 084 return state.isOpened() ? state.getServerName() : null; 085 } catch (KeeperException ke) { 086 return null; 087 } 088 } 089 090 /** 091 * Gets the meta region location, if available, and waits for up to the specified timeout if not 092 * immediately available. Given the zookeeper notification could be delayed, we will try to get 093 * the latest data. 094 * @param zkw reference to the {@link ZKWatcher} which also contains configuration and 095 * operation 096 * @param timeout maximum time to wait, in millis 097 * @return server name for server hosting meta region formatted as per {@link ServerName}, or null 098 * if none available 099 * @throws InterruptedException if interrupted while waiting 100 * @throws NotAllMetaRegionsOnlineException if a meta or root region is not online 101 */ 102 @RestrictedApi(explanation = "Should only be called in tests", link = "", 103 allowedOnPath = ".*/src/test/.*") 104 public static ServerName waitMetaRegionLocation(ZKWatcher zkw, long timeout) 105 throws InterruptedException, NotAllMetaRegionsOnlineException { 106 return waitMetaRegionLocation(zkw, RegionInfo.DEFAULT_REPLICA_ID, timeout); 107 } 108 109 /** 110 * Gets the meta region location, if available, and waits for up to the specified timeout if not 111 * immediately available. Given the zookeeper notification could be delayed, we will try to get 112 * the latest data. 113 * @param zkw reference to the {@link ZKWatcher} which also contains configuration and 114 * operation 115 * @param replicaId the ID of the replica 116 * @param timeout maximum time to wait, in millis 117 * @return server name for server hosting meta region formatted as per {@link ServerName}, or null 118 * if none available 119 * @throws InterruptedException if waiting for the socket operation fails 120 * @throws NotAllMetaRegionsOnlineException if a meta or root region is not online 121 */ 122 private static ServerName waitMetaRegionLocation(ZKWatcher zkw, int replicaId, long timeout) 123 throws InterruptedException, NotAllMetaRegionsOnlineException { 124 try { 125 if (ZKUtil.checkExists(zkw, zkw.getZNodePaths().baseZNode) == -1) { 126 String errorMsg = "Check the value configured in 'zookeeper.znode.parent'. " 127 + "There could be a mismatch with the one configured in the master."; 128 LOG.error(errorMsg); 129 throw new IllegalArgumentException(errorMsg); 130 } 131 } catch (KeeperException e) { 132 throw new IllegalStateException("KeeperException while trying to check baseZNode:", e); 133 } 134 ServerName sn = blockUntilAvailable(zkw, replicaId, timeout); 135 136 if (sn == null) { 137 throw new NotAllMetaRegionsOnlineException("Timed out; " + timeout + "ms"); 138 } 139 140 return sn; 141 } 142 143 /** 144 * Sets the location of <code>hbase:meta</code> in ZooKeeper to the specified server address. 145 * @param zookeeper zookeeper reference 146 * @param serverName The server hosting <code>hbase:meta</code> 147 * @param state The region transition state 148 * @throws KeeperException unexpected zookeeper exception 149 */ 150 @RestrictedApi(explanation = "Should only be called in tests", link = "", 151 allowedOnPath = ".*/src/test/.*") 152 public static void setMetaLocation(ZKWatcher zookeeper, ServerName serverName, 153 RegionState.State state) throws KeeperException { 154 setMetaLocation(zookeeper, serverName, RegionInfo.DEFAULT_REPLICA_ID, state); 155 } 156 157 /** 158 * Sets the location of <code>hbase:meta</code> in ZooKeeper to the specified server address. 159 * @param zookeeper reference to the {@link ZKWatcher} which also contains configuration and 160 * operation 161 * @param serverName the name of the server 162 * @param replicaId the ID of the replica 163 * @param state the state of the region 164 * @throws KeeperException if a ZooKeeper operation fails 165 */ 166 public static void setMetaLocation(ZKWatcher zookeeper, ServerName serverName, int replicaId, 167 RegionState.State state) throws KeeperException { 168 if (serverName == null) { 169 LOG.warn("Tried to set null ServerName in {}; skipping -- ServerName required", 170 TableName.META_TABLE_NAME); 171 return; 172 } 173 LOG.info("Setting {} replicaId={} location in ZooKeeper as {}, state={}", 174 TableName.META_TABLE_NAME, replicaId, serverName, state); 175 // Make the MetaRegionServer pb and then get its bytes and save this as 176 // the znode content. 177 MetaRegionServer pbrsr = 178 MetaRegionServer.newBuilder().setServer(ProtobufUtil.toServerName(serverName)) 179 .setRpcVersion(HConstants.RPC_CURRENT_VERSION).setState(state.convert()).build(); 180 byte[] data = ProtobufUtil.prependPBMagic(pbrsr.toByteArray()); 181 try { 182 ZKUtil.setData(zookeeper, zookeeper.getZNodePaths().getZNodeForReplica(replicaId), data); 183 } catch (KeeperException.NoNodeException nne) { 184 if (replicaId == RegionInfo.DEFAULT_REPLICA_ID) { 185 LOG.debug("{} region location doesn't exist, create it", TableName.META_TABLE_NAME); 186 } else { 187 LOG.debug("{} region location doesn't exist for replicaId={}, create it", 188 TableName.META_TABLE_NAME, replicaId); 189 } 190 ZKUtil.createAndWatch(zookeeper, zookeeper.getZNodePaths().getZNodeForReplica(replicaId), 191 data); 192 } 193 } 194 195 /** 196 * Load the meta region state from the meta server ZNode. 197 */ 198 @RestrictedApi(explanation = "Should only be called in self or tests", link = "", 199 allowedOnPath = ".*/src/test/.*|.*/MetaTableLocator\\.java") 200 public static RegionState getMetaRegionState(ZKWatcher zkw) throws KeeperException { 201 return getMetaRegionState(zkw, RegionInfo.DEFAULT_REPLICA_ID); 202 } 203 204 /** 205 * Load the meta region state from the meta region server ZNode. 206 * @param zkw reference to the {@link ZKWatcher} which also contains configuration and 207 * operation 208 * @param replicaId the ID of the replica 209 * @throws KeeperException if a ZooKeeper operation fails 210 */ 211 public static RegionState getMetaRegionState(ZKWatcher zkw, int replicaId) 212 throws KeeperException { 213 RegionState regionState = null; 214 try { 215 byte[] data = ZKUtil.getData(zkw, zkw.getZNodePaths().getZNodeForReplica(replicaId)); 216 regionState = ProtobufUtil.parseMetaRegionStateFrom(data, replicaId); 217 } catch (DeserializationException e) { 218 throw ZKUtil.convert(e); 219 } catch (InterruptedException e) { 220 Thread.currentThread().interrupt(); 221 } 222 return regionState; 223 } 224 225 /** 226 * Deletes the location of <code>hbase:meta</code> in ZooKeeper. 227 * @param zookeeper zookeeper reference 228 * @throws KeeperException unexpected zookeeper exception 229 */ 230 @RestrictedApi(explanation = "Should only be called in tests", link = "", 231 allowedOnPath = ".*/src/test/.*") 232 public static void deleteMetaLocation(ZKWatcher zookeeper) throws KeeperException { 233 deleteMetaLocation(zookeeper, RegionInfo.DEFAULT_REPLICA_ID); 234 } 235 236 public static void deleteMetaLocation(ZKWatcher zookeeper, int replicaId) throws KeeperException { 237 if (replicaId == RegionInfo.DEFAULT_REPLICA_ID) { 238 LOG.info("Deleting {} region location in ZooKeeper", TableName.META_TABLE_NAME); 239 } else { 240 LOG.info("Deleting {} for {} region location in ZooKeeper", TableName.META_TABLE_NAME, 241 replicaId); 242 } 243 try { 244 // Just delete the node. Don't need any watches. 245 ZKUtil.deleteNode(zookeeper, zookeeper.getZNodePaths().getZNodeForReplica(replicaId)); 246 } catch (KeeperException.NoNodeException nne) { 247 // Has already been deleted 248 } 249 } 250 251 /** 252 * Wait until the meta region is available and is not in transition. 253 * @param zkw reference to the {@link ZKWatcher} which also contains configuration and 254 * constants 255 * @param replicaId the ID of the replica 256 * @param timeout maximum time to wait in millis 257 * @return ServerName or null if we timed out. 258 * @throws InterruptedException if waiting for the socket operation fails 259 */ 260 private static ServerName blockUntilAvailable(final ZKWatcher zkw, int replicaId, 261 final long timeout) throws InterruptedException { 262 if (timeout < 0) { 263 throw new IllegalArgumentException(); 264 } 265 266 if (zkw == null) { 267 throw new IllegalArgumentException(); 268 } 269 270 long startTime = EnvironmentEdgeManager.currentTime(); 271 ServerName sn = null; 272 while (true) { 273 sn = getMetaRegionLocation(zkw, replicaId); 274 if ( 275 sn != null || (EnvironmentEdgeManager.currentTime() - startTime) 276 > timeout - HConstants.SOCKET_RETRY_WAIT_MS 277 ) { 278 break; 279 } 280 Thread.sleep(HConstants.SOCKET_RETRY_WAIT_MS); 281 } 282 return sn; 283 } 284}