View Javadoc

1   /**
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  package org.apache.hadoop.hbase.master;
20  
21  
22  import static org.junit.Assert.assertEquals;
23  import static org.junit.Assert.assertFalse;
24  import static org.junit.Assert.assertTrue;
25  import static org.junit.Assert.fail;
26  
27  import java.io.IOException;
28  import java.util.Collection;
29  
30  import org.apache.commons.logging.Log;
31  import org.apache.commons.logging.LogFactory;
32  import org.apache.hadoop.conf.Configuration;
33  import org.apache.hadoop.hbase.HBaseTestingUtility;
34  import org.apache.hadoop.hbase.HConstants;
35  import org.apache.hadoop.hbase.HRegionInfo;
36  import org.apache.hadoop.hbase.MediumTests;
37  import org.apache.hadoop.hbase.MiniHBaseCluster;
38  import org.apache.hadoop.hbase.TableDescriptors;
39  import org.apache.hadoop.hbase.TableName;
40  import org.apache.hadoop.hbase.client.Durability;
41  import org.apache.hadoop.hbase.client.HTable;
42  import org.apache.hadoop.hbase.client.Put;
43  import org.apache.hadoop.hbase.client.Result;
44  import org.apache.hadoop.hbase.client.ResultScanner;
45  import org.apache.hadoop.hbase.client.Scan;
46  import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
47  import org.apache.hadoop.hbase.regionserver.HRegionServer;
48  import org.apache.hadoop.hbase.util.Bytes;
49  import org.apache.hadoop.hbase.util.Threads;
50  import org.junit.AfterClass;
51  import org.junit.Assert;
52  import org.junit.Before;
53  import org.junit.BeforeClass;
54  import org.junit.Test;
55  import org.junit.experimental.categories.Category;
56  import org.mockito.Mockito;
57  import org.mockito.internal.util.reflection.Whitebox;
58  
59  /**
60   * Test open and close of regions using zk.
61   */
62  @Category(MediumTests.class)
63  public class TestZKBasedOpenCloseRegion {
64    private static final Log LOG = LogFactory.getLog(TestZKBasedOpenCloseRegion.class);
65    private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
66    private static final TableName TABLENAME =
67        TableName.valueOf("TestZKBasedOpenCloseRegion");
68    private static final byte [][] FAMILIES = new byte [][] {Bytes.toBytes("a"),
69      Bytes.toBytes("b"), Bytes.toBytes("c")};
70    private static int countOfRegions;
71  
72    @BeforeClass public static void beforeAllTests() throws Exception {
73      Configuration c = TEST_UTIL.getConfiguration();
74      c.setBoolean("dfs.support.append", true);
75      c.setInt("hbase.regionserver.info.port", 0);
76      TEST_UTIL.startMiniCluster(2);
77      TEST_UTIL.createTable(TABLENAME, FAMILIES);
78      HTable t = new HTable(TEST_UTIL.getConfiguration(), TABLENAME);
79      countOfRegions = TEST_UTIL.createMultiRegions(t, getTestFamily());
80      waitUntilAllRegionsAssigned();
81      addToEachStartKey(countOfRegions);
82      t.close();
83      TEST_UTIL.getHBaseCluster().getMaster().assignmentManager.initializeHandlerTrackers();
84    }
85  
86    @AfterClass public static void afterAllTests() throws Exception {
87      TEST_UTIL.shutdownMiniCluster();
88    }
89  
90    @Before public void setup() throws IOException {
91      if (TEST_UTIL.getHBaseCluster().getLiveRegionServerThreads().size() < 2) {
92        // Need at least two servers.
93        LOG.info("Started new server=" +
94          TEST_UTIL.getHBaseCluster().startRegionServer());
95  
96      }
97      waitUntilAllRegionsAssigned();
98      waitOnRIT();
99    }
100 
101   /**
102    * Test we reopen a region once closed.
103    * @throws Exception
104    */
105   @Test (timeout=300000) public void testReOpenRegion()
106   throws Exception {
107     MiniHBaseCluster cluster = TEST_UTIL.getHBaseCluster();
108     LOG.info("Number of region servers = " +
109       cluster.getLiveRegionServerThreads().size());
110 
111     int rsIdx = 0;
112     HRegionServer regionServer =
113       TEST_UTIL.getHBaseCluster().getRegionServer(rsIdx);
114     HRegionInfo hri = getNonMetaRegion(ProtobufUtil.getOnlineRegions(regionServer));
115     LOG.debug("Asking RS to close region " + hri.getRegionNameAsString());
116 
117     LOG.info("Unassign " + hri.getRegionNameAsString());
118     cluster.getMaster().assignmentManager.unassign(hri);
119 
120     while (!cluster.getMaster().assignmentManager.wasClosedHandlerCalled(hri)) {
121       Threads.sleep(100);
122     }
123 
124     while (!cluster.getMaster().assignmentManager.wasOpenedHandlerCalled(hri)) {
125       Threads.sleep(100);
126     }
127 
128     LOG.info("Done with testReOpenRegion");
129   }
130 
131   private HRegionInfo getNonMetaRegion(final Collection<HRegionInfo> regions) {
132     HRegionInfo hri = null;
133     for (HRegionInfo i: regions) {
134       LOG.info(i.getRegionNameAsString());
135       if (!i.isMetaRegion()) {
136         hri = i;
137         break;
138       }
139     }
140     return hri;
141   }
142 
143   /**
144    * This test shows how a region won't be able to be assigned to a RS
145    * if it's already "processing" it.
146    * @throws Exception
147    */
148   @Test
149   public void testRSAlreadyProcessingRegion() throws Exception {
150     LOG.info("starting testRSAlreadyProcessingRegion");
151     MiniHBaseCluster cluster = TEST_UTIL.getHBaseCluster();
152 
153     HRegionServer hr0 =
154         cluster.getLiveRegionServerThreads().get(0).getRegionServer();
155     HRegionServer hr1 =
156         cluster.getLiveRegionServerThreads().get(1).getRegionServer();
157     HRegionInfo hri = getNonMetaRegion(ProtobufUtil.getOnlineRegions(hr0));
158 
159     // fake that hr1 is processing the region
160     hr1.getRegionsInTransitionInRS().putIfAbsent(hri.getEncodedNameAsBytes(), true);
161 
162     // now ask the master to move the region to hr1, will fail
163     TEST_UTIL.getHBaseAdmin().move(hri.getEncodedNameAsBytes(),
164         Bytes.toBytes(hr1.getServerName().toString()));
165 
166     // make sure the region came back
167     assertEquals(hr1.getOnlineRegion(hri.getEncodedNameAsBytes()), null);
168 
169     // remove the block and reset the boolean
170     hr1.getRegionsInTransitionInRS().remove(hri.getEncodedNameAsBytes());
171 
172     // now try moving a region when there is no region in transition.
173     hri = getNonMetaRegion(ProtobufUtil.getOnlineRegions(hr1));
174 
175     TEST_UTIL.getHBaseAdmin().move(hri.getEncodedNameAsBytes(),
176         Bytes.toBytes(hr0.getServerName().toString()));
177 
178     while (!cluster.getMaster().assignmentManager.wasOpenedHandlerCalled(hri)) {
179       Threads.sleep(100);
180     }
181 
182     // make sure the region has moved from the original RS
183     assertTrue(hr1.getOnlineRegion(hri.getEncodedNameAsBytes()) == null);
184 
185   }
186 
187   @Test (timeout=300000) public void testCloseRegion()
188   throws Exception {
189     LOG.info("Running testCloseRegion");
190     MiniHBaseCluster cluster = TEST_UTIL.getHBaseCluster();
191     LOG.info("Number of region servers = " + cluster.getLiveRegionServerThreads().size());
192 
193     int rsIdx = 0;
194     HRegionServer regionServer = TEST_UTIL.getHBaseCluster().getRegionServer(rsIdx);
195     HRegionInfo hri = getNonMetaRegion(ProtobufUtil.getOnlineRegions(regionServer));
196     LOG.debug("Asking RS to close region " + hri.getRegionNameAsString());
197 
198     cluster.getMaster().assignmentManager.unassign(hri);
199 
200     while (!cluster.getMaster().assignmentManager.wasClosedHandlerCalled(hri)) {
201       Threads.sleep(100);
202     }
203     LOG.info("Done with testCloseRegion");
204   }
205 
206   private void waitOnRIT() {
207     // Close worked but we are going to open the region elsewhere.  Before going on, make sure
208     // this completes.
209     while (TEST_UTIL.getHBaseCluster().getMaster().getAssignmentManager().
210         getRegionStates().isRegionsInTransition()) {
211       LOG.info("Waiting on regions in transition: " +
212         TEST_UTIL.getHBaseCluster().getMaster().getAssignmentManager().
213           getRegionStates().getRegionsInTransition());
214       Threads.sleep(10);
215     }
216   }
217 
218   /**
219    * If region open fails with IOException in openRegion() while doing tableDescriptors.get()
220    * the region should not add into regionsInTransitionInRS map
221    * @throws Exception
222    */
223   @Test
224   public void testRegionOpenFailsDueToIOException() throws Exception {
225     HRegionInfo REGIONINFO = new HRegionInfo(TableName.valueOf("t"),
226         HConstants.EMPTY_START_ROW, HConstants.EMPTY_START_ROW);
227     HRegionServer regionServer = TEST_UTIL.getHBaseCluster().getRegionServer(0);
228     TableDescriptors htd = Mockito.mock(TableDescriptors.class);
229     Object orizinalState = Whitebox.getInternalState(regionServer,"tableDescriptors");
230     Whitebox.setInternalState(regionServer, "tableDescriptors", htd);
231     Mockito.doThrow(new IOException()).when(htd).get((TableName) Mockito.any());
232     try {
233       ProtobufUtil.openRegion(regionServer, regionServer.getServerName(), REGIONINFO);
234       fail("It should throw IOException ");
235     } catch (IOException e) {
236     }
237     Whitebox.setInternalState(regionServer, "tableDescriptors", orizinalState);
238     assertFalse("Region should not be in RIT",
239         regionServer.getRegionsInTransitionInRS().containsKey(REGIONINFO.getEncodedNameAsBytes()));
240   }
241 
242   private static void waitUntilAllRegionsAssigned()
243   throws IOException {
244     HTable meta = new HTable(TEST_UTIL.getConfiguration(), TableName.META_TABLE_NAME);
245     while (true) {
246       int rows = 0;
247       Scan scan = new Scan();
248       scan.addColumn(HConstants.CATALOG_FAMILY, HConstants.SERVER_QUALIFIER);
249       ResultScanner s = meta.getScanner(scan);
250       for (Result r = null; (r = s.next()) != null;) {
251         byte [] b =
252           r.getValue(HConstants.CATALOG_FAMILY, HConstants.SERVER_QUALIFIER);
253         if (b == null || b.length <= 0) {
254           break;
255         }
256         rows++;
257       }
258       s.close();
259       // If I get to here and all rows have a Server, then all have been assigned.
260       if (rows >= countOfRegions) {
261         break;
262       }
263       LOG.info("Found=" + rows);
264       Threads.sleep(1000);
265     }
266     meta.close();
267   }
268 
269   /*
270    * Add to each of the regions in hbase:meta a value.  Key is the startrow of the
271    * region (except its 'aaa' for first region).  Actual value is the row name.
272    * @param expected
273    * @return
274    * @throws IOException
275    */
276   private static int addToEachStartKey(final int expected) throws IOException {
277     HTable t = new HTable(TEST_UTIL.getConfiguration(), TABLENAME);
278     HTable meta = new HTable(TEST_UTIL.getConfiguration(),
279         TableName.META_TABLE_NAME);
280     int rows = 0;
281     Scan scan = new Scan();
282     scan.addColumn(HConstants.CATALOG_FAMILY, HConstants.REGIONINFO_QUALIFIER);
283     ResultScanner s = meta.getScanner(scan);
284     for (Result r = null; (r = s.next()) != null;) {
285       HRegionInfo hri = HRegionInfo.getHRegionInfo(r);
286       if (hri == null) break;
287       if(!hri.getTable().equals(TABLENAME)) {
288         continue;
289       }
290       // If start key, add 'aaa'.
291       byte [] row = getStartKey(hri);
292       Put p = new Put(row);
293       p.setDurability(Durability.SKIP_WAL);
294       p.add(getTestFamily(), getTestQualifier(), row);
295       t.put(p);
296       rows++;
297     }
298     s.close();
299     Assert.assertEquals(expected, rows);
300     t.close();
301     meta.close();
302     return rows;
303   }
304 
305   private static byte [] getStartKey(final HRegionInfo hri) {
306     return Bytes.equals(HConstants.EMPTY_START_ROW, hri.getStartKey())?
307         Bytes.toBytes("aaa"): hri.getStartKey();
308   }
309 
310   private static byte [] getTestFamily() {
311     return FAMILIES[0];
312   }
313 
314   private static byte [] getTestQualifier() {
315     return getTestFamily();
316   }
317 
318   public static void main(String args[]) throws Exception {
319     TestZKBasedOpenCloseRegion.beforeAllTests();
320 
321     TestZKBasedOpenCloseRegion test = new TestZKBasedOpenCloseRegion();
322     test.setup();
323     test.testCloseRegion();
324 
325     TestZKBasedOpenCloseRegion.afterAllTests();
326   }
327 
328 }
329