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 java.io.IOException; 021import java.nio.ByteBuffer; 022import java.util.ArrayList; 023import java.util.List; 024import org.apache.hadoop.conf.Configuration; 025import org.apache.hadoop.fs.FSDataInputStream; 026import org.apache.hadoop.fs.FileSystem; 027import org.apache.hadoop.fs.Path; 028import org.apache.hadoop.hbase.CellComparatorImpl; 029import org.apache.hadoop.hbase.HBaseClassTestRule; 030import org.apache.hadoop.hbase.HBaseTestingUtil; 031import org.apache.hadoop.hbase.KeyValue; 032import org.apache.hadoop.hbase.io.encoding.DataBlockEncoding; 033import org.apache.hadoop.hbase.testclassification.IOTests; 034import org.apache.hadoop.hbase.testclassification.MediumTests; 035import org.junit.Assert; 036import org.junit.Before; 037import org.junit.ClassRule; 038import org.junit.Test; 039import org.junit.experimental.categories.Category; 040 041@Category({ IOTests.class, MediumTests.class }) 042public class TestRowIndexV1DataEncoder { 043 @ClassRule 044 public static final HBaseClassTestRule CLASS_RULE = 045 HBaseClassTestRule.forClass(TestRowIndexV1DataEncoder.class); 046 047 private static final HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil(); 048 049 private Configuration conf; 050 private FileSystem fs; 051 private DataBlockEncoding dataBlockEncoding; 052 053 @Before 054 public void setUp() throws IOException { 055 conf = TEST_UTIL.getConfiguration(); 056 fs = FileSystem.get(conf); 057 dataBlockEncoding = DataBlockEncoding.ROW_INDEX_V1; 058 } 059 060 @Test 061 public void testBlockCountWritten() throws IOException { 062 Path hfilePath = new Path(TEST_UTIL.getDataTestDir(), "testHFileFormatV3"); 063 final int entryCount = 10000; 064 writeDataToHFile(hfilePath, entryCount); 065 } 066 067 private void writeDataToHFile(Path hfilePath, int entryCount) throws IOException { 068 HFileContext context = 069 new HFileContextBuilder().withBlockSize(1024).withDataBlockEncoding(dataBlockEncoding) 070 .withCellComparator(CellComparatorImpl.COMPARATOR).build(); 071 CacheConfig cacheConfig = new CacheConfig(conf); 072 HFile.Writer writer = new HFile.WriterFactory(conf, cacheConfig).withPath(fs, hfilePath) 073 .withFileContext(context).create(); 074 075 List<KeyValue> keyValues = new ArrayList<>(entryCount); 076 077 writeKeyValues(entryCount, writer, keyValues); 078 079 FSDataInputStream fsdis = fs.open(hfilePath); 080 081 long fileSize = fs.getFileStatus(hfilePath).getLen(); 082 FixedFileTrailer trailer = FixedFileTrailer.readFromStream(fsdis, fileSize); 083 084 // HBASE-23788 085 // kv size = 24 bytes, block size = 1024 bytes 086 // per row encoded data written = (4 (Row index) + 24 (Cell size) + 1 (MVCC)) bytes = 29 bytes 087 // creating block size of (29 * 36) bytes = 1044 bytes 088 // Number of blocks = ceil((29 * 10000) / 1044) = 278 089 // Without the patch it would have produced 244 blocks (each block of 1236 bytes) 090 // Earlier this would create blocks ~20% greater than the block size of 1024 bytes 091 // After this patch actual block size is ~2% greater than the block size of 1024 bytes 092 Assert.assertEquals(278, trailer.getDataIndexCount()); 093 } 094 095 private void writeKeyValues(int entryCount, HFile.Writer writer, List<KeyValue> keyValues) 096 throws IOException { 097 for (int i = 0; i < entryCount; ++i) { 098 byte[] keyBytes = intToBytes(i); 099 100 byte[] valueBytes = new byte[0]; 101 KeyValue keyValue = new KeyValue(keyBytes, null, null, valueBytes); 102 103 writer.append(keyValue); 104 keyValues.add(keyValue); 105 } 106 writer.close(); 107 } 108 109 private byte[] intToBytes(final int i) { 110 ByteBuffer bb = ByteBuffer.allocate(4); 111 bb.putInt(i); 112 return bb.array(); 113 } 114}