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.regionserver.handler;
20  
21  import static org.junit.Assert.*;
22  
23  import java.io.IOException;
24  
25  import org.apache.commons.logging.Log;
26  import org.apache.commons.logging.LogFactory;
27  import org.apache.hadoop.hbase.*;
28  import org.apache.hadoop.hbase.executor.EventType;
29  import org.apache.hadoop.hbase.regionserver.HRegion;
30  import org.apache.hadoop.hbase.regionserver.RegionServerServices;
31  import org.apache.hadoop.hbase.util.Bytes;
32  import org.apache.hadoop.hbase.util.MockServer;
33  import org.apache.hadoop.hbase.zookeeper.ZKAssign;
34  import org.apache.hadoop.hbase.zookeeper.ZKUtil;
35  import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
36  import org.apache.zookeeper.KeeperException;
37  import org.apache.zookeeper.KeeperException.NodeExistsException;
38  import org.junit.AfterClass;
39  import org.junit.Before;
40  import org.junit.BeforeClass;
41  import org.junit.Test;
42  import org.junit.experimental.categories.Category;
43  
44  /**
45   * Test of the {@link OpenRegionHandler}.
46   */
47  @Category(MediumTests.class)
48  public class TestOpenRegionHandler {
49    static final Log LOG = LogFactory.getLog(TestOpenRegionHandler.class);
50    private final static HBaseTestingUtility HTU = HBaseTestingUtility.createLocalHTU();
51    private static HTableDescriptor TEST_HTD;
52    private HRegionInfo TEST_HRI;
53  
54    private int testIndex = 0;
55  
56    @BeforeClass public static void before() throws Exception {
57      HTU.startMiniZKCluster();
58      TEST_HTD = new HTableDescriptor(TableName.valueOf("TestOpenRegionHandler.java"));
59    }
60  
61    @AfterClass public static void after() throws IOException {
62      TEST_HTD = null;
63      HTU.shutdownMiniZKCluster();
64    }
65  
66    /**
67     * Before each test, use a different HRI, so the different tests
68     * don't interfere with each other. This allows us to use just
69     * a single ZK cluster for the whole suite.
70     */
71    @Before
72    public void setupHRI() {
73      TEST_HRI = new HRegionInfo(TEST_HTD.getTableName(),
74        Bytes.toBytes(testIndex),
75        Bytes.toBytes(testIndex + 1));
76      testIndex++;
77    }
78  
79    /**
80     * Test the openregionhandler can deal with its znode being yanked out from
81     * under it.
82     * @see <a href="https://issues.apache.org/jira/browse/HBASE-3627">HBASE-3627</a>
83     * @throws IOException
84     * @throws NodeExistsException
85     * @throws KeeperException
86     */
87    @Test public void testYankingRegionFromUnderIt()
88    throws IOException, NodeExistsException, KeeperException {
89      final Server server = new MockServer(HTU);
90      final RegionServerServices rss = HTU.createMockRegionServerService();
91  
92      HTableDescriptor htd = TEST_HTD;
93      final HRegionInfo hri = TEST_HRI;
94      HRegion region =
95           HRegion.createHRegion(hri, HTU.getDataTestDir(), HTU
96              .getConfiguration(), htd);
97      assertNotNull(region);
98      try {
99        OpenRegionHandler handler = new OpenRegionHandler(server, rss, hri, htd) {
100         HRegion openRegion() {
101           // Open region first, then remove znode as though it'd been hijacked.
102           HRegion region = super.openRegion();
103 
104           // Don't actually open region BUT remove the znode as though it'd
105           // been hijacked on us.
106           ZooKeeperWatcher zkw = this.server.getZooKeeper();
107           String node = ZKAssign.getNodeName(zkw, hri.getEncodedName());
108           try {
109             ZKUtil.deleteNodeFailSilent(zkw, node);
110           } catch (KeeperException e) {
111             throw new RuntimeException("Ugh failed delete of " + node, e);
112           }
113           return region;
114         }
115       };
116       rss.getRegionsInTransitionInRS().put(
117         hri.getEncodedNameAsBytes(), Boolean.TRUE);
118       // Call process without first creating OFFLINE region in zk, see if
119       // exception or just quiet return (expected).
120       handler.process();
121       rss.getRegionsInTransitionInRS().put(
122         hri.getEncodedNameAsBytes(), Boolean.TRUE);
123       ZKAssign.createNodeOffline(server.getZooKeeper(), hri, server.getServerName());
124       // Call process again but this time yank the zk znode out from under it
125       // post OPENING; again will expect it to come back w/o NPE or exception.
126       handler.process();
127     } finally {
128       HRegion.closeHRegion(region);
129     }
130   }
131   
132   /**
133    * Test the openregionhandler can deal with perceived failure of transitioning to OPENED state
134    * due to intermittent zookeeper malfunctioning.
135    * @see <a href="https://issues.apache.org/jira/browse/HBASE-9387">HBASE-9387</a>
136    * @throws IOException
137    * @throws NodeExistsException
138    * @throws KeeperException
139    */
140   @Test
141   public void testRegionServerAbortionDueToFailureTransitioningToOpened()
142       throws IOException, NodeExistsException, KeeperException {
143     final Server server = new MockServer(HTU);
144     final RegionServerServices rss = HTU.createMockRegionServerService();
145 
146     HTableDescriptor htd = TEST_HTD;
147     final HRegionInfo hri = TEST_HRI;
148     HRegion region =
149          HRegion.createHRegion(hri, HTU.getDataTestDir(), HTU
150             .getConfiguration(), htd);
151     assertNotNull(region);
152     try {
153       OpenRegionHandler handler = new OpenRegionHandler(server, rss, hri, htd) {
154         boolean transitionToOpened(final HRegion r) throws IOException {
155           // remove znode simulating intermittent zookeeper connection issue
156           ZooKeeperWatcher zkw = this.server.getZooKeeper();
157           String node = ZKAssign.getNodeName(zkw, hri.getEncodedName());
158           try {
159             ZKUtil.deleteNodeFailSilent(zkw, node);
160           } catch (KeeperException e) {
161             throw new RuntimeException("Ugh failed delete of " + node, e);
162           }
163           // then try to transition to OPENED
164           return super.transitionToOpened(r);
165         }
166       };
167       rss.getRegionsInTransitionInRS().put(
168         hri.getEncodedNameAsBytes(), Boolean.TRUE);
169       // Call process without first creating OFFLINE region in zk, see if
170       // exception or just quiet return (expected).
171       handler.process();
172       rss.getRegionsInTransitionInRS().put(
173         hri.getEncodedNameAsBytes(), Boolean.TRUE);
174       ZKAssign.createNodeOffline(server.getZooKeeper(), hri, server.getServerName());
175       // Call process again but this time yank the zk znode out from under it
176       // post OPENING; again will expect it to come back w/o NPE or exception.
177       handler.process();
178     } catch (IOException ioe) {
179     } finally {
180       HRegion.closeHRegion(region);
181     }
182     // Region server is expected to abort due to OpenRegionHandler perceiving transitioning
183     // to OPENED as failed
184     // This was corresponding to the second handler.process() call above.
185     assertTrue("region server should have aborted", rss.isAborted());
186   }
187   
188   @Test
189   public void testFailedOpenRegion() throws Exception {
190     Server server = new MockServer(HTU);
191     RegionServerServices rsServices = HTU.createMockRegionServerService();
192 
193     // Create it OFFLINE, which is what it expects
194     ZKAssign.createNodeOffline(server.getZooKeeper(), TEST_HRI, server.getServerName());
195 
196     // Create the handler
197     OpenRegionHandler handler =
198       new OpenRegionHandler(server, rsServices, TEST_HRI, TEST_HTD) {
199         @Override
200         HRegion openRegion() {
201           // Fake failure of opening a region due to an IOE, which is caught
202           return null;
203         }
204     };
205     rsServices.getRegionsInTransitionInRS().put(
206       TEST_HRI.getEncodedNameAsBytes(), Boolean.TRUE);
207     handler.process();
208 
209     // Handler should have transitioned it to FAILED_OPEN
210     RegionTransition rt = RegionTransition.parseFrom(
211       ZKAssign.getData(server.getZooKeeper(), TEST_HRI.getEncodedName()));
212     assertEquals(EventType.RS_ZK_REGION_FAILED_OPEN, rt.getEventType());
213   }
214   
215   @Test
216   public void testFailedUpdateMeta() throws Exception {
217     Server server = new MockServer(HTU);
218     RegionServerServices rsServices = HTU.createMockRegionServerService();
219 
220     // Create it OFFLINE, which is what it expects
221     ZKAssign.createNodeOffline(server.getZooKeeper(), TEST_HRI, server.getServerName());
222 
223     // Create the handler
224     OpenRegionHandler handler =
225       new OpenRegionHandler(server, rsServices, TEST_HRI, TEST_HTD) {
226         @Override
227         boolean updateMeta(final HRegion r) {
228           // Fake failure of updating META
229           return false;
230         }
231     };
232     rsServices.getRegionsInTransitionInRS().put(
233       TEST_HRI.getEncodedNameAsBytes(), Boolean.TRUE);
234     handler.process();
235 
236     // Handler should have transitioned it to FAILED_OPEN
237     RegionTransition rt = RegionTransition.parseFrom(
238       ZKAssign.getData(server.getZooKeeper(), TEST_HRI.getEncodedName()));
239     assertEquals(EventType.RS_ZK_REGION_FAILED_OPEN, rt.getEventType());
240   }
241   
242   @Test
243   public void testTransitionToFailedOpenEvenIfCleanupFails() throws Exception {
244     Server server = new MockServer(HTU);
245     RegionServerServices rsServices = HTU.createMockRegionServerService();
246     // Create it OFFLINE, which is what it expects
247     ZKAssign.createNodeOffline(server.getZooKeeper(), TEST_HRI, server.getServerName());
248     // Create the handler
249     OpenRegionHandler handler = new OpenRegionHandler(server, rsServices, TEST_HRI, TEST_HTD) {
250       @Override
251       boolean updateMeta(HRegion r) {
252         return false;
253       };
254 
255       @Override
256       void cleanupFailedOpen(HRegion region) throws IOException {
257         throw new IOException("FileSystem got closed.");
258       }
259     };
260     rsServices.getRegionsInTransitionInRS().put(TEST_HRI.getEncodedNameAsBytes(), Boolean.TRUE);
261     try {
262       handler.process();
263     } catch (Exception e) {
264       // Ignore the IOException that we have thrown from cleanupFailedOpen
265     }
266     RegionTransition rt = RegionTransition.parseFrom(ZKAssign.getData(server.getZooKeeper(),
267         TEST_HRI.getEncodedName()));
268     assertEquals(EventType.RS_ZK_REGION_FAILED_OPEN, rt.getEventType());
269   }
270 
271   @Test
272   public void testTransitionToFailedOpenFromOffline() throws Exception {
273     Server server = new MockServer(HTU);
274     RegionServerServices rsServices = HTU.createMockRegionServerService(server.getServerName());
275     // Create it OFFLINE, which is what it expects
276     ZKAssign.createNodeOffline(server.getZooKeeper(), TEST_HRI, server.getServerName());
277     // Create the handler
278     OpenRegionHandler handler = new OpenRegionHandler(server, rsServices, TEST_HRI, TEST_HTD) {
279 
280       @Override
281       boolean transitionZookeeperOfflineToOpening(String encodedName, int versionOfOfflineNode) {
282         return false;
283       }
284     };
285     rsServices.getRegionsInTransitionInRS().put(TEST_HRI.getEncodedNameAsBytes(), Boolean.TRUE);
286 
287     handler.process();
288 
289     RegionTransition rt = RegionTransition.parseFrom(ZKAssign.getData(server.getZooKeeper(),
290         TEST_HRI.getEncodedName()));
291     assertEquals(EventType.RS_ZK_REGION_FAILED_OPEN, rt.getEventType());
292   }
293 
294 }
295