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.util;
20  
21  import static org.junit.Assert.assertTrue;
22  
23  import java.io.IOException;
24  import java.util.List;
25  
26  import org.apache.commons.logging.Log;
27  import org.apache.commons.logging.LogFactory;
28  import org.apache.hadoop.conf.Configuration;
29  import org.apache.hadoop.fs.FileSystem;
30  import org.apache.hadoop.fs.Path;
31  import org.apache.hadoop.hbase.*;
32  import org.apache.hadoop.hbase.catalog.CatalogTracker;
33  import org.apache.hadoop.hbase.catalog.MetaReader;
34  import org.apache.hadoop.hbase.client.HBaseAdmin;
35  import org.apache.hadoop.hbase.client.Put;
36  import org.apache.hadoop.hbase.client.Durability;
37  import org.apache.hadoop.hbase.regionserver.HRegion;
38  import org.junit.Test;
39  import org.junit.experimental.categories.Category;
40  
41  /**
42   * Tests merging a normal table's regions
43   */
44  @Category(MediumTests.class)
45  public class TestMergeTable {
46    private static final Log LOG = LogFactory.getLog(TestMergeTable.class);
47    private final HBaseTestingUtility UTIL = new HBaseTestingUtility();
48    private static final byte [] COLUMN_NAME = Bytes.toBytes("contents");
49    private static final byte [] VALUE;
50    static {
51      // We will use the same value for the rows as that is not really important here
52      String partialValue = String.valueOf(System.currentTimeMillis());
53      StringBuilder val = new StringBuilder();
54      while (val.length() < 1024) {
55        val.append(partialValue);
56      }
57      VALUE = Bytes.toBytes(val.toString());
58    }
59  
60    /**
61     * Test merge.
62     * Hand-makes regions of a mergeable size and adds the hand-made regions to
63     * hand-made meta.  The hand-made regions are created offline.  We then start
64     * up mini cluster, disables the hand-made table and starts in on merging.
65     * @throws Exception
66     */
67    @Test (timeout=300000) public void testMergeTable() throws Exception {
68      // Table we are manually creating offline.
69      HTableDescriptor desc = new HTableDescriptor(org.apache.hadoop.hbase.TableName.valueOf(Bytes.toBytes("test")));
70      desc.addFamily(new HColumnDescriptor(COLUMN_NAME));
71  
72      // Set maximum regionsize down.
73      UTIL.getConfiguration().setLong(HConstants.HREGION_MAX_FILESIZE, 64L * 1024L * 1024L);
74      // Make it so we don't split.
75      UTIL.getConfiguration().setInt("hbase.regionserver.regionSplitLimit", 0);
76      // Startup hdfs.  Its in here we'll be putting our manually made regions.
77      UTIL.startMiniDFSCluster(1);
78      // Create hdfs hbase rootdir.
79      Path rootdir = UTIL.createRootDir();
80      FileSystem fs = FileSystem.get(UTIL.getConfiguration());
81      if (fs.exists(rootdir)) {
82        if (fs.delete(rootdir, true)) {
83          LOG.info("Cleaned up existing " + rootdir);
84        }
85      }
86  
87      // Now create three data regions: The first is too large to merge since it
88      // will be > 64 MB in size. The second two will be smaller and will be
89      // selected for merging.
90  
91      // To ensure that the first region is larger than 64MB we need to write at
92      // least 65536 rows. We will make certain by writing 70000
93      byte [] row_70001 = Bytes.toBytes("row_70001");
94      byte [] row_80001 = Bytes.toBytes("row_80001");
95  
96      // Create regions and populate them at same time.  Create the tabledir
97      // for them first.
98      new FSTableDescriptors(fs, rootdir).createTableDescriptor(desc);
99      HRegion [] regions = {
100       createRegion(desc, null, row_70001, 1, 70000, rootdir),
101       createRegion(desc, row_70001, row_80001, 70001, 10000, rootdir),
102       createRegion(desc, row_80001, null, 80001, 11000, rootdir)
103     };
104 
105     // Now create the root and meta regions and insert the data regions
106     // created above into hbase:meta
107     setupMeta(rootdir, regions);
108     try {
109       LOG.info("Starting mini zk cluster");
110       UTIL.startMiniZKCluster();
111       LOG.info("Starting mini hbase cluster");
112       UTIL.startMiniHBaseCluster(1, 1);
113       Configuration c = new Configuration(UTIL.getConfiguration());
114       CatalogTracker ct = new CatalogTracker(c);
115       ct.start();
116       List<HRegionInfo> originalTableRegions =
117         MetaReader.getTableRegions(ct, desc.getTableName());
118       LOG.info("originalTableRegions size=" + originalTableRegions.size() +
119         "; " + originalTableRegions);
120       HBaseAdmin admin = new HBaseAdmin(c);
121       admin.disableTable(desc.getTableName());
122       HMerge.merge(c, FileSystem.get(c), desc.getTableName());
123       List<HRegionInfo> postMergeTableRegions =
124         MetaReader.getTableRegions(ct, desc.getTableName());
125       LOG.info("postMergeTableRegions size=" + postMergeTableRegions.size() +
126         "; " + postMergeTableRegions);
127       assertTrue("originalTableRegions=" + originalTableRegions.size() +
128         ", postMergeTableRegions=" + postMergeTableRegions.size(),
129         postMergeTableRegions.size() < originalTableRegions.size());
130       LOG.info("Done with merge");
131     } finally {
132       UTIL.shutdownMiniCluster();
133       LOG.info("After cluster shutdown");
134     }
135   }
136 
137   private HRegion createRegion(final HTableDescriptor desc,
138       byte [] startKey, byte [] endKey, int firstRow, int nrows, Path rootdir)
139   throws IOException {
140     HRegionInfo hri = new HRegionInfo(desc.getTableName(), startKey, endKey);
141     HRegion region = HRegion.createHRegion(hri, rootdir, UTIL.getConfiguration(), desc);
142     LOG.info("Created region " + region.getRegionNameAsString());
143     for(int i = firstRow; i < firstRow + nrows; i++) {
144       Put put = new Put(Bytes.toBytes("row_" + String.format("%1$05d", i)));
145       put.setDurability(Durability.SKIP_WAL);
146       put.add(COLUMN_NAME, null,  VALUE);
147       region.put(put);
148       if (i % 10000 == 0) {
149         LOG.info("Flushing write #" + i);
150         region.flushcache();
151       }
152     }
153     HRegion.closeHRegion(region);
154     return region;
155   }
156 
157   protected void setupMeta(Path rootdir, final HRegion [] regions)
158   throws IOException {
159     HRegion meta =
160       HRegion.createHRegion(HRegionInfo.FIRST_META_REGIONINFO, rootdir,
161       UTIL.getConfiguration(), HTableDescriptor.META_TABLEDESC);
162     for (HRegion r: regions) {
163       HRegion.addRegionToMETA(meta, r);
164     }
165     HRegion.closeHRegion(meta);
166   }
167 
168 }
169