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.HColumnDescriptor;
33  import org.apache.hadoop.hbase.testclassification.MediumTests;
34  import org.apache.hadoop.hbase.RegionTransition;
35  import org.apache.hadoop.hbase.Server;
36  import org.apache.hadoop.hbase.TableName;
37  import org.apache.hadoop.hbase.coordination.OpenRegionCoordination;
38  import org.apache.hadoop.hbase.coordination.ZkCoordinatedStateManager;
39  import org.apache.hadoop.hbase.exceptions.DeserializationException;
40  import org.apache.hadoop.hbase.executor.EventType;
41  import org.apache.hadoop.hbase.regionserver.HRegion;
42  import org.apache.hadoop.hbase.regionserver.RegionServerServices;
43  import org.apache.hadoop.hbase.coordination.ZkCloseRegionCoordination;
44  import org.apache.hadoop.hbase.util.Bytes;
45  import org.apache.hadoop.hbase.util.MockServer;
46  import org.apache.hadoop.hbase.zookeeper.ZKAssign;
47  import org.apache.zookeeper.KeeperException;
48  import org.apache.zookeeper.KeeperException.NodeExistsException;
49  import org.junit.AfterClass;
50  import org.junit.Before;
51  import org.junit.BeforeClass;
52  import org.junit.Test;
53  import org.junit.experimental.categories.Category;
54  import org.mockito.Mockito;
55  
56  /**
57   * Test of the {@link CloseRegionHandler}.
58   */
59  @Category(MediumTests.class)
60  public class TestCloseRegionHandler {
61    static final Log LOG = LogFactory.getLog(TestCloseRegionHandler.class);
62    private final static HBaseTestingUtility HTU = HBaseTestingUtility.createLocalHTU();
63    private static final HTableDescriptor TEST_HTD =
64      new HTableDescriptor(TableName.valueOf("TestCloseRegionHandler"));
65    private static final HColumnDescriptor fam = new HColumnDescriptor("fam");
66    static {
67      TEST_HTD.addFamily(fam);
68    }
69    private HRegionInfo TEST_HRI;
70    private int testIndex = 0;
71  
72    @BeforeClass public static void before() throws Exception {
73      HTU.getConfiguration().setBoolean("hbase.assignment.usezk", true);
74      HTU.startMiniZKCluster();
75    }
76  
77    @AfterClass public static void after() throws IOException {
78      HTU.shutdownMiniZKCluster();
79    }
80  
81    /**
82     * Before each test, use a different HRI, so the different tests
83     * don't interfere with each other. This allows us to use just
84     * a single ZK cluster for the whole suite.
85     */
86    @Before
87    public void setupHRI() {
88      TEST_HRI = new HRegionInfo(TEST_HTD.getTableName(),
89        Bytes.toBytes(testIndex),
90        Bytes.toBytes(testIndex + 1));
91      testIndex++;
92    }
93  
94    /**
95     * Test that if we fail a flush, abort gets set on close.
96     * @see <a href="https://issues.apache.org/jira/browse/HBASE-4270">HBASE-4270</a>
97     * @throws IOException
98     * @throws NodeExistsException
99     * @throws KeeperException
100    */
101   @Test public void testFailedFlushAborts()
102   throws IOException, NodeExistsException, KeeperException {
103     final Server server = new MockServer(HTU, false);
104     final RegionServerServices rss = HTU.createMockRegionServerService();
105     HTableDescriptor htd = TEST_HTD;
106     final HRegionInfo hri =
107       new HRegionInfo(htd.getTableName(), HConstants.EMPTY_END_ROW,
108         HConstants.EMPTY_END_ROW);
109     HRegion region = HTU.createLocalHRegion(hri,  htd);
110     try {
111       assertNotNull(region);
112       // Spy on the region so can throw exception when close is called.
113       HRegion spy = Mockito.spy(region);
114       final boolean abort = false;
115       Mockito.when(spy.close(abort)).
116       thenThrow(new IOException("Mocked failed close!"));
117       // The CloseRegionHandler will try to get an HRegion that corresponds
118       // to the passed hri -- so insert the region into the online region Set.
119       rss.addToOnlineRegions(spy);
120       // Assert the Server is NOT stopped before we call close region.
121       assertFalse(server.isStopped());
122 
123       ZkCoordinatedStateManager consensusProvider = new ZkCoordinatedStateManager();
124       consensusProvider.initialize(server);
125       consensusProvider.start();
126 
127       ZkCloseRegionCoordination.ZkCloseRegionDetails zkCrd =
128         new ZkCloseRegionCoordination.ZkCloseRegionDetails();
129       zkCrd.setPublishStatusInZk(false);
130       zkCrd.setExpectedVersion(-1);
131 
132       CloseRegionHandler handler = new CloseRegionHandler(server, rss, hri, false,
133             consensusProvider.getCloseRegionCoordination(), zkCrd);
134       boolean throwable = false;
135       try {
136         handler.process();
137       } catch (Throwable t) {
138         throwable = true;
139       } finally {
140         assertTrue(throwable);
141         // Abort calls stop so stopped flag should be set.
142         assertTrue(server.isStopped());
143       }
144     } finally {
145       HRegion.closeHRegion(region);
146     }
147   }
148 
149      /**
150       * Test if close region can handle ZK closing node version mismatch
151       * @throws IOException
152       * @throws NodeExistsException
153       * @throws KeeperException
154      * @throws DeserializationException
155       */
156      @Test public void testZKClosingNodeVersionMismatch()
157      throws IOException, NodeExistsException, KeeperException, DeserializationException {
158        final Server server = new MockServer(HTU);
159        final RegionServerServices rss = HTU.createMockRegionServerService();
160 
161        HTableDescriptor htd = TEST_HTD;
162        final HRegionInfo hri = TEST_HRI;
163 
164        ZkCoordinatedStateManager coordinationProvider = new ZkCoordinatedStateManager();
165        coordinationProvider.initialize(server);
166        coordinationProvider.start();
167 
168        // open a region first so that it can be closed later
169        OpenRegion(server, rss, htd, hri, coordinationProvider.getOpenRegionCoordination());
170 
171        // close the region
172        // Create it CLOSING, which is what Master set before sending CLOSE RPC
173        int versionOfClosingNode = ZKAssign.createNodeClosing(server.getZooKeeper(),
174          hri, server.getServerName());
175 
176        // The CloseRegionHandler will validate the expected version
177        // Given it is set to invalid versionOfClosingNode+1,
178        // CloseRegionHandler should be M_ZK_REGION_CLOSING
179 
180        ZkCloseRegionCoordination.ZkCloseRegionDetails zkCrd =
181          new ZkCloseRegionCoordination.ZkCloseRegionDetails();
182        zkCrd.setPublishStatusInZk(true);
183        zkCrd.setExpectedVersion(versionOfClosingNode+1);
184 
185        CloseRegionHandler handler = new CloseRegionHandler(server, rss, hri, false,
186          coordinationProvider.getCloseRegionCoordination(), zkCrd);
187        handler.process();
188 
189        // Handler should remain in M_ZK_REGION_CLOSING
190        RegionTransition rt =
191          RegionTransition.parseFrom(ZKAssign.getData(server.getZooKeeper(), hri.getEncodedName()));
192        assertTrue(rt.getEventType().equals(EventType.M_ZK_REGION_CLOSING ));
193      }
194 
195      /**
196       * Test if the region can be closed properly
197       * @throws IOException
198       * @throws NodeExistsException
199       * @throws KeeperException
200      * @throws org.apache.hadoop.hbase.exceptions.DeserializationException
201       */
202      @Test public void testCloseRegion()
203      throws IOException, NodeExistsException, KeeperException, DeserializationException {
204        final Server server = new MockServer(HTU);
205        final RegionServerServices rss = HTU.createMockRegionServerService();
206 
207        HTableDescriptor htd = TEST_HTD;
208        HRegionInfo hri = TEST_HRI;
209 
210        ZkCoordinatedStateManager coordinationProvider = new ZkCoordinatedStateManager();
211        coordinationProvider.initialize(server);
212        coordinationProvider.start();
213 
214        // open a region first so that it can be closed later
215        OpenRegion(server, rss, htd, hri, coordinationProvider.getOpenRegionCoordination());
216 
217        // close the region
218        // Create it CLOSING, which is what Master set before sending CLOSE RPC
219        int versionOfClosingNode = ZKAssign.createNodeClosing(server.getZooKeeper(),
220          hri, server.getServerName());
221 
222        // The CloseRegionHandler will validate the expected version
223        // Given it is set to correct versionOfClosingNode,
224        // CloseRegionHandlerit should be RS_ZK_REGION_CLOSED
225 
226        ZkCloseRegionCoordination.ZkCloseRegionDetails zkCrd =
227          new ZkCloseRegionCoordination.ZkCloseRegionDetails();
228        zkCrd.setPublishStatusInZk(true);
229        zkCrd.setExpectedVersion(versionOfClosingNode);
230 
231        CloseRegionHandler handler = new CloseRegionHandler(server, rss, hri, false,
232          coordinationProvider.getCloseRegionCoordination(), zkCrd);
233        handler.process();
234        // Handler should have transitioned it to RS_ZK_REGION_CLOSED
235        RegionTransition rt = RegionTransition.parseFrom(
236          ZKAssign.getData(server.getZooKeeper(), hri.getEncodedName()));
237        assertTrue(rt.getEventType().equals(EventType.RS_ZK_REGION_CLOSED));
238      }
239 
240      private void OpenRegion(Server server, RegionServerServices rss,
241          HTableDescriptor htd, HRegionInfo hri, OpenRegionCoordination coordination)
242      throws IOException, NodeExistsException, KeeperException, DeserializationException {
243        // Create it OFFLINE node, which is what Master set before sending OPEN RPC
244        ZKAssign.createNodeOffline(server.getZooKeeper(), hri, server.getServerName());
245 
246        OpenRegionCoordination.OpenRegionDetails ord =
247          coordination.getDetailsForNonCoordinatedOpening();
248        OpenRegionHandler openHandler =
249          new OpenRegionHandler(server, rss, hri, htd, -1, coordination, ord);
250        rss.getRegionsInTransitionInRS().put(hri.getEncodedNameAsBytes(), Boolean.TRUE);
251        openHandler.process();
252        // This parse is not used?
253        RegionTransition.parseFrom(ZKAssign.getData(server.getZooKeeper(), hri.getEncodedName()));
254        // delete the node, which is what Master do after the region is opened
255        ZKAssign.deleteNode(server.getZooKeeper(), hri.getEncodedName(),
256          EventType.RS_ZK_REGION_OPENED, server.getServerName());
257      }
258 
259 }
260