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;
20  
21  import static org.junit.Assert.assertEquals;
22  import static org.junit.Assert.assertNull;
23  import static org.junit.Assert.assertTrue;
24  import static org.junit.Assert.assertFalse;
25  
26  import java.io.IOException;
27  import java.net.ConnectException;
28  
29  import org.apache.commons.logging.Log;
30  import org.apache.commons.logging.LogFactory;
31  import org.apache.hadoop.conf.Configuration;
32  import org.apache.hadoop.hbase.client.ClusterConnection;
33  import org.apache.hadoop.hbase.client.HConnectionTestingUtility;
34  import org.apache.hadoop.hbase.ipc.PayloadCarryingRpcController;
35  import org.apache.hadoop.hbase.ipc.RpcControllerFactory;
36  import org.apache.hadoop.hbase.ipc.ServerNotRunningYetException;
37  import org.apache.hadoop.hbase.master.RegionState;
38  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos;
39  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.GetRegionInfoRequest;
40  import org.apache.hadoop.hbase.protobuf.generated.ClientProtos;
41  import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.GetRequest;
42  import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.GetResponse;
43  import org.apache.hadoop.hbase.testclassification.MediumTests;
44  import org.apache.hadoop.hbase.util.Threads;
45  import org.apache.hadoop.hbase.zookeeper.MetaTableLocator;
46  import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
47  import org.apache.zookeeper.KeeperException;
48  import org.junit.After;
49  import org.junit.AfterClass;
50  import org.junit.Before;
51  import org.junit.BeforeClass;
52  import org.junit.Test;
53  import org.junit.experimental.categories.Category;
54  import org.mockito.Mockito;
55  
56  import com.google.protobuf.RpcController;
57  import com.google.protobuf.ServiceException;
58  
59  /**
60   * Test {@link org.apache.hadoop.hbase.zookeeper.MetaTableLocator}
61   */
62  @Category(MediumTests.class)
63  public class TestMetaTableLocator {
64    private static final Log LOG = LogFactory.getLog(TestMetaTableLocator.class);
65    private static final HBaseTestingUtility UTIL = new HBaseTestingUtility();
66    private static final ServerName SN =
67        ServerName.valueOf("example.org", 1234, System.currentTimeMillis());
68    private ZooKeeperWatcher watcher;
69    private Abortable abortable;
70  
71    @BeforeClass public static void beforeClass() throws Exception {
72      // Set this down so tests run quicker
73      UTIL.getConfiguration().setInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, 3);
74      UTIL.startMiniZKCluster();
75    }
76  
77    @AfterClass public static void afterClass() throws IOException {
78      UTIL.getZkCluster().shutdown();
79    }
80  
81    @Before public void before() throws IOException {
82      this.abortable = new Abortable() {
83        @Override
84        public void abort(String why, Throwable e) {
85          LOG.info(why, e);
86        }
87  
88        @Override
89        public boolean isAborted()  {
90          return false;
91        }
92      };
93      this.watcher = new ZooKeeperWatcher(UTIL.getConfiguration(),
94        this.getClass().getSimpleName(), this.abortable, true);
95    }
96  
97    @After public void after() {
98      try {
99        // Clean out meta location or later tests will be confused... they presume
100       // start fresh in zk.
101       new MetaTableLocator().deleteMetaLocation(this.watcher);
102     } catch (KeeperException e) {
103       LOG.warn("Unable to delete hbase:meta location", e);
104     }
105 
106     this.watcher.close();
107   }
108 
109   /**
110    * Test normal operations
111    */
112   @Test public void testMetaLookup()
113           throws IOException, InterruptedException, ServiceException, KeeperException {
114     final ClientProtos.ClientService.BlockingInterface client =
115             Mockito.mock(ClientProtos.ClientService.BlockingInterface.class);
116 
117     Mockito.when(client.get((RpcController)Mockito.any(), (GetRequest)Mockito.any())).
118             thenReturn(GetResponse.newBuilder().build());
119 
120     final MetaTableLocator mtl = new MetaTableLocator();
121     assertNull(mtl.getMetaRegionLocation(this.watcher));
122     for (RegionState.State state : RegionState.State.values()) {
123       if (state.equals(RegionState.State.OPEN))
124         continue;
125       MetaTableLocator.setMetaLocation(this.watcher, SN, state);
126       assertNull(mtl.getMetaRegionLocation(this.watcher));
127       assertEquals(state, MetaTableLocator.getMetaRegionState(this.watcher).getState());
128     }
129     MetaTableLocator.setMetaLocation(this.watcher, SN, RegionState.State.OPEN);
130     assertEquals(mtl.getMetaRegionLocation(this.watcher), SN);
131     assertEquals(RegionState.State.OPEN,
132       MetaTableLocator.getMetaRegionState(this.watcher).getState());
133 
134     mtl.deleteMetaLocation(this.watcher);
135     assertNull(MetaTableLocator.getMetaRegionState(this.watcher).getServerName());
136     assertEquals(MetaTableLocator.getMetaRegionState(this.watcher).getState(),
137       RegionState.State.OFFLINE);
138     assertNull(mtl.getMetaRegionLocation(this.watcher));
139   }
140 
141 
142   /**
143    * Test interruptable while blocking wait on meta.
144    * @throws IOException
145    * @throws ServiceException
146    * @throws InterruptedException
147    */
148   @Test public void testInterruptWaitOnMeta()
149   throws IOException, InterruptedException, ServiceException {
150     final ClientProtos.ClientService.BlockingInterface client =
151       Mockito.mock(ClientProtos.ClientService.BlockingInterface.class);
152 
153     Mockito.when(client.get((RpcController)Mockito.any(), (GetRequest)Mockito.any())).
154     thenReturn(GetResponse.newBuilder().build());
155 
156     final MetaTableLocator mtl = new MetaTableLocator();
157     ServerName meta = new MetaTableLocator().getMetaRegionLocation(this.watcher);
158     assertNull(meta);
159     Thread t = new Thread() {
160       @Override
161       public void run() {
162         try {
163           mtl.waitMetaRegionLocation(watcher);
164         } catch (InterruptedException e) {
165           throw new RuntimeException("Interrupted", e);
166         }
167       }
168     };
169     t.start();
170     while (!t.isAlive())
171       Threads.sleep(1);
172     Threads.sleep(1);
173     assertTrue(t.isAlive());
174     mtl.stop();
175     // Join the thread... should exit shortly.
176     t.join();
177   }
178 
179   private void testVerifyMetaRegionLocationWithException(Exception ex)
180   throws IOException, InterruptedException, KeeperException, ServiceException {
181     // Mock an ClientProtocol.
182     final ClientProtos.ClientService.BlockingInterface implementation =
183       Mockito.mock(ClientProtos.ClientService.BlockingInterface.class);
184 
185     ClusterConnection connection = mockConnection(null, implementation);
186 
187     // If a 'get' is called on mocked interface, throw connection refused.
188     Mockito.when(implementation.get((RpcController) Mockito.any(), (GetRequest) Mockito.any())).
189       thenThrow(new ServiceException(ex));
190 
191     long timeout = UTIL.getConfiguration().
192             getLong("hbase.catalog.verification.timeout", 1000);
193     MetaTableLocator.setMetaLocation(this.watcher, SN, RegionState.State.OPENING);
194     assertFalse(new MetaTableLocator().verifyMetaRegionLocation(
195       connection, watcher, timeout));
196 
197     MetaTableLocator.setMetaLocation(this.watcher, SN, RegionState.State.OPEN);
198     assertFalse(new MetaTableLocator().verifyMetaRegionLocation(
199             connection, watcher, timeout));
200   }
201 
202   /**
203    * Test we survive a connection refused {@link ConnectException}
204    * @throws IOException
205    * @throws InterruptedException
206    * @throws KeeperException
207    * @throws ServiceException
208    */
209   @Test
210   public void testGetMetaServerConnectionFails()
211   throws IOException, InterruptedException, KeeperException, ServiceException {
212     testVerifyMetaRegionLocationWithException(new ConnectException("Connection refused"));
213   }
214 
215   /**
216    * Test that verifyMetaRegionLocation properly handles getting a
217    * ServerNotRunningException. See HBASE-4470.
218    * Note this doesn't check the exact exception thrown in the
219    * HBASE-4470 as there it is thrown from getHConnection() and
220    * here it is thrown from get() -- but those are both called
221    * from the same function anyway, and this way is less invasive than
222    * throwing from getHConnection would be.
223    *
224    * @throws IOException
225    * @throws InterruptedException
226    * @throws KeeperException
227    * @throws ServiceException
228    */
229   @Test
230   public void testVerifyMetaRegionServerNotRunning()
231   throws IOException, InterruptedException, KeeperException, ServiceException {
232     testVerifyMetaRegionLocationWithException(new ServerNotRunningYetException("mock"));
233   }
234 
235   /**
236    * Test get of meta region fails properly if nothing to connect to.
237    * @throws IOException
238    * @throws InterruptedException
239    * @throws KeeperException
240    * @throws ServiceException
241    */
242   @Test
243   public void testVerifyMetaRegionLocationFails()
244   throws IOException, InterruptedException, KeeperException, ServiceException {
245     ClusterConnection connection = Mockito.mock(ClusterConnection.class);
246     ServiceException connectException =
247       new ServiceException(new ConnectException("Connection refused"));
248     final AdminProtos.AdminService.BlockingInterface implementation =
249       Mockito.mock(AdminProtos.AdminService.BlockingInterface.class);
250     Mockito.when(implementation.getRegionInfo((RpcController)Mockito.any(),
251       (GetRegionInfoRequest)Mockito.any())).thenThrow(connectException);
252     Mockito.when(connection.getAdmin(Mockito.any(ServerName.class))).
253       thenReturn(implementation);
254         RpcControllerFactory controllerFactory = Mockito.mock(RpcControllerFactory.class);
255         Mockito.when(controllerFactory.newController()).thenReturn(
256           Mockito.mock(PayloadCarryingRpcController.class));
257         Mockito.when(connection.getRpcControllerFactory()).thenReturn(controllerFactory);
258 
259     ServerName sn = ServerName.valueOf("example.com", 1234, System.currentTimeMillis());
260     MetaTableLocator.setMetaLocation(this.watcher,
261             sn,
262             RegionState.State.OPENING);
263     assertFalse(new MetaTableLocator().verifyMetaRegionLocation(connection, watcher, 100));
264     MetaTableLocator.setMetaLocation(this.watcher, sn, RegionState.State.OPEN);
265     assertFalse(new MetaTableLocator().verifyMetaRegionLocation(connection, watcher, 100));
266   }
267 
268   @Test (expected = NotAllMetaRegionsOnlineException.class)
269   public void testTimeoutWaitForMeta()
270   throws IOException, InterruptedException {
271     new MetaTableLocator().waitMetaRegionLocation(watcher, 100);
272   }
273 
274   /**
275    * Test waiting on meat w/ no timeout specified.
276    * @throws IOException
277    * @throws InterruptedException
278    * @throws KeeperException
279    */
280   @Test public void testNoTimeoutWaitForMeta()
281   throws IOException, InterruptedException, KeeperException {
282     final MetaTableLocator mtl = new MetaTableLocator();
283     ServerName hsa = mtl.getMetaRegionLocation(watcher);
284     assertNull(hsa);
285 
286     // Now test waiting on meta location getting set.
287     Thread t = new WaitOnMetaThread();
288     startWaitAliveThenWaitItLives(t, 1);
289     // Set a meta location.
290     MetaTableLocator.setMetaLocation(this.watcher, SN, RegionState.State.OPEN);
291     hsa = SN;
292     // Join the thread... should exit shortly.
293     t.join();
294     // Now meta is available.
295     assertTrue(mtl.getMetaRegionLocation(watcher).equals(hsa));
296   }
297 
298   /**
299    * @param admin An {@link AdminProtos.AdminService.BlockingInterface} instance; you'll likely
300    * want to pass a mocked HRS; can be null.
301    * @param client A mocked ClientProtocol instance, can be null
302    * @return Mock up a connection that returns a {@link Configuration} when
303    * {@link HConnection#getConfiguration()} is called, a 'location' when
304    * {@link HConnection#getRegionLocation(byte[], byte[], boolean)} is called,
305    * and that returns the passed {@link AdminProtos.AdminService.BlockingInterface} instance when
306    * {@link HConnection#getAdmin(ServerName)} is called, returns the passed
307    * {@link ClientProtos.ClientService.BlockingInterface} instance when
308    * {@link HConnection#getClient(ServerName)} is called.
309    * @throws IOException
310    */
311   private ClusterConnection mockConnection(final AdminProtos.AdminService.BlockingInterface admin,
312       final ClientProtos.ClientService.BlockingInterface client)
313   throws IOException {
314     ClusterConnection connection =
315       HConnectionTestingUtility.getMockedConnection(UTIL.getConfiguration());
316     Mockito.doNothing().when(connection).close();
317     // Make it so we return any old location when asked.
318     final HRegionLocation anyLocation = new HRegionLocation(HRegionInfo.FIRST_META_REGIONINFO, SN);
319     Mockito.when(connection.getRegionLocation((TableName) Mockito.any(),
320         (byte[]) Mockito.any(), Mockito.anyBoolean())).
321       thenReturn(anyLocation);
322     Mockito.when(connection.locateRegion((TableName) Mockito.any(),
323         (byte[]) Mockito.any())).
324       thenReturn(anyLocation);
325     if (admin != null) {
326       // If a call to getHRegionConnection, return this implementation.
327       Mockito.when(connection.getAdmin(Mockito.any(ServerName.class))).
328         thenReturn(admin);
329     }
330     if (client != null) {
331       // If a call to getClient, return this implementation.
332       Mockito.when(connection.getClient(Mockito.any(ServerName.class))).
333         thenReturn(client);
334     }
335     return connection;
336   }
337 
338   private void startWaitAliveThenWaitItLives(final Thread t, final int ms) {
339     t.start();
340     while(!t.isAlive()) {
341       // Wait
342     }
343     // Wait one second.
344     Threads.sleep(ms);
345     assertTrue("Assert " + t.getName() + " still waiting", t.isAlive());
346   }
347 
348   /**
349    * Wait on META.
350    */
351   class WaitOnMetaThread extends Thread {
352 
353     WaitOnMetaThread() {
354       super("WaitOnMeta");
355     }
356 
357     @Override
358     public void run() {
359       try {
360         doWaiting();
361       } catch (InterruptedException e) {
362         throw new RuntimeException("Failed wait", e);
363       }
364       LOG.info("Exiting " + getName());
365     }
366 
367     void doWaiting() throws InterruptedException {
368       try {
369         while (new MetaTableLocator().waitMetaRegionLocation(watcher, 10000) == null);
370       } catch (NotAllMetaRegionsOnlineException e) {
371         //Ignore
372       }
373     }
374   }
375 }