View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.hadoop.hbase.master;
19  
20  
21  import static org.junit.Assert.assertEquals;
22  import static org.junit.Assert.assertFalse;
23  import static org.junit.Assert.assertTrue;
24  
25  import java.io.IOException;
26  import java.net.InetAddress;
27  import java.net.UnknownHostException;
28  import java.util.ArrayList;
29  import java.util.List;
30  
31  import org.apache.hadoop.conf.Configuration;
32  import org.apache.hadoop.hbase.Abortable;
33  import org.apache.hadoop.hbase.TableName;
34  import org.apache.hadoop.hbase.exceptions.DeserializationException;
35  import org.apache.hadoop.hbase.HBaseTestingUtility;
36  import org.apache.hadoop.hbase.HConstants;
37  import org.apache.hadoop.hbase.HRegionInfo;
38  import org.apache.hadoop.hbase.ServerLoad;
39  import org.apache.hadoop.hbase.Server;
40  import org.apache.hadoop.hbase.ServerName;
41  import org.apache.hadoop.hbase.ZooKeeperConnectionException;
42  import org.apache.hadoop.hbase.catalog.CatalogTracker;
43  import org.apache.hadoop.hbase.catalog.MetaMockingUtil;
44  import org.apache.hadoop.hbase.client.HConnection;
45  import org.apache.hadoop.hbase.client.HConnectionTestingUtility;
46  import org.apache.hadoop.hbase.client.Result;
47  import org.apache.hadoop.hbase.monitoring.MonitoredTask;
48  import org.apache.hadoop.hbase.regionserver.RegionOpeningState;
49  import org.apache.hadoop.hbase.util.FSUtils;
50  import org.apache.hadoop.hbase.util.Threads;
51  import org.apache.hadoop.hbase.zookeeper.MetaRegionTracker;
52  import org.apache.hadoop.hbase.zookeeper.ZKUtil;
53  import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
54  import org.apache.hadoop.hbase.MediumTests;
55  import org.apache.zookeeper.KeeperException;
56  import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
57  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.NameStringPair;
58  import org.apache.hadoop.hbase.protobuf.generated.RegionServerStatusProtos.RegionServerReportRequest;
59  import org.apache.hadoop.hbase.protobuf.generated.RegionServerStatusProtos.RegionServerStartupRequest;
60  import org.apache.hadoop.hbase.protobuf.generated.RegionServerStatusProtos.RegionServerStartupResponse;
61  import com.google.protobuf.ServiceException;
62  import org.junit.After;
63  import org.junit.AfterClass;
64  import org.junit.BeforeClass;
65  import org.junit.Test;
66  import org.mockito.Mockito;
67  import org.junit.experimental.categories.Category;
68  import org.apache.commons.logging.Log;
69  import org.apache.commons.logging.LogFactory;
70  
71  /**
72   * Standup the master and fake it to test various aspects of master function.
73   * Does NOT spin up a mini hbase nor mini dfs cluster testing master (it does
74   * put up a zk cluster but this is usually pretty fast compared).  Also, should
75   * be possible to inject faults at points difficult to get at in cluster context.
76   * TODO: Speed up the zk connection by Master.  It pauses 5 seconds establishing
77   * session.
78   */
79  @Category(MediumTests.class)
80  public class TestMasterNoCluster {
81    private static final Log LOG = LogFactory.getLog(TestMasterNoCluster.class);
82    private static final HBaseTestingUtility TESTUTIL = new HBaseTestingUtility();
83  
84    @BeforeClass
85    public static void setUpBeforeClass() throws Exception {
86      Configuration c = TESTUTIL.getConfiguration();
87      // We use local filesystem.  Set it so it writes into the testdir.
88      FSUtils.setRootDir(c, TESTUTIL.getDataTestDir());
89      // Startup a mini zk cluster.
90      TESTUTIL.startMiniZKCluster();
91    }
92  
93    @AfterClass
94    public static void tearDownAfterClass() throws Exception {
95      TESTUTIL.shutdownMiniZKCluster();
96    }
97  
98    @After
99    public void tearDown()
100   throws KeeperException, ZooKeeperConnectionException, IOException {
101     // Make sure zk is clean before we run the next test.
102     ZooKeeperWatcher zkw = new ZooKeeperWatcher(TESTUTIL.getConfiguration(),
103         "@Before", new Abortable() {
104       @Override
105       public void abort(String why, Throwable e) {
106         throw new RuntimeException(why, e);
107       }
108 
109       @Override
110       public boolean isAborted() {
111         return false;
112       }
113     });
114     ZKUtil.deleteNodeRecursively(zkw, zkw.baseZNode);
115     zkw.close();
116   }
117 
118   /**
119    * Test starting master then stopping it before its fully up.
120    * @throws IOException
121    * @throws KeeperException
122    * @throws InterruptedException
123    */
124   @Test (timeout=30000)
125   public void testStopDuringStart()
126   throws IOException, KeeperException, InterruptedException {
127     HMaster master = new HMaster(TESTUTIL.getConfiguration());
128     master.start();
129     // Immediately have it stop.  We used hang in assigning meta.
130     master.stopMaster();
131     master.join();
132   }
133 
134   /**
135    * Test master failover.
136    * Start up three fake regionservers and a master.
137    * @throws IOException
138    * @throws KeeperException
139    * @throws InterruptedException
140    */
141   @Test (timeout=30000)
142   public void testFailover()
143   throws IOException, KeeperException, InterruptedException, ServiceException {
144     final long now = System.currentTimeMillis();
145     // Names for our three servers.  Make the port numbers match hostname.
146     // Will come in use down in the server when we need to figure how to respond.
147     final ServerName sn0 = ServerName.valueOf("0.example.org", 0, now);
148     final ServerName sn1 = ServerName.valueOf("1.example.org", 1, now);
149     final ServerName sn2 = ServerName.valueOf("2.example.org", 2, now);
150     final ServerName [] sns = new ServerName [] {sn0, sn1, sn2};
151     // Put up the mock servers
152     final Configuration conf = TESTUTIL.getConfiguration();
153     final MockRegionServer rs0 = new MockRegionServer(conf, sn0);
154     final MockRegionServer rs1 = new MockRegionServer(conf, sn1);
155     final MockRegionServer rs2 = new MockRegionServer(conf, sn2);
156     // Put some data into the servers.  Make it look like sn0 has the metaH
157     // Put data into sn2 so it looks like it has a few regions for a table named 't'.
158     MetaRegionTracker.setMetaLocation(rs0.getZooKeeper(), rs0.getServerName());
159     final TableName tableName = TableName.valueOf("t");
160     Result [] results = new Result [] {
161       MetaMockingUtil.getMetaTableRowResult(
162         new HRegionInfo(tableName, HConstants.EMPTY_START_ROW, HBaseTestingUtility.KEYS[1]),
163         rs2.getServerName()),
164       MetaMockingUtil.getMetaTableRowResult(
165         new HRegionInfo(tableName, HBaseTestingUtility.KEYS[1], HBaseTestingUtility.KEYS[2]),
166         rs2.getServerName()),
167       MetaMockingUtil.getMetaTableRowResult(new HRegionInfo(tableName, HBaseTestingUtility.KEYS[2],
168           HConstants.EMPTY_END_ROW),
169         rs2.getServerName())
170     };
171     rs1.setNextResults(HRegionInfo.FIRST_META_REGIONINFO.getRegionName(), results);
172 
173     // Create master.  Subclass to override a few methods so we can insert mocks
174     // and get notification on transitions.  We need to fake out any rpcs the
175     // master does opening/closing regions.  Also need to fake out the address
176     // of the 'remote' mocked up regionservers.
177     HMaster master = new HMaster(conf) {
178       InetAddress getRemoteInetAddress(final int port, final long serverStartCode)
179       throws UnknownHostException {
180         // Return different address dependent on port passed.
181         ServerName sn = sns[port];
182         return InetAddress.getByAddress(sn.getHostname(),
183           new byte [] {10, 0, 0, (byte)sn.getPort()});
184       }
185 
186       @Override
187       ServerManager createServerManager(Server master, MasterServices services)
188       throws IOException {
189         ServerManager sm = super.createServerManager(master, services);
190         // Spy on the created servermanager
191         ServerManager spy = Mockito.spy(sm);
192         // Fake a successful open.
193         Mockito.doReturn(RegionOpeningState.OPENED).when(spy).
194           sendRegionOpen((ServerName)Mockito.any(), (HRegionInfo)Mockito.any(),
195             Mockito.anyInt(), Mockito.anyListOf(ServerName.class));
196         return spy;
197       }
198 
199       @Override
200       CatalogTracker createCatalogTracker(ZooKeeperWatcher zk,
201           Configuration conf, Abortable abortable)
202       throws IOException {
203         // Insert a mock for the connection used by the CatalogTracker.  Any
204         // regionserver should do.  Use TESTUTIL.getConfiguration rather than
205         // the conf from the master; the conf will already have an HConnection
206         // associate so the below mocking of a connection will fail.
207         HConnection connection =
208           HConnectionTestingUtility.getMockedConnectionAndDecorate(TESTUTIL.getConfiguration(),
209             rs0, rs0, rs0.getServerName(), HRegionInfo.FIRST_META_REGIONINFO);
210         return new CatalogTracker(zk, conf, connection, abortable);
211       }
212 
213       @Override
214       void initNamespace() {
215       }
216     };
217     master.start();
218 
219     try {
220       // Wait till master is up ready for RPCs.
221       while (!master.isRpcServerOpen()) Threads.sleep(10);
222       // Fake master that there are regionservers out there.  Report in.
223       for (int i = 0; i < sns.length; i++) {
224         RegionServerReportRequest.Builder request = RegionServerReportRequest.newBuilder();;
225         ServerName sn = ServerName.parseVersionedServerName(sns[i].getVersionedBytes());
226         request.setServer(ProtobufUtil.toServerName(sn));
227         request.setLoad(ServerLoad.EMPTY_SERVERLOAD.obtainServerLoadPB());
228         master.regionServerReport(null, request.build());
229       }
230       // Master should now come up.
231       while (!master.isInitialized()) {Threads.sleep(10);}
232       assertTrue(master.isInitialized());
233     } finally {
234       rs0.stop("Test is done");
235       rs1.stop("Test is done");
236       rs2.stop("Test is done");
237       master.stopMaster();
238       master.join();
239     }
240   }
241 
242   /**
243    * Test starting master getting it up post initialized state using mocks.
244    * @throws IOException
245    * @throws KeeperException
246    * @throws InterruptedException
247    * @throws DeserializationException
248    * @throws ServiceException
249    */
250   @Test (timeout=60000)
251   public void testCatalogDeploys()
252       throws Exception {
253     final Configuration conf = TESTUTIL.getConfiguration();
254     conf.setInt(ServerManager.WAIT_ON_REGIONSERVERS_MINTOSTART, 1);
255     conf.setInt(ServerManager.WAIT_ON_REGIONSERVERS_MAXTOSTART, 1);
256 
257     final long now = System.currentTimeMillis();
258     // Name for our single mocked up regionserver.
259     final ServerName sn = ServerName.valueOf("0.example.org", 0, now);
260     // Here is our mocked up regionserver.  Create it now.  Need it setting up
261     // master next.
262     final MockRegionServer rs0 = new MockRegionServer(conf, sn);
263 
264     // Create master.  Subclass to override a few methods so we can insert mocks
265     // and get notification on transitions.  We need to fake out any rpcs the
266     // master does opening/closing regions.  Also need to fake out the address
267     // of the 'remote' mocked up regionservers.
268     HMaster master = new HMaster(conf) {
269       InetAddress getRemoteInetAddress(final int port, final long serverStartCode)
270       throws UnknownHostException {
271         // Interject an unchecked, nonsense InetAddress; i.e. no resolve.
272         return InetAddress.getByAddress(rs0.getServerName().getHostname(),
273           new byte [] {10, 0, 0, 0});
274       }
275 
276       @Override
277       ServerManager createServerManager(Server master, MasterServices services)
278       throws IOException {
279         ServerManager sm = super.createServerManager(master, services);
280         // Spy on the created servermanager
281         ServerManager spy = Mockito.spy(sm);
282         // Fake a successful open.
283         Mockito.doReturn(RegionOpeningState.OPENED).when(spy).
284           sendRegionOpen((ServerName)Mockito.any(), (HRegionInfo)Mockito.any(),
285             Mockito.anyInt(), Mockito.anyListOf(ServerName.class));
286         return spy;
287       }
288 
289       @Override
290       CatalogTracker createCatalogTracker(ZooKeeperWatcher zk,
291           Configuration conf, Abortable abortable)
292       throws IOException {
293         // Insert a mock for the connection used by the CatalogTracker.   Use
294         // TESTUTIL.getConfiguration rather than the conf from the master; the
295         // conf will already have an HConnection associate so the below mocking
296         // of a connection will fail.
297         HConnection connection =
298           HConnectionTestingUtility.getMockedConnectionAndDecorate(TESTUTIL.getConfiguration(),
299             rs0, rs0, rs0.getServerName(), HRegionInfo.FIRST_META_REGIONINFO);
300         return new CatalogTracker(zk, conf, connection, abortable);
301       }
302 
303       @Override
304       void initNamespace() {
305       }
306     };
307     master.start();
308     LOG.info("Master has started");
309 
310     try {
311       // Wait till master is up ready for RPCs.
312       while (!master.isRpcServerOpen()) Threads.sleep(10);
313       LOG.info("RpcServerOpen has started");
314 
315       // Fake master that there is a regionserver out there.  Report in.
316       RegionServerStartupRequest.Builder request = RegionServerStartupRequest.newBuilder();
317       request.setPort(rs0.getServerName().getPort());
318       request.setServerStartCode(rs0.getServerName().getStartcode());
319       request.setServerCurrentTime(now);
320       RegionServerStartupResponse result =
321         master.regionServerStartup(null, request.build());
322       String rshostname = new String();
323       for (NameStringPair e : result.getMapEntriesList()) {
324         if (e.getName().toString().equals(HConstants.KEY_FOR_HOSTNAME_SEEN_BY_MASTER)) {
325           rshostname = e.getValue();
326         }
327       }
328       // Assert hostname is as expected.
329       assertEquals(rs0.getServerName().getHostname(), rshostname);
330       // Now master knows there is at least one regionserver checked in and so
331       // it'll wait a while to see if more and when none, will assign meta
332       // to this single server.  Will do an rpc open but we've
333       // mocked it above in our master override to return 'success'.  As part of
334       // region open, master will have set an unassigned znode for the region up
335       // into zk for the regionserver to transition.  Lets do that now to
336       // complete fake of a successful open.
337       Mocking.fakeRegionServerRegionOpenInZK(master, rs0.getZooKeeper(),
338         rs0.getServerName(), HRegionInfo.FIRST_META_REGIONINFO);
339       LOG.info("fakeRegionServerRegionOpenInZK has started");
340 
341       // Need to set meta location as r0.  Usually the regionserver does this
342       // when its figured it just opened the meta region by setting the meta
343       // location up into zk.  Since we're mocking regionserver, need to do this
344       // ourselves.
345       MetaRegionTracker.setMetaLocation(rs0.getZooKeeper(), rs0.getServerName());
346       // Master should now come up.
347       while (!master.isInitialized()) {Threads.sleep(10);}
348       assertTrue(master.isInitialized());
349     } finally {
350       rs0.stop("Test is done");
351       master.stopMaster();
352       master.join();
353     }
354   }
355 
356   @Test
357   public void testNotPullingDeadRegionServerFromZK()
358       throws IOException, KeeperException, InterruptedException {
359     final Configuration conf = TESTUTIL.getConfiguration();
360     final ServerName newServer = ServerName.valueOf("test.sample", 1, 101);
361     final ServerName deadServer = ServerName.valueOf("test.sample", 1, 100);
362     final MockRegionServer rs0 = new MockRegionServer(conf, newServer);
363 
364     HMaster master = new HMaster(conf) {
365       @Override
366       void assignMeta(MonitoredTask status) {
367       }
368 
369       @Override
370       void initializeZKBasedSystemTrackers() throws IOException,
371       InterruptedException, KeeperException {
372         super.initializeZKBasedSystemTrackers();
373         // Record a newer server in server manager at first
374         serverManager.recordNewServer(newServer, ServerLoad.EMPTY_SERVERLOAD);
375 
376         List<ServerName> onlineServers = new ArrayList<ServerName>();
377         onlineServers.add(deadServer);
378         onlineServers.add(newServer);
379         // Mock the region server tracker to pull the dead server from zk
380         regionServerTracker = Mockito.spy(regionServerTracker);
381         Mockito.doReturn(onlineServers).when(
382           regionServerTracker).getOnlineServers();
383       }
384 
385       @Override
386       CatalogTracker createCatalogTracker(ZooKeeperWatcher zk,
387           Configuration conf, Abortable abortable)
388       throws IOException {
389         // Insert a mock for the connection used by the CatalogTracker.  Any
390         // regionserver should do.  Use TESTUTIL.getConfiguration rather than
391         // the conf from the master; the conf will already have an HConnection
392         // associate so the below mocking of a connection will fail.
393         HConnection connection =
394           HConnectionTestingUtility.getMockedConnectionAndDecorate(TESTUTIL.getConfiguration(),
395             rs0, rs0, rs0.getServerName(), HRegionInfo.FIRST_META_REGIONINFO);
396         return new CatalogTracker(zk, conf, connection, abortable);
397       }
398 
399       @Override
400       void initNamespace() {
401       }
402     };
403     master.start();
404 
405     try {
406       // Wait till master is initialized.
407       while (!master.initialized) Threads.sleep(10);
408       LOG.info("Master is initialized");
409 
410       assertFalse("The dead server should not be pulled in",
411         master.serverManager.isServerOnline(deadServer));
412     } finally {
413       master.stopMaster();
414       master.join();
415     }
416   }
417 }