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.HBaseTestingUtility;
29  import org.apache.hadoop.hbase.HConstants;
30  import org.apache.hadoop.hbase.HRegionInfo;
31  import org.apache.hadoop.hbase.HTableDescriptor;
32  import org.apache.hadoop.hbase.testclassification.MediumTests;
33  import org.apache.hadoop.hbase.RegionTransition;
34  import org.apache.hadoop.hbase.Server;
35  import org.apache.hadoop.hbase.TableName;
36  import org.apache.hadoop.hbase.exceptions.DeserializationException;
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.MockServer;
42  import org.apache.hadoop.hbase.zookeeper.ZKAssign;
43  import org.apache.zookeeper.KeeperException;
44  import org.apache.zookeeper.KeeperException.NodeExistsException;
45  import org.junit.AfterClass;
46  import org.junit.Before;
47  import org.junit.BeforeClass;
48  import org.junit.Test;
49  import org.junit.experimental.categories.Category;
50  import org.mockito.Mockito;
51  
52  /**
53   * Test of the {@link CloseRegionHandler}.
54   */
55  @Category(MediumTests.class)
56  public class TestCloseRegionHandler {
57    static final Log LOG = LogFactory.getLog(TestCloseRegionHandler.class);
58    private final static HBaseTestingUtility HTU = HBaseTestingUtility.createLocalHTU();
59    private static final HTableDescriptor TEST_HTD =
60      new HTableDescriptor(TableName.valueOf("TestCloseRegionHandler"));
61    private HRegionInfo TEST_HRI;
62    private int testIndex = 0;
63  
64    @BeforeClass public static void before() throws Exception {
65      HTU.getConfiguration().setBoolean("hbase.assignment.usezk", true);
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 = HTU.createMockRegionServerService();
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 = HTU.createLocalHRegion(hri,  htd);
102     try {
103       assertNotNull(region);
104       // Spy on the region so can throw exception when close is called.
105       HRegion spy = Mockito.spy(region);
106       final boolean abort = false;
107       Mockito.when(spy.close(abort)).
108       thenThrow(new RuntimeException("Mocked failed close!"));
109       // The CloseRegionHandler will try to get an HRegion that corresponds
110       // to the passed hri -- so insert the region into the online region Set.
111       rss.addToOnlineRegions(spy);
112       // Assert the Server is NOT stopped before we call close region.
113       assertFalse(server.isStopped());
114       CloseRegionHandler handler =
115           new CloseRegionHandler(server, rss, hri, false, false, -1);
116       boolean throwable = false;
117       try {
118         handler.process();
119       } catch (Throwable t) {
120         throwable = true;
121       } finally {
122         assertTrue(throwable);
123         // Abort calls stop so stopped flag should be set.
124         assertTrue(server.isStopped());
125       }
126     } finally {
127       HRegion.closeHRegion(region);
128     }
129   }
130   
131      /**
132       * Test if close region can handle ZK closing node version mismatch
133       * @throws IOException
134       * @throws NodeExistsException
135       * @throws KeeperException
136      * @throws DeserializationException 
137       */
138      @Test public void testZKClosingNodeVersionMismatch()
139      throws IOException, NodeExistsException, KeeperException, DeserializationException {
140        final Server server = new MockServer(HTU);
141        final RegionServerServices rss = HTU.createMockRegionServerService();
142 
143        HTableDescriptor htd = TEST_HTD;
144        final HRegionInfo hri = TEST_HRI;
145    
146        // open a region first so that it can be closed later
147        OpenRegion(server, rss, htd, hri);
148    
149        // close the region
150        // Create it CLOSING, which is what Master set before sending CLOSE RPC
151        int versionOfClosingNode = ZKAssign.createNodeClosing(server.getZooKeeper(),
152          hri, server.getServerName());
153    
154        // The CloseRegionHandler will validate the expected version
155        // Given it is set to invalid versionOfClosingNode+1,
156        // CloseRegionHandler should be M_ZK_REGION_CLOSING
157        CloseRegionHandler handler =
158          new CloseRegionHandler(server, rss, hri, false, true,
159          versionOfClosingNode+1);
160        handler.process();
161    
162        // Handler should remain in M_ZK_REGION_CLOSING
163        RegionTransition rt =
164          RegionTransition.parseFrom(ZKAssign.getData(server.getZooKeeper(), hri.getEncodedName()));
165        assertTrue(rt.getEventType().equals(EventType.M_ZK_REGION_CLOSING ));
166      }
167   
168      /**
169       * Test if the region can be closed properly
170       * @throws IOException
171       * @throws NodeExistsException
172       * @throws KeeperException
173      * @throws org.apache.hadoop.hbase.exceptions.DeserializationException
174       */
175      @Test public void testCloseRegion()
176      throws IOException, NodeExistsException, KeeperException, DeserializationException {
177        final Server server = new MockServer(HTU);
178        final RegionServerServices rss = HTU.createMockRegionServerService();
179    
180        HTableDescriptor htd = TEST_HTD;
181        HRegionInfo hri = TEST_HRI;
182    
183        // open a region first so that it can be closed later
184        OpenRegion(server, rss, htd, hri);
185    
186        // close the region
187        // Create it CLOSING, which is what Master set before sending CLOSE RPC
188        int versionOfClosingNode = ZKAssign.createNodeClosing(server.getZooKeeper(),
189          hri, server.getServerName());
190    
191        // The CloseRegionHandler will validate the expected version
192        // Given it is set to correct versionOfClosingNode,
193        // CloseRegionHandlerit should be RS_ZK_REGION_CLOSED
194        CloseRegionHandler handler =
195          new CloseRegionHandler(server, rss, hri, false, true,
196          versionOfClosingNode);
197        handler.process();
198        // Handler should have transitioned it to RS_ZK_REGION_CLOSED
199        RegionTransition rt = RegionTransition.parseFrom(
200          ZKAssign.getData(server.getZooKeeper(), hri.getEncodedName()));
201        assertTrue(rt.getEventType().equals(EventType.RS_ZK_REGION_CLOSED));
202      }
203 
204      private void OpenRegion(Server server, RegionServerServices rss,
205          HTableDescriptor htd, HRegionInfo hri)
206      throws IOException, NodeExistsException, KeeperException, DeserializationException {
207        // Create it OFFLINE node, which is what Master set before sending OPEN RPC
208        ZKAssign.createNodeOffline(server.getZooKeeper(), hri, server.getServerName());
209        OpenRegionHandler openHandler = new OpenRegionHandler(server, rss, hri, htd);
210        rss.getRegionsInTransitionInRS().put(hri.getEncodedNameAsBytes(), Boolean.TRUE);
211        openHandler.process();
212        // This parse is not used?
213        RegionTransition.parseFrom(ZKAssign.getData(server.getZooKeeper(), hri.getEncodedName()));
214        // delete the node, which is what Master do after the region is opened
215        ZKAssign.deleteNode(server.getZooKeeper(), hri.getEncodedName(),
216          EventType.RS_ZK_REGION_OPENED, server.getServerName());
217      }
218 
219 }
220