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