1   /**
2    * Copyright 2010 The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  package org.apache.hadoop.hbase.catalog;
21  
22  import static org.junit.Assert.assertEquals;
23  import static org.junit.Assert.assertFalse;
24  import static org.junit.Assert.assertNull;
25  import static org.junit.Assert.assertTrue;
26  
27  import java.io.IOException;
28  import java.util.List;
29  import java.util.concurrent.atomic.AtomicBoolean;
30  
31  import org.apache.commons.logging.Log;
32  import org.apache.commons.logging.LogFactory;
33  import org.apache.hadoop.conf.Configuration;
34  import org.apache.hadoop.hbase.Abortable;
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.ServerName;
39  import org.apache.hadoop.hbase.client.HBaseAdmin;
40  import org.apache.hadoop.hbase.client.HTable;
41  import org.apache.hadoop.hbase.util.Bytes;
42  import org.apache.hadoop.hbase.util.Pair;
43  import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
44  import org.junit.AfterClass;
45  import org.junit.Before;
46  import org.junit.BeforeClass;
47  import org.junit.Test;
48  
49  /**
50   * Test {@link MetaReader}, {@link MetaEditor}, and {@link RootLocationEditor}.
51   */
52  public class TestMetaReaderEditor {
53    private static final Log LOG = LogFactory.getLog(TestMetaReaderEditor.class);
54    private static final  HBaseTestingUtility UTIL = new HBaseTestingUtility();
55    private ZooKeeperWatcher zkw;
56    private CatalogTracker ct;
57    private final static Abortable ABORTABLE = new Abortable() {
58      private final AtomicBoolean abort = new AtomicBoolean(false);
59  
60      @Override
61      public void abort(String why, Throwable e) {
62        LOG.info(why, e);
63        abort.set(true);
64      }
65      
66      @Override
67      public boolean isAborted() {
68        return abort.get();
69      }
70      
71    };
72  
73    @BeforeClass public static void beforeClass() throws Exception {
74      UTIL.startMiniCluster(3);
75    }
76  
77    @Before public void setup() throws IOException, InterruptedException {
78      Configuration c = new Configuration(UTIL.getConfiguration());
79      // Tests to 4 retries every 5 seconds. Make it try every 1 second so more
80      // responsive.  1 second is default as is ten retries.
81      c.setLong("hbase.client.pause", 1000);
82      c.setInt("hbase.client.retries.number", 10);
83      zkw = new ZooKeeperWatcher(c, "TestMetaReaderEditor", ABORTABLE);
84      ct = new CatalogTracker(zkw, c, ABORTABLE);
85      ct.start();
86    }
87  
88    @AfterClass public static void afterClass() throws Exception {
89      UTIL.shutdownMiniCluster();
90    }
91  
92    /**
93     * Does {@link MetaReader#getRegion(CatalogTracker, byte[])} and a write
94     * against .META. while its hosted server is restarted to prove our retrying
95     * works.
96     * @throws IOException
97     * @throws InterruptedException
98     */
99    @Test (timeout = 180000) public void testRetrying()
100   throws IOException, InterruptedException {
101     final String name = "testRetrying";
102     LOG.info("Started " + name);
103     final byte [] nameBytes = Bytes.toBytes(name);
104     HTable t = UTIL.createTable(nameBytes, HConstants.CATALOG_FAMILY);
105     int regionCount = UTIL.createMultiRegions(t, HConstants.CATALOG_FAMILY);
106     // Test it works getting a region from just made user table.
107     final List<HRegionInfo> regions =
108       testGettingTableRegions(this.ct, nameBytes, regionCount);
109     MetaTask reader = new MetaTask(this.ct, "reader") {
110       @Override
111       void metaTask() throws Throwable {
112         testGetRegion(this.ct, regions.get(0));
113         LOG.info("Read " + regions.get(0).getEncodedName());
114       }
115     };
116     MetaTask writer = new MetaTask(this.ct, "writer") {
117       @Override
118       void metaTask() throws Throwable {
119         MetaEditor.addRegionToMeta(this.ct, regions.get(0));
120         LOG.info("Wrote " + regions.get(0).getEncodedName());
121       }
122     };
123     reader.start();
124     writer.start();
125     // Make sure reader and writer are working.
126     assertTrue(reader.isProgressing());
127     assertTrue(writer.isProgressing());
128     // Kill server hosting meta -- twice  . See if our reader/writer ride over the
129     // meta moves.  They'll need to retry.
130     for (int i = 0; i < 2; i++) {
131       LOG.info("Restart=" + i);
132       UTIL.ensureSomeRegionServersAvailable(2);
133       int index = -1;
134       do {
135         index = UTIL.getMiniHBaseCluster().getServerWithMeta();
136       } while (index == -1);
137       UTIL.getMiniHBaseCluster().abortRegionServer(index);
138       UTIL.getMiniHBaseCluster().waitOnRegionServer(index);
139     }
140     assertTrue(reader.toString(), reader.isProgressing());
141     assertTrue(writer.toString(), writer.isProgressing());
142     reader.stop = true;
143     writer.stop = true;
144     reader.join();
145     writer.join();
146   }
147 
148   /**
149    * Thread that runs a MetaReader/MetaEditor task until asked stop.
150    */
151   abstract static class MetaTask extends Thread {
152     boolean stop = false;
153     int count = 0;
154     Throwable t = null;
155     final CatalogTracker ct;
156 
157     MetaTask(final CatalogTracker ct, final String name) {
158       super(name);
159       this.ct = ct;
160     }
161 
162     @Override
163     public void run() {
164       try {
165         while(!this.stop) {
166           LOG.info("Before " + this.getName()+ ", count=" + this.count);
167           metaTask();
168           this.count += 1;
169           LOG.info("After " + this.getName() + ", count=" + this.count);
170           Thread.sleep(100);
171         }
172       } catch (Throwable t) {
173         LOG.info(this.getName() + " failed", t);
174         this.t = t;
175       }
176     }
177 
178     boolean isProgressing() throws InterruptedException {
179       int currentCount = this.count;
180       while(currentCount == this.count) {
181         if (!isAlive()) return false;
182         if (this.t != null) return false;
183         Thread.sleep(10);
184       }
185       return true;
186     }
187 
188     @Override
189     public String toString() {
190       return "count=" + this.count + ", t=" +
191         (this.t == null? "null": this.t.toString());
192     }
193 
194     abstract void metaTask() throws Throwable;
195   }
196 
197   @Test public void testGetRegionsCatalogTables()
198   throws IOException, InterruptedException {
199     List<HRegionInfo> regions =
200       MetaReader.getTableRegions(ct, HConstants.META_TABLE_NAME);
201     assertTrue(regions.size() >= 1);
202     assertTrue(MetaReader.getTableRegionsAndLocations(ct,
203       Bytes.toString(HConstants.META_TABLE_NAME)).size() >= 1);
204     assertTrue(MetaReader.getTableRegionsAndLocations(ct,
205       Bytes.toString(HConstants.ROOT_TABLE_NAME)).size() == 1);
206   }
207 
208   @Test public void testTableExists() throws IOException {
209     final String name = "testTableExists";
210     final byte [] nameBytes = Bytes.toBytes(name);
211     assertFalse(MetaReader.tableExists(ct, name));
212     UTIL.createTable(nameBytes, HConstants.CATALOG_FAMILY);
213     assertTrue(MetaReader.tableExists(ct, name));
214     HBaseAdmin admin = UTIL.getHBaseAdmin();
215     admin.disableTable(name);
216     admin.deleteTable(name);
217     assertFalse(MetaReader.tableExists(ct, name));
218     assertTrue(MetaReader.tableExists(ct,
219       Bytes.toString(HConstants.META_TABLE_NAME)));
220     assertTrue(MetaReader.tableExists(ct,
221       Bytes.toString(HConstants.ROOT_TABLE_NAME)));
222   }
223 
224   @Test public void testGetRegion() throws IOException, InterruptedException {
225     final String name = "testGetRegion";
226     LOG.info("Started " + name);
227     // Test get on non-existent region.
228     Pair<HRegionInfo, ServerName> pair =
229       MetaReader.getRegion(ct, Bytes.toBytes("nonexistent-region"));
230     assertNull(pair);
231     // Test it works getting a region from meta/root.
232     pair =
233       MetaReader.getRegion(ct, HRegionInfo.FIRST_META_REGIONINFO.getRegionName());
234     assertEquals(HRegionInfo.FIRST_META_REGIONINFO.getEncodedName(),
235       pair.getFirst().getEncodedName());
236     LOG.info("Finished " + name);
237   }
238 
239   // Test for the optimization made in HBASE-3650
240   @Test public void testScanMetaForTable()
241   throws IOException, InterruptedException {
242     final String name = "testScanMetaForTable";
243     LOG.info("Started " + name);
244 
245     /** Create 5 tables
246      - testScanMetaForTable
247      - testScanMetaForTable0
248      - testScanMetaForTable1
249      - testScanMetaForTable2
250      - testScanMetaForTablf
251     **/
252 
253     UTIL.createTable(Bytes.toBytes(name), HConstants.CATALOG_FAMILY);
254     for (int i = 3; i < 3; i ++) {
255       UTIL.createTable(Bytes.toBytes(name+i), HConstants.CATALOG_FAMILY);
256     }
257     // name that is +1 greater than the first one (e+1=f)
258     byte[] greaterName = Bytes.toBytes("testScanMetaForTablf");
259     UTIL.createTable(greaterName, HConstants.CATALOG_FAMILY);
260 
261     // Now make sure we only get the regions from 1 of the tables at a time
262 
263     assertEquals(1, MetaReader.getTableRegions(ct, Bytes.toBytes(name)).size());
264     for (int i = 3; i < 3; i ++) {
265       assertEquals(1, MetaReader.getTableRegions(ct, Bytes.toBytes(name+i)).size());
266     }
267     assertEquals(1, MetaReader.getTableRegions(ct, greaterName).size());
268   }
269 
270   private static List<HRegionInfo> testGettingTableRegions(final CatalogTracker ct,
271       final byte [] nameBytes, final int regionCount)
272   throws IOException, InterruptedException {
273     List<HRegionInfo> regions = MetaReader.getTableRegions(ct, nameBytes);
274     assertEquals(regionCount, regions.size());
275     Pair<HRegionInfo, ServerName> pair =
276       MetaReader.getRegion(ct, regions.get(0).getRegionName());
277     assertEquals(regions.get(0).getEncodedName(),
278       pair.getFirst().getEncodedName());
279     return regions;
280   }
281 
282   private static void testGetRegion(final CatalogTracker ct,
283       final HRegionInfo region)
284   throws IOException, InterruptedException {
285     Pair<HRegionInfo, ServerName> pair =
286       MetaReader.getRegion(ct, region.getRegionName());
287     assertEquals(region.getEncodedName(),
288       pair.getFirst().getEncodedName());
289   }
290 }