001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.io.hfile;
019
020import static org.junit.Assert.assertEquals;
021import static org.junit.Assert.assertNotEquals;
022import static org.junit.Assert.assertTrue;
023
024import com.codahale.metrics.MetricRegistry;
025import java.io.ByteArrayOutputStream;
026import java.io.PrintStream;
027import org.apache.hadoop.conf.Configuration;
028import org.apache.hadoop.fs.FileSystem;
029import org.apache.hadoop.fs.Path;
030import org.apache.hadoop.hbase.HBaseClassTestRule;
031import org.apache.hadoop.hbase.HBaseTestingUtility;
032import org.apache.hadoop.hbase.regionserver.TestHRegionServerBulkLoad;
033import org.apache.hadoop.hbase.testclassification.IOTests;
034import org.apache.hadoop.hbase.testclassification.SmallTests;
035import org.apache.hadoop.hbase.util.Bytes;
036import org.apache.hadoop.hbase.util.CommonFSUtils;
037import org.junit.After;
038import org.junit.Before;
039import org.junit.ClassRule;
040import org.junit.Test;
041import org.junit.experimental.categories.Category;
042import org.slf4j.Logger;
043import org.slf4j.LoggerFactory;
044
045@Category({ IOTests.class, SmallTests.class })
046public class TestHFilePrettyPrinter {
047
048  @ClassRule
049  public static final HBaseClassTestRule CLASS_RULE =
050    HBaseClassTestRule.forClass(TestHFilePrettyPrinter.class);
051
052  private static final Logger LOG = LoggerFactory.getLogger(TestHFilePrettyPrinter.class);
053
054  private final static HBaseTestingUtility UTIL = new HBaseTestingUtility();
055  private static FileSystem fs;
056  private static Configuration conf;
057  private static byte[] cf = Bytes.toBytes("cf");
058  private static byte[] fam = Bytes.toBytes("fam");
059  private static byte[] value = Bytes.toBytes("val");
060  private static PrintStream original;
061  private static PrintStream ps;
062  private static ByteArrayOutputStream stream;
063
064  @Before
065  public void setup() throws Exception {
066    conf = UTIL.getConfiguration();
067    // Runs on local filesystem. Test does not need sync. Turn off checks.
068    conf.setBoolean(CommonFSUtils.UNSAFE_STREAM_CAPABILITY_ENFORCE, false);
069    fs = UTIL.getTestFileSystem();
070    stream = new ByteArrayOutputStream();
071    ps = new PrintStream(stream);
072  }
073
074  @After
075  public void teardown() {
076    original = System.out;
077    System.setOut(original);
078  }
079
080  @Test
081  public void testHFilePrettyPrinterNonRootDir() throws Exception {
082    Path fileNotInRootDir = UTIL.getDataTestDir("hfile");
083    TestHRegionServerBulkLoad.createHFile(fs, fileNotInRootDir, cf, fam, value, 1000);
084    assertNotEquals("directory used is not an HBase root dir", UTIL.getDefaultRootDirPath(),
085      fileNotInRootDir);
086
087    System.setOut(ps);
088    new HFilePrettyPrinter(conf).run(new String[] { "-v", String.valueOf(fileNotInRootDir) });
089    String result = new String(stream.toByteArray());
090    String expectedResult = "Scanning -> " + fileNotInRootDir + "\n" + "Scanned kv count -> 1000\n";
091    assertEquals(expectedResult, result);
092  }
093
094  @Test
095  public void testHFilePrettyPrinterRootDir() throws Exception {
096    Path rootPath = CommonFSUtils.getRootDir(conf);
097    String rootString = rootPath + rootPath.SEPARATOR;
098    Path fileInRootDir = new Path(rootString + "hfile");
099    TestHRegionServerBulkLoad.createHFile(fs, fileInRootDir, cf, fam, value, 1000);
100    assertTrue("directory used is a root dir", fileInRootDir.toString().startsWith(rootString));
101
102    System.setOut(ps);
103    HFilePrettyPrinter printer = new HFilePrettyPrinter();
104    printer.setConf(conf);
105    printer.processFile(fileInRootDir, true);
106    printer.run(new String[] { "-v", String.valueOf(fileInRootDir) });
107    String result = new String(stream.toByteArray());
108    String expectedResult = "Scanning -> " + fileInRootDir + "\n" + "Scanned kv count -> 1000\n";
109    assertEquals(expectedResult, result);
110  }
111
112  @Test
113  public void testHFilePrettyPrinterSeekFirstRow() throws Exception {
114    Path fileNotInRootDir = UTIL.getDataTestDir("hfile");
115    TestHRegionServerBulkLoad.createHFile(fs, fileNotInRootDir, cf, fam, value, 1000);
116    assertNotEquals("directory used is not an HBase root dir", UTIL.getDefaultRootDirPath(),
117      fileNotInRootDir);
118
119    HFile.Reader reader =
120      HFile.createReader(fs, fileNotInRootDir, CacheConfig.DISABLED, true, conf);
121    String firstRowKey = new String(reader.getFirstRowKey().get());
122
123    System.setOut(ps);
124    new HFilePrettyPrinter(conf)
125      .run(new String[] { "-v", "-w" + firstRowKey, String.valueOf(fileNotInRootDir) });
126    String result = new String(stream.toByteArray());
127    String expectedResult = "Scanning -> " + fileNotInRootDir + "\n" + "Scanned kv count -> 1\n";
128    assertEquals(expectedResult, result);
129  }
130
131  @Test
132  public void testHistograms() throws Exception {
133    Path fileNotInRootDir = UTIL.getDataTestDir("hfile");
134    TestHRegionServerBulkLoad.createHFile(fs, fileNotInRootDir, cf, fam, value, 1000);
135    assertNotEquals("directory used is not an HBase root dir", UTIL.getDefaultRootDirPath(),
136      fileNotInRootDir);
137
138    System.setOut(ps);
139    new HFilePrettyPrinter(conf).run(new String[] { "-s", "-d", String.valueOf(fileNotInRootDir) });
140    String result = stream.toString();
141    LOG.info(result);
142
143    // split out the output into sections based on the headers
144    String[] headers =
145      new String[] { "Key length", "Val length", "Row size (bytes)", "Row size (columns)" };
146    // for each section, there is a corresponding expected (count, range) pairs
147    int[][] expectations = new int[][] { new int[] { 0, 10, 1000, 50 }, new int[] { 0, 1, 1000, 3 },
148      new int[] { 0, 10, 1000, 50 }, new int[] { 1000, 1 }, };
149
150    for (int i = 0; i < headers.length - 1; i++) {
151      int idx = result.indexOf(headers[i]);
152      int nextIdx = result.indexOf(headers[i + 1]);
153
154      assertContainsRanges(result.substring(idx, nextIdx), expectations[i]);
155    }
156  }
157
158  private void assertContainsRanges(String result, int... rangeCountPairs) {
159    for (int i = 0; i < rangeCountPairs.length - 1; i += 2) {
160      String expected = rangeCountPairs[i + 1] + " <= " + rangeCountPairs[i];
161      assertTrue("expected:\n" + result + "\nto contain: '" + expected + "'",
162        result.contains(expected));
163    }
164  }
165
166  @Test
167  public void testKeyValueStats() {
168    HFilePrettyPrinter.KeyValueStats stats =
169      new HFilePrettyPrinter.KeyValueStats(new MetricRegistry(), "test");
170    long[] ranges = stats.getRanges();
171    for (long range : ranges) {
172      stats.update(range - 1, true);
173    }
174
175    assertEquals(ranges[ranges.length - 1] - 1, stats.getMax());
176    assertEquals(ranges[0] - 1, stats.getMin());
177
178    int total = 1;
179    for (long range : ranges) {
180      long val = stats.getCountAtOrBelow(range);
181      assertEquals(total++, val);
182    }
183
184  }
185}