001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.regionserver;
019
020import static org.junit.Assert.assertEquals;
021import static org.junit.Assert.assertFalse;
022import static org.junit.Assert.assertNull;
023import static org.junit.Assert.assertTrue;
024
025import java.util.List;
026import java.util.concurrent.Semaphore;
027import org.apache.hadoop.hbase.HBaseClassTestRule;
028import org.apache.hadoop.hbase.HBaseTestingUtility;
029import org.apache.hadoop.hbase.ServerName;
030import org.apache.hadoop.hbase.testclassification.MediumTests;
031import org.apache.hadoop.hbase.testclassification.RegionServerTests;
032import org.apache.hadoop.hbase.zookeeper.MasterAddressTracker;
033import org.apache.hadoop.hbase.zookeeper.ZKListener;
034import org.apache.hadoop.hbase.zookeeper.ZKUtil;
035import org.apache.hadoop.hbase.zookeeper.ZKWatcher;
036import org.apache.hadoop.hbase.zookeeper.ZNodePaths;
037import org.junit.After;
038import org.junit.AfterClass;
039import org.junit.BeforeClass;
040import org.junit.ClassRule;
041import org.junit.Rule;
042import org.junit.Test;
043import org.junit.experimental.categories.Category;
044import org.junit.rules.TestName;
045import org.slf4j.Logger;
046import org.slf4j.LoggerFactory;
047
048@Category({RegionServerTests.class, MediumTests.class})
049public class TestMasterAddressTracker {
050
051  @ClassRule
052  public static final HBaseClassTestRule CLASS_RULE =
053      HBaseClassTestRule.forClass(TestMasterAddressTracker.class);
054
055  private static final Logger LOG = LoggerFactory.getLogger(TestMasterAddressTracker.class);
056
057  private final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
058  // Cleaned up after each unit test.
059  private static ZKWatcher zk;
060
061  @Rule
062  public TestName name = new TestName();
063
064  @After
065  public void cleanUp() {
066    if (zk != null) {
067      zk.close();
068    }
069  }
070
071  @BeforeClass
072  public static void setUpBeforeClass() throws Exception {
073    TEST_UTIL.startMiniZKCluster();
074  }
075
076  @AfterClass
077  public static void tearDownAfterClass() throws Exception {
078    TEST_UTIL.shutdownMiniZKCluster();
079  }
080
081  @Test
082  public void testDeleteIfEquals() throws Exception {
083    final ServerName sn = ServerName.valueOf("localhost", 1234, System.currentTimeMillis());
084    final MasterAddressTracker addressTracker = setupMasterTracker(sn, 1772);
085    try {
086      assertFalse("shouldn't have deleted wrong master server.",
087          MasterAddressTracker.deleteIfEquals(addressTracker.getWatcher(), "some other string."));
088    } finally {
089      assertTrue("Couldn't clean up master",
090          MasterAddressTracker.deleteIfEquals(addressTracker.getWatcher(), sn.toString()));
091    }
092  }
093
094  /**
095   * create an address tracker instance
096   * @param sn if not-null set the active master
097   * @param infoPort if there is an active master, set its info port.
098   */
099  private MasterAddressTracker setupMasterTracker(final ServerName sn, final int infoPort)
100      throws Exception {
101    zk = new ZKWatcher(TEST_UTIL.getConfiguration(),
102        name.getMethodName(), null);
103    ZKUtil.createAndFailSilent(zk, zk.getZNodePaths().baseZNode);
104    ZKUtil.createAndFailSilent(zk, zk.getZNodePaths().backupMasterAddressesZNode);
105
106    // Should not have a master yet
107    MasterAddressTracker addressTracker = new MasterAddressTracker(zk, null);
108    addressTracker.start();
109    assertFalse(addressTracker.hasMaster());
110    zk.registerListener(addressTracker);
111
112    // Use a listener to capture when the node is actually created
113    NodeCreationListener listener = new NodeCreationListener(zk,
114            zk.getZNodePaths().masterAddressZNode);
115    zk.registerListener(listener);
116
117    if (sn != null) {
118      LOG.info("Creating master node");
119      MasterAddressTracker.setMasterAddress(zk, zk.getZNodePaths().masterAddressZNode,
120              sn, infoPort);
121
122      // Wait for the node to be created
123      LOG.info("Waiting for master address manager to be notified");
124      listener.waitForCreation();
125      LOG.info("Master node created");
126    }
127    return addressTracker;
128  }
129
130  /**
131   * Unit tests that uses ZooKeeper but does not use the master-side methods
132   * but rather acts directly on ZK.
133   * @throws Exception
134   */
135  @Test
136  public void testMasterAddressTrackerFromZK() throws Exception {
137    // Create the master node with a dummy address
138    final int infoPort = 1235;
139    final ServerName sn = ServerName.valueOf("localhost", 1234, System.currentTimeMillis());
140    final MasterAddressTracker addressTracker = setupMasterTracker(sn, infoPort);
141    try {
142      assertTrue(addressTracker.hasMaster());
143      ServerName pulledAddress = addressTracker.getMasterAddress();
144      assertTrue(pulledAddress.equals(sn));
145      assertEquals(infoPort, addressTracker.getMasterInfoPort());
146    } finally {
147      assertTrue("Couldn't clean up master",
148          MasterAddressTracker.deleteIfEquals(addressTracker.getWatcher(), sn.toString()));
149    }
150  }
151
152
153  @Test
154  public void testParsingNull() throws Exception {
155    assertNull("parse on null data should return null.", MasterAddressTracker.parse(null));
156  }
157
158  @Test
159  public void testNoBackups() throws Exception {
160    final ServerName sn = ServerName.valueOf("localhost", 1234, System.currentTimeMillis());
161    final MasterAddressTracker addressTracker = setupMasterTracker(sn, 1772);
162    try {
163      assertEquals("Should receive 0 for backup not found.", 0,
164          addressTracker.getBackupMasterInfoPort(
165              ServerName.valueOf("doesnotexist.example.com", 1234, System.currentTimeMillis())));
166    } finally {
167      assertTrue("Couldn't clean up master",
168          MasterAddressTracker.deleteIfEquals(addressTracker.getWatcher(), sn.toString()));
169    }
170  }
171
172  @Test
173  public void testNoMaster() throws Exception {
174    final MasterAddressTracker addressTracker = setupMasterTracker(null, 1772);
175    assertFalse(addressTracker.hasMaster());
176    assertNull("should get null master when none active.", addressTracker.getMasterAddress());
177    assertEquals("Should receive 0 for backup not found.", 0, addressTracker.getMasterInfoPort());
178  }
179
180  @Test
181  public void testBackupMasters() throws Exception {
182    final ServerName sn = ServerName.valueOf("localhost", 5678, System.currentTimeMillis());
183    final MasterAddressTracker addressTracker = setupMasterTracker(sn, 1111);
184    assertTrue(addressTracker.hasMaster());
185    ServerName activeMaster = addressTracker.getMasterAddress();
186    assertEquals(activeMaster, sn);
187    // No current backup masters
188    List<ServerName> backupMasters = addressTracker.getBackupMasters();
189    assertEquals(0, backupMasters.size());
190    ServerName backupMaster1 = ServerName.valueOf("localhost", 2222, -1);
191    ServerName backupMaster2 = ServerName.valueOf("localhost", 3333, -1);
192    String backupZNode1 = ZNodePaths.joinZNode(
193        zk.getZNodePaths().backupMasterAddressesZNode, backupMaster1.toString());
194    String backupZNode2 = ZNodePaths.joinZNode(
195        zk.getZNodePaths().backupMasterAddressesZNode, backupMaster2.toString());
196    // Add a backup master
197    MasterAddressTracker.setMasterAddress(zk, backupZNode1, backupMaster1, 2222);
198    MasterAddressTracker.setMasterAddress(zk, backupZNode2, backupMaster2, 3333);
199    backupMasters = addressTracker.getBackupMasters();
200    assertEquals(2, backupMasters.size());
201    assertTrue(backupMasters.contains(backupMaster1));
202    assertTrue(backupMasters.contains(backupMaster2));
203  }
204
205  public static class NodeCreationListener extends ZKListener {
206    private static final Logger LOG = LoggerFactory.getLogger(NodeCreationListener.class);
207
208    private Semaphore lock;
209    private String node;
210
211    public NodeCreationListener(ZKWatcher watcher, String node) {
212      super(watcher);
213      lock = new Semaphore(0);
214      this.node = node;
215    }
216
217    @Override
218    public void nodeCreated(String path) {
219      if(path.equals(node)) {
220        LOG.debug("nodeCreated(" + path + ")");
221        lock.release();
222      }
223    }
224
225    public void waitForCreation() throws InterruptedException {
226      lock.acquire();
227    }
228  }
229
230}
231