1   /**
2    * Copyright 2011 The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  package org.apache.hadoop.hbase.regionserver.handler;
21  
22  import static org.junit.Assert.assertEquals;
23  import static org.junit.Assert.assertNotNull;
24  
25  import java.io.IOException;
26  
27  import org.apache.commons.logging.Log;
28  import org.apache.commons.logging.LogFactory;
29  import org.apache.hadoop.conf.Configuration;
30  import org.apache.hadoop.hbase.HBaseTestingUtility;
31  import org.apache.hadoop.hbase.HConstants;
32  import org.apache.hadoop.hbase.HRegionInfo;
33  import org.apache.hadoop.hbase.HTableDescriptor;
34  import org.apache.hadoop.hbase.MediumTests;
35  import org.apache.hadoop.hbase.MiniHBaseCluster;
36  import org.apache.hadoop.hbase.Server;
37  import org.apache.hadoop.hbase.executor.EventHandler.EventType;
38  import org.apache.hadoop.hbase.executor.RegionTransitionData;
39  import org.apache.hadoop.hbase.regionserver.HRegion;
40  import org.apache.hadoop.hbase.regionserver.HRegionServer;
41  import org.apache.hadoop.hbase.regionserver.RegionAlreadyInTransitionException;
42  import org.apache.hadoop.hbase.regionserver.RegionServerServices;
43  import org.apache.hadoop.hbase.util.Bytes;
44  import org.apache.hadoop.hbase.util.MockRegionServerServices;
45  import org.apache.hadoop.hbase.util.MockServer;
46  import org.apache.hadoop.hbase.zookeeper.ZKAssign;
47  import org.apache.hadoop.hbase.zookeeper.ZKUtil;
48  import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
49  import org.apache.zookeeper.KeeperException;
50  import org.apache.zookeeper.KeeperException.NodeExistsException;
51  import org.junit.AfterClass;
52  import org.junit.Before;
53  import org.junit.BeforeClass;
54  import org.junit.Test;
55  import org.junit.experimental.categories.Category;
56  
57  /**
58   * Test of the {@link OpenRegionHandler}.
59   */
60  @Category(MediumTests.class)
61  public class TestOpenRegionHandler {
62    static final Log LOG = LogFactory.getLog(TestOpenRegionHandler.class);
63    private final static HBaseTestingUtility HTU = new HBaseTestingUtility();
64    private static HTableDescriptor TEST_HTD;
65    private HRegionInfo TEST_HRI;
66  
67    private int testIndex = 0;
68  
69    @BeforeClass public static void before() throws Exception {
70      Configuration c = HTU.getConfiguration();
71      c.setClass(HConstants.REGION_SERVER_IMPL, TestOpenRegionHandlerRegionServer.class,
72                HRegionServer.class);    
73      HTU.startMiniCluster();
74      TEST_HTD = new HTableDescriptor("TestOpenRegionHandler.java");
75    }
76  
77    @AfterClass public static void after() throws IOException {
78      TEST_HTD = null;
79      try {
80        HTU.shutdownMiniCluster();
81      } catch (Exception e) {
82        throw new IOException(e);
83      }
84    }
85  
86    /**
87     * Before each test, use a different HRI, so the different tests
88     * don't interfere with each other. This allows us to use just
89     * a single ZK cluster for the whole suite.
90     */
91    @Before
92    public void setupHRI() {
93      TEST_HRI = new HRegionInfo(TEST_HTD.getName(),
94        Bytes.toBytes(testIndex),
95        Bytes.toBytes(testIndex + 1));
96      testIndex++;
97    }
98  
99    /**
100    * Test the openregionhandler can deal with its znode being yanked out from
101    * under it.
102    * @see <a href="https://issues.apache.org/jira/browse/HBASE-3627">HBASE-3627</a>
103    * @throws IOException
104    * @throws NodeExistsException
105    * @throws KeeperException
106    */
107   @Test public void testYankingRegionFromUnderIt()
108   throws IOException, NodeExistsException, KeeperException {
109     final Server server = new MockServer(HTU);
110     final RegionServerServices rss = new MockRegionServerServices();
111 
112     HTableDescriptor htd = TEST_HTD;
113     final HRegionInfo hri = TEST_HRI;
114     HRegion region =
115          HRegion.createHRegion(hri, HTU.getDataTestDir(), HTU
116             .getConfiguration(), htd);
117     assertNotNull(region);
118     try {
119       OpenRegionHandler handler = new OpenRegionHandler(server, rss, hri, htd) {
120         HRegion openRegion() {
121           // Open region first, then remove znode as though it'd been hijacked.
122           HRegion region = super.openRegion();
123 
124           // Don't actually open region BUT remove the znode as though it'd
125           // been hijacked on us.
126           ZooKeeperWatcher zkw = this.server.getZooKeeper();
127           String node = ZKAssign.getNodeName(zkw, hri.getEncodedName());
128           try {
129             ZKUtil.deleteNodeFailSilent(zkw, node);
130           } catch (KeeperException e) {
131             throw new RuntimeException("Ugh failed delete of " + node, e);
132           }
133           return region;
134         }
135       };
136       // Call process without first creating OFFLINE region in zk, see if
137       // exception or just quiet return (expected).
138       handler.process();
139       ZKAssign.createNodeOffline(server.getZooKeeper(), hri, server.getServerName());
140       // Call process again but this time yank the zk znode out from under it
141       // post OPENING; again will expect it to come back w/o NPE or exception.
142       handler.process();
143     } finally {
144       HRegion.closeHRegion(region);
145     }
146   }
147   
148   @Test
149   public void testFailedOpenRegion() throws Exception {
150     Server server = new MockServer(HTU);
151     RegionServerServices rsServices = new MockRegionServerServices();
152 
153     // Create it OFFLINE, which is what it expects
154     ZKAssign.createNodeOffline(server.getZooKeeper(), TEST_HRI, server.getServerName());
155     ZKAssign.transitionNodeOpening(server.getZooKeeper(), TEST_HRI, server.getServerName());
156 
157     // Create the handler
158     OpenRegionHandler handler =
159       new OpenRegionHandler(server, rsServices, TEST_HRI, TEST_HTD) {
160         @Override
161         HRegion openRegion() {
162           // Fake failure of opening a region due to an IOE, which is caught
163           return null;
164         }
165     };
166     handler.process();
167 
168     // Handler should have transitioned it to FAILED_OPEN
169     RegionTransitionData data =
170       ZKAssign.getData(server.getZooKeeper(), TEST_HRI.getEncodedName());
171     assertEquals(EventType.RS_ZK_REGION_FAILED_OPEN, data.getEventType());
172   }
173   
174   @Test
175   public void testFailedUpdateMeta() throws Exception {
176     Server server = new MockServer(HTU);
177     RegionServerServices rsServices = new MockRegionServerServices();
178 
179     // Create it OFFLINE, which is what it expects
180     ZKAssign.createNodeOffline(server.getZooKeeper(), TEST_HRI, server.getServerName());
181     ZKAssign.transitionNodeOpening(server.getZooKeeper(), TEST_HRI, server.getServerName());
182     // Create the handler
183     OpenRegionHandler handler =
184       new OpenRegionHandler(server, rsServices, TEST_HRI, TEST_HTD) {
185         @Override
186         boolean updateMeta(final HRegion r) {
187           // Fake failure of updating META
188           return false;
189         }
190     };
191     handler.process();
192 
193     // Handler should have transitioned it to FAILED_OPEN
194     RegionTransitionData data =
195       ZKAssign.getData(server.getZooKeeper(), TEST_HRI.getEncodedName());
196     assertEquals(EventType.RS_ZK_REGION_FAILED_OPEN, data.getEventType());
197   }
198   
199   public static class TestOpenRegionHandlerRegionServer extends HRegionServer {
200     public TestOpenRegionHandlerRegionServer(Configuration conf)
201         throws IOException, InterruptedException {
202       super(conf);
203     }
204     @Override
205     public boolean addRegionsInTransition(HRegionInfo region,
206         String currentAction) throws RegionAlreadyInTransitionException {
207       return super.addRegionsInTransition(region, currentAction);
208     }
209   }
210   
211   @Test
212   public void testTransitionToFailedOpenEvenIfCleanupFails() throws Exception {
213     MiniHBaseCluster cluster = HTU.getHBaseCluster();
214     HRegionServer server =
215         cluster.getLiveRegionServerThreads().get(0).getRegionServer();
216     // Create it OFFLINE, which is what it expects
217     ZKAssign.createNodeOffline(server.getZooKeeper(), TEST_HRI, server.getServerName());
218     ZKAssign.transitionNodeOpening(server.getZooKeeper(), TEST_HRI, server.getServerName());
219     // Create the handler
220     OpenRegionHandler handler = new OpenRegionHandler(server, server, TEST_HRI, TEST_HTD) {
221       @Override
222       boolean updateMeta(HRegion r) {
223         return false;
224       };
225 
226       @Override
227       void cleanupFailedOpen(HRegion region) throws IOException {
228         throw new IOException("FileSystem got closed.");
229       }
230     };
231     ((TestOpenRegionHandlerRegionServer)server).addRegionsInTransition(TEST_HRI, "OPEN");
232     try {
233       handler.process();
234     } catch (Exception e) {
235       // Ignore the IOException that we have thrown from cleanupFailedOpen
236     }
237     RegionTransitionData data =
238         ZKAssign.getData(server.getZooKeeper(), TEST_HRI.getEncodedName());
239     assertEquals(EventType.RS_ZK_REGION_FAILED_OPEN, data.getEventType());
240   }
241   
242 
243   @org.junit.Rule
244   public org.apache.hadoop.hbase.ResourceCheckerJUnitRule cu =
245     new org.apache.hadoop.hbase.ResourceCheckerJUnitRule();
246 }
247