| /** |
| * Copyright 2009 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 static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertNotNull; |
| import static org.junit.Assert.assertNull; |
| import static org.junit.Assert.fail; |
| |
| import java.io.IOException; |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Method; |
| import java.util.concurrent.ExecutorService; |
| import java.util.concurrent.SynchronousQueue; |
| import java.util.concurrent.ThreadPoolExecutor; |
| import java.util.concurrent.TimeUnit; |
| |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| import org.apache.hadoop.conf.Configuration; |
| import org.apache.hadoop.hbase.client.Get; |
| import org.apache.hadoop.hbase.client.HBaseAdmin; |
| import org.apache.hadoop.hbase.client.HConnection; |
| import org.apache.hadoop.hbase.client.HConnectionManager; |
| import org.apache.hadoop.hbase.client.HTable; |
| import org.apache.hadoop.hbase.client.Put; |
| import org.apache.hadoop.hbase.master.HMaster; |
| import org.apache.hadoop.hbase.util.Bytes; |
| import org.apache.hadoop.hbase.util.Threads; |
| import org.apache.hadoop.hbase.zookeeper.ZKConfig; |
| 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.ZooDefs; |
| import org.apache.zookeeper.ZooKeeper; |
| import org.apache.zookeeper.ZooKeeper.States; |
| import org.junit.AfterClass; |
| import org.junit.Assert; |
| import org.junit.Before; |
| import org.junit.BeforeClass; |
| import org.junit.Test; |
| import org.junit.experimental.categories.Category; |
| |
| @Category(LargeTests.class) |
| public class TestZooKeeper { |
| private final Log LOG = LogFactory.getLog(this.getClass()); |
| |
| private final static HBaseTestingUtility |
| TEST_UTIL = new HBaseTestingUtility(); |
| private static HConnection persistentConnection; |
| |
| /** |
| * @throws java.lang.Exception |
| */ |
| @BeforeClass |
| public static void setUpBeforeClass() throws Exception { |
| // create a connection *before* the cluster is started, to validate that the |
| // connection's ZK trackers are initialized on demand |
| persistentConnection = HConnectionManager.createConnection(TEST_UTIL.getConfiguration()); |
| |
| // Test we can first start the ZK cluster by itself |
| TEST_UTIL.startMiniZKCluster(); |
| TEST_UTIL.getConfiguration().setBoolean("dfs.support.append", true); |
| TEST_UTIL.startMiniCluster(2); |
| } |
| |
| /** |
| * @throws java.lang.Exception |
| */ |
| @AfterClass |
| public static void tearDownAfterClass() throws Exception { |
| persistentConnection.close(); |
| TEST_UTIL.shutdownMiniCluster(); |
| } |
| |
| /** |
| * @throws java.lang.Exception |
| */ |
| @Before |
| public void setUp() throws Exception { |
| TEST_UTIL.ensureSomeRegionServersAvailable(2); |
| } |
| |
| private ZooKeeperWatcher getZooKeeperWatcher(HConnection c) throws |
| NoSuchMethodException, InvocationTargetException, IllegalAccessException { |
| |
| Method getterZK = c.getClass().getMethod("getKeepAliveZooKeeperWatcher"); |
| getterZK.setAccessible(true); |
| |
| return (ZooKeeperWatcher) getterZK.invoke(c); |
| } |
| |
| /** |
| * See HBASE-1232 and http://wiki.apache.org/hadoop/ZooKeeper/FAQ#4. |
| * @throws IOException |
| * @throws InterruptedException |
| */ |
| // fails frequently, disabled for now, see HBASE-6406 |
| // @Test |
| public void testClientSessionExpired() |
| throws Exception { |
| LOG.info("testClientSessionExpired"); |
| Configuration c = new Configuration(TEST_UTIL.getConfiguration()); |
| new HTable(c, HConstants.META_TABLE_NAME).close(); |
| HConnection connection = HConnectionManager.getConnection(c); |
| ZooKeeperWatcher connectionZK = connection.getZooKeeperWatcher(); |
| TEST_UTIL.expireSession(connectionZK, false); |
| |
| // provoke session expiration by doing something with ZK |
| ZKUtil.dump(connectionZK); |
| |
| // Check that the old ZK connection is closed, means we did expire |
| System.err.println("ZooKeeper should have timed out"); |
| LOG.info("state=" + connectionZK.getRecoverableZooKeeper().getState()); |
| Assert.assertTrue(connectionZK.getRecoverableZooKeeper().getState(). |
| equals(States.CLOSED)); |
| |
| // Check that the client recovered |
| ZooKeeperWatcher newConnectionZK = connection.getZooKeeperWatcher(); |
| States state = newConnectionZK.getRecoverableZooKeeper().getState(); |
| LOG.info("state=" + state); |
| Assert.assertTrue(state.equals(States.CONNECTED) || state.equals(States.CONNECTING)); |
| } |
| |
| @Test |
| public void testRegionServerSessionExpired() throws Exception { |
| LOG.info("Starting testRegionServerSessionExpired"); |
| int metaIndex = TEST_UTIL.getMiniHBaseCluster().getServerWithMeta(); |
| TEST_UTIL.expireRegionServerSession(metaIndex); |
| testSanity(); |
| } |
| |
| // @Test Disabled because seems to make no sense expiring master session |
| // and then trying to create table (down in testSanity); on master side |
| // it will fail because the master's session has expired -- St.Ack 07/24/2012 |
| public void testMasterSessionExpired() throws Exception { |
| LOG.info("Starting testMasterSessionExpired"); |
| TEST_UTIL.expireMasterSession(); |
| testSanity(); |
| } |
| |
| /** |
| * Make sure we can use the cluster |
| * @throws Exception |
| */ |
| private void testSanity() throws Exception { |
| String tableName = "test"+System.currentTimeMillis(); |
| HBaseAdmin admin = new HBaseAdmin(new Configuration(TEST_UTIL.getConfiguration())); |
| testAdminSanity(admin, tableName); |
| HTable table = new HTable(new Configuration(TEST_UTIL.getConfiguration()), tableName); |
| testTableSanity(table, tableName); |
| } |
| |
| private void testSanity(HConnection conn, ExecutorService pool) throws Exception { |
| String tableName = "test"+System.currentTimeMillis(); |
| HBaseAdmin admin = new HBaseAdmin(persistentConnection); |
| testAdminSanity(admin, tableName); |
| HTable table = new HTable(Bytes.toBytes(tableName), persistentConnection, pool); |
| testTableSanity(table, tableName); |
| |
| } |
| private void testAdminSanity(HBaseAdmin admin, String tableName) throws Exception { |
| HTableDescriptor desc = new HTableDescriptor(tableName); |
| HColumnDescriptor family = new HColumnDescriptor("fam"); |
| desc.addFamily(family); |
| LOG.info("Creating table " + tableName); |
| admin.createTable(desc); |
| } |
| |
| private void testTableSanity(HTable table, String tableName) throws Exception { |
| Put put = new Put(Bytes.toBytes("testrow")); |
| put.add(Bytes.toBytes("fam"), Bytes.toBytes("col"), Bytes.toBytes("testdata")); |
| LOG.info("Putting table " + tableName); |
| table.put(put); |
| table.close(); |
| } |
| |
| @Test |
| public void testMultipleZK() { |
| try { |
| HTable localMeta = |
| new HTable(new Configuration(TEST_UTIL.getConfiguration()), HConstants.META_TABLE_NAME); |
| Configuration otherConf = new Configuration(TEST_UTIL.getConfiguration()); |
| otherConf.set(HConstants.ZOOKEEPER_QUORUM, "127.0.0.1"); |
| HTable ipMeta = new HTable(otherConf, HConstants.META_TABLE_NAME); |
| |
| // dummy, just to open the connection |
| localMeta.exists(new Get(HConstants.LAST_ROW)); |
| ipMeta.exists(new Get(HConstants.LAST_ROW)); |
| |
| // make sure they aren't the same |
| assertFalse(HConnectionManager.getConnection(localMeta.getConfiguration()).getZooKeeperWatcher() |
| == HConnectionManager.getConnection(otherConf).getZooKeeperWatcher()); |
| assertFalse(HConnectionManager.getConnection(localMeta.getConfiguration()) |
| .getZooKeeperWatcher().getQuorum().equals(HConnectionManager |
| .getConnection(otherConf).getZooKeeperWatcher().getQuorum())); |
| localMeta.close(); |
| ipMeta.close(); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| fail(); |
| } |
| } |
| |
| /** |
| * Create a bunch of znodes in a hierarchy, try deleting one that has childs |
| * (it will fail), then delete it recursively, then delete the last znode |
| * @throws Exception |
| */ |
| @Test |
| public void testZNodeDeletes() throws Exception { |
| ZooKeeperWatcher zkw = new ZooKeeperWatcher( |
| new Configuration(TEST_UTIL.getConfiguration()), |
| TestZooKeeper.class.getName(), null); |
| ZKUtil.createWithParents(zkw, "/l1/l2/l3/l4"); |
| try { |
| ZKUtil.deleteNode(zkw, "/l1/l2"); |
| fail("We should not be able to delete if znode has childs"); |
| } catch (KeeperException ex) { |
| assertNotNull(ZKUtil.getDataNoWatch(zkw, "/l1/l2/l3/l4", null)); |
| } |
| ZKUtil.deleteNodeRecursively(zkw, "/l1/l2"); |
| // make sure it really is deleted |
| assertNull(ZKUtil.getDataNoWatch(zkw, "/l1/l2/l3/l4", null)); |
| |
| // do the same delete again and make sure it doesn't crash |
| ZKUtil.deleteNodeRecursively(zkw, "/l1/l2"); |
| |
| ZKUtil.deleteNode(zkw, "/l1"); |
| assertNull(ZKUtil.getDataNoWatch(zkw, "/l1/l2", null)); |
| } |
| |
| @Test |
| public void testClusterKey() throws Exception { |
| testKey("server", "2181", "hbase"); |
| testKey("server1,server2,server3", "2181", "hbase"); |
| try { |
| ZKUtil.transformClusterKey("2181:hbase"); |
| } catch (IOException ex) { |
| // OK |
| } |
| } |
| |
| /** |
| * Test with a connection that existed before the cluster was started |
| */ |
| @Test |
| public void testPersistentConnection() throws Exception { |
| ExecutorService pool = new ThreadPoolExecutor(1, 10, 10, TimeUnit.SECONDS, |
| new SynchronousQueue<Runnable>()); |
| testSanity(persistentConnection, pool); |
| } |
| |
| private void testKey(String ensemble, String port, String znode) |
| throws IOException { |
| Configuration conf = new Configuration(); |
| String key = ensemble+":"+port+":"+znode; |
| String[] parts = ZKUtil.transformClusterKey(key); |
| assertEquals(ensemble, parts[0]); |
| assertEquals(port, parts[1]); |
| assertEquals(znode, parts[2]); |
| ZKUtil.applyClusterKeyToConf(conf, key); |
| assertEquals(parts[0], conf.get(HConstants.ZOOKEEPER_QUORUM)); |
| assertEquals(parts[1], conf.get(HConstants.ZOOKEEPER_CLIENT_PORT)); |
| assertEquals(parts[2], conf.get(HConstants.ZOOKEEPER_ZNODE_PARENT)); |
| String reconstructedKey = ZKUtil.getZooKeeperClusterKey(conf); |
| assertEquals(key, reconstructedKey); |
| } |
| |
| /** |
| * A test for HBASE-3238 |
| * @throws IOException A connection attempt to zk failed |
| * @throws InterruptedException One of the non ZKUtil actions was interrupted |
| * @throws KeeperException Any of the zookeeper connections had a |
| * KeeperException |
| */ |
| @Test |
| public void testCreateSilentIsReallySilent() throws InterruptedException, |
| KeeperException, IOException { |
| Configuration c = TEST_UTIL.getConfiguration(); |
| |
| String aclZnode = "/aclRoot"; |
| String quorumServers = ZKConfig.getZKQuorumServersString(c); |
| int sessionTimeout = 5 * 1000; // 5 seconds |
| ZooKeeper zk = new ZooKeeper(quorumServers, sessionTimeout, EmptyWatcher.instance); |
| zk.addAuthInfo("digest", "hbase:rox".getBytes()); |
| |
| // Assumes the root of the ZooKeeper space is writable as it creates a node |
| // wherever the cluster home is defined. |
| ZooKeeperWatcher zk2 = new ZooKeeperWatcher(TEST_UTIL.getConfiguration(), |
| "testMasterAddressManagerFromZK", null); |
| |
| // I set this acl after the attempted creation of the cluster home node. |
| // Add retries in case of retryable zk exceptions. |
| while (true) { |
| try { |
| zk.setACL("/", ZooDefs.Ids.CREATOR_ALL_ACL, -1); |
| break; |
| } catch (KeeperException e) { |
| switch (e.code()) { |
| case CONNECTIONLOSS: |
| case SESSIONEXPIRED: |
| case OPERATIONTIMEOUT: |
| LOG.warn("Possibly transient ZooKeeper exception: " + e); |
| Threads.sleep(100); |
| break; |
| default: |
| throw e; |
| } |
| } |
| } |
| |
| while (true) { |
| try { |
| zk.create(aclZnode, null, ZooDefs.Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT); |
| break; |
| } catch (KeeperException e) { |
| switch (e.code()) { |
| case CONNECTIONLOSS: |
| case SESSIONEXPIRED: |
| case OPERATIONTIMEOUT: |
| LOG.warn("Possibly transient ZooKeeper exception: " + e); |
| Threads.sleep(100); |
| break; |
| default: |
| throw e; |
| } |
| } |
| } |
| zk.close(); |
| ZKUtil.createAndFailSilent(zk2, aclZnode); |
| } |
| |
| /** |
| * Test should not fail with NPE when getChildDataAndWatchForNewChildren |
| * invoked with wrongNode |
| */ |
| @Test |
| public void testGetChildDataAndWatchForNewChildrenShouldNotThrowNPE() |
| throws Exception { |
| ZooKeeperWatcher zkw = new ZooKeeperWatcher(TEST_UTIL.getConfiguration(), |
| "testGetChildDataAndWatchForNewChildrenShouldNotThrowNPE", null); |
| ZKUtil.getChildDataAndWatchForNewChildren(zkw, "/wrongNode"); |
| } |
| |
| /** |
| * Master recovery when the znode already exists. Internally, this |
| * test differs from {@link #testMasterSessionExpired} because here |
| * the master znode will exist in ZK. |
| */ |
| @Test(timeout=20000) |
| public void testMasterZKSessionRecoveryFailure() throws Exception { |
| MiniHBaseCluster cluster = TEST_UTIL.getHBaseCluster(); |
| HMaster m = cluster.getMaster(); |
| m.abort("Test recovery from zk session expired", |
| new KeeperException.SessionExpiredException()); |
| assertFalse(m.isStopped()); |
| testSanity(); |
| } |
| |
| @org.junit.Rule |
| public org.apache.hadoop.hbase.ResourceCheckerJUnitRule cu = |
| new org.apache.hadoop.hbase.ResourceCheckerJUnitRule(); |
| } |
| |