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.catalog;
20  
21  import static org.junit.Assert.assertEquals;
22  import static org.junit.Assert.assertFalse;
23  import static org.junit.Assert.assertNull;
24  import static org.junit.Assert.assertTrue;
25  
26  import java.io.IOException;
27  import java.util.List;
28  import java.util.concurrent.atomic.AtomicBoolean;
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.Abortable;
34  import org.apache.hadoop.hbase.TableName;
35  import org.apache.hadoop.hbase.HBaseTestingUtility;
36  import org.apache.hadoop.hbase.HConstants;
37  import org.apache.hadoop.hbase.HRegionInfo;
38  import org.apache.hadoop.hbase.testclassification.MediumTests;
39  import org.apache.hadoop.hbase.ServerName;
40  import org.apache.hadoop.hbase.client.HBaseAdmin;
41  import org.apache.hadoop.hbase.client.HTable;
42  import org.apache.hadoop.hbase.util.Bytes;
43  import org.apache.hadoop.hbase.util.Pair;
44  import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
45  import org.junit.AfterClass;
46  import org.junit.BeforeClass;
47  import org.junit.Test;
48  import org.junit.experimental.categories.Category;
49  
50  /**
51   * Test {@link MetaReader}, {@link MetaEditor}.
52   */
53  @Category(MediumTests.class)
54  public class TestMetaReaderEditor {
55    private static final Log LOG = LogFactory.getLog(TestMetaReaderEditor.class);
56    private static final  HBaseTestingUtility UTIL = new HBaseTestingUtility();
57    private static ZooKeeperWatcher zkw;
58    private static CatalogTracker CT;
59    private final static Abortable ABORTABLE = new Abortable() {
60      private final AtomicBoolean abort = new AtomicBoolean(false);
61  
62      @Override
63      public void abort(String why, Throwable e) {
64        LOG.info(why, e);
65        abort.set(true);
66      }
67      
68      @Override
69      public boolean isAborted() {
70        return abort.get();
71      }
72    };
73  
74    @BeforeClass public static void beforeClass() throws Exception {
75      UTIL.startMiniCluster(3);
76  
77      Configuration c = new Configuration(UTIL.getConfiguration());
78      // Tests to 4 retries every 5 seconds. Make it try every 1 second so more
79      // responsive.  1 second is default as is ten retries.
80      c.setLong("hbase.client.pause", 1000);
81      c.setInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, 10);
82      zkw = new ZooKeeperWatcher(c, "TestMetaReaderEditor", ABORTABLE);
83      CT = new CatalogTracker(zkw, c, ABORTABLE);
84      CT.start();
85    }
86  
87    @AfterClass public static void afterClass() throws Exception {
88      ABORTABLE.abort("test ending", null);
89      CT.stop();
90      UTIL.shutdownMiniCluster();
91    }
92  
93    /**
94     * Does {@link MetaReader#getRegion(CatalogTracker, byte[])} and a write
95     * against hbase:meta while its hosted server is restarted to prove our retrying
96     * works.
97     * @throws IOException
98     * @throws InterruptedException
99     */
100   @Test public void testRetrying()
101   throws IOException, InterruptedException {
102     final TableName name =
103         TableName.valueOf("testRetrying");
104     LOG.info("Started " + name);
105     HTable t = UTIL.createTable(name, HConstants.CATALOG_FAMILY);
106     int regionCount = UTIL.createMultiRegions(t, HConstants.CATALOG_FAMILY);
107     // Test it works getting a region from just made user table.
108     final List<HRegionInfo> regions =
109       testGettingTableRegions(CT, name, regionCount);
110     MetaTask reader = new MetaTask(CT, "reader") {
111       @Override
112       void metaTask() throws Throwable {
113         testGetRegion(this.ct, regions.get(0));
114         LOG.info("Read " + regions.get(0).getEncodedName());
115       }
116     };
117     MetaTask writer = new MetaTask(CT, "writer") {
118       @Override
119       void metaTask() throws Throwable {
120         MetaEditor.addRegionToMeta(this.ct, regions.get(0));
121         LOG.info("Wrote " + regions.get(0).getEncodedName());
122       }
123     };
124     reader.start();
125     writer.start();
126 
127     // We're gonna check how it takes. If it takes too long, we will consider
128     //  it as a fail. We can't put that in the @Test tag as we want to close
129     //  the threads nicely
130     final long timeOut = 180000;
131     long startTime = System.currentTimeMillis();
132 
133     try {
134       // Make sure reader and writer are working.
135       assertTrue(reader.isProgressing());
136       assertTrue(writer.isProgressing());
137 
138       // Kill server hosting meta -- twice  . See if our reader/writer ride over the
139       // meta moves.  They'll need to retry.
140       for (int i = 0; i < 2; i++) {
141         LOG.info("Restart=" + i);
142         UTIL.ensureSomeRegionServersAvailable(2);
143         int index = -1;
144         do {
145           index = UTIL.getMiniHBaseCluster().getServerWithMeta();
146         } while (index == -1 &&
147           startTime + timeOut < System.currentTimeMillis());
148 
149         if (index != -1){
150           UTIL.getMiniHBaseCluster().abortRegionServer(index);
151           UTIL.getMiniHBaseCluster().waitOnRegionServer(index);
152         }
153       }
154 
155       assertTrue("reader: " + reader.toString(), reader.isProgressing());
156       assertTrue("writer: " + writer.toString(), writer.isProgressing());
157     } catch (IOException e) {
158       throw e;
159     } finally {
160       reader.stop = true;
161       writer.stop = true;
162       reader.join();
163       writer.join();
164       t.close();
165     }
166     long exeTime = System.currentTimeMillis() - startTime;
167     assertTrue("Timeout: test took " + exeTime / 1000 + " sec", exeTime < timeOut);
168   }
169 
170   /**
171    * Thread that runs a MetaReader/MetaEditor task until asked stop.
172    */
173   abstract static class MetaTask extends Thread {
174     boolean stop = false;
175     int count = 0;
176     Throwable t = null;
177     final CatalogTracker ct;
178 
179     MetaTask(final CatalogTracker ct, final String name) {
180       super(name);
181       this.ct = ct;
182     }
183 
184     @Override
185     public void run() {
186       try {
187         while(!this.stop) {
188           LOG.info("Before " + this.getName()+ ", count=" + this.count);
189           metaTask();
190           this.count += 1;
191           LOG.info("After " + this.getName() + ", count=" + this.count);
192           Thread.sleep(100);
193         }
194       } catch (Throwable t) {
195         LOG.info(this.getName() + " failed", t);
196         this.t = t;
197       }
198     }
199 
200     boolean isProgressing() throws InterruptedException {
201       int currentCount = this.count;
202       while(currentCount == this.count) {
203         if (!isAlive()) return false;
204         if (this.t != null) return false;
205         Thread.sleep(10);
206       }
207       return true;
208     }
209 
210     @Override
211     public String toString() {
212       return "count=" + this.count + ", t=" +
213         (this.t == null? "null": this.t.toString());
214     }
215 
216     abstract void metaTask() throws Throwable;
217   }
218 
219   @Test public void testGetRegionsCatalogTables()
220   throws IOException, InterruptedException {
221     List<HRegionInfo> regions =
222       MetaReader.getTableRegions(CT, TableName.META_TABLE_NAME);
223     assertTrue(regions.size() >= 1);
224     assertTrue(MetaReader.getTableRegionsAndLocations(CT,
225       TableName.META_TABLE_NAME).size() >= 1);
226   }
227 
228   @Test public void testTableExists() throws IOException {
229     final TableName name =
230         TableName.valueOf("testTableExists");
231     assertFalse(MetaReader.tableExists(CT, name));
232     UTIL.createTable(name, HConstants.CATALOG_FAMILY);
233     assertTrue(MetaReader.tableExists(CT, name));
234     HBaseAdmin admin = UTIL.getHBaseAdmin();
235     admin.disableTable(name);
236     admin.deleteTable(name);
237     assertFalse(MetaReader.tableExists(CT, name));
238     assertTrue(MetaReader.tableExists(CT,
239       TableName.META_TABLE_NAME));
240   }
241 
242   @Test public void testGetRegion() throws IOException, InterruptedException {
243     final String name = "testGetRegion";
244     LOG.info("Started " + name);
245     // Test get on non-existent region.
246     Pair<HRegionInfo, ServerName> pair =
247       MetaReader.getRegion(CT, Bytes.toBytes("nonexistent-region"));
248     assertNull(pair);
249     LOG.info("Finished " + name);
250   }
251 
252   // Test for the optimization made in HBASE-3650
253   @Test public void testScanMetaForTable()
254   throws IOException, InterruptedException {
255     final TableName name =
256         TableName.valueOf("testScanMetaForTable");
257     LOG.info("Started " + name);
258 
259     /** Create 2 tables
260      - testScanMetaForTable
261      - testScanMetaForTablf
262     **/
263 
264     UTIL.createTable(name, HConstants.CATALOG_FAMILY);
265     // name that is +1 greater than the first one (e+1=f)
266     TableName greaterName =
267         TableName.valueOf("testScanMetaForTablf");
268     UTIL.createTable(greaterName, HConstants.CATALOG_FAMILY);
269 
270     // Now make sure we only get the regions from 1 of the tables at a time
271 
272     assertEquals(1, MetaReader.getTableRegions(CT, name).size());
273     assertEquals(1, MetaReader.getTableRegions(CT, greaterName).size());
274   }
275 
276   private static List<HRegionInfo> testGettingTableRegions(final CatalogTracker ct,
277       final TableName name, final int regionCount)
278   throws IOException, InterruptedException {
279     List<HRegionInfo> regions = MetaReader.getTableRegions(ct, name);
280     assertEquals(regionCount, regions.size());
281     Pair<HRegionInfo, ServerName> pair =
282       MetaReader.getRegion(ct, regions.get(0).getRegionName());
283     assertEquals(regions.get(0).getEncodedName(),
284       pair.getFirst().getEncodedName());
285     return regions;
286   }
287 
288   private static void testGetRegion(final CatalogTracker ct,
289       final HRegionInfo region)
290   throws IOException, InterruptedException {
291     Pair<HRegionInfo, ServerName> pair =
292       MetaReader.getRegion(ct, region.getRegionName());
293     assertEquals(region.getEncodedName(),
294       pair.getFirst().getEncodedName());
295   }
296 
297 }
298