001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.zookeeper; 019 020import static org.junit.Assert.assertEquals; 021import static org.junit.Assert.assertFalse; 022import static org.junit.Assert.assertNotNull; 023import static org.junit.Assert.assertTrue; 024import static org.junit.Assert.fail; 025 026import java.util.concurrent.atomic.AtomicBoolean; 027import org.apache.hadoop.conf.Configuration; 028import org.apache.hadoop.hbase.Abortable; 029import org.apache.hadoop.hbase.HBaseClassTestRule; 030import org.apache.hadoop.hbase.HBaseConfiguration; 031import org.apache.hadoop.hbase.HBaseZKTestingUtility; 032import org.apache.hadoop.hbase.Stoppable; 033import org.apache.hadoop.hbase.log.HBaseMarkers; 034import org.apache.hadoop.hbase.testclassification.MediumTests; 035import org.apache.hadoop.hbase.testclassification.ZKTests; 036import org.apache.hadoop.hbase.util.Bytes; 037import org.apache.hadoop.hbase.util.Threads; 038import org.junit.AfterClass; 039import org.junit.BeforeClass; 040import org.junit.ClassRule; 041import org.junit.Test; 042import org.junit.experimental.categories.Category; 043import org.slf4j.Logger; 044import org.slf4j.LoggerFactory; 045 046@Category({ ZKTests.class, MediumTests.class }) 047public class TestZKLeaderManager { 048 049 @ClassRule 050 public static final HBaseClassTestRule CLASS_RULE = 051 HBaseClassTestRule.forClass(TestZKLeaderManager.class); 052 053 private static final Logger LOG = LoggerFactory.getLogger(TestZKLeaderManager.class); 054 055 private static final String LEADER_ZNODE = 056 "/test/" + TestZKLeaderManager.class.getSimpleName(); 057 058 private static class MockAbortable implements Abortable { 059 private boolean aborted; 060 061 @Override 062 public void abort(String why, Throwable e) { 063 aborted = true; 064 LOG.error(HBaseMarkers.FATAL, "Aborting during test: "+why, e); 065 fail("Aborted during test: " + why); 066 } 067 068 @Override 069 public boolean isAborted() { 070 return aborted; 071 } 072 } 073 074 private static class MockLeader extends Thread implements Stoppable { 075 private volatile boolean stopped; 076 private ZKWatcher watcher; 077 private ZKLeaderManager zkLeader; 078 private AtomicBoolean master = new AtomicBoolean(false); 079 private int index; 080 081 public MockLeader(ZKWatcher watcher, int index) { 082 setDaemon(true); 083 setName("TestZKLeaderManager-leader-" + index); 084 this.index = index; 085 this.watcher = watcher; 086 this.zkLeader = new ZKLeaderManager(watcher, LEADER_ZNODE, 087 Bytes.toBytes(index), this); 088 } 089 090 public boolean isMaster() { 091 return master.get(); 092 } 093 094 public int getIndex() { 095 return index; 096 } 097 098 public ZKWatcher getWatcher() { 099 return watcher; 100 } 101 102 @Override 103 public void run() { 104 while (!stopped) { 105 zkLeader.start(); 106 zkLeader.waitToBecomeLeader(); 107 master.set(true); 108 109 while (master.get() && !stopped) { 110 try { 111 Thread.sleep(10); 112 } catch (InterruptedException ignored) {} 113 } 114 } 115 } 116 117 public void abdicate() { 118 zkLeader.stepDownAsLeader(); 119 master.set(false); 120 } 121 122 @Override 123 public void stop(String why) { 124 stopped = true; 125 abdicate(); 126 Threads.sleep(100); 127 watcher.close(); 128 } 129 130 @Override 131 public boolean isStopped() { 132 return stopped; 133 } 134 } 135 136 private static HBaseZKTestingUtility TEST_UTIL; 137 private static MockLeader[] CANDIDATES; 138 139 @BeforeClass 140 public static void setupBeforeClass() throws Exception { 141 TEST_UTIL = new HBaseZKTestingUtility(); 142 TEST_UTIL.startMiniZKCluster(); 143 Configuration conf = TEST_UTIL.getConfiguration(); 144 145 // use an abortable to fail the test in the case of any KeeperExceptions 146 MockAbortable abortable = new MockAbortable(); 147 int count = 5; 148 CANDIDATES = new MockLeader[count]; 149 for (int i = 0; i < count; i++) { 150 ZKWatcher watcher = newZK(conf, "server"+i, abortable); 151 CANDIDATES[i] = new MockLeader(watcher, i); 152 CANDIDATES[i].start(); 153 } 154 } 155 156 @AfterClass 157 public static void tearDownAfterClass() throws Exception { 158 TEST_UTIL.shutdownMiniZKCluster(); 159 } 160 161 @Test 162 public void testLeaderSelection() throws Exception { 163 MockLeader currentLeader = getCurrentLeader(); 164 // one leader should have been found 165 assertNotNull("Leader should exist", currentLeader); 166 LOG.debug("Current leader index is "+currentLeader.getIndex()); 167 168 byte[] znodeData = ZKUtil.getData(currentLeader.getWatcher(), LEADER_ZNODE); 169 assertNotNull("Leader znode should contain leader index", znodeData); 170 assertTrue("Leader znode should not be empty", znodeData.length > 0); 171 int storedIndex = Bytes.toInt(znodeData); 172 LOG.debug("Stored leader index in ZK is "+storedIndex); 173 assertEquals("Leader znode should match leader index", 174 currentLeader.getIndex(), storedIndex); 175 176 // force a leader transition 177 currentLeader.abdicate(); 178 179 // check for new leader 180 currentLeader = getCurrentLeader(); 181 // one leader should have been found 182 assertNotNull("New leader should exist after abdication", currentLeader); 183 LOG.debug("New leader index is "+currentLeader.getIndex()); 184 185 znodeData = ZKUtil.getData(currentLeader.getWatcher(), LEADER_ZNODE); 186 assertNotNull("Leader znode should contain leader index", znodeData); 187 assertTrue("Leader znode should not be empty", znodeData.length > 0); 188 storedIndex = Bytes.toInt(znodeData); 189 LOG.debug("Stored leader index in ZK is "+storedIndex); 190 assertEquals("Leader znode should match leader index", 191 currentLeader.getIndex(), storedIndex); 192 193 // force another transition by stopping the current 194 currentLeader.stop("Stopping for test"); 195 196 // check for new leader 197 currentLeader = getCurrentLeader(); 198 // one leader should have been found 199 assertNotNull("New leader should exist after stop", currentLeader); 200 LOG.debug("New leader index is "+currentLeader.getIndex()); 201 202 znodeData = ZKUtil.getData(currentLeader.getWatcher(), LEADER_ZNODE); 203 assertNotNull("Leader znode should contain leader index", znodeData); 204 assertTrue("Leader znode should not be empty", znodeData.length > 0); 205 storedIndex = Bytes.toInt(znodeData); 206 LOG.debug("Stored leader index in ZK is "+storedIndex); 207 assertEquals("Leader znode should match leader index", 208 currentLeader.getIndex(), storedIndex); 209 210 // with a second stop we can guarantee that a previous leader has resumed leading 211 currentLeader.stop("Stopping for test"); 212 213 // check for new 214 currentLeader = getCurrentLeader(); 215 assertNotNull("New leader should exist", currentLeader); 216 } 217 218 private MockLeader getCurrentLeader() throws Exception { 219 MockLeader currentLeader = null; 220 outer: 221 // Wait up to 10 secs for initial leader 222 for (int i = 0; i < 1000; i++) { 223 for (int j = 0; j < CANDIDATES.length; j++) { 224 if (CANDIDATES[j].isMaster()) { 225 // should only be one leader 226 if (currentLeader != null) { 227 fail("Both candidate "+currentLeader.getIndex()+" and "+j+" claim to be leader!"); 228 } 229 currentLeader = CANDIDATES[j]; 230 } 231 } 232 if (currentLeader != null) { 233 break outer; 234 } 235 Threads.sleep(100); 236 } 237 return currentLeader; 238 } 239 240 private static ZKWatcher newZK(Configuration conf, String name, 241 Abortable abort) throws Exception { 242 Configuration copy = HBaseConfiguration.create(conf); 243 ZKWatcher zk = new ZKWatcher(copy, name, abort); 244 return zk; 245 } 246 247}