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