View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.hadoop.hbase.regionserver.handler;
19  
20  import static org.junit.Assert.assertFalse;
21  import static org.junit.Assert.assertNotNull;
22  import static org.junit.Assert.assertTrue;
23  
24  import java.io.IOException;
25  
26  import org.apache.commons.logging.Log;
27  import org.apache.commons.logging.LogFactory;
28  import org.apache.hadoop.hbase.TableName;
29  import org.apache.hadoop.hbase.exceptions.DeserializationException;
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.RegionTransition;
36  import org.apache.hadoop.hbase.Server;
37  import org.apache.hadoop.hbase.executor.EventType;
38  import org.apache.hadoop.hbase.regionserver.HRegion;
39  import org.apache.hadoop.hbase.regionserver.RegionServerServices;
40  import org.apache.hadoop.hbase.util.Bytes;
41  import org.apache.hadoop.hbase.util.MockRegionServerServices;
42  import org.apache.hadoop.hbase.util.MockServer;
43  import org.apache.hadoop.hbase.zookeeper.ZKAssign;
44  import org.apache.zookeeper.KeeperException;
45  import org.apache.zookeeper.KeeperException.NodeExistsException;
46  import org.junit.AfterClass;
47  import org.junit.Before;
48  import org.junit.BeforeClass;
49  import org.junit.Test;
50  import org.junit.experimental.categories.Category;
51  import org.mockito.Mockito;
52  
53  /**
54   * Test of the {@link CloseRegionHandler}.
55   */
56  @Category(MediumTests.class)
57  public class TestCloseRegionHandler {
58    static final Log LOG = LogFactory.getLog(TestCloseRegionHandler.class);
59    private final static HBaseTestingUtility HTU = new HBaseTestingUtility();
60    private static final HTableDescriptor TEST_HTD =
61      new HTableDescriptor(TableName.valueOf("TestCloseRegionHandler"));
62    private HRegionInfo TEST_HRI;
63    private int testIndex = 0;
64  
65    @BeforeClass public static void before() throws Exception {
66      HTU.startMiniZKCluster();
67    }
68  
69    @AfterClass public static void after() throws IOException {
70      HTU.shutdownMiniZKCluster();
71    }
72  
73    /**
74     * Before each test, use a different HRI, so the different tests
75     * don't interfere with each other. This allows us to use just
76     * a single ZK cluster for the whole suite.
77     */
78    @Before
79    public void setupHRI() {
80      TEST_HRI = new HRegionInfo(TEST_HTD.getTableName(),
81        Bytes.toBytes(testIndex),
82        Bytes.toBytes(testIndex + 1));
83      testIndex++;
84    }
85  
86    /**
87     * Test that if we fail a flush, abort gets set on close.
88     * @see <a href="https://issues.apache.org/jira/browse/HBASE-4270">HBASE-4270</a>
89     * @throws IOException
90     * @throws NodeExistsException
91     * @throws KeeperException
92     */
93    @Test public void testFailedFlushAborts()
94    throws IOException, NodeExistsException, KeeperException {
95      final Server server = new MockServer(HTU, false);
96      final RegionServerServices rss = new MockRegionServerServices();
97      HTableDescriptor htd = TEST_HTD;
98      final HRegionInfo hri =
99        new HRegionInfo(htd.getTableName(), HConstants.EMPTY_END_ROW,
100         HConstants.EMPTY_END_ROW);
101     HRegion region =
102       HRegion.createHRegion(hri, HTU.getDataTestDir(),
103         HTU.getConfiguration(), htd);
104     try {
105       assertNotNull(region);
106       // Spy on the region so can throw exception when close is called.
107       HRegion spy = Mockito.spy(region);
108       final boolean abort = false;
109       Mockito.when(spy.close(abort)).
110       thenThrow(new RuntimeException("Mocked failed close!"));
111       // The CloseRegionHandler will try to get an HRegion that corresponds
112       // to the passed hri -- so insert the region into the online region Set.
113       rss.addToOnlineRegions(spy);
114       // Assert the Server is NOT stopped before we call close region.
115       assertFalse(server.isStopped());
116       CloseRegionHandler handler =
117           new CloseRegionHandler(server, rss, hri, false, false, -1);
118       boolean throwable = false;
119       try {
120         handler.process();
121       } catch (Throwable t) {
122         throwable = true;
123       } finally {
124         assertTrue(throwable);
125         // Abort calls stop so stopped flag should be set.
126         assertTrue(server.isStopped());
127       }
128     } finally {
129       HRegion.closeHRegion(region);
130     }
131   }
132   
133      /**
134       * Test if close region can handle ZK closing node version mismatch
135       * @throws IOException
136       * @throws NodeExistsException
137       * @throws KeeperException
138      * @throws DeserializationException 
139       */
140      @Test public void testZKClosingNodeVersionMismatch()
141      throws IOException, NodeExistsException, KeeperException, DeserializationException {
142        final Server server = new MockServer(HTU);
143        final MockRegionServerServices rss = new MockRegionServerServices();
144        rss.setFileSystem(HTU.getTestFileSystem());
145    
146        HTableDescriptor htd = TEST_HTD;
147        final HRegionInfo hri = TEST_HRI;
148    
149        // open a region first so that it can be closed later
150        OpenRegion(server, rss, htd, hri);
151    
152        // close the region
153        // Create it CLOSING, which is what Master set before sending CLOSE RPC
154        int versionOfClosingNode = ZKAssign.createNodeClosing(server.getZooKeeper(),
155          hri, server.getServerName());
156    
157        // The CloseRegionHandler will validate the expected version
158        // Given it is set to invalid versionOfClosingNode+1,
159        // CloseRegionHandler should be M_ZK_REGION_CLOSING
160        CloseRegionHandler handler =
161          new CloseRegionHandler(server, rss, hri, false, true,
162          versionOfClosingNode+1);
163        handler.process();
164    
165        // Handler should remain in M_ZK_REGION_CLOSING
166        RegionTransition rt =
167          RegionTransition.parseFrom(ZKAssign.getData(server.getZooKeeper(), hri.getEncodedName()));
168        assertTrue(rt.getEventType().equals(EventType.M_ZK_REGION_CLOSING ));
169      }
170   
171      /**
172       * Test if the region can be closed properly
173       * @throws IOException
174       * @throws NodeExistsException
175       * @throws KeeperException
176      * @throws org.apache.hadoop.hbase.exceptions.DeserializationException
177       */
178      @Test public void testCloseRegion()
179      throws IOException, NodeExistsException, KeeperException, DeserializationException {
180        final Server server = new MockServer(HTU);
181        final MockRegionServerServices rss = new MockRegionServerServices();
182        rss.setFileSystem(HTU.getTestFileSystem());
183    
184        HTableDescriptor htd = TEST_HTD;
185        HRegionInfo hri = TEST_HRI;
186    
187        // open a region first so that it can be closed later
188        OpenRegion(server, rss, htd, hri);
189    
190        // close the region
191        // Create it CLOSING, which is what Master set before sending CLOSE RPC
192        int versionOfClosingNode = ZKAssign.createNodeClosing(server.getZooKeeper(),
193          hri, server.getServerName());
194    
195        // The CloseRegionHandler will validate the expected version
196        // Given it is set to correct versionOfClosingNode,
197        // CloseRegionHandlerit should be RS_ZK_REGION_CLOSED
198        CloseRegionHandler handler =
199          new CloseRegionHandler(server, rss, hri, false, true,
200          versionOfClosingNode);
201        handler.process();
202        // Handler should have transitioned it to RS_ZK_REGION_CLOSED
203        RegionTransition rt = RegionTransition.parseFrom(
204          ZKAssign.getData(server.getZooKeeper(), hri.getEncodedName()));
205        assertTrue(rt.getEventType().equals(EventType.RS_ZK_REGION_CLOSED));
206      }
207 
208      private void OpenRegion(Server server, RegionServerServices rss,
209          HTableDescriptor htd, HRegionInfo hri)
210      throws IOException, NodeExistsException, KeeperException, DeserializationException {
211        // Create it OFFLINE node, which is what Master set before sending OPEN RPC
212        ZKAssign.createNodeOffline(server.getZooKeeper(), hri, server.getServerName());
213        OpenRegionHandler openHandler = new OpenRegionHandler(server, rss, hri, htd);
214        rss.getRegionsInTransitionInRS().put(hri.getEncodedNameAsBytes(), Boolean.TRUE);
215        openHandler.process();
216        // This parse is not used?
217        RegionTransition.parseFrom(ZKAssign.getData(server.getZooKeeper(), hri.getEncodedName()));
218        // delete the node, which is what Master do after the region is opened
219        ZKAssign.deleteNode(server.getZooKeeper(), hri.getEncodedName(), EventType.RS_ZK_REGION_OPENED);
220      }  
221 
222 }
223