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