View Javadoc

1   /*
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  package org.apache.hadoop.hbase.master;
20  
21  import static org.junit.Assert.assertEquals;
22  import static org.junit.Assert.assertFalse;
23  import static org.junit.Assert.assertNotNull;
24  import static org.junit.Assert.assertTrue;
25  
26  import java.io.IOException;
27  import java.io.InterruptedIOException;
28  import java.util.ArrayList;
29  import java.util.List;
30  import java.util.concurrent.Semaphore;
31  
32  import org.apache.commons.logging.Log;
33  import org.apache.commons.logging.LogFactory;
34  import org.apache.hadoop.conf.Configuration;
35  import org.apache.hadoop.hbase.ChoreService;
36  import org.apache.hadoop.hbase.CoordinatedStateManager;
37  import org.apache.hadoop.hbase.HBaseTestingUtility;
38  import org.apache.hadoop.hbase.Waiter;
39  import org.apache.hadoop.hbase.testclassification.MediumTests;
40  import org.apache.hadoop.hbase.Server;
41  import org.apache.hadoop.hbase.ServerName;
42  import org.apache.hadoop.hbase.client.ClusterConnection;
43  import org.apache.hadoop.hbase.monitoring.MonitoredTask;
44  import org.apache.hadoop.hbase.zookeeper.ClusterStatusTracker;
45  import org.apache.hadoop.hbase.zookeeper.MasterAddressTracker;
46  import org.apache.hadoop.hbase.zookeeper.MetaTableLocator;
47  import org.apache.hadoop.hbase.zookeeper.ZKUtil;
48  import org.apache.hadoop.hbase.zookeeper.ZooKeeperListener;
49  import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
50  import org.apache.zookeeper.KeeperException;
51  import org.junit.AfterClass;
52  import org.junit.BeforeClass;
53  import org.junit.Test;
54  import org.junit.experimental.categories.Category;
55  import org.mockito.Mockito;
56  
57  /**
58   * Test the {@link ActiveMasterManager}.
59   */
60  @Category(MediumTests.class)
61  public class TestActiveMasterManager {
62    private final static Log LOG = LogFactory.getLog(TestActiveMasterManager.class);
63    private final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
64  
65    @BeforeClass
66    public static void setUpBeforeClass() throws Exception {
67      TEST_UTIL.startMiniZKCluster();
68    }
69  
70    @AfterClass
71    public static void tearDownAfterClass() throws Exception {
72      TEST_UTIL.shutdownMiniZKCluster();
73    }
74  
75    @Test public void testRestartMaster() throws IOException, KeeperException {
76      try (ZooKeeperWatcher zk = new ZooKeeperWatcher(TEST_UTIL.getConfiguration(),
77          "testActiveMasterManagerFromZK", null, true)) {
78        try {
79          ZKUtil.deleteNode(zk, zk.getMasterAddressZNode());
80          ZKUtil.deleteNode(zk, zk.clusterStateZNode);
81        } catch (KeeperException.NoNodeException nne) {
82        }
83  
84        // Create the master node with a dummy address
85        ServerName master = ServerName.valueOf("localhost", 1, System.currentTimeMillis());
86        // Should not have a master yet
87        DummyMaster dummyMaster = new DummyMaster(zk, master);
88        ClusterStatusTracker clusterStatusTracker =
89            dummyMaster.getClusterStatusTracker();
90        ActiveMasterManager activeMasterManager =
91            dummyMaster.getActiveMasterManager();
92        assertFalse(activeMasterManager.clusterHasActiveMaster.get());
93  
94        // First test becoming the active master uninterrupted
95        MonitoredTask status = Mockito.mock(MonitoredTask.class);
96        clusterStatusTracker.setClusterUp();
97  
98        activeMasterManager.blockUntilBecomingActiveMaster(100, status);
99        assertTrue(activeMasterManager.clusterHasActiveMaster.get());
100       assertMaster(zk, master);
101 
102       // Now pretend master restart
103       DummyMaster secondDummyMaster = new DummyMaster(zk, master);
104       ActiveMasterManager secondActiveMasterManager =
105           secondDummyMaster.getActiveMasterManager();
106       assertFalse(secondActiveMasterManager.clusterHasActiveMaster.get());
107       activeMasterManager.blockUntilBecomingActiveMaster(100, status);
108       assertTrue(activeMasterManager.clusterHasActiveMaster.get());
109       assertMaster(zk, master);
110     }
111   }
112 
113   /**
114    * Unit tests that uses ZooKeeper but does not use the master-side methods
115    * but rather acts directly on ZK.
116    * @throws Exception
117    */
118   @Test
119   public void testActiveMasterManagerFromZK() throws Exception {
120     try (ZooKeeperWatcher zk = new ZooKeeperWatcher(TEST_UTIL.getConfiguration(),
121         "testActiveMasterManagerFromZK", null, true)) {
122       try {
123         ZKUtil.deleteNode(zk, zk.getMasterAddressZNode());
124         ZKUtil.deleteNode(zk, zk.clusterStateZNode);
125       } catch (KeeperException.NoNodeException nne) {
126       }
127 
128       // Create the master node with a dummy address
129       ServerName firstMasterAddress =
130           ServerName.valueOf("localhost", 1, System.currentTimeMillis());
131       ServerName secondMasterAddress =
132           ServerName.valueOf("localhost", 2, System.currentTimeMillis());
133 
134       // Should not have a master yet
135       DummyMaster ms1 = new DummyMaster(zk, firstMasterAddress);
136       ActiveMasterManager activeMasterManager =
137           ms1.getActiveMasterManager();
138       assertFalse(activeMasterManager.clusterHasActiveMaster.get());
139 
140       // First test becoming the active master uninterrupted
141       ClusterStatusTracker clusterStatusTracker =
142           ms1.getClusterStatusTracker();
143       clusterStatusTracker.setClusterUp();
144       activeMasterManager.blockUntilBecomingActiveMaster(100,
145           Mockito.mock(MonitoredTask.class));
146       assertTrue(activeMasterManager.clusterHasActiveMaster.get());
147       assertMaster(zk, firstMasterAddress);
148 
149       // New manager will now try to become the active master in another thread
150       WaitToBeMasterThread t = new WaitToBeMasterThread(zk, secondMasterAddress);
151       t.start();
152       // Wait for this guy to figure out there is another active master
153       // Wait for 1 second at most
154       int sleeps = 0;
155       while (!t.manager.clusterHasActiveMaster.get() && sleeps < 100) {
156         Thread.sleep(10);
157         sleeps++;
158       }
159 
160       // Both should see that there is an active master
161       assertTrue(activeMasterManager.clusterHasActiveMaster.get());
162       assertTrue(t.manager.clusterHasActiveMaster.get());
163       // But secondary one should not be the active master
164       assertFalse(t.isActiveMaster);
165 
166       // Close the first server and delete it's master node
167       ms1.stop("stopping first server");
168 
169       // Use a listener to capture when the node is actually deleted
170       NodeDeletionListener listener = new NodeDeletionListener(zk, zk.getMasterAddressZNode());
171       zk.registerListener(listener);
172 
173       LOG.info("Deleting master node");
174       ZKUtil.deleteNode(zk, zk.getMasterAddressZNode());
175 
176       // Wait for the node to be deleted
177       LOG.info("Waiting for active master manager to be notified");
178       listener.waitForDeletion();
179       LOG.info("Master node deleted");
180 
181       // Now we expect the secondary manager to have and be the active master
182       // Wait for 1 second at most
183       sleeps = 0;
184       while (!t.isActiveMaster && sleeps < 100) {
185         Thread.sleep(10);
186         sleeps++;
187       }
188       LOG.debug("Slept " + sleeps + " times");
189 
190       assertTrue(t.manager.clusterHasActiveMaster.get());
191       assertTrue(t.isActiveMaster);
192 
193       LOG.info("Deleting master node");
194 
195       ZKUtil.deleteNode(zk, zk.getMasterAddressZNode());
196     }
197   }
198 
199   @Test
200   public void testBackupMasterUpdates() throws Exception {
201     Configuration conf = TEST_UTIL.getConfiguration();
202     try (ZooKeeperWatcher zk = new ZooKeeperWatcher(
203         conf, "testBackupMasterUpdates", null, true)) {
204       ServerName sn1 = ServerName.valueOf("localhost", 1, -1);
205       DummyMaster master1 = new DummyMaster(zk, sn1);
206       final ActiveMasterManager activeMasterManager = master1.getActiveMasterManager();
207       activeMasterManager.blockUntilBecomingActiveMaster(100,
208           Mockito.mock(MonitoredTask.class));
209       assertEquals(sn1, activeMasterManager.getActiveMasterServerName());
210       assertEquals(0, activeMasterManager.getBackupMasters().size());
211       // Add backup masters
212       final List<String> backupZNodes = new ArrayList<>();
213       for (int i = 1; i <= 10; i++) {
214         ServerName backupSn = ServerName.valueOf("localhost", 1000 + i, -1);
215         String backupZn = ZKUtil.joinZNode(zk.backupMasterAddressesZNode, backupSn.toString());
216         backupZNodes.add(backupZn);
217         MasterAddressTracker.setMasterAddress(zk, backupZn, backupSn, 1234);
218         TEST_UTIL.waitFor(10000,
219             new Waiter.Predicate<Exception>() {
220               @Override
221               public boolean evaluate() throws Exception {
222                 return activeMasterManager.getBackupMasters().size() == backupZNodes.size();
223               }
224             });
225       }
226       // Remove backup masters
227       int numBackups = backupZNodes.size();
228       for (String backupZNode: backupZNodes) {
229         ZKUtil.deleteNode(zk, backupZNode);
230         final int currentBackups = --numBackups;
231         TEST_UTIL.waitFor(10000,
232             new Waiter.Predicate<Exception>() {
233               @Override
234               public boolean evaluate() throws Exception {
235                 return activeMasterManager.getBackupMasters().size() == currentBackups;
236               }
237             });
238       }
239     }
240   }
241 
242   /**
243    * Assert there is an active master and that it has the specified address.
244    * @param zk single Zookeeper watcher
245    * @param expectedAddress the expected address of the master
246    * @throws KeeperException unexpected Zookeeper exception
247    * @throws IOException if an IO problem is encountered
248    */
249   private void assertMaster(ZooKeeperWatcher zk,
250                             ServerName expectedAddress)
251       throws KeeperException, IOException {
252     ServerName readAddress = MasterAddressTracker.getMasterAddress(zk);
253     assertNotNull(readAddress);
254     assertTrue(expectedAddress.equals(readAddress));
255   }
256 
257   public static class WaitToBeMasterThread extends Thread {
258 
259     ActiveMasterManager manager;
260     DummyMaster dummyMaster;
261     boolean isActiveMaster;
262 
263     public WaitToBeMasterThread(ZooKeeperWatcher zk, ServerName address)
264         throws InterruptedIOException {
265       this.dummyMaster = new DummyMaster(zk,address);
266       this.manager = this.dummyMaster.getActiveMasterManager();
267       isActiveMaster = false;
268     }
269 
270     @Override
271     public void run() {
272       manager.blockUntilBecomingActiveMaster(100,
273           Mockito.mock(MonitoredTask.class));
274       LOG.info("Second master has become the active master!");
275       isActiveMaster = true;
276     }
277   }
278 
279   public static class NodeDeletionListener extends ZooKeeperListener {
280     private static final Log LOG = LogFactory.getLog(NodeDeletionListener.class);
281 
282     private Semaphore lock;
283     private String node;
284 
285     public NodeDeletionListener(ZooKeeperWatcher watcher, String node) {
286       super(watcher);
287       lock = new Semaphore(0);
288       this.node = node;
289     }
290 
291     @Override
292     public void nodeDeleted(String path) {
293       if(path.equals(node)) {
294         LOG.debug("nodeDeleted(" + path + ")");
295         lock.release();
296       }
297     }
298 
299     public void waitForDeletion() throws InterruptedException {
300       lock.acquire();
301     }
302   }
303 
304   /**
305    * Dummy Master Implementation.
306    */
307   public static class DummyMaster implements Server {
308     private volatile boolean stopped;
309     private ClusterStatusTracker clusterStatusTracker;
310     private ActiveMasterManager activeMasterManager;
311 
312     public DummyMaster(ZooKeeperWatcher zk, ServerName master) throws InterruptedIOException {
313       this.clusterStatusTracker =
314           new ClusterStatusTracker(zk, this);
315       clusterStatusTracker.start();
316 
317       this.activeMasterManager =
318           new ActiveMasterManager(zk, master, this);
319       zk.registerListener(activeMasterManager);
320     }
321 
322     @Override
323     public void abort(final String msg, final Throwable t) {}
324 
325     @Override
326     public boolean isAborted() {
327       return false;
328     }
329 
330     @Override
331     public Configuration getConfiguration() {
332       return null;
333     }
334 
335     @Override
336     public ZooKeeperWatcher getZooKeeper() {
337       return null;
338     }
339 
340     @Override
341     public CoordinatedStateManager getCoordinatedStateManager() {
342       return null;
343     }
344 
345     @Override
346     public ServerName getServerName() {
347       return null;
348     }
349 
350     @Override
351     public boolean isStopped() {
352       return this.stopped;
353     }
354 
355     @Override
356     public void stop(String why) {
357       this.stopped = true;
358     }
359 
360     @Override
361     public ClusterConnection getConnection() {
362       return null;
363     }
364 
365     @Override
366     public MetaTableLocator getMetaTableLocator() {
367       return null;
368     }
369 
370     public ClusterStatusTracker getClusterStatusTracker() {
371       return clusterStatusTracker;
372     }
373 
374     public ActiveMasterManager getActiveMasterManager() {
375       return activeMasterManager;
376     }
377 
378     @Override
379     public ChoreService getChoreService() {
380       return null;
381     }
382   }
383 }