Index: src/test/java/org/apache/hadoop/hbase/HBaseTestingUtility.java =================================================================== --- src/test/java/org/apache/hadoop/hbase/HBaseTestingUtility.java (revision 964096) +++ src/test/java/org/apache/hadoop/hbase/HBaseTestingUtility.java (working copy) @@ -708,16 +708,13 @@ expireSession(rs.getZooKeeper(), rs); } - public void expireSession(ZooKeeperWatcher nodeZK, ServerStatus server) + public void expireSession(ZooKeeperWatcher nodeZK, ServerController server) throws Exception { - ZooKeeperWatcher zkw = new ZooKeeperWatcher(conf, - ZooKeeperWatcher.class.getName(), server); - zkw.registerListener(EmptyWatcher.instance); String quorumServers = ZKConfig.getZKQuorumServersString(conf); int sessionTimeout = 5 * 1000; // 5 seconds - byte[] password = nodeZK.getSessionPassword(); - long sessionID = nodeZK.getSessionID(); + byte[] password = nodeZK.getZooKeeper().getSessionPasswd(); + long sessionID = nodeZK.getZooKeeper().getSessionId(); ZooKeeper zk = new ZooKeeper(quorumServers, sessionTimeout, EmptyWatcher.instance, sessionID, password); @@ -744,8 +741,10 @@ * * @return The HBaseAdmin instance. * @throws MasterNotRunningException + * @throws ZooKeeperConnectionException */ - public HBaseAdmin getHBaseAdmin() throws MasterNotRunningException { + public HBaseAdmin getHBaseAdmin() + throws MasterNotRunningException, ZooKeeperConnectionException { if (hbaseAdmin == null) { hbaseAdmin = new HBaseAdmin(getConfiguration()); } Index: src/test/java/org/apache/hadoop/hbase/regionserver/TestMasterAddressManager.java =================================================================== --- src/test/java/org/apache/hadoop/hbase/regionserver/TestMasterAddressManager.java (revision 964096) +++ src/test/java/org/apache/hadoop/hbase/regionserver/TestMasterAddressManager.java (working copy) @@ -29,6 +29,7 @@ import org.apache.hadoop.hbase.HBaseTestingUtility; import org.apache.hadoop.hbase.HServerAddress; import org.apache.hadoop.hbase.util.Bytes; +import org.apache.hadoop.hbase.zookeeper.ZKUtil; import org.apache.hadoop.hbase.zookeeper.ZooKeeperListener; import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher; import org.apache.zookeeper.CreateMode; @@ -60,7 +61,7 @@ ZooKeeperWatcher zk = new ZooKeeperWatcher(TEST_UTIL.getConfiguration(), "testMasterAddressManagerFromZK", null); - zk.createZNodeIfNotExists(zk.baseZNode); + ZKUtil.createIfNotExists(zk, zk.baseZNode); // Should not have a master yet MasterAddressManager addressManager = new MasterAddressManager(zk, null); @@ -77,8 +78,7 @@ int port = 1234; HServerAddress dummyAddress = new HServerAddress(host, port); LOG.info("Creating master node"); - zk.createZNodeIfNotExists(zk.masterAddressZNode, - Bytes.toBytes(dummyAddress.toString()), CreateMode.EPHEMERAL, false); + ZKUtil.setAddressAndWatch(zk, zk.masterAddressZNode, dummyAddress); // Wait for the node to be created LOG.info("Waiting for master address manager to be notified"); Index: src/test/java/org/apache/hadoop/hbase/TestZooKeeper.java =================================================================== --- src/test/java/org/apache/hadoop/hbase/TestZooKeeper.java (revision 964096) +++ src/test/java/org/apache/hadoop/hbase/TestZooKeeper.java (working copy) @@ -29,6 +29,8 @@ import org.apache.hadoop.hbase.client.HTable; import org.apache.hadoop.hbase.client.Put; import org.apache.hadoop.hbase.util.Bytes; +import org.apache.hadoop.hbase.zookeeper.ZKConfig; +import org.apache.hadoop.hbase.zookeeper.ZKUtil; import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher; import org.apache.hadoop.hbase.zookeeper.ZooKeeperWrapper; import org.apache.zookeeper.KeeperException; @@ -92,15 +94,12 @@ throws IOException, InterruptedException { new HTable(conf, HConstants.META_TABLE_NAME); - ZooKeeperWatcher zkw = new ZooKeeperWatcher(conf, - TestZooKeeper.class.getName(), null); - zkw.registerListener(EmptyWatcher.instance); - String quorumServers = zkw.getQuorumServers(); + String quorumServers = ZKConfig.getZKQuorumServersString(conf); int sessionTimeout = 5 * 1000; // 5 seconds HConnection connection = HConnectionManager.getConnection(conf); - ZooKeeperWrapper connectionZK = connection.getZooKeeperWrapper(); - long sessionID = connectionZK.getSessionID(); - byte[] password = connectionZK.getSessionPassword(); + ZooKeeperWatcher connectionZK = connection.getZooKeeperWatcher(); + long sessionID = connectionZK.getZooKeeper().getSessionId(); + byte[] password = connectionZK.getZooKeeper().getSessionPasswd(); ZooKeeper zk = new ZooKeeper(quorumServers, sessionTimeout, EmptyWatcher.instance, sessionID, password); @@ -163,12 +162,11 @@ ipMeta.exists(new Get(HConstants.LAST_ROW)); // make sure they aren't the same - assertFalse(HConnectionManager.getClientZooKeeperWatcher(conf) - .getZooKeeperWrapper() == HConnectionManager.getClientZooKeeperWatcher( - otherConf).getZooKeeperWrapper()); + assertFalse(HConnectionManager.getConnection(conf).getZooKeeperWatcher() + == HConnectionManager.getConnection(otherConf).getZooKeeperWatcher()); assertFalse(HConnectionManager.getConnection(conf) - .getZooKeeperWrapper().getQuorumServers().equals(HConnectionManager - .getConnection(otherConf).getZooKeeperWrapper().getQuorumServers())); + .getZooKeeperWatcher().getQuorum().equals(HConnectionManager + .getConnection(otherConf).getZooKeeperWatcher().getQuorum())); } catch (Exception e) { e.printStackTrace(); fail(); @@ -184,17 +182,16 @@ public void testZNodeDeletes() throws Exception { ZooKeeperWatcher zkw = new ZooKeeperWatcher(conf, TestZooKeeper.class.getName(), null); - zkw.registerListener(EmptyWatcher.instance); - zkw.ensureExists("/l1/l2/l3/l4"); + ZKUtil.createWithParents(zkw, "/l1/l2/l3/l4"); try { - zkw.deleteZNode("/l1/l2"); + ZKUtil.deleteNode(zkw, "/l1/l2"); fail("We should not be able to delete if znode has childs"); } catch (KeeperException ex) { - assertNotNull(zkw.getData("/l1/l2/l3", "l4")); + assertNotNull(ZKUtil.getDataNoWatch(zkw, "/l1/l2/l3/l4", null)); } - zkw.deleteZNode("/l1/l2", true); - assertNull(zkw.getData("/l1/l2/l3", "l4")); - zkw.deleteZNode("/l1"); - assertNull(zkw.getData("/l1", "l2")); + ZKUtil.deleteNodeRecursively(zkw, "/l1/l2"); + assertNull(ZKUtil.getDataNoWatch(zkw, "/l1/l2/l3/l4", null)); + ZKUtil.deleteNode(zkw, "/l1"); + assertNull(ZKUtil.getDataNoWatch(zkw, "/l1/l2", null)); } } \ No newline at end of file Index: src/test/java/org/apache/hadoop/hbase/master/TestActiveMasterManager.java =================================================================== --- src/test/java/org/apache/hadoop/hbase/master/TestActiveMasterManager.java (revision 964096) +++ src/test/java/org/apache/hadoop/hbase/master/TestActiveMasterManager.java (working copy) @@ -64,9 +64,9 @@ ZooKeeperWatcher zk = new ZooKeeperWatcher(TEST_UTIL.getConfiguration(), "testActiveMasterManagerFromZK", null); - zk.createZNodeIfNotExists(zk.baseZNode); + ZKUtil.createIfNotExists(zk, zk.baseZNode); try { - zk.deleteZNode(zk.masterAddressZNode); + ZKUtil.deleteNode(zk, zk.masterAddressZNode); } catch(KeeperException.NoNodeException nne) {} // Create the master node with a dummy address @@ -111,7 +111,7 @@ zk.registerListener(listener); LOG.info("Deleting master node"); - zk.deleteZNode(zk.masterAddressZNode); + ZKUtil.deleteNode(zk, zk.masterAddressZNode); // Wait for the node to be deleted LOG.info("Waiting for active master manager to be notified"); Index: src/test/java/org/apache/hadoop/hbase/master/TestRestartCluster.java =================================================================== --- src/test/java/org/apache/hadoop/hbase/master/TestRestartCluster.java (revision 964096) +++ src/test/java/org/apache/hadoop/hbase/master/TestRestartCluster.java (working copy) @@ -31,6 +31,7 @@ import org.apache.hadoop.hbase.executor.HBaseEventHandler.HBaseEventType; import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.hbase.util.Writables; +import org.apache.hadoop.hbase.zookeeper.ZKUtil; import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher; import org.junit.AfterClass; import org.junit.Before; @@ -62,8 +63,8 @@ zooKeeper = new ZooKeeperWatcher(conf, "cluster1", null); // create the unassigned region, throw up a region opened state for META - String unassignedZNode = zooKeeper.getRegionInTransitionZNode(); - zooKeeper.createZNodeIfNotExists(unassignedZNode); + String unassignedZNode = zooKeeper.assignmentZNode; + ZKUtil.createIfNotExists(zooKeeper, unassignedZNode); byte[] data = null; HBaseEventType hbEventType = HBaseEventType.RS2ZK_REGION_OPENED; try { Index: src/main/java/org/apache/hadoop/hbase/ServerStatus.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/ServerStatus.java (revision 964096) +++ src/main/java/org/apache/hadoop/hbase/ServerStatus.java (working copy) @@ -1,49 +0,0 @@ -/** - * Copyright 2010 The Apache Software Foundation - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hadoop.hbase; - -import org.apache.hadoop.conf.Configuration; -import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher; - -/** - * Set of functions that are exposed by any HBase server (implemented by the - * master and region server). - */ -public interface ServerStatus { - /** - * Return the address of the current server. - */ - public HServerAddress getHServerAddress(); - - /** - * Get the configuration object for this server. - */ - public Configuration getConfiguration(); - - /** - * Get the ZooKeeper instance for this server. - */ - public ZooKeeperWatcher getZooKeeper(); - - /** - * Stub method into ServerStatus to move forward with ZK cleanup. - */ - public void abortServer(); -} Index: src/main/java/org/apache/hadoop/hbase/ServerController.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/ServerController.java (revision 0) +++ src/main/java/org/apache/hadoop/hbase/ServerController.java (revision 0) @@ -0,0 +1,49 @@ +/** + * Copyright 2010 The Apache Software Foundation + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.hbase; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher; + +/** + * Set of functions that are exposed by any HBase process (implemented by the + * master, region server, and client). + */ +public interface ServerController { + /** + * Return the address of the current server. + */ + public HServerAddress getHServerAddress(); + + /** + * Get the configuration object for this server. + */ + public Configuration getConfiguration(); + + /** + * Get the ZooKeeper instance for this server. + */ + public ZooKeeperWatcher getZooKeeper(); + + /** + * Stub method into ServerStatus to move forward with ZK cleanup. + */ + public void abortServer(); +} Index: src/main/java/org/apache/hadoop/hbase/thrift/ThriftServer.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/thrift/ThriftServer.java (revision 964096) +++ src/main/java/org/apache/hadoop/hbase/thrift/ThriftServer.java (working copy) @@ -18,6 +18,17 @@ package org.apache.hadoop.hbase.thrift; +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLineParser; import org.apache.commons.cli.HelpFormatter; @@ -36,6 +47,7 @@ import org.apache.hadoop.hbase.HTableDescriptor; import org.apache.hadoop.hbase.KeyValue; import org.apache.hadoop.hbase.MasterNotRunningException; +import org.apache.hadoop.hbase.ZooKeeperConnectionException; import org.apache.hadoop.hbase.client.Delete; import org.apache.hadoop.hbase.client.Get; import org.apache.hadoop.hbase.client.HBaseAdmin; @@ -73,17 +85,6 @@ import org.apache.thrift.transport.TServerTransport; import org.apache.thrift.transport.TTransportFactory; -import java.io.IOException; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; - /** * ThriftServer - this class starts up a Thrift server which implements the * Hbase API specified in the Hbase.thrift IDL file. @@ -185,8 +186,10 @@ * Constructs an HBaseHandler object. * * @throws MasterNotRunningException + * @throws ZooKeeperConnectionException */ - HBaseHandler() throws MasterNotRunningException { + HBaseHandler() + throws MasterNotRunningException, ZooKeeperConnectionException { conf = HBaseConfiguration.create(); admin = new HBaseAdmin(conf); scannerMap = new HashMap(); Index: src/main/java/org/apache/hadoop/hbase/zookeeper/ZooKeeperWatcher.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/zookeeper/ZooKeeperWatcher.java (revision 964096) +++ src/main/java/org/apache/hadoop/hbase/zookeeper/ZooKeeperWatcher.java (working copy) @@ -27,7 +27,7 @@ import org.apache.commons.logging.LogFactory; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.HConstants; -import org.apache.hadoop.hbase.ServerStatus; +import org.apache.hadoop.hbase.ServerController; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; @@ -36,32 +36,35 @@ /** * Acts as the single ZooKeeper Watcher. One instance of this is instantiated * for each Master, RegionServer, and client process. - * + * * This is the only class that implements {@link Watcher}. Other internal * classes which need to be notified of ZooKeeper events must register with * the local instance of this watcher via {@link #registerListener}. - * + * * This class also holds and manages the connection to ZooKeeper. Code to deal * with connection related events and exceptions are handled here. */ public class ZooKeeperWatcher extends ZooKeeperWrapper implements Watcher { private static final Log LOG = LogFactory.getLog(ZooKeeperWatcher.class); - + // name of this watcher (for logging only) private String name; - + + // zookeeper quorum + private String quorum; + // zookeeper connection private ZooKeeper zooKeeper; - + // server controller - private ServerStatus server; - + private ServerController server; + // listeners to be notified private final Set listeners = new CopyOnWriteArraySet(); // node names - + // base znode for this cluster public String baseZNode; // znode containing location of server hosting root region @@ -74,33 +77,39 @@ public String clusterStateZNode; // znode used for region transitioning and assignment public String assignmentZNode; - + /** * Instantiate a ZooKeeper connection and watcher. * @param name name of this watcher, for logging/debug purposes only - * @throws IOException + * @throws IOException */ - public ZooKeeperWatcher(Configuration conf, String name, ServerStatus server) + public ZooKeeperWatcher(Configuration conf, String name, + ServerController server) throws IOException { super(conf, name); this.name = name; - this.zooKeeper = ZKUtil.connect(conf, this); + this.quorum = ZKConfig.getZKQuorumServersString(conf); + this.zooKeeper = ZKUtil.connect(conf, quorum, this); this.server = server; info("Connected to ZooKeeper"); setNodeNames(conf); try { + // Create all the necessary "directories" of znodes + // TODO: Move this to an init method somewhere so not everyone calls it? ZKUtil.createIfNotExists(this, baseZNode); + ZKUtil.createIfNotExists(this, assignmentZNode); + ZKUtil.createIfNotExists(this, rsZNode); } catch (KeeperException e) { error("Unexpected KeeperException creating base node", e); throw new IOException(e); } } - + /** * Set the local variable node names using the specified configuration. */ private void setNodeNames(Configuration conf) { - baseZNode = conf.get(HConstants.ZOOKEEPER_ZNODE_PARENT, + baseZNode = conf.get(HConstants.ZOOKEEPER_ZNODE_PARENT, HConstants.DEFAULT_ZOOKEEPER_ZNODE_PARENT); rootServerZNode = ZKUtil.joinZNode(baseZNode, conf.get("zookeeper.znode.rootserver", "root-region-server")); @@ -113,7 +122,7 @@ assignmentZNode = ZKUtil.joinZNode(baseZNode, conf.get("zookeeper.znode.regionInTransition", "unassigned")); } - + /** * Register the specified listener to receive ZooKeeper events. * @param listener @@ -121,18 +130,27 @@ public void registerListener(ZooKeeperListener listener) { listeners.add(listener); } - + /** * Get the connection to ZooKeeper. * @return connection reference to zookeeper */ + @Override public ZooKeeper getZooKeeper() { return zooKeeper; } - + /** + * Get the quorum address of this instance. + * @returns quorum string of this zookeeper connection instance + */ + public String getQuorum() { + return quorum; + } + + /** * Method called from ZooKeeper for events and connection status. - * + * * Valid events are passed along to listeners. Connection status changes * are dealt with locally. */ @@ -142,10 +160,10 @@ "type: " + event.getType() + ", " + "state:" + event.getState() + ", " + "path: " + event.getPath()); - + // While we are still using both ZKWs, need to call parent process() super.process(event); - + switch(event.getType()) { // If event type is NONE, this is a connection status change @@ -153,30 +171,30 @@ connectionEvent(event); break; } - + // Otherwise pass along to the listeners - + case NodeCreated: { for(ZooKeeperListener listener : listeners) { listener.nodeCreated(event.getPath()); } break; } - + case NodeDeleted: { for(ZooKeeperListener listener : listeners) { listener.nodeDeleted(event.getPath()); } break; } - + case NodeDataChanged: { for(ZooKeeperListener listener : listeners) { listener.nodeDataChanged(event.getPath()); } break; } - + case NodeChildrenChanged: { for(ZooKeeperListener listener : listeners) { listener.nodeChildrenChanged(event.getPath()); @@ -187,12 +205,12 @@ } // Connection management - + /** * Called when there is a connection-related event via the Watcher callback. - * + * * If Disconnected or Expired, this should shutdown the cluster. - * + * * @param event */ private void connectionEvent(WatchedEvent event) { @@ -200,7 +218,7 @@ // SyncConnected is normal, ignore case SyncConnected: break; - + // Abort the server if Disconnected or Expired // TODO: Åny reason to handle these two differently? case Disconnected: @@ -214,32 +232,32 @@ break; } } - + /** * Handles KeeperExceptions in client calls. - * + * * This may be temporary but for now this gives one place to deal with these. - * + * * TODO: Currently this method rethrows the exception to let the caller handle - * + * * @param ke - * @throws KeeperException + * @throws KeeperException */ public void keeperException(KeeperException ke) throws KeeperException { error("Received unexpected KeeperException, re-throwing exception", ke); throw ke; } - + /** * Handles InterruptedExceptions in client calls. - * + * * This may be temporary but for now this gives one place to deal with these. - * + * * TODO: Currently, this method does nothing. * Is this ever expected to happen? Do we abort or can we let it run? * Maybe this should be logged as WARN? It shouldn't happen? - * + * * @param ie */ public void interruptedException(InterruptedException ie) { @@ -248,7 +266,7 @@ } // Logging methods - + /** * Exposed info logging method so our zookeeper output is named. * @param string log line @@ -309,8 +327,9 @@ /** * Close the connection to ZooKeeper. - * @throws InterruptedException + * @throws InterruptedException */ + @Override public void close() { try { if(zooKeeper != null) { Index: src/main/java/org/apache/hadoop/hbase/zookeeper/ZKUtil.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/zookeeper/ZKUtil.java (revision 964096) +++ src/main/java/org/apache/hadoop/hbase/zookeeper/ZKUtil.java (working copy) @@ -19,14 +19,21 @@ */ package org.apache.hadoop.hbase.zookeeper; +import java.io.BufferedReader; import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.net.Socket; +import java.util.ArrayList; import java.util.List; import java.util.Properties; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hbase.HConstants; import org.apache.hadoop.hbase.HServerAddress; +import org.apache.hadoop.hbase.HServerInfo; import org.apache.hadoop.hbase.util.Bytes; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; @@ -65,6 +72,12 @@ throws IOException { Properties properties = ZKConfig.makeZKProps(conf); String quorum = ZKConfig.getZKQuorumServersString(properties); + return connect(conf, quorum, watcher); + } + + public static ZooKeeper connect(Configuration conf, String quorum, + Watcher watcher) + throws IOException { if(quorum == null) { throw new IOException("Unable to determine ZooKeeper quorum"); } @@ -91,6 +104,61 @@ return prefix + ZNODE_PATH_SEPARATOR + suffix; } + /** + * Returns the full path of the immediate parent of the specified node. + * @param node path to get parent of + * @return parent of path, null if passed the root node or an invalid node + */ + public static String getParent(String node) { + int idx = node.lastIndexOf(ZNODE_PATH_SEPARATOR); + return idx <= 0 ? null : node.substring(0, idx); + } + + /** + * Get the unique node-name for the specified regionserver. + * + * Used when a server puts up an ephemeral node for itself and needs to use + * a unique name. + * + * Returns the fully-qualified znode path. + * + * @param serverInfo server information + * @return unique, zookeeper-safe znode path for the server instance + */ + public static String getNodeName(HServerInfo serverInfo) { + return serverInfo.getServerName(); + } + + /** + * Get the key to the ZK ensemble for this configuration without + * adding a name at the end + * @param conf Configuration to use to build the key + * @return ensemble key without a name + */ + public static String getZooKeeperClusterKey(Configuration conf) { + return getZooKeeperClusterKey(conf, null); + } + + /** + * Get the key to the ZK ensemble for this configuration and append + * a name at the end + * @param conf Configuration to use to build the key + * @param name Name that should be appended at the end if not empty or null + * @return ensemble key with a name (if any) + */ + public static String getZooKeeperClusterKey(Configuration conf, String name) { + String quorum = conf.get(HConstants.ZOOKEEPER_QUORUM.replaceAll( + "[\\t\\n\\x0B\\f\\r]", "")); + StringBuilder builder = new StringBuilder(quorum); + builder.append(":"); + builder.append(conf.get(HConstants.ZOOKEEPER_ZNODE_PARENT)); + if (name != null && !name.isEmpty()) { + builder.append(","); + builder.append(name); + } + return builder.toString(); + } + // // Existence checks and watches // @@ -122,6 +190,33 @@ } } + /** + * Check if the specified node exists. Sets no watches. + * + * Returns true if node exists, false if not. Returns an exception if there + * is an unexpected zookeeper exception. + * + * @param zkw zk reference + * @param znode path of node to watch + * @return true if znode exists, false if does not exist or error + * @throws KeeperException if unexpected zookeeper exception + */ + public static boolean checkExists(ZooKeeperWatcher zkw, String znode) + throws KeeperException { + try { + Stat s = zkw.getZooKeeper().exists(znode, null); + return s != null ? true : false; + } catch (KeeperException e) { + zkw.warn("Unable to set watcher on znode (" + znode + ")", e); + zkw.keeperException(e); + return false; + } catch (InterruptedException e) { + zkw.warn("Unable to set watcher on znode (" + znode + ")", e); + zkw.interruptedException(e); + return false; + } + } + // // Znode listings // @@ -164,6 +259,69 @@ } /** + * Lists the children of the specified znode, retrieving the data of each + * child as a server address. + * + * Used to list the currently online regionservers and their addresses. + * + * Sets no watches at all, this method is best effort. + * + * Returns an empty list if the node has no children. Returns null if the + * parent node itself does not exist. + * + * @param zkw zookeeper reference + * @param znode node to get children of as addresses + * @return list of data of children of specified znode, empty if no children, + * null if parent does not exist + * @throws KeeperException if unexpected zookeeper exception + */ + public static List listChildrenAndGetAsAddresses( + ZooKeeperWatcher zkw, String znode) + throws KeeperException { + List children = listChildrenNoWatch(zkw, znode); + if(children == null) { + return null; + } + List addresses = + new ArrayList(children.size()); + for(String child : children) { + addresses.add(getDataAsAddress(zkw, joinZNode(znode, child))); + } + return addresses; + } + + /** + * Lists the children of the specified znode without setting any watches. + * + * Used to list the currently online regionservers and their addresses. + * + * Sets no watches at all, this method is best effort. + * + * Returns an empty list if the node has no children. Returns null if the + * parent node itself does not exist. + * + * @param zkw zookeeper reference + * @param znode node to get children of as addresses + * @return list of data of children of specified znode, empty if no children, + * null if parent does not exist + * @throws KeeperException if unexpected zookeeper exception + */ + public static List listChildrenNoWatch( + ZooKeeperWatcher zkw, String znode) + throws KeeperException { + List children = null; + try { + // List the children without watching + children = zkw.getZooKeeper().getChildren(znode, null); + } catch(KeeperException.NoNodeException nne) { + return null; + } catch(InterruptedException ie) { + zkw.interruptedException(ie); + } + return children; + } + + /** * Checks if the specified znode has any children. Sets no watches. * * Returns true if the node exists and has children. Returns false if the @@ -236,6 +394,44 @@ } /** + * Get the data at the specified znode without setting a watch. + * + * Returns the data if the node exists. Returns null if the node does not + * exist. + * + * Sets the stats of the node in the passed Stat object. Pass a null stat if + * not interested. + * + * @param zkw zk reference + * @param znode path of node + * @param stat node status to set if node exists + * @return data of the specified znode, or null if does not exist + * @throws KeeperException if unexpected zookeeper exception + */ + public static byte [] getDataNoWatch(ZooKeeperWatcher zkw, String znode, + Stat stat) + throws KeeperException { + try { + byte [] data = zkw.getZooKeeper().getData(znode, zkw, stat); + zkw.debug("Retrieved " + data.length + " bytes of data from znode (" + + znode + ") and set a watcher"); + return data; + } catch (KeeperException.NoNodeException e) { + zkw.debug("Unable to get data of znode (" + znode + ") " + + "because node does not exist (not necessarily an error)"); + return null; + } catch (KeeperException e) { + zkw.warn("Unable to get data of znode (" + znode + ")", e); + zkw.keeperException(e); + return null; + } catch (InterruptedException e) { + zkw.warn("Unable to get data of znode (" + znode + ")", e); + zkw.interruptedException(e); + return null; + } + } + + /** * Get the data at the specified znode, deserialize it as an HServerAddress, * and set a watch. * @@ -261,6 +457,35 @@ } /** + * Update the data of an existing node with the expected version to have the + * specified data. + * + * Throws an exception if there is a version mismatch or some other problem. + * + * Sets no watches under any conditions. + * + * @param zkw zk reference + * @param znode + * @param data + * @param expectedVersion + * @throws KeeperException if unexpected zookeeper exception + * @throws KeeperException.BadVersionException if version mismatch + */ + public static void updateExistingNodeData(ZooKeeperWatcher zkw, String znode, + byte [] data, int expectedVersion) + throws KeeperException { + try { + zkw.getZooKeeper().setData(znode, data, expectedVersion); + } catch(InterruptedException ie) { + zkw.interruptedException(ie); + } + } + + // + // Data setting + // + + /** * Set the specified znode to be an ephemeral node carrying the specified * server address. Used by masters for their ephemeral node and regionservers * for their ephemeral node. @@ -284,6 +509,11 @@ return createEphemeralNodeAndWatch(zkw, znode, Bytes.toBytes(address.toString())); } + + // + // Node creation + // + /** * * Set the specified znode to be an ephemeral node carrying the specified @@ -321,6 +551,42 @@ } /** + * + * Set the specified znode to be a persistent node carrying the specified + * data. + * + * Returns true if the node was successfully created, false if the node + * already existed. + * + * If the node is created successfully, a watcher is also set on the node. + * + * If the node is not created successfully because it already exists, this + * method will also set a watcher on the node. + * + * If there is another problem, a KeeperException will be thrown. + * + * @param zkw zk reference + * @param znode path of node + * @param data data of node + * @return true if node created, false if not, watch set in both cases + * @throws KeeperException if unexpected zookeeper exception + */ + public static boolean createPersistentNodeIfNotExists( + ZooKeeperWatcher zkw, String znode, byte [] data) + throws KeeperException { + try { + zkw.getZooKeeper().create(znode, data, Ids.OPEN_ACL_UNSAFE, + CreateMode.EPHEMERAL); + } catch (KeeperException.NodeExistsException nee) { + return false; + } catch (InterruptedException e) { + zkw.interruptedException(e); + return false; + } + return true; + } + + /** * Creates the specified node, if the node does not exist. Does not set a * watch and fails silently if the node already exists. * @@ -341,4 +607,152 @@ zkw.interruptedException(ie); } } + + /** + * Creates the specified node and all parent nodes required for it to exist. + * + * No watches are set and no errors are thrown if the node already exists. + * + * The nodes created are persistent and open access. + * + * @param zkw zk reference + * @param znode path of node + * @throws KeeperException if unexpected zookeeper exception + */ + public static void createWithParents(ZooKeeperWatcher zkw, + String znode) + throws KeeperException { + try { + if(znode == null) { + return; + } + zkw.getZooKeeper().create(znode, new byte[0], Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT); + } catch(KeeperException.NodeExistsException nee) { + return; + } catch(KeeperException.NoNodeException nne) { + createWithParents(zkw, getParent(znode)); + createWithParents(zkw, znode); + } catch(InterruptedException ie) { + zkw.interruptedException(ie); + } + } + + // + // Deletes + // + + /** + * Delete the specified node. Sets no watches. Throws all exceptions. + */ + public static void deleteNode(ZooKeeperWatcher zkw, String node) + throws KeeperException { + try { + zkw.getZooKeeper().delete(node, -1); + } catch(InterruptedException ie) { + } + } + + /** + * Delete the specified node and all of it's children. + * + * Sets no watches. Throws all exceptions besides dealing with deletion of + * children. + */ + public static void deleteNodeRecursively(ZooKeeperWatcher zkw, String node) + throws KeeperException { + try { + List children = ZKUtil.listChildrenNoWatch(zkw, node); + if(!children.isEmpty()) { + for(String child : children) { + deleteNodeRecursively(zkw, joinZNode(node, child)); + } + } + zkw.getZooKeeper().delete(node, -1); + } catch(InterruptedException ie) { + } + } + // + // ZooKeeper cluster information + // + + /** @return String dump of everything in ZooKeeper. */ + public static String dump(ZooKeeperWatcher zkw) { + StringBuilder sb = new StringBuilder(); + try { + sb.append("\nHBase tree in ZooKeeper is rooted at ").append(zkw.baseZNode); + sb.append("\n Cluster up? ").append(checkExists(zkw, zkw.clusterStateZNode)); + sb.append("\n Master address: ").append( + getDataAsAddress(zkw, zkw.masterAddressZNode)); + sb.append("\n Region server holding ROOT: ").append( + getDataAsAddress(zkw, zkw.rootServerZNode)); + sb.append("\n Region servers:"); + for (HServerAddress address : listChildrenAndGetAsAddresses(zkw, + zkw.rsZNode)) { + sb.append("\n - ").append(address); + } + sb.append("\n Quorum Server Statistics:"); + String[] servers = zkw.getQuorum().split(","); + for (String server : servers) { + sb.append("\n - ").append(server); + try { + String[] stat = getServerStats(server); + for (String s : stat) { + sb.append("\n ").append(s); + } + } catch (Exception e) { + sb.append("\n ERROR: ").append(e.getMessage()); + } + } + } catch(KeeperException ke) { + sb.append("\n FATAL ZooKeeper Exception!\n"); + sb.append("\n " + ke.getMessage()); + } + return sb.toString(); + } + + /** + * Gets the statistics from the given server. Uses a 1 minute timeout. + * + * @param server The server to get the statistics from. + * @return The array of response strings. + * @throws IOException When the socket communication fails. + */ + public static String[] getServerStats(String server) + throws IOException { + return getServerStats(server, 60 * 1000); + } + + /** + * Gets the statistics from the given server. + * + * @param server The server to get the statistics from. + * @param timeout The socket timeout to use. + * @return The array of response strings. + * @throws IOException When the socket communication fails. + */ + public static String[] getServerStats(String server, int timeout) + throws IOException { + String[] sp = server.split(":"); + Socket socket = new Socket(sp[0], + sp.length > 1 ? Integer.parseInt(sp[1]) : 2181); + socket.setSoTimeout(timeout); + PrintWriter out = new PrintWriter(socket.getOutputStream(), true); + BufferedReader in = new BufferedReader(new InputStreamReader( + socket.getInputStream())); + out.println("stat"); + out.flush(); + ArrayList res = new ArrayList(); + while (true) { + String line = in.readLine(); + if (line != null) { + res.add(line); + } else { + break; + } + } + socket.close(); + return res.toArray(new String[res.size()]); + } + } \ No newline at end of file Index: src/main/java/org/apache/hadoop/hbase/zookeeper/ZKConfig.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/zookeeper/ZKConfig.java (revision 964096) +++ src/main/java/org/apache/hadoop/hbase/zookeeper/ZKConfig.java (working copy) @@ -39,7 +39,7 @@ */ public class ZKConfig { private static final Log LOG = LogFactory.getLog(ZKConfig.class); - + private static final String VARIABLE_START = "${"; private static final int VARIABLE_START_LENGTH = VARIABLE_START.length(); private static final String VARIABLE_END = "}"; @@ -49,7 +49,7 @@ private static final int ZK_CFG_PROPERTY_SIZE = ZK_CFG_PROPERTY.length(); private static final String ZK_CLIENT_PORT_KEY = ZK_CFG_PROPERTY + "clientPort"; - + /** * Make a Properties object holding ZooKeeper config equivalent to zoo.cfg. * If there is a zoo.cfg in the classpath, simply read it in. Otherwise parse @@ -178,9 +178,9 @@ } return properties; } - + /** - * Return the ZK Quorum servers string given zk properties returned by + * Return the ZK Quorum servers string given zk properties returned by * makeZKProps * @param properties * @return @@ -240,7 +240,7 @@ return hostPortBuilder.toString(); } - + /** * Return the ZK Quorum servers string given the specified configuration. * @param properties Index: src/main/java/org/apache/hadoop/hbase/avro/AvroServer.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/avro/AvroServer.java (revision 964096) +++ src/main/java/org/apache/hadoop/hbase/avro/AvroServer.java (working copy) @@ -20,10 +20,7 @@ import java.io.IOException; import java.nio.ByteBuffer; -import java.util.Collection; import java.util.HashMap; -import java.util.Iterator; -import java.util.List; import org.apache.avro.Schema; import org.apache.avro.generic.GenericArray; @@ -33,33 +30,18 @@ import org.apache.avro.util.Utf8; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; - import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.HBaseConfiguration; -import org.apache.hadoop.hbase.HColumnDescriptor; import org.apache.hadoop.hbase.HTableDescriptor; import org.apache.hadoop.hbase.MasterNotRunningException; import org.apache.hadoop.hbase.TableExistsException; -import org.apache.hadoop.hbase.client.Delete; -import org.apache.hadoop.hbase.client.Get; -import org.apache.hadoop.hbase.client.HBaseAdmin; -import org.apache.hadoop.hbase.client.HTable; -import org.apache.hadoop.hbase.client.HTableInterface; -import org.apache.hadoop.hbase.client.HTablePool; -import org.apache.hadoop.hbase.client.Put; -import org.apache.hadoop.hbase.client.Result; -import org.apache.hadoop.hbase.client.ResultScanner; -import org.apache.hadoop.hbase.client.Scan; -import org.apache.hadoop.hbase.util.Bytes; - +import org.apache.hadoop.hbase.ZooKeeperConnectionException; import org.apache.hadoop.hbase.avro.generated.AClusterStatus; -import org.apache.hadoop.hbase.avro.generated.AColumnValue; -import org.apache.hadoop.hbase.avro.generated.ACompressionAlgorithm; import org.apache.hadoop.hbase.avro.generated.ADelete; import org.apache.hadoop.hbase.avro.generated.AFamilyDescriptor; import org.apache.hadoop.hbase.avro.generated.AGet; -import org.apache.hadoop.hbase.avro.generated.AIllegalArgument; import org.apache.hadoop.hbase.avro.generated.AIOError; +import org.apache.hadoop.hbase.avro.generated.AIllegalArgument; import org.apache.hadoop.hbase.avro.generated.AMasterNotRunning; import org.apache.hadoop.hbase.avro.generated.APut; import org.apache.hadoop.hbase.avro.generated.AResult; @@ -67,6 +49,13 @@ import org.apache.hadoop.hbase.avro.generated.ATableDescriptor; import org.apache.hadoop.hbase.avro.generated.ATableExists; import org.apache.hadoop.hbase.avro.generated.HBase; +import org.apache.hadoop.hbase.client.HBaseAdmin; +import org.apache.hadoop.hbase.client.HTableInterface; +import org.apache.hadoop.hbase.client.HTablePool; +import org.apache.hadoop.hbase.client.Result; +import org.apache.hadoop.hbase.client.ResultScanner; +import org.apache.hadoop.hbase.client.Scan; +import org.apache.hadoop.hbase.util.Bytes; /** * Start an Avro server @@ -137,8 +126,9 @@ * Constructs an HBaseImpl object. * * @throws MasterNotRunningException + * @throws ZooKeeperConnectionException */ - HBaseImpl() throws MasterNotRunningException { + HBaseImpl() throws MasterNotRunningException, ZooKeeperConnectionException { conf = HBaseConfiguration.create(); admin = new HBaseAdmin(conf); htablePool = new HTablePool(conf, 10); Index: src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java (revision 964096) +++ src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java (working copy) @@ -71,7 +71,7 @@ import org.apache.hadoop.hbase.LocalHBaseCluster; import org.apache.hadoop.hbase.NotServingRegionException; import org.apache.hadoop.hbase.RemoteExceptionHandler; -import org.apache.hadoop.hbase.ServerStatus; +import org.apache.hadoop.hbase.ServerController; import org.apache.hadoop.hbase.UnknownRowLockException; import org.apache.hadoop.hbase.UnknownScannerException; import org.apache.hadoop.hbase.YouAreDeadException; @@ -109,13 +109,14 @@ import org.apache.hadoop.net.DNS; import org.apache.hadoop.util.Progressable; import org.apache.hadoop.util.StringUtils; +import org.apache.zookeeper.KeeperException; /** * HRegionServer makes a set of HRegions available to clients. It checks in with * the HMaster. There are many HRegionServers in a single HBase deployment. */ public class HRegionServer implements HRegionInterface, - HBaseRPCErrorHandler, Runnable, Stoppable, ServerStatus { + HBaseRPCErrorHandler, Runnable, Stoppable, ServerController { public static final Log LOG = LogFactory.getLog(HRegionServer.class); private static final HMsg REPORT_EXITING = new HMsg(Type.MSG_REPORT_EXITING); private static final HMsg REPORT_QUIESCED = new HMsg(Type.MSG_REPORT_QUIESCED); @@ -1171,11 +1172,15 @@ if (LOG.isDebugEnabled()) LOG.debug("sending initial server load: " + hsl); lastMsg = System.currentTimeMillis(); - zooKeeper.writeRSLocation(this.serverInfo); + ZKUtil.setAddressAndWatch(zooKeeper, + ZKUtil.joinZNode(zooKeeper.rsZNode, ZKUtil.getNodeName(serverInfo)), + address); result = this.hbaseMaster.regionServerStartup(this.serverInfo); break; } catch (IOException e) { LOG.warn("error telling master we are up", e); + } catch (KeeperException e) { + LOG.warn("error putting up ephemeral node in zookeeper", e); } sleeper.sleep(lastMsg); } Index: src/main/java/org/apache/hadoop/hbase/regionserver/MasterAddressManager.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/regionserver/MasterAddressManager.java (revision 964096) +++ src/main/java/org/apache/hadoop/hbase/regionserver/MasterAddressManager.java (working copy) @@ -22,7 +22,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.hbase.HServerAddress; -import org.apache.hadoop.hbase.ServerStatus; +import org.apache.hadoop.hbase.ServerController; import org.apache.hadoop.hbase.zookeeper.ZKUtil; import org.apache.hadoop.hbase.zookeeper.ZooKeeperListener; import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher; @@ -49,7 +49,7 @@ private HServerAddress masterAddress; // Status and controller for the regionserver - private ServerStatus status; + private ServerController status; /** * Construct a master address listener with the specified zookeeper reference. @@ -60,7 +60,7 @@ * * @param watcher zk reference and watcher */ - public MasterAddressManager(ZooKeeperWatcher watcher, ServerStatus status) { + public MasterAddressManager(ZooKeeperWatcher watcher, ServerController status) { super(watcher); this.status = status; this.masterAddress = null; Index: src/main/java/org/apache/hadoop/hbase/regionserver/RSZookeeperUpdater.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/regionserver/RSZookeeperUpdater.java (revision 964096) +++ src/main/java/org/apache/hadoop/hbase/regionserver/RSZookeeperUpdater.java (working copy) @@ -8,21 +8,24 @@ import org.apache.hadoop.hbase.executor.RegionTransitionEventData; import org.apache.hadoop.hbase.executor.HBaseEventHandler.HBaseEventType; import org.apache.hadoop.hbase.util.Writables; +import org.apache.hadoop.hbase.zookeeper.ZKUtil; import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher; import org.apache.zookeeper.CreateMode; +import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.data.Stat; /** - * This is a helper class for region servers to update various states in - * Zookeeper. The various updates are abstracted out here. - * - * The "startRegionXXX" methods are to be called first, followed by the - * "finishRegionXXX" methods. Supports updating zookeeper periodically as a + * This is a helper class for region servers to update various states in + * Zookeeper. The various updates are abstracted out here. + * + * The "startRegionXXX" methods are to be called first, followed by the + * "finishRegionXXX" methods. Supports updating zookeeper periodically as a * part of the "startRegionXXX". Currently handles the following state updates: * - Close region * - Open region */ // TODO: make this thread local, in which case it is re-usable per thread +// TODO: After open/close is direct RPC, move this logic into Handlers public class RSZookeeperUpdater { private static final Log LOG = LogFactory.getLog(RSZookeeperUpdater.class); private final String regionServerName; @@ -36,59 +39,67 @@ String regionName) { this(zooKeeper, regionServerName, regionName, 0); } - + public RSZookeeperUpdater(ZooKeeperWatcher zooKeeper, String regionServerName, String regionName, int zkVersion) { this.zooKeeper = zooKeeper; this.regionServerName = regionServerName; this.regionName = regionName; // get the region ZNode we have to create - this.regionZNode = zooKeeper.getZNode(zooKeeper.assignmentZNode, regionName); + this.regionZNode = ZKUtil.joinZNode(zooKeeper.assignmentZNode, regionName); this.zkVersion = zkVersion; } - + /** - * This method updates the various states in ZK to inform the master that the + * This method updates the various states in ZK to inform the master that the * region server has started closing the region. * @param updatePeriodically - if true, periodically updates the state in ZK */ public void startRegionCloseEvent(HMsg hmsg, boolean updatePeriodically) throws IOException { - // if this ZNode already exists, something is wrong - if(zooKeeper.exists(regionZNode, true)) { - String msg = "ZNode " + regionZNode + " already exists in ZooKeeper, will NOT close region."; - LOG.error(msg); - throw new IOException(msg); + // Try to create the node with a CLOSING state, if already exists, + // something is wrong + try { + if(ZKUtil.createPersistentNodeIfNotExists(zooKeeper, regionZNode, + makeZKEventData(HBaseEventType.RS2ZK_REGION_CLOSING, hmsg))) { + String msg = "ZNode " + regionZNode + " already exists in ZooKeeper, will NOT close region."; + LOG.error(msg); + throw new IOException(msg); + } + } catch (KeeperException e) { + zooKeeper.error("Unexpected exception trying to create unassigned node", e); + throw new IOException(e); } - - // create the region node in the unassigned directory first - zooKeeper.createZNodeIfNotExists(regionZNode, null, CreateMode.PERSISTENT, true); - // update the data for "regionName" ZNode in unassigned to CLOSING - updateZKWithEventData(HBaseEventType.RS2ZK_REGION_CLOSING, hmsg); - // TODO: implement the updatePeriodically logic here } /** - * This method updates the states in ZK to signal that the region has been + * This method updates the states in ZK to signal that the region has been * closed. This will stop the periodic updater thread if one was started. * @throws IOException */ - public void finishRegionCloseEvent(HMsg hmsg) throws IOException { + public void finishRegionCloseEvent(HMsg hmsg) throws IOException { // TODO: stop the updatePeriodically here // update the data for "regionName" ZNode in unassigned to CLOSED updateZKWithEventData(HBaseEventType.RS2ZK_REGION_CLOSED, hmsg); } - + /** - * This method updates the various states in ZK to inform the master that the + * This method updates the various states in ZK to inform the master that the * region server has started opening the region. * @param updatePeriodically - if true, periodically updates the state in ZK */ - public void startRegionOpenEvent(HMsg hmsg, boolean updatePeriodically) throws IOException { + public void startRegionOpenEvent(HMsg hmsg, boolean updatePeriodically) + throws IOException { Stat stat = new Stat(); - byte[] data = zooKeeper.readZNode(regionZNode, stat); + byte[] data = null; + try { + data = ZKUtil.getDataNoWatch(zooKeeper, regionZNode, stat); + } catch (KeeperException e) { + zooKeeper.error("ZooKeeper error", e); + throw new IOException(e); + } // if there is no ZNode for this region, something is wrong if(data == null) { String msg = "ZNode " + regionZNode + " does not exist in ZooKeeper, will NOT open region."; @@ -108,12 +119,12 @@ // update the data for "regionName" ZNode in unassigned to CLOSING updateZKWithEventData(HBaseEventType.RS2ZK_REGION_OPENING, hmsg); - + // TODO: implement the updatePeriodically logic here } - + /** - * This method updates the states in ZK to signal that the region has been + * This method updates the states in ZK to signal that the region has been * opened. This will stop the periodic updater thread if one was started. * @throws IOException */ @@ -123,7 +134,7 @@ // update the data for "regionName" ZNode in unassigned to CLOSED updateZKWithEventData(HBaseEventType.RS2ZK_REGION_OPENED, hmsg); } - + public boolean isClosingRegion() { return (lastUpdatedState == HBaseEventType.RS2ZK_REGION_CLOSING); } @@ -141,19 +152,42 @@ updateZKWithEventData(HBaseEventType.RS2ZK_REGION_CLOSED, hmsg); } - private void updateZKWithEventData(HBaseEventType hbEventType, HMsg hmsg) throws IOException { - // update the data for "regionName" ZNode in unassigned to "hbEventType" - byte[] data = null; + /** + * Make the serialized data to put into unassigned znodes for the specified + * event type and message. + * @param eventType + * @param hmsg + * @return serialized data + */ + private byte [] makeZKEventData(HBaseEventType eventType, HMsg hmsg) + throws IOException { + return Writables.getBytes(new RegionTransitionEventData(eventType, + regionServerName, hmsg)); + } + + /** + * Update the data for this region to the serialized form of the specified + * event type and message. + * @param hbEventType + * @param hmsg + * @throws IOException + */ + private void updateZKWithEventData(HBaseEventType eventType, HMsg hmsg) + throws IOException { + byte[] data = makeZKEventData(eventType, hmsg); + LOG.debug("Updating ZNode " + regionZNode + + " with [" + eventType + "]" + + " expected version = " + zkVersion); try { - data = Writables.getBytes(new RegionTransitionEventData(hbEventType, regionServerName, hmsg)); - } catch (IOException e) { - LOG.error("Error creating event data for " + hbEventType, e); + ZKUtil.updateExistingNodeData(zooKeeper, regionZNode, data, zkVersion); + } catch(KeeperException.BadVersionException e) { + zooKeeper.error("Version mismatch on unassigned znode when updating", e); + throw new IOException(e); + } catch(KeeperException e) { + zooKeeper.error("Unexpected exception trying to update unassigned node", e); + throw new IOException(e); } - LOG.debug("Updating ZNode " + regionZNode + - " with [" + hbEventType + "]" + - " expected version = " + zkVersion); - lastUpdatedState = hbEventType; - zooKeeper.writeZNode(regionZNode, data, zkVersion, true); + lastUpdatedState = eventType; zkVersion++; } } Index: src/main/java/org/apache/hadoop/hbase/ZooKeeperConnectionException.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/ZooKeeperConnectionException.java (revision 0) +++ src/main/java/org/apache/hadoop/hbase/ZooKeeperConnectionException.java (revision 0) @@ -0,0 +1,49 @@ +/** + * Copyright 2010 The Apache Software Foundation + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.hbase; + +import java.io.IOException; + +/** + * Thrown if the client can't connect to zookeeper + */ +public class ZooKeeperConnectionException extends IOException { + private static final long serialVersionUID = 1L << 23 - 1L; + /** default constructor */ + public ZooKeeperConnectionException() { + super(); + } + + /** + * Constructor + * @param s message + */ + public ZooKeeperConnectionException(String s) { + super(s); + } + + /** + * Constructor taking another exception. + * @param e Exception to grab data from. + */ + public ZooKeeperConnectionException(Exception e) { + super(e); + } +} Index: src/main/java/org/apache/hadoop/hbase/master/MasterStatus.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/master/MasterStatus.java (revision 964096) +++ src/main/java/org/apache/hadoop/hbase/master/MasterStatus.java (working copy) @@ -21,7 +21,7 @@ import java.util.concurrent.atomic.AtomicBoolean; -import org.apache.hadoop.hbase.ServerStatus; +import org.apache.hadoop.hbase.ServerController; import org.apache.hadoop.hbase.client.ServerConnection; /** @@ -31,7 +31,7 @@ * TODO: this list has to be cleaned up, this is a re-factor only change that * preserves the functions in the interface. */ -public interface MasterStatus extends ServerStatus { +public interface MasterStatus extends ServerController { /** * Return the server manager for region server related info Index: src/main/java/org/apache/hadoop/hbase/master/HMaster.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/master/HMaster.java (revision 964096) +++ src/main/java/org/apache/hadoop/hbase/master/HMaster.java (working copy) @@ -53,6 +53,7 @@ import org.apache.hadoop.hbase.MiniZooKeeperCluster; import org.apache.hadoop.hbase.RemoteExceptionHandler; import org.apache.hadoop.hbase.TableExistsException; +import org.apache.hadoop.hbase.ZooKeeperConnectionException; import org.apache.hadoop.hbase.client.Get; import org.apache.hadoop.hbase.client.HBaseAdmin; import org.apache.hadoop.hbase.client.MetaScanner; @@ -1136,6 +1137,9 @@ } catch (MasterNotRunningException e) { LOG.error("Master not running"); System.exit(0); + } catch (ZooKeeperConnectionException e) { + LOG.error("ZooKeeper not available"); + System.exit(0); } try { adm.shutdown(); Index: src/main/java/org/apache/hadoop/hbase/client/HConnectionManager.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/client/HConnectionManager.java (revision 964096) +++ src/main/java/org/apache/hadoop/hbase/client/HConnectionManager.java (working copy) @@ -50,22 +50,23 @@ import org.apache.hadoop.hbase.KeyValue; import org.apache.hadoop.hbase.MasterNotRunningException; import org.apache.hadoop.hbase.RemoteExceptionHandler; +import org.apache.hadoop.hbase.ServerController; import org.apache.hadoop.hbase.TableNotFoundException; +import org.apache.hadoop.hbase.ZooKeeperConnectionException; import org.apache.hadoop.hbase.client.MetaScanner.MetaScannerVisitor; import org.apache.hadoop.hbase.ipc.HBaseRPC; import org.apache.hadoop.hbase.ipc.HBaseRPCProtocolVersion; import org.apache.hadoop.hbase.ipc.HMasterInterface; import org.apache.hadoop.hbase.ipc.HRegionInterface; +import org.apache.hadoop.hbase.regionserver.MasterAddressManager; import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.hbase.util.MetaUtils; import org.apache.hadoop.hbase.util.SoftValueSortedMap; import org.apache.hadoop.hbase.util.Writables; +import org.apache.hadoop.hbase.zookeeper.ZKUtil; import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher; -import org.apache.hadoop.hbase.zookeeper.ZooKeeperWrapper; import org.apache.hadoop.ipc.RemoteException; -import org.apache.zookeeper.WatchedEvent; -import org.apache.zookeeper.Watcher; -import org.apache.zookeeper.Watcher.Event.KeeperState; +import org.apache.zookeeper.KeeperException; /** * A non-instantiable class that manages connections to multiple tables in @@ -107,16 +108,15 @@ } }; - private static final Map ZK_WRAPPERS = - new HashMap(); - /** * Get the connection object for the instance specified by the configuration * If no current connection exists, create a new connection for that instance * @param conf configuration * @return HConnection object for the instance specified by the configuration + * @throws ZooKeeperConnectionException */ - public static HConnection getConnection(Configuration conf) { + public static HConnection getConnection(Configuration conf) + throws ZooKeeperConnectionException { TableServers connection; Integer key = HBaseConfiguration.hashCode(conf); synchronized (HBASE_INSTANCES) { @@ -148,7 +148,7 @@ /** * Delete information for all connections. * @param stopProxy stop the proxy as well - * @throws IOException + * @throws IOException */ public static void deleteAllConnections(boolean stopProxy) { synchronized (HBASE_INSTANCES) { @@ -158,103 +158,16 @@ } } } - synchronized (ZK_WRAPPERS) { - for (ClientZKWatcher watch : ZK_WRAPPERS.values()) { - watch.resetZooKeeper(); - } - } } /** - * Get a watcher of a zookeeper connection for a given quorum address. - * If the connection isn't established, a new one is created. - * This acts like a multiton. - * @param conf configuration - * @return ZKW watcher - * @throws IOException if a remote or network exception occurs - */ - public static synchronized ClientZKWatcher getClientZooKeeperWatcher( - Configuration conf) throws IOException { - if (!ZK_WRAPPERS.containsKey( - ZooKeeperWrapper.getZookeeperClusterKey(conf))) { - ZK_WRAPPERS.put(ZooKeeperWrapper.getZookeeperClusterKey(conf), - new ClientZKWatcher(conf)); - } - return ZK_WRAPPERS.get(ZooKeeperWrapper.getZookeeperClusterKey(conf)); - } - - /** - * This class is responsible to handle connection and reconnection - * to a zookeeper quorum. - * - */ - public static class ClientZKWatcher implements Watcher { - - static final Log LOG = LogFactory.getLog(ClientZKWatcher.class); - private ZooKeeperWrapper zooKeeperWrapper; - private Configuration conf; - - /** - * Takes a configuration to pass it to ZKW but won't instanciate it - * @param conf configuration - */ - public ClientZKWatcher(Configuration conf) { - this.conf = conf; - } - - /** - * Called by ZooKeeper when an event occurs on our connection. We use this to - * detect our session expiring. When our session expires, we have lost our - * connection to ZooKeeper. Our handle is dead, and we need to recreate it. - * - * See http://hadoop.apache.org/zookeeper/docs/current/zookeeperProgrammers.html#ch_zkSessions - * for more information. - * - * @param event WatchedEvent witnessed by ZooKeeper. - */ - public void process(WatchedEvent event) { - KeeperState state = event.getState(); - if(!state.equals(KeeperState.SyncConnected)) { - LOG.debug("Got ZooKeeper event, state: " + state + ", type: " - + event.getType() + ", path: " + event.getPath()); - } - if (state == KeeperState.Expired) { - resetZooKeeper(); - } - } - - /** - * Get this watcher's ZKW, instantiate it if necessary. - * @return ZKW - * @throws java.io.IOException if a remote or network exception occurs - */ - public synchronized ZooKeeperWrapper getZooKeeperWrapper() throws IOException { - if(zooKeeperWrapper == null) { - zooKeeperWrapper = new ZooKeeperWatcher(conf, - HConnectionManager.class.getName(), null); - zooKeeperWrapper.registerListener(this); - } - return zooKeeperWrapper; - } - - /** - * Clear this connection to zookeeper. - */ - private synchronized void resetZooKeeper() { - if (zooKeeperWrapper != null) { - zooKeeperWrapper.close(); - zooKeeperWrapper = null; - } - } - } - - /** * It is provided for unit test cases which verify the behavior of region * location cache prefetch. * @return Number of cached regions for the table. + * @throws ZooKeeperConnectionException */ static int getCachedRegionCount(Configuration conf, - byte[] tableName) { + byte[] tableName) throws ZooKeeperConnectionException { TableServers connection = (TableServers)getConnection(conf); return connection.getNumberOfCachedRegionLocations(tableName); } @@ -263,15 +176,16 @@ * It's provided for unit test cases which verify the behavior of region * location cache prefetch. * @return true if the region where the table and row reside is cached. + * @throws ZooKeeperConnectionException */ static boolean isRegionCached(Configuration conf, - byte[] tableName, byte[] row) { + byte[] tableName, byte[] row) throws ZooKeeperConnectionException { TableServers connection = (TableServers)getConnection(conf); return connection.isRegionCached(tableName, row); } /* Encapsulates finding the servers for an HBase instance */ - static class TableServers implements ServerConnection { + static class TableServers implements ServerConnection, ServerController { static final Log LOG = LogFactory.getLog(TableServers.class); private final Class serverInterfaceClass; private final long pause; @@ -284,6 +198,10 @@ private volatile boolean closed; private volatile HMasterInterface master; private volatile boolean masterChecked; + // ZooKeeper reference + private ZooKeeperWatcher zooKeeper; + // ZooKeeper-based master address tracker + private MasterAddressManager masterAddressManager; private final Object rootRegionLock = new Object(); private final Object metaRegionLock = new Object(); @@ -312,7 +230,8 @@ * @param conf Configuration object */ @SuppressWarnings("unchecked") - public TableServers(Configuration conf) { + public TableServers(Configuration conf) + throws ZooKeeperConnectionException { this.conf = conf; String serverClassName = @@ -340,14 +259,20 @@ this.prefetchRegionLimit = conf.getInt("hbase.client.prefetch.limit", 10); + // initialize zookeeper and master address manager + getZooKeeperWatcher(); + masterAddressManager = new MasterAddressManager(zooKeeper, this); + masterAddressManager.monitorMaster(); + this.master = null; this.masterChecked = false; } private long getPauseTime(int tries) { int ntries = tries; - if (ntries >= HConstants.RETRY_BACKOFF.length) + if (ntries >= HConstants.RETRY_BACKOFF.length) { ntries = HConstants.RETRY_BACKOFF.length - 1; + } return this.pause * HConstants.RETRY_BACKOFF[ntries]; } @@ -365,12 +290,22 @@ this.rootRegionLocation = rootRegion; } - public HMasterInterface getMaster() throws MasterNotRunningException { - ZooKeeperWrapper zk; + public HMasterInterface getMaster() + throws MasterNotRunningException, ZooKeeperConnectionException { + + // Check if we already have a good master connection + if (master != null) { + if(master.isMasterRunning()) { + return master; + } + } + + // If not, we need to connect to ZK to get the + ZooKeeperWatcher zk; try { - zk = getZooKeeperWrapper(); + zk = getZooKeeperWatcher(); } catch (IOException e) { - throw new MasterNotRunningException(e); + throw new ZooKeeperConnectionException(e); } HServerAddress masterLocation = null; @@ -382,7 +317,11 @@ tries++) { try { - masterLocation = zk.readMasterAddressOrThrow(); + masterLocation = masterAddressManager.getMasterAddress(); + if(masterLocation == null) { + LOG.info("ZooKeeper available but no active master location found"); + throw new MasterNotRunningException(); + } HMasterInterface tryMaster = (HMasterInterface)HBaseRPC.getProxy( HMasterInterface.class, HBaseRPCProtocolVersion.versionID, @@ -424,20 +363,20 @@ return this.master; } - public boolean isMasterRunning() { + public boolean isMasterRunning() + throws MasterNotRunningException, ZooKeeperConnectionException { if (this.master == null) { - try { - getMaster(); - - } catch (MasterNotRunningException e) { - return false; - } + getMaster(); } - return true; + boolean isRunning = master.isMasterRunning(); + if(isRunning) { + return true; + } + throw new MasterNotRunningException(); } public boolean tableExists(final byte [] tableName) - throws MasterNotRunningException { + throws MasterNotRunningException, ZooKeeperConnectionException { getMaster(); if (tableName == null) { throw new IllegalArgumentException("Table name cannot be null"); @@ -1047,10 +986,24 @@ return getHRegionConnection(regionServer, false); } - public synchronized ZooKeeperWrapper getZooKeeperWrapper() - throws IOException { - return HConnectionManager.getClientZooKeeperWatcher(conf) - .getZooKeeperWrapper(); + /** + * Get the ZooKeeper instance for this TableServers instance. + * + * If ZK has not been initialized yet, this will connect to ZK. + * @returns zookeeper reference + * @throws ZooKeeperConncetionException if there's a problem connecting to zk + */ + public synchronized ZooKeeperWatcher getZooKeeperWatcher() + throws ZooKeeperConnectionException { + if(zooKeeper == null) { + try { + zooKeeper = new ZooKeeperWatcher(conf, + ZKUtil.getZooKeeperClusterKey(conf), this); + } catch (IOException e) { + throw new ZooKeeperConnectionException(e); + } + } + return zooKeeper; } /* @@ -1065,7 +1018,12 @@ // We lazily instantiate the ZooKeeper object because we don't want to // make the constructor have to throw IOException or handle it itself. - ZooKeeperWrapper zk = getZooKeeperWrapper(); + ZooKeeperWatcher zk; + try { + zk = getZooKeeperWatcher(); + } catch (IOException e) { + throw new ZooKeeperConnectionException(e); + } HServerAddress rootRegionAddress = null; for (int tries = 0; tries < numRetries; tries++) { @@ -1074,7 +1032,13 @@ while (rootRegionAddress == null && localTimeouts < numRetries) { // Don't read root region until we're out of safe mode so we know // that the meta regions have been assigned. - rootRegionAddress = zk.readRootRegionLocation(); + try { + rootRegionAddress = ZKUtil.getDataAsAddress(zk, zk.rootServerZNode); + } catch (KeeperException e) { + LOG.error("Unexpected ZooKeeper error attempting to read the root " + + "region server address"); + throw new IOException(e); + } if (rootRegionAddress == null) { try { if (LOG.isDebugEnabled()) { @@ -1333,8 +1297,12 @@ public int processBatchOfRows(final ArrayList list, final byte[] tableName) throws IOException { - if (list.isEmpty()) return 0; - if (list.size() > 1) Collections.sort(list); + if (list.isEmpty()) { + return 0; + } + if (list.size() > 1) { + Collections.sort(list); + } Batch b = new Batch(this) { @SuppressWarnings("unchecked") @Override @@ -1356,8 +1324,12 @@ public int processBatchOfDeletes(final List list, final byte[] tableName) throws IOException { - if (list.isEmpty()) return 0; - if (list.size() > 1) Collections.sort(list); + if (list.isEmpty()) { + return 0; + } + if (list.size() > 1) { + Collections.sort(list); + } Batch b = new Batch(this) { @SuppressWarnings("unchecked") @Override @@ -1606,5 +1578,40 @@ new HRegionLocation(e.getKey(), e.getValue())); } } + + // ServerController implementation so that we can use ZooKeeperWatcher + // Our abort() call does the ZK reset() as was previously done when + // getting ZK expiration + // TODO: Maybe this is not right. Should there be a super-interface to + // ServerStatus/Controller that _just_ has the abort method? + // The only method that really makes no sense here is get address + + @Override + public void abortServer() { + if(zooKeeper != null) { + zooKeeper.close(); + zooKeeper = null; + } + } + + @Override + public Configuration getConfiguration() { + return conf; + } + + @Override + public HServerAddress getHServerAddress() { + return null; + } + + @Override + public ZooKeeperWatcher getZooKeeper() { + try { + return getZooKeeperWatcher(); + } catch (IOException e) { + LOG.error("Problem getting zk watcher", e); + return null; + } + } } } Index: src/main/java/org/apache/hadoop/hbase/client/HConnection.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/client/HConnection.java (revision 964096) +++ src/main/java/org/apache/hadoop/hbase/client/HConnection.java (working copy) @@ -19,50 +19,55 @@ */ package org.apache.hadoop.hbase.client; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutorService; + import org.apache.hadoop.hbase.HRegionInfo; import org.apache.hadoop.hbase.HRegionLocation; import org.apache.hadoop.hbase.HServerAddress; import org.apache.hadoop.hbase.HTableDescriptor; import org.apache.hadoop.hbase.MasterNotRunningException; +import org.apache.hadoop.hbase.ZooKeeperConnectionException; import org.apache.hadoop.hbase.ipc.HMasterInterface; import org.apache.hadoop.hbase.ipc.HRegionInterface; -import org.apache.hadoop.hbase.zookeeper.ZooKeeperWrapper; +import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ExecutorService; - /** * Cluster connection. * {@link HConnectionManager} manages instances of this class. */ public interface HConnection { /** - * Retrieve ZooKeeperWrapper used by the connection. - * @return ZooKeeperWrapper handle being used by the connection. + * Retrieve ZooKeeperWatcher used by the connection. + * @return ZooKeeperWatcher handle being used by the connection. * @throws IOException if a remote or network exception occurs */ - public ZooKeeperWrapper getZooKeeperWrapper() throws IOException; + public ZooKeeperWatcher getZooKeeperWatcher() throws IOException; /** * @return proxy connection to master server for this instance * @throws MasterNotRunningException if the master is not running + * @throws ZooKeeperConnectionException if unable to connect to zookeeper */ - public HMasterInterface getMaster() throws MasterNotRunningException; + public HMasterInterface getMaster() + throws MasterNotRunningException, ZooKeeperConnectionException; /** @return - true if the master server is running */ - public boolean isMasterRunning(); + public boolean isMasterRunning() + throws MasterNotRunningException, ZooKeeperConnectionException; /** * Checks if tableName exists. * @param tableName Table to check. * @return True if table exists already. * @throws MasterNotRunningException if the master is not running + * @throws ZooKeeperConnectionException if unable to connect to zookeeper */ public boolean tableExists(final byte [] tableName) - throws MasterNotRunningException; + throws MasterNotRunningException, ZooKeeperConnectionException; /** * A table that isTableEnabled == false and isTableDisabled == false Index: src/main/java/org/apache/hadoop/hbase/client/HBaseAdmin.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/client/HBaseAdmin.java (revision 964096) +++ src/main/java/org/apache/hadoop/hbase/client/HBaseAdmin.java (working copy) @@ -19,6 +19,11 @@ */ package org.apache.hadoop.hbase.client; +import java.io.IOException; +import java.util.Arrays; +import java.util.Map; +import java.util.NavigableMap; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.conf.Configuration; @@ -33,6 +38,7 @@ import org.apache.hadoop.hbase.RegionException; import org.apache.hadoop.hbase.RemoteExceptionHandler; import org.apache.hadoop.hbase.TableExistsException; +import org.apache.hadoop.hbase.ZooKeeperConnectionException; import org.apache.hadoop.hbase.io.ImmutableBytesWritable; import org.apache.hadoop.hbase.ipc.HMasterInterface; import org.apache.hadoop.hbase.ipc.HRegionInterface; @@ -43,11 +49,6 @@ import org.apache.hadoop.io.Writable; import org.apache.hadoop.ipc.RemoteException; -import java.io.IOException; -import java.util.Arrays; -import java.util.Map; -import java.util.NavigableMap; - /** * Provides administrative functions for HBase */ @@ -58,20 +59,22 @@ private volatile Configuration conf; private final long pause; private final int numRetries; - private volatile HMasterInterface master; /** * Constructor * * @param conf Configuration object * @throws MasterNotRunningException if the master is not running + * @throws ZooKeeperConnectionException if unable to connect to zookeeper */ - public HBaseAdmin(Configuration conf) throws MasterNotRunningException { + public HBaseAdmin(Configuration conf) + throws MasterNotRunningException, ZooKeeperConnectionException { this.connection = HConnectionManager.getConnection(conf); this.conf = conf; this.pause = conf.getLong("hbase.client.pause", 30 * 1000); this.numRetries = conf.getInt("hbase.client.retries.number", 5); - this.master = connection.getMaster(); + // make sure we can get to the master + connection.getMaster(); } /** @return HConnection used by this object. */ @@ -80,15 +83,21 @@ } /** + * Get a connection to the currently set master. * @return proxy connection to master server for this instance * @throws MasterNotRunningException if the master is not running + * @throws ZooKeeperConnectionException if unable to connect to zookeeper */ - public HMasterInterface getMaster() throws MasterNotRunningException{ + public HMasterInterface getMaster() + throws MasterNotRunningException, ZooKeeperConnectionException { return this.connection.getMaster(); } - /** @return - true if the master server is running */ - public boolean isMasterRunning() { + /** @return - true if the master server is running + * @throws ZooKeeperConnectionException + * @throws MasterNotRunningException */ + public boolean isMasterRunning() + throws MasterNotRunningException, ZooKeeperConnectionException { return this.connection.isMasterRunning(); } @@ -96,9 +105,10 @@ * @param tableName Table to check. * @return True if table exists already. * @throws MasterNotRunningException if the master is not running + * @throws ZooKeeperConnectionException if unable to connect to zookeeper */ public boolean tableExists(final String tableName) - throws MasterNotRunningException { + throws MasterNotRunningException, ZooKeeperConnectionException { return tableExists(Bytes.toBytes(tableName)); } @@ -106,12 +116,11 @@ * @param tableName Table to check. * @return True if table exists already. * @throws MasterNotRunningException if the master is not running + * @throws ZooKeeperConnectionException if unable to connect to zookeeper */ public boolean tableExists(final byte [] tableName) - throws MasterNotRunningException { - if (this.master == null) { - throw new MasterNotRunningException("master has been shut down"); - } + throws MasterNotRunningException, ZooKeeperConnectionException { + connection.isMasterRunning(); return connection.tableExists(tableName); } @@ -143,8 +152,9 @@ private long getPauseTime(int tries) { int triesCount = tries; - if (triesCount >= HConstants.RETRY_BACKOFF.length) + if (triesCount >= HConstants.RETRY_BACKOFF.length) { triesCount = HConstants.RETRY_BACKOFF.length - 1; + } return this.pause * HConstants.RETRY_BACKOFF[triesCount]; } @@ -273,12 +283,9 @@ */ public void createTableAsync(HTableDescriptor desc, byte [][] splitKeys) throws IOException { - if (this.master == null) { - throw new MasterNotRunningException("master has been shut down"); - } HTableDescriptor.isLegalTableName(desc.getName()); try { - this.master.createTable(desc, splitKeys); + getMaster().createTable(desc, splitKeys); } catch (RemoteException e) { throw RemoteExceptionHandler.decodeRemoteException(e); } @@ -303,13 +310,11 @@ * @throws IOException if a remote or network exception occurs */ public void deleteTable(final byte [] tableName) throws IOException { - if (this.master == null) { - throw new MasterNotRunningException("master has been shut down"); - } + isMasterRunning(); HTableDescriptor.isLegalTableName(tableName); HRegionLocation firstMetaServer = getFirstMetaServerForTable(tableName); try { - this.master.deleteTable(tableName); + getMaster().deleteTable(tableName); } catch (RemoteException e) { throw RemoteExceptionHandler.decodeRemoteException(e); } @@ -397,21 +402,21 @@ * @throws IOException if a remote or network exception occurs */ public void enableTable(final byte [] tableName) throws IOException { - if (this.master == null) { - throw new MasterNotRunningException("master has been shut down"); - } + isMasterRunning(); // Wait until all regions are enabled boolean enabled = false; for (int tries = 0; tries < this.numRetries; tries++) { try { - this.master.enableTable(tableName); + getMaster().enableTable(tableName); } catch (RemoteException e) { throw RemoteExceptionHandler.decodeRemoteException(e); } enabled = isTableEnabled(tableName); - if (enabled) break; + if (enabled) { + break; + } long sleep = getPauseTime(tries); if (LOG.isDebugEnabled()) { LOG.debug("Sleeping= " + sleep + "ms, waiting for all regions to be " + @@ -427,9 +432,10 @@ Bytes.toString(tableName)); } } - if (!enabled) + if (!enabled) { throw new IOException("Unable to enable table " + Bytes.toString(tableName)); + } LOG.info("Enabled table " + Bytes.toString(tableName)); } @@ -454,20 +460,20 @@ * @throws IOException if a remote or network exception occurs */ public void disableTable(final byte [] tableName) throws IOException { - if (this.master == null) { - throw new MasterNotRunningException("master has been shut down"); - } + isMasterRunning(); // Wait until all regions are disabled boolean disabled = false; for (int tries = 0; tries < this.numRetries; tries++) { try { - this.master.disableTable(tableName); + getMaster().disableTable(tableName); } catch (RemoteException e) { throw RemoteExceptionHandler.decodeRemoteException(e); } disabled = isTableDisabled(tableName); - if (disabled) break; + if (disabled) { + break; + } if (LOG.isDebugEnabled()) { LOG.debug("Sleep. Waiting for all regions to be disabled from " + Bytes.toString(tableName)); @@ -556,12 +562,9 @@ */ public void addColumn(final byte [] tableName, HColumnDescriptor column) throws IOException { - if (this.master == null) { - throw new MasterNotRunningException("master has been shut down"); - } HTableDescriptor.isLegalTableName(tableName); try { - this.master.addColumn(tableName, column); + getMaster().addColumn(tableName, column); } catch (RemoteException e) { throw RemoteExceptionHandler.decodeRemoteException(e); } @@ -590,12 +593,9 @@ */ public void deleteColumn(final byte [] tableName, final byte [] columnName) throws IOException { - if (this.master == null) { - throw new MasterNotRunningException("master has been shut down"); - } HTableDescriptor.isLegalTableName(tableName); try { - this.master.deleteColumn(tableName, columnName); + getMaster().deleteColumn(tableName, columnName); } catch (RemoteException e) { throw RemoteExceptionHandler.decodeRemoteException(e); } @@ -629,12 +629,9 @@ public void modifyColumn(final byte [] tableName, final byte [] columnName, HColumnDescriptor descriptor) throws IOException { - if (this.master == null) { - throw new MasterNotRunningException("master has been shut down"); - } HTableDescriptor.isLegalTableName(tableName); try { - this.master.modifyColumn(tableName, columnName, descriptor); + getMaster().modifyColumn(tableName, columnName, descriptor); } catch (RemoteException e) { throw RemoteExceptionHandler.decodeRemoteException(e); } @@ -813,9 +810,6 @@ public void modifyTable(final byte [] tableName, HConstants.Modify op, Object... args) throws IOException { - if (this.master == null) { - throw new MasterNotRunningException("master has been shut down"); - } // Let pass if its a catalog table. Used by admins. if (tableName != null && !MetaUtils.isMetaTableName(tableName)) { // This will throw exception @@ -831,7 +825,7 @@ } arr = new Writable[1]; arr[0] = (HTableDescriptor)args[0]; - this.master.modifyTable(tableName, op, arr); + getMaster().modifyTable(tableName, op, arr); break; case TABLE_COMPACT: @@ -851,7 +845,7 @@ "ImmutableBytesWritable"); } } - this.master.modifyTable(tableName, op, arr); + getMaster().modifyTable(tableName, op, arr); break; case CLOSE_REGION: @@ -873,7 +867,7 @@ "ImmutableBytesWritable, not " + args[i]); } } - this.master.modifyTable(tableName, op, arr); + getMaster().modifyTable(tableName, op, arr); break; default: @@ -889,15 +883,11 @@ * @throws IOException if a remote or network exception occurs */ public synchronized void shutdown() throws IOException { - if (this.master == null) { - throw new MasterNotRunningException("master has been shut down"); - } + isMasterRunning(); try { - this.master.shutdown(); + getMaster().shutdown(); } catch (RemoteException e) { throw RemoteExceptionHandler.decodeRemoteException(e); - } finally { - this.master = null; } } @@ -906,10 +896,7 @@ * @throws IOException if a remote or network exception occurs */ public ClusterStatus getClusterStatus() throws IOException { - if (this.master == null) { - throw new MasterNotRunningException("master has been shut down"); - } - return this.master.getClusterStatus(); + return getMaster().getClusterStatus(); } private HRegionLocation getFirstMetaServerForTable(final byte [] tableName) @@ -923,9 +910,10 @@ * * @param conf system configuration * @throws MasterNotRunningException if a remote or network exception occurs + * @throws ZooKeeperConnectionException if unable to connect to zookeeper */ public static void checkHBaseAvailable(Configuration conf) - throws MasterNotRunningException { + throws MasterNotRunningException, ZooKeeperConnectionException { Configuration copyOfConf = HBaseConfiguration.create(conf); copyOfConf.setInt("hbase.client.retries.number", 1); new HBaseAdmin(copyOfConf); Index: src/main/java/org/apache/hadoop/hbase/client/HTable.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/client/HTable.java (revision 964096) +++ src/main/java/org/apache/hadoop/hbase/client/HTable.java (working copy) @@ -32,6 +32,7 @@ import org.apache.hadoop.hbase.KeyValue; import org.apache.hadoop.hbase.NotServingRegionException; import org.apache.hadoop.hbase.UnknownScannerException; +import org.apache.hadoop.hbase.ZooKeeperConnectionException; import org.apache.hadoop.hbase.client.MetaScanner.MetaScannerVisitor; import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.hbase.util.Pair; @@ -626,7 +627,7 @@ public Boolean call() throws IOException { return server.checkAndDelete( location.getRegionInfo().getRegionName(), - row, family, qualifier, value, delete) + row, family, qualifier, value, delete) ? Boolean.TRUE : Boolean.FALSE; } } @@ -1097,10 +1098,12 @@ Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0); - if (!t.isDaemon()) - t.setDaemon(true); - if (t.getPriority() != Thread.NORM_PRIORITY) - t.setPriority(Thread.NORM_PRIORITY); + if (!t.isDaemon()) { + t.setDaemon(true); + } + if (t.getPriority() != Thread.NORM_PRIORITY) { + t.setPriority(Thread.NORM_PRIORITY); + } return t; } } @@ -1112,9 +1115,10 @@ * @param tableName name of table to configure. * @param enable Set to true to enable region cache prefetch. Or set to * false to disable it. + * @throws ZooKeeperConnectionException */ public static void setRegionCachePrefetch(final byte[] tableName, - boolean enable) { + boolean enable) throws ZooKeeperConnectionException { HConnectionManager.getConnection(HBaseConfiguration.create()). setRegionCachePrefetch(tableName, enable); } @@ -1127,9 +1131,10 @@ * @param tableName name of table to configure. * @param enable Set to true to enable region cache prefetch. Or set to * false to disable it. + * @throws ZooKeeperConnectionException */ public static void setRegionCachePrefetch(final Configuration conf, - final byte[] tableName, boolean enable) { + final byte[] tableName, boolean enable) throws ZooKeeperConnectionException { HConnectionManager.getConnection(conf).setRegionCachePrefetch( tableName, enable); } @@ -1140,9 +1145,10 @@ * @param tableName name of table to check * @return true if table's region cache prefecth is enabled. Otherwise * it is disabled. + * @throws ZooKeeperConnectionException */ public static boolean getRegionCachePrefetch(final Configuration conf, - final byte[] tableName) { + final byte[] tableName) throws ZooKeeperConnectionException { return HConnectionManager.getConnection(conf).getRegionCachePrefetch( tableName); } @@ -1152,8 +1158,9 @@ * @param tableName name of table to check * @return true if table's region cache prefecth is enabled. Otherwise * it is disabled. + * @throws ZooKeeperConnectionException */ - public static boolean getRegionCachePrefetch(final byte[] tableName) { + public static boolean getRegionCachePrefetch(final byte[] tableName) throws ZooKeeperConnectionException { return HConnectionManager.getConnection(HBaseConfiguration.create()). getRegionCachePrefetch(tableName); } Index: src/main/java/org/apache/hadoop/hbase/client/ServerConnectionManager.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/client/ServerConnectionManager.java (revision 964096) +++ src/main/java/org/apache/hadoop/hbase/client/ServerConnectionManager.java (working copy) @@ -21,6 +21,7 @@ package org.apache.hadoop.hbase.client; import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hbase.ZooKeeperConnectionException; /** @@ -38,8 +39,9 @@ * If no current connection exists, create a new connection for that instance * @param conf configuration * @return HConnection object for the instance specified by the configuration + * @throws ZooKeeperConnectionException */ - public static ServerConnection getConnection(Configuration conf) { + public static ServerConnection getConnection(Configuration conf) throws ZooKeeperConnectionException { return (ServerConnection) HConnectionManager.getConnection(conf); } } Index: src/main/resources/hbase-webapps/regionserver/regionserver.jsp =================================================================== --- src/main/resources/hbase-webapps/regionserver/regionserver.jsp (revision 964096) +++ src/main/resources/hbase-webapps/regionserver/regionserver.jsp (working copy) @@ -42,7 +42,7 @@ HBase Version<%= org.apache.hadoop.hbase.util.VersionInfo.getVersion() %>, r<%= org.apache.hadoop.hbase.util.VersionInfo.getRevision() %>HBase version and svn revision HBase Compiled<%= org.apache.hadoop.hbase.util.VersionInfo.getDate() %>, <%= org.apache.hadoop.hbase.util.VersionInfo.getUser() %>When HBase version was compiled and by whom Metrics<%= metrics.toString() %>RegionServer Metrics; file and heap sizes are in megabytes -Zookeeper Quorum<%= regionServer.getZooKeeper().getQuorumServers() %>Addresses of all registered ZK servers +Zookeeper Quorum<%= regionServer.getZooKeeper().getQuorum() %>Addresses of all registered ZK servers

Online Regions

Index: src/main/resources/hbase-webapps/master/zk.jsp =================================================================== --- src/main/resources/hbase-webapps/master/zk.jsp (revision 964096) +++ src/main/resources/hbase-webapps/master/zk.jsp (working copy) @@ -4,7 +4,8 @@ import="org.apache.hadoop.hbase.client.HBaseAdmin" import="org.apache.hadoop.hbase.client.HConnection" import="org.apache.hadoop.hbase.HRegionInfo" - import="org.apache.hadoop.hbase.zookeeper.ZooKeeperWrapper" + import="org.apache.hadoop.hbase.zookeeper.ZKUtil" + import="org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher" import="org.apache.hadoop.hbase.HBaseConfiguration" import="org.apache.hadoop.hbase.master.HMaster" import="org.apache.hadoop.hbase.HConstants"%><% @@ -12,7 +13,7 @@ Configuration conf = master.getConfiguration(); HBaseAdmin hbadmin = new HBaseAdmin(conf); HConnection connection = hbadmin.getConnection(); - ZooKeeperWrapper wrapper = connection.getZooKeeperWrapper(); + ZooKeeperWatcher watcher = connection.getZooKeeperWatcher(); %> @@ -29,7 +30,7 @@
-<%= wrapper.dump() %>
+<%= ZKUtil.dump(watcher) %>