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