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}