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