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.MediumTests;
33  import org.apache.hadoop.hbase.Server;
34  import org.apache.hadoop.hbase.executor.EventHandler.EventType;
35  import org.apache.hadoop.hbase.executor.RegionTransitionData;
36  import org.apache.hadoop.hbase.regionserver.HRegion;
37  import org.apache.hadoop.hbase.regionserver.RegionServerServices;
38  import org.apache.hadoop.hbase.util.Bytes;
39  import org.apache.hadoop.hbase.util.MockRegionServerServices;
40  import org.apache.hadoop.hbase.util.MockServer;
41  import org.apache.hadoop.hbase.zookeeper.ZKAssign;
42  import org.apache.zookeeper.KeeperException;
43  import org.apache.zookeeper.KeeperException.NodeExistsException;
44  import org.junit.AfterClass;
45  import org.junit.Before;
46  import org.junit.BeforeClass;
47  import org.junit.Test;
48  import org.junit.experimental.categories.Category;
49  import org.mockito.Mockito;
50  
51  /**
52   * Test of the {@link CloseRegionHandler}.
53   */
54  @Category(MediumTests.class)
55  public class TestCloseRegionHandler {
56    static final Log LOG = LogFactory.getLog(TestCloseRegionHandler.class);
57    private final static HBaseTestingUtility HTU = new HBaseTestingUtility();
58    private static final HTableDescriptor TEST_HTD =
59      new HTableDescriptor("TestCloseRegionHandler");
60    private HRegionInfo TEST_HRI;
61    private int testIndex = 0;
62  
63    @BeforeClass public static void before() throws Exception {
64      HTU.startMiniZKCluster();
65    }
66  
67    @AfterClass public static void after() throws IOException {
68      HTU.shutdownMiniZKCluster();
69    }
70  
71    /**
72     * Before each test, use a different HRI, so the different tests
73     * don't interfere with each other. This allows us to use just
74     * a single ZK cluster for the whole suite.
75     */
76    @Before
77    public void setupHRI() {
78      TEST_HRI = new HRegionInfo(TEST_HTD.getName(),
79        Bytes.toBytes(testIndex),
80        Bytes.toBytes(testIndex + 1));
81      testIndex++;
82    }
83  
84    /**
85     * Test that if we fail a flush, abort gets set on close.
86     * @see <a href="https://issues.apache.org/jira/browse/HBASE-4270">HBASE-4270</a>
87     * @throws IOException
88     * @throws NodeExistsException
89     * @throws KeeperException
90     */
91    @Test public void testFailedFlushAborts()
92    throws IOException, NodeExistsException, KeeperException {
93      final Server server = new MockServer(HTU, false);
94      final RegionServerServices rss = new MockRegionServerServices();
95      HTableDescriptor htd = TEST_HTD;
96      final HRegionInfo hri =
97        new HRegionInfo(htd.getName(), HConstants.EMPTY_END_ROW,
98          HConstants.EMPTY_END_ROW);
99      HRegion region =
100       HRegion.createHRegion(hri, HTU.getDataTestDir(),
101         HTU.getConfiguration(), 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       */
137      @Test public void testZKClosingNodeVersionMismatch()
138      throws IOException, NodeExistsException, KeeperException {
139        final Server server = new MockServer(HTU);
140        final MockRegionServerServices rss = new MockRegionServerServices();
141        rss.setFileSystem(HTU.getTestFileSystem());
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        RegionTransitionData data =
164          ZKAssign.getData(server.getZooKeeper(), hri.getEncodedName());
165        assertTrue(EventType.M_ZK_REGION_CLOSING == data.getEventType());
166      }
167   
168      /**
169       * Test if the region can be closed properly
170       * @throws IOException
171       * @throws NodeExistsException
172       * @throws KeeperException
173       */
174      @Test public void testCloseRegion()
175      throws IOException, NodeExistsException, KeeperException {
176        final Server server = new MockServer(HTU);
177        final MockRegionServerServices rss = new MockRegionServerServices();
178        rss.setFileSystem(HTU.getTestFileSystem());
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        RegionTransitionData data =
200          ZKAssign.getData(server.getZooKeeper(), hri.getEncodedName());
201        assertTrue(EventType.RS_ZK_REGION_CLOSED == data.getEventType());
202      }
203      private void OpenRegion(Server server, RegionServerServices rss,
204            HTableDescriptor htd, HRegionInfo hri)
205            throws IOException, NodeExistsException, KeeperException {
206            // Create it OFFLINE node, which is what Master set before sending OPEN RPC
207 
208 
209        ZKAssign.createNodeOffline(server.getZooKeeper(), hri, server.getServerName());
210        int version = ZKAssign.transitionNodeOpening(server.getZooKeeper(), hri, server.getServerName());
211        OpenRegionHandler openHandler = new OpenRegionHandler(server, rss, hri, htd, version);
212        openHandler.process();
213        RegionTransitionData data = ZKAssign.getData(server.getZooKeeper(), hri.getEncodedName());
214 
215        // delete the node, which is what Master do after the region is opened
216        ZKAssign.deleteNode(server.getZooKeeper(), hri.getEncodedName(),
217          EventType.RS_ZK_REGION_OPENED);
218      }
219 
220   @org.junit.Rule
221   public org.apache.hadoop.hbase.ResourceCheckerJUnitRule cu =
222     new org.apache.hadoop.hbase.ResourceCheckerJUnitRule();
223 }
224