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