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 org.apache.hadoop.hbase.classification.InterfaceAudience;
21  import org.apache.hadoop.hbase.Abortable;
22  import org.apache.hadoop.hbase.HConstants;
23  import org.apache.hadoop.hbase.HRegionInfo;
24  import org.apache.hadoop.hbase.exceptions.DeserializationException;
25  import org.apache.hadoop.hbase.ServerName;
26  import org.apache.hadoop.hbase.master.RegionState;
27  import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
28  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos;
29  import org.apache.hadoop.hbase.protobuf.generated.ZooKeeperProtos;
30  import org.apache.zookeeper.KeeperException;
31  
32  import com.google.common.base.Stopwatch;
33  import com.google.protobuf.InvalidProtocolBufferException;
34  
35  /**
36   * Tracks the meta region server location node in zookeeper.
37   * Meta region location is set by <code>RegionServerServices</code>.
38   * This class has a watcher on the meta location and notices changes.
39   */
40  @InterfaceAudience.Private
41  public class MetaRegionTracker extends ZooKeeperNodeTracker {
42    /**
43     * Creates a meta region location tracker.
44     *
45     * <p>After construction, use {@link #start} to kick off tracking.
46     *
47     * @param watcher
48     * @param abortable
49     */
50    public MetaRegionTracker(ZooKeeperWatcher watcher, Abortable abortable) {
51      super(watcher, watcher.metaServerZNode, abortable);
52    }
53  
54    /**
55     * Checks if the meta region location is available.
56     * @return true if meta region location is available, false if not
57     */
58    public boolean isLocationAvailable() {
59      return getMetaRegionLocation() != null;
60    }
61  
62    /**
63     * Gets the meta region location, if available.  Does not block.  Sets a watcher.
64     * @return server name or null if we failed to get the data.
65     * @throws InterruptedException
66     */
67    public ServerName getMetaRegionLocation() {
68      RegionState regionState = getMetaRegionState();
69      return regionState.isOpened() ? regionState.getServerName() : null;
70    }
71  
72    /**
73     * Gets the meta region location, if available.  Does not block.  Does not set
74     * a watcher (In this regard it differs from {@link #getMetaRegionLocation}.
75     * @param zkw
76     * @return server name or null if we failed to get the data.
77     * @throws KeeperException
78     */
79    public static ServerName getMetaRegionLocation(final ZooKeeperWatcher zkw) throws KeeperException {
80      byte[] data = ZKUtil.getData(zkw, zkw.metaServerZNode);
81      RegionState regionState = getMetaRegionState(data);
82      return regionState.isOpened() ? regionState.getServerName() : null;
83    }
84  
85    /**
86     * Gets the meta region location, if available, and waits for up to the
87     * specified timeout if not immediately available.
88     * Given the zookeeper notification could be delayed, we will try to
89     * get the latest data.
90     * @param timeout maximum time to wait, in millis
91     * @return server name for server hosting meta region formatted as per
92     * {@link ServerName}, or null if none available
93     * @throws InterruptedException if interrupted while waiting
94     */
95    public ServerName waitMetaRegionLocation(long timeout)
96    throws InterruptedException {
97      if (false == checkIfBaseNodeAvailable()) {
98        String errorMsg = "Check the value configured in 'zookeeper.znode.parent'. "
99            + "There could be a mismatch with the one configured in the master.";
100       LOG.error(errorMsg);
101       throw new IllegalArgumentException(errorMsg);
102     }
103     return blockUntilAvailable(timeout);
104    
105   }
106   
107   /**
108    * Wait until the meta region is available and is not in transition.
109    * @param zkw zookeeper connection to use
110    * @param timeout maximum time to wait, in millis
111    * @return ServerName or null if we timed out.
112    * @throws InterruptedException
113    */
114   private ServerName blockUntilAvailable(final long timeout) throws InterruptedException {
115     if (timeout < 0) throw new IllegalArgumentException("Timeout shouldn't be less than zero");
116     Stopwatch sw = new Stopwatch().start();
117     ServerName sn = null;
118     try {
119       while (true) {
120         sn = getMetaRegionLocation();
121         if (sn != null || sw.elapsedMillis() > timeout - HConstants.SOCKET_RETRY_WAIT_MS) {
122           break;
123         }
124         Thread.sleep(HConstants.SOCKET_RETRY_WAIT_MS);
125       }
126     } finally {
127       sw.stop();
128     }
129     return sn;
130   }
131 
132   /**
133    * Sets the location of <code>hbase:meta</code> in ZooKeeper to the
134    * specified server address.
135    * @param zookeeper zookeeper reference
136    * @param location The server hosting <code>hbase:meta</code>
137    * @throws KeeperException unexpected zookeeper exception
138    */
139   public static void setMetaLocation(ZooKeeperWatcher zookeeper,
140                                      final ServerName location, final RegionState.State regionState)
141   throws KeeperException {
142     LOG.info("Setting hbase:meta region location in ZooKeeper as " + location);
143     // Make the MetaRegionServer pb and then get its bytes and save this as
144     // the znode content.
145     byte [] data = toByteArray(location, regionState);
146     try {
147       ZKUtil.createAndWatch(zookeeper, zookeeper.metaServerZNode, data);
148     } catch(KeeperException.NodeExistsException nee) {
149       LOG.debug("META region location already existed, updated location");
150       ZKUtil.setData(zookeeper, zookeeper.metaServerZNode, data);
151     }
152   }
153 
154   /**
155    * Build up the znode content.
156    * @param sn What to put into the znode.
157    * @return The content of the meta-region-server znode
158    */
159   static byte [] toByteArray(final ServerName sn, final RegionState.State regionState) {
160     // ZNode content is a pb message preceded by some pb magic.
161     HBaseProtos.ServerName pbsn =
162       HBaseProtos.ServerName.newBuilder()
163                             .setHostName(sn.getHostname())
164                             .setPort(sn.getPort())
165                             .setStartCode(sn.getStartcode())
166                             .build();
167 
168     ZooKeeperProtos.MetaRegionServer pbrsr =
169       ZooKeeperProtos.MetaRegionServer.newBuilder()
170                                       .setServer(pbsn)
171                                       .setRpcVersion(HConstants.RPC_CURRENT_VERSION)
172                                       .setState(regionState.convert())
173                                       .build();
174     return ProtobufUtil.prependPBMagic(pbrsr.toByteArray());
175   }
176 
177   /**
178    * Deletes the location of <code>hbase:meta</code> in ZooKeeper.
179    * @param zookeeper zookeeper reference
180    * @throws KeeperException unexpected zookeeper exception
181    */
182   public static void deleteMetaLocation(ZooKeeperWatcher zookeeper)
183   throws KeeperException {
184     LOG.info("Unsetting hbase:meta region location in ZooKeeper");
185     try {
186       // Just delete the node.  Don't need any watches.
187       ZKUtil.deleteNode(zookeeper, zookeeper.metaServerZNode);
188     } catch(KeeperException.NoNodeException nne) {
189       // Has already been deleted
190     }
191   }
192 
193   /**
194    * Wait until the meta region is available.
195    * @param zkw
196    * @param timeout
197    * @return ServerName or null if we timed out.
198    * @throws InterruptedException
199    */
200   public static ServerName blockUntilAvailable(final ZooKeeperWatcher zkw,
201       final long timeout)
202   throws InterruptedException {
203     byte [] data = ZKUtil.blockUntilAvailable(zkw, zkw.metaServerZNode, timeout);
204     RegionState regionState =  getMetaRegionState(data);
205     return regionState.isOpened() ? regionState.getServerName() : null;
206   }
207   
208   
209   private static RegionState getMetaRegionState(byte[] data) {
210     RegionState.State state = RegionState.State.OPEN;
211     ServerName serverName = null;
212     try {
213       if (data != null && data.length > 0 && ProtobufUtil.isPBMagicPrefix(data)) {
214         try {
215           int prefixLen = ProtobufUtil.lengthOfPBMagic();
216           ZooKeeperProtos.MetaRegionServer rl =
217               ZooKeeperProtos.MetaRegionServer.PARSER.parseFrom(data, prefixLen, data.length
218                   - prefixLen);
219           if (rl.hasState()) {
220             state = RegionState.State.convert(rl.getState());
221           }
222           HBaseProtos.ServerName sn = rl.getServer();
223           serverName = ServerName.valueOf(sn.getHostName(), sn.getPort(), sn.getStartCode());
224         } catch (InvalidProtocolBufferException e) {
225           throw new DeserializationException("Unable to parse root region location");
226         }
227       } else {
228         // old style of meta region location?
229         serverName = ServerName.parseFrom(data);
230       }
231     } catch (DeserializationException e) {
232       LOG.warn("Failed parse", e);
233       return null;
234     }
235     if (serverName == null) {
236       state = RegionState.State.OFFLINE;
237     }
238     return new RegionState(HRegionInfo.FIRST_META_REGIONINFO, state, serverName);
239   }
240   
241   /**
242    * Get meta region state
243    * @return RegionState
244    */
245   public  RegionState getMetaRegionState() {
246     byte[] data = super.getData(true);
247     return getMetaRegionState(data);
248   }
249 }