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.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.startMiniZKCluster();
66    }
67  
68    @AfterClass public static void after() throws IOException {
69      HTU.shutdownMiniZKCluster();
70    }
71  
72    /**
73     * Before each test, use a different HRI, so the different tests
74     * don't interfere with each other. This allows us to use just
75     * a single ZK cluster for the whole suite.
76     */
77    @Before
78    public void setupHRI() {
79      TEST_HRI = new HRegionInfo(TEST_HTD.getTableName(),
80        Bytes.toBytes(testIndex),
81        Bytes.toBytes(testIndex + 1));
82      testIndex++;
83    }
84  
85    /**
86     * Test that if we fail a flush, abort gets set on close.
87     * @see <a href="https://issues.apache.org/jira/browse/HBASE-4270">HBASE-4270</a>
88     * @throws IOException
89     * @throws NodeExistsException
90     * @throws KeeperException
91     */
92    @Test public void testFailedFlushAborts()
93    throws IOException, NodeExistsException, KeeperException {
94      final Server server = new MockServer(HTU, false);
95      final RegionServerServices rss = HTU.createMockRegionServerService();
96      HTableDescriptor htd = TEST_HTD;
97      final HRegionInfo hri =
98        new HRegionInfo(htd.getTableName(), HConstants.EMPTY_END_ROW,
99          HConstants.EMPTY_END_ROW);
100     HRegion region = HTU.createLocalHRegion(hri,  htd);
101     try {
102       assertNotNull(region);
103       // Spy on the region so can throw exception when close is called.
104       HRegion spy = Mockito.spy(region);
105       final boolean abort = false;
106       Mockito.when(spy.close(abort)).
107       thenThrow(new RuntimeException("Mocked failed close!"));
108       // The CloseRegionHandler will try to get an HRegion that corresponds
109       // to the passed hri -- so insert the region into the online region Set.
110       rss.addToOnlineRegions(spy);
111       // Assert the Server is NOT stopped before we call close region.
112       assertFalse(server.isStopped());
113       CloseRegionHandler handler =
114           new CloseRegionHandler(server, rss, hri, false, false, -1);
115       boolean throwable = false;
116       try {
117         handler.process();
118       } catch (Throwable t) {
119         throwable = true;
120       } finally {
121         assertTrue(throwable);
122         // Abort calls stop so stopped flag should be set.
123         assertTrue(server.isStopped());
124       }
125     } finally {
126       HRegion.closeHRegion(region);
127     }
128   }
129   
130      /**
131       * Test if close region can handle ZK closing node version mismatch
132       * @throws IOException
133       * @throws NodeExistsException
134       * @throws KeeperException
135      * @throws DeserializationException 
136       */
137      @Test public void testZKClosingNodeVersionMismatch()
138      throws IOException, NodeExistsException, KeeperException, DeserializationException {
139        final Server server = new MockServer(HTU);
140        final RegionServerServices rss = HTU.createMockRegionServerService();
141 
142        HTableDescriptor htd = TEST_HTD;
143        final HRegionInfo hri = TEST_HRI;
144    
145        // open a region first so that it can be closed later
146        OpenRegion(server, rss, htd, hri);
147    
148        // close the region
149        // Create it CLOSING, which is what Master set before sending CLOSE RPC
150        int versionOfClosingNode = ZKAssign.createNodeClosing(server.getZooKeeper(),
151          hri, server.getServerName());
152    
153        // The CloseRegionHandler will validate the expected version
154        // Given it is set to invalid versionOfClosingNode+1,
155        // CloseRegionHandler should be M_ZK_REGION_CLOSING
156        CloseRegionHandler handler =
157          new CloseRegionHandler(server, rss, hri, false, true,
158          versionOfClosingNode+1);
159        handler.process();
160    
161        // Handler should remain in M_ZK_REGION_CLOSING
162        RegionTransition rt =
163          RegionTransition.parseFrom(ZKAssign.getData(server.getZooKeeper(), hri.getEncodedName()));
164        assertTrue(rt.getEventType().equals(EventType.M_ZK_REGION_CLOSING ));
165      }
166   
167      /**
168       * Test if the region can be closed properly
169       * @throws IOException
170       * @throws NodeExistsException
171       * @throws KeeperException
172      * @throws org.apache.hadoop.hbase.exceptions.DeserializationException
173       */
174      @Test public void testCloseRegion()
175      throws IOException, NodeExistsException, KeeperException, DeserializationException {
176        final Server server = new MockServer(HTU);
177        final RegionServerServices rss = HTU.createMockRegionServerService();
178    
179        HTableDescriptor htd = TEST_HTD;
180        HRegionInfo hri = TEST_HRI;
181    
182        // open a region first so that it can be closed later
183        OpenRegion(server, rss, htd, hri);
184    
185        // close the region
186        // Create it CLOSING, which is what Master set before sending CLOSE RPC
187        int versionOfClosingNode = ZKAssign.createNodeClosing(server.getZooKeeper(),
188          hri, server.getServerName());
189    
190        // The CloseRegionHandler will validate the expected version
191        // Given it is set to correct versionOfClosingNode,
192        // CloseRegionHandlerit should be RS_ZK_REGION_CLOSED
193        CloseRegionHandler handler =
194          new CloseRegionHandler(server, rss, hri, false, true,
195          versionOfClosingNode);
196        handler.process();
197        // Handler should have transitioned it to RS_ZK_REGION_CLOSED
198        RegionTransition rt = RegionTransition.parseFrom(
199          ZKAssign.getData(server.getZooKeeper(), hri.getEncodedName()));
200        assertTrue(rt.getEventType().equals(EventType.RS_ZK_REGION_CLOSED));
201      }
202 
203      private void OpenRegion(Server server, RegionServerServices rss,
204          HTableDescriptor htd, HRegionInfo hri)
205      throws IOException, NodeExistsException, KeeperException, DeserializationException {
206        // Create it OFFLINE node, which is what Master set before sending OPEN RPC
207        ZKAssign.createNodeOffline(server.getZooKeeper(), hri, server.getServerName());
208        OpenRegionHandler openHandler = new OpenRegionHandler(server, rss, hri, htd);
209        rss.getRegionsInTransitionInRS().put(hri.getEncodedNameAsBytes(), Boolean.TRUE);
210        openHandler.process();
211        // This parse is not used?
212        RegionTransition.parseFrom(ZKAssign.getData(server.getZooKeeper(), hri.getEncodedName()));
213        // delete the node, which is what Master do after the region is opened
214        ZKAssign.deleteNode(server.getZooKeeper(), hri.getEncodedName(),
215          EventType.RS_ZK_REGION_OPENED, server.getServerName());
216      }
217 
218 }
219