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.apache.hadoop.hbase.HConstants.BUCKET_CACHE_IOENGINE_KEY; 021import static org.apache.hadoop.hbase.HConstants.BUCKET_CACHE_SIZE_KEY; 022import static org.apache.hadoop.hbase.io.ByteBuffAllocator.BUFFER_SIZE_KEY; 023import static org.apache.hadoop.hbase.io.ByteBuffAllocator.MAX_BUFFER_COUNT_KEY; 024import static org.apache.hadoop.hbase.io.ByteBuffAllocator.MIN_ALLOCATE_SIZE_KEY; 025import static org.apache.hadoop.hbase.io.hfile.BlockCacheFactory.BLOCKCACHE_POLICY_KEY; 026import static org.apache.hadoop.hbase.io.hfile.CacheConfig.EVICT_BLOCKS_ON_CLOSE_KEY; 027import static org.junit.Assert.assertEquals; 028import static org.junit.Assert.assertFalse; 029import static org.junit.Assert.assertNull; 030import static org.junit.Assert.assertTrue; 031import static org.junit.Assert.fail; 032 033import java.io.DataInput; 034import java.io.DataOutput; 035import java.io.IOException; 036import java.nio.ByteBuffer; 037import java.util.ArrayList; 038import java.util.Arrays; 039import java.util.List; 040import java.util.Objects; 041import java.util.Random; 042import java.util.concurrent.ThreadLocalRandom; 043import org.apache.hadoop.conf.Configuration; 044import org.apache.hadoop.fs.FSDataInputStream; 045import org.apache.hadoop.fs.FSDataOutputStream; 046import org.apache.hadoop.fs.FileStatus; 047import org.apache.hadoop.fs.FileSystem; 048import org.apache.hadoop.fs.Path; 049import org.apache.hadoop.hbase.ArrayBackedTag; 050import org.apache.hadoop.hbase.ByteBufferKeyValue; 051import org.apache.hadoop.hbase.Cell; 052import org.apache.hadoop.hbase.CellBuilder; 053import org.apache.hadoop.hbase.CellBuilderFactory; 054import org.apache.hadoop.hbase.CellBuilderType; 055import org.apache.hadoop.hbase.CellComparatorImpl; 056import org.apache.hadoop.hbase.CellUtil; 057import org.apache.hadoop.hbase.ExtendedCellBuilderFactory; 058import org.apache.hadoop.hbase.HBaseClassTestRule; 059import org.apache.hadoop.hbase.HBaseCommonTestingUtil; 060import org.apache.hadoop.hbase.HBaseConfiguration; 061import org.apache.hadoop.hbase.HBaseTestingUtil; 062import org.apache.hadoop.hbase.HConstants; 063import org.apache.hadoop.hbase.KeyValue; 064import org.apache.hadoop.hbase.KeyValue.Type; 065import org.apache.hadoop.hbase.KeyValueUtil; 066import org.apache.hadoop.hbase.MetaCellComparator; 067import org.apache.hadoop.hbase.PrivateCellUtil; 068import org.apache.hadoop.hbase.Tag; 069import org.apache.hadoop.hbase.io.ByteBuffAllocator; 070import org.apache.hadoop.hbase.io.compress.Compression; 071import org.apache.hadoop.hbase.io.encoding.DataBlockEncoder; 072import org.apache.hadoop.hbase.io.encoding.DataBlockEncoding; 073import org.apache.hadoop.hbase.io.encoding.IndexBlockEncoding; 074import org.apache.hadoop.hbase.io.hfile.HFile.Reader; 075import org.apache.hadoop.hbase.io.hfile.HFile.Writer; 076import org.apache.hadoop.hbase.io.hfile.ReaderContext.ReaderType; 077import org.apache.hadoop.hbase.nio.ByteBuff; 078import org.apache.hadoop.hbase.regionserver.StoreFileWriter; 079import org.apache.hadoop.hbase.testclassification.IOTests; 080import org.apache.hadoop.hbase.testclassification.SmallTests; 081import org.apache.hadoop.hbase.util.ByteBufferUtils; 082import org.apache.hadoop.hbase.util.Bytes; 083import org.apache.hadoop.io.Writable; 084import org.junit.Assert; 085import org.junit.BeforeClass; 086import org.junit.ClassRule; 087import org.junit.Rule; 088import org.junit.Test; 089import org.junit.experimental.categories.Category; 090import org.junit.rules.TestName; 091import org.mockito.Mockito; 092import org.slf4j.Logger; 093import org.slf4j.LoggerFactory; 094 095/** 096 * test hfile features. 097 */ 098@Category({ IOTests.class, SmallTests.class }) 099public class TestHFile { 100 101 @ClassRule 102 public static final HBaseClassTestRule CLASS_RULE = HBaseClassTestRule.forClass(TestHFile.class); 103 104 @Rule 105 public TestName testName = new TestName(); 106 107 private static final Logger LOG = LoggerFactory.getLogger(TestHFile.class); 108 private static final int NUM_VALID_KEY_TYPES = KeyValue.Type.values().length - 2; 109 private static final HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil(); 110 private static String ROOT_DIR = TEST_UTIL.getDataTestDir("TestHFile").toString(); 111 private final int minBlockSize = 512; 112 private static String localFormatter = "%010d"; 113 private static CacheConfig cacheConf; 114 private static Configuration conf; 115 private static FileSystem fs; 116 117 @BeforeClass 118 public static void setUp() throws Exception { 119 conf = TEST_UTIL.getConfiguration(); 120 cacheConf = new CacheConfig(conf); 121 fs = TEST_UTIL.getTestFileSystem(); 122 } 123 124 public static Reader createReaderFromStream(ReaderContext context, CacheConfig cacheConf, 125 Configuration conf) throws IOException { 126 HFileInfo fileInfo = new HFileInfo(context, conf); 127 Reader preadReader = HFile.createReader(context, fileInfo, cacheConf, conf); 128 fileInfo.initMetaAndIndex(preadReader); 129 preadReader.close(); 130 context = new ReaderContextBuilder() 131 .withFileSystemAndPath(context.getFileSystem(), context.getFilePath()) 132 .withReaderType(ReaderType.STREAM).build(); 133 Reader streamReader = HFile.createReader(context, fileInfo, cacheConf, conf); 134 return streamReader; 135 } 136 137 private ByteBuffAllocator initAllocator(boolean reservoirEnabled, int bufSize, int bufCount, 138 int minAllocSize) { 139 Configuration that = HBaseConfiguration.create(conf); 140 that.setInt(BUFFER_SIZE_KEY, bufSize); 141 that.setInt(MAX_BUFFER_COUNT_KEY, bufCount); 142 // All ByteBuffers will be allocated from the buffers. 143 that.setInt(MIN_ALLOCATE_SIZE_KEY, minAllocSize); 144 return ByteBuffAllocator.create(that, reservoirEnabled); 145 } 146 147 private void fillByteBuffAllocator(ByteBuffAllocator alloc, int bufCount) { 148 // Fill the allocator with bufCount ByteBuffer 149 List<ByteBuff> buffs = new ArrayList<>(); 150 for (int i = 0; i < bufCount; i++) { 151 buffs.add(alloc.allocateOneBuffer()); 152 Assert.assertEquals(alloc.getFreeBufferCount(), 0); 153 } 154 buffs.forEach(ByteBuff::release); 155 Assert.assertEquals(alloc.getFreeBufferCount(), bufCount); 156 } 157 158 @Test 159 public void testReaderWithoutBlockCache() throws Exception { 160 int bufCount = 32; 161 // AllByteBuffers will be allocated from the buffers. 162 ByteBuffAllocator alloc = initAllocator(true, 64 * 1024, bufCount, 0); 163 fillByteBuffAllocator(alloc, bufCount); 164 // start write to store file. 165 Path path = writeStoreFile(); 166 try { 167 readStoreFile(path, conf, alloc); 168 } catch (Exception e) { 169 // fail test 170 assertTrue(false); 171 } 172 Assert.assertEquals(bufCount, alloc.getFreeBufferCount()); 173 alloc.clean(); 174 } 175 176 /** 177 * Test case for HBASE-22127 in LruBlockCache. 178 */ 179 @Test 180 public void testReaderWithLRUBlockCache() throws Exception { 181 int bufCount = 1024, blockSize = 64 * 1024; 182 ByteBuffAllocator alloc = initAllocator(true, bufCount, blockSize, 0); 183 fillByteBuffAllocator(alloc, bufCount); 184 Path storeFilePath = writeStoreFile(); 185 // Open the file reader with LRUBlockCache 186 BlockCache lru = new LruBlockCache(1024 * 1024 * 32, blockSize, true, conf); 187 CacheConfig cacheConfig = new CacheConfig(conf, null, lru, alloc); 188 HFile.Reader reader = HFile.createReader(fs, storeFilePath, cacheConfig, true, conf); 189 long offset = 0; 190 while (offset < reader.getTrailer().getLoadOnOpenDataOffset()) { 191 BlockCacheKey key = new BlockCacheKey(storeFilePath.getName(), offset); 192 HFileBlock block = reader.readBlock(offset, -1, true, true, false, true, null, null); 193 offset += block.getOnDiskSizeWithHeader(); 194 // Ensure the block is an heap one. 195 Cacheable cachedBlock = lru.getBlock(key, false, false, true); 196 Assert.assertNotNull(cachedBlock); 197 Assert.assertTrue(cachedBlock instanceof HFileBlock); 198 Assert.assertFalse(((HFileBlock) cachedBlock).isSharedMem()); 199 // Should never allocate off-heap block from allocator because ensure that it's LRU. 200 Assert.assertEquals(bufCount, alloc.getFreeBufferCount()); 201 block.release(); // return back the ByteBuffer back to allocator. 202 } 203 reader.close(); 204 Assert.assertEquals(bufCount, alloc.getFreeBufferCount()); 205 alloc.clean(); 206 lru.shutdown(); 207 } 208 209 private BlockCache initCombinedBlockCache(final String l1CachePolicy) { 210 Configuration that = HBaseConfiguration.create(conf); 211 that.setFloat(BUCKET_CACHE_SIZE_KEY, 32); // 32MB for bucket cache. 212 that.set(BUCKET_CACHE_IOENGINE_KEY, "offheap"); 213 that.set(BLOCKCACHE_POLICY_KEY, l1CachePolicy); 214 BlockCache bc = BlockCacheFactory.createBlockCache(that); 215 Assert.assertNotNull(bc); 216 Assert.assertTrue(bc instanceof CombinedBlockCache); 217 return bc; 218 } 219 220 /** 221 * Test case for HBASE-22127 in CombinedBlockCache 222 */ 223 @Test 224 public void testReaderWithCombinedBlockCache() throws Exception { 225 int bufCount = 1024, blockSize = 64 * 1024; 226 ByteBuffAllocator alloc = initAllocator(true, bufCount, blockSize, 0); 227 fillByteBuffAllocator(alloc, bufCount); 228 Path storeFilePath = writeStoreFile(); 229 // Open the file reader with CombinedBlockCache 230 BlockCache combined = initCombinedBlockCache("LRU"); 231 conf.setBoolean(EVICT_BLOCKS_ON_CLOSE_KEY, true); 232 CacheConfig cacheConfig = new CacheConfig(conf, null, combined, alloc); 233 HFile.Reader reader = HFile.createReader(fs, storeFilePath, cacheConfig, true, conf); 234 long offset = 0; 235 while (offset < reader.getTrailer().getLoadOnOpenDataOffset()) { 236 BlockCacheKey key = new BlockCacheKey(storeFilePath.getName(), offset); 237 HFileBlock block = reader.readBlock(offset, -1, true, true, false, true, null, null); 238 offset += block.getOnDiskSizeWithHeader(); 239 // Read the cached block. 240 Cacheable cachedBlock = combined.getBlock(key, false, false, true); 241 try { 242 Assert.assertNotNull(cachedBlock); 243 Assert.assertTrue(cachedBlock instanceof HFileBlock); 244 HFileBlock hfb = (HFileBlock) cachedBlock; 245 // Data block will be cached in BucketCache, so it should be an off-heap block. 246 if (hfb.getBlockType().isData()) { 247 Assert.assertTrue(hfb.isSharedMem()); 248 } else { 249 // Non-data block will be cached in LRUBlockCache, so it must be an on-heap block. 250 Assert.assertFalse(hfb.isSharedMem()); 251 } 252 } finally { 253 cachedBlock.release(); 254 } 255 block.release(); // return back the ByteBuffer back to allocator. 256 } 257 reader.close(); 258 combined.shutdown(); 259 Assert.assertEquals(bufCount, alloc.getFreeBufferCount()); 260 alloc.clean(); 261 } 262 263 /** 264 * Tests that we properly allocate from the off-heap or on-heap when LRUCache is configured. In 265 * this case, the determining factor is whether we end up caching the block or not. So the below 266 * test cases try different permutations of enabling/disabling via CacheConfig and via user 267 * request (cacheblocks), along with different expected block types. 268 */ 269 @Test 270 public void testReaderBlockAllocationWithLRUCache() throws IOException { 271 // false because caching is fully enabled 272 testReaderBlockAllocationWithLRUCache(true, true, null, false); 273 // false because we only look at cache config when expectedBlockType is non-null 274 testReaderBlockAllocationWithLRUCache(false, true, null, false); 275 // false because cacheBlock is true and even with cache config is disabled, we still cache 276 // important blocks like indexes 277 testReaderBlockAllocationWithLRUCache(false, true, BlockType.INTERMEDIATE_INDEX, false); 278 // true because since it's a DATA block, we honor the cache config 279 testReaderBlockAllocationWithLRUCache(false, true, BlockType.DATA, true); 280 // true for the following 2 because cacheBlock takes precedence over cache config 281 testReaderBlockAllocationWithLRUCache(true, false, null, true); 282 testReaderBlockAllocationWithLRUCache(true, false, BlockType.INTERMEDIATE_INDEX, false); 283 // false for the following 3 because both cache config and cacheBlock are false. 284 // per above, INDEX would supersede cache config, but not cacheBlock 285 testReaderBlockAllocationWithLRUCache(false, false, null, true); 286 testReaderBlockAllocationWithLRUCache(false, false, BlockType.INTERMEDIATE_INDEX, true); 287 testReaderBlockAllocationWithLRUCache(false, false, BlockType.DATA, true); 288 } 289 290 private void testReaderBlockAllocationWithLRUCache(boolean cacheConfigCacheBlockOnRead, 291 boolean cacheBlock, BlockType blockType, boolean expectSharedMem) throws IOException { 292 int bufCount = 1024, blockSize = 64 * 1024; 293 ByteBuffAllocator alloc = initAllocator(true, blockSize, bufCount, 0); 294 fillByteBuffAllocator(alloc, bufCount); 295 Path storeFilePath = writeStoreFile(); 296 Configuration myConf = new Configuration(conf); 297 298 myConf.setBoolean(CacheConfig.CACHE_DATA_ON_READ_KEY, cacheConfigCacheBlockOnRead); 299 // Open the file reader with LRUBlockCache 300 BlockCache lru = new LruBlockCache(1024 * 1024 * 32, blockSize, true, myConf); 301 CacheConfig cacheConfig = new CacheConfig(myConf, null, lru, alloc); 302 HFile.Reader reader = HFile.createReader(fs, storeFilePath, cacheConfig, true, myConf); 303 long offset = 0; 304 while (offset < reader.getTrailer().getLoadOnOpenDataOffset()) { 305 long read = readAtOffsetWithAllocationAsserts(alloc, reader, offset, cacheBlock, blockType, 306 expectSharedMem); 307 if (read < 0) { 308 break; 309 } 310 311 offset += read; 312 } 313 314 reader.close(); 315 Assert.assertEquals(bufCount, alloc.getFreeBufferCount()); 316 alloc.clean(); 317 lru.shutdown(); 318 } 319 320 /** 321 * Tests that we properly allocate from the off-heap or on-heap when CombinedCache is configured. 322 * In this case, we should always use off-heap unless the block is an INDEX (which always goes to 323 * L1 cache which is on-heap) 324 */ 325 @Test 326 public void testReaderBlockAllocationWithCombinedCache() throws IOException { 327 // true because caching is fully enabled and block type null 328 testReaderBlockAllocationWithCombinedCache(true, true, null, true); 329 // false because caching is fully enabled, index block type always goes to on-heap L1 330 testReaderBlockAllocationWithCombinedCache(true, true, BlockType.INTERMEDIATE_INDEX, false); 331 // true because cacheBlocks takes precedence over cache config which block type is null 332 testReaderBlockAllocationWithCombinedCache(false, true, null, true); 333 // false because caching is enabled and block type is index, which always goes to L1 334 testReaderBlockAllocationWithCombinedCache(false, true, BlockType.INTERMEDIATE_INDEX, false); 335 // true because since it's a DATA block, we honor the cache config 336 testReaderBlockAllocationWithCombinedCache(false, true, BlockType.DATA, true); 337 // true for the following 2 because cacheBlock takes precedence over cache config 338 // with caching disabled, we always go to off-heap 339 testReaderBlockAllocationWithCombinedCache(true, false, null, true); 340 testReaderBlockAllocationWithCombinedCache(true, false, BlockType.INTERMEDIATE_INDEX, false); 341 // true for the following 3, because with caching disabled we always go to off-heap 342 testReaderBlockAllocationWithCombinedCache(false, false, null, true); 343 testReaderBlockAllocationWithCombinedCache(false, false, BlockType.INTERMEDIATE_INDEX, true); 344 testReaderBlockAllocationWithCombinedCache(false, false, BlockType.DATA, true); 345 } 346 347 private void testReaderBlockAllocationWithCombinedCache(boolean cacheConfigCacheBlockOnRead, 348 boolean cacheBlock, BlockType blockType, boolean expectSharedMem) throws IOException { 349 int bufCount = 1024, blockSize = 64 * 1024; 350 ByteBuffAllocator alloc = initAllocator(true, blockSize, bufCount, 0); 351 fillByteBuffAllocator(alloc, bufCount); 352 Path storeFilePath = writeStoreFile(); 353 // Open the file reader with CombinedBlockCache 354 BlockCache combined = initCombinedBlockCache("LRU"); 355 Configuration myConf = new Configuration(conf); 356 357 myConf.setBoolean(CacheConfig.CACHE_DATA_ON_READ_KEY, cacheConfigCacheBlockOnRead); 358 myConf.setBoolean(EVICT_BLOCKS_ON_CLOSE_KEY, true); 359 360 CacheConfig cacheConfig = new CacheConfig(myConf, null, combined, alloc); 361 HFile.Reader reader = HFile.createReader(fs, storeFilePath, cacheConfig, true, myConf); 362 long offset = 0; 363 while (offset < reader.getTrailer().getLoadOnOpenDataOffset()) { 364 long read = readAtOffsetWithAllocationAsserts(alloc, reader, offset, cacheBlock, blockType, 365 expectSharedMem); 366 if (read < 0) { 367 break; 368 } 369 370 offset += read; 371 } 372 373 reader.close(); 374 combined.shutdown(); 375 Assert.assertEquals(bufCount, alloc.getFreeBufferCount()); 376 alloc.clean(); 377 } 378 379 private long readAtOffsetWithAllocationAsserts(ByteBuffAllocator alloc, HFile.Reader reader, 380 long offset, boolean cacheBlock, BlockType blockType, boolean expectSharedMem) 381 throws IOException { 382 HFileBlock block; 383 try { 384 block = reader.readBlock(offset, -1, cacheBlock, true, false, true, blockType, null); 385 } catch (IOException e) { 386 if (e.getMessage().contains("Expected block type")) { 387 return -1; 388 } 389 throw e; 390 } 391 392 Assert.assertEquals(expectSharedMem, block.isSharedMem()); 393 394 if (expectSharedMem) { 395 Assert.assertTrue(alloc.getFreeBufferCount() < alloc.getTotalBufferCount()); 396 } else { 397 // Should never allocate off-heap block from allocator because ensure that it's LRU. 398 Assert.assertEquals(alloc.getTotalBufferCount(), alloc.getFreeBufferCount()); 399 } 400 401 try { 402 return block.getOnDiskSizeWithHeader(); 403 } finally { 404 block.release(); // return back the ByteBuffer back to allocator. 405 } 406 } 407 408 private void readStoreFile(Path storeFilePath, Configuration conf, ByteBuffAllocator alloc) 409 throws Exception { 410 // Open the file reader with block cache disabled. 411 CacheConfig cache = new CacheConfig(conf, null, null, alloc); 412 HFile.Reader reader = HFile.createReader(fs, storeFilePath, cache, true, conf); 413 long offset = 0; 414 while (offset < reader.getTrailer().getLoadOnOpenDataOffset()) { 415 HFileBlock block = reader.readBlock(offset, -1, false, true, false, true, null, null); 416 offset += block.getOnDiskSizeWithHeader(); 417 block.release(); // return back the ByteBuffer back to allocator. 418 } 419 reader.close(); 420 } 421 422 private Path writeStoreFile() throws IOException { 423 Path storeFileParentDir = new Path(TEST_UTIL.getDataTestDir(), "TestHFile"); 424 HFileContext meta = new HFileContextBuilder().withBlockSize(64 * 1024).build(); 425 StoreFileWriter sfw = new StoreFileWriter.Builder(conf, fs).withOutputDir(storeFileParentDir) 426 .withFileContext(meta).build(); 427 final int rowLen = 32; 428 Random rand = ThreadLocalRandom.current(); 429 for (int i = 0; i < 1000; ++i) { 430 byte[] k = RandomKeyValueUtil.randomOrderedKey(rand, i); 431 byte[] v = RandomKeyValueUtil.randomValue(rand); 432 int cfLen = rand.nextInt(k.length - rowLen + 1); 433 KeyValue kv = new KeyValue(k, 0, rowLen, k, rowLen, cfLen, k, rowLen + cfLen, 434 k.length - rowLen - cfLen, rand.nextLong(), generateKeyType(rand), v, 0, v.length); 435 sfw.append(kv); 436 } 437 438 sfw.close(); 439 return sfw.getPath(); 440 } 441 442 public static KeyValue.Type generateKeyType(Random rand) { 443 if (rand.nextBoolean()) { 444 // Let's make half of KVs puts. 445 return KeyValue.Type.Put; 446 } else { 447 KeyValue.Type keyType = KeyValue.Type.values()[1 + rand.nextInt(NUM_VALID_KEY_TYPES)]; 448 if (keyType == KeyValue.Type.Minimum || keyType == KeyValue.Type.Maximum) { 449 throw new RuntimeException("Generated an invalid key type: " + keyType + ". " 450 + "Probably the layout of KeyValue.Type has changed."); 451 } 452 return keyType; 453 } 454 } 455 456 /** 457 * Test empty HFile. Test all features work reasonably when hfile is empty of entries. 458 */ 459 @Test 460 public void testEmptyHFile() throws IOException { 461 Path f = new Path(ROOT_DIR, testName.getMethodName()); 462 HFileContext context = new HFileContextBuilder().withIncludesTags(false).build(); 463 Writer w = 464 HFile.getWriterFactory(conf, cacheConf).withPath(fs, f).withFileContext(context).create(); 465 w.close(); 466 Reader r = HFile.createReader(fs, f, cacheConf, true, conf); 467 assertFalse(r.getFirstKey().isPresent()); 468 assertFalse(r.getLastKey().isPresent()); 469 } 470 471 /** 472 * Create 0-length hfile and show that it fails 473 */ 474 @Test 475 public void testCorrupt0LengthHFile() throws IOException { 476 Path f = new Path(ROOT_DIR, testName.getMethodName()); 477 FSDataOutputStream fsos = fs.create(f); 478 fsos.close(); 479 480 try { 481 Reader r = HFile.createReader(fs, f, cacheConf, true, conf); 482 } catch (CorruptHFileException | IllegalArgumentException che) { 483 // Expected failure 484 return; 485 } 486 fail("Should have thrown exception"); 487 } 488 489 @Test 490 public void testCorruptOutOfOrderHFileWrite() throws IOException { 491 Path path = new Path(ROOT_DIR, testName.getMethodName()); 492 FSDataOutputStream mockedOutputStream = Mockito.mock(FSDataOutputStream.class); 493 String columnFamily = "MyColumnFamily"; 494 String tableName = "MyTableName"; 495 HFileContext fileContext = 496 new HFileContextBuilder().withHFileName(testName.getMethodName() + "HFile") 497 .withBlockSize(minBlockSize).withColumnFamily(Bytes.toBytes(columnFamily)) 498 .withTableName(Bytes.toBytes(tableName)).withHBaseCheckSum(false) 499 .withCompression(Compression.Algorithm.NONE).withCompressTags(false).build(); 500 HFileWriterImpl writer = 501 new HFileWriterImpl(conf, cacheConf, path, mockedOutputStream, fileContext); 502 CellBuilder cellBuilder = CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY); 503 byte[] row = Bytes.toBytes("foo"); 504 byte[] qualifier = Bytes.toBytes("qualifier"); 505 byte[] cf = Bytes.toBytes(columnFamily); 506 byte[] val = Bytes.toBytes("fooVal"); 507 long firstTS = 100L; 508 long secondTS = 101L; 509 Cell firstCell = cellBuilder.setRow(row).setValue(val).setTimestamp(firstTS) 510 .setQualifier(qualifier).setFamily(cf).setType(Cell.Type.Put).build(); 511 Cell secondCell = cellBuilder.setRow(row).setValue(val).setTimestamp(secondTS) 512 .setQualifier(qualifier).setFamily(cf).setType(Cell.Type.Put).build(); 513 // second Cell will sort "higher" than the first because later timestamps should come first 514 writer.append(firstCell); 515 try { 516 writer.append(secondCell); 517 } catch (IOException ie) { 518 String message = ie.getMessage(); 519 Assert.assertTrue(message.contains("not lexically larger")); 520 Assert.assertTrue(message.contains(tableName)); 521 Assert.assertTrue(message.contains(columnFamily)); 522 return; 523 } 524 Assert.fail("Exception wasn't thrown even though Cells were appended in the wrong order!"); 525 } 526 527 public static void truncateFile(FileSystem fs, Path src, Path dst) throws IOException { 528 FileStatus fst = fs.getFileStatus(src); 529 long len = fst.getLen(); 530 len = len / 2; 531 532 // create a truncated hfile 533 FSDataOutputStream fdos = fs.create(dst); 534 byte[] buf = new byte[(int) len]; 535 FSDataInputStream fdis = fs.open(src); 536 fdis.read(buf); 537 fdos.write(buf); 538 fdis.close(); 539 fdos.close(); 540 } 541 542 /** 543 * Create a truncated hfile and verify that exception thrown. 544 */ 545 @Test 546 public void testCorruptTruncatedHFile() throws IOException { 547 Path f = new Path(ROOT_DIR, testName.getMethodName()); 548 HFileContext context = new HFileContextBuilder().build(); 549 Writer w = HFile.getWriterFactory(conf, cacheConf).withPath(this.fs, f).withFileContext(context) 550 .create(); 551 writeSomeRecords(w, 0, 100, false); 552 w.close(); 553 554 Path trunc = new Path(f.getParent(), "trucated"); 555 truncateFile(fs, w.getPath(), trunc); 556 557 try { 558 HFile.createReader(fs, trunc, cacheConf, true, conf); 559 } catch (CorruptHFileException | IllegalArgumentException che) { 560 // Expected failure 561 return; 562 } 563 fail("Should have thrown exception"); 564 } 565 566 // write some records into the hfile 567 // write them twice 568 private int writeSomeRecords(Writer writer, int start, int n, boolean useTags) 569 throws IOException { 570 String value = "value"; 571 KeyValue kv; 572 for (int i = start; i < (start + n); i++) { 573 String key = String.format(localFormatter, Integer.valueOf(i)); 574 if (useTags) { 575 Tag t = new ArrayBackedTag((byte) 1, "myTag1"); 576 Tag[] tags = new Tag[1]; 577 tags[0] = t; 578 kv = new KeyValue(Bytes.toBytes(key), Bytes.toBytes("family"), Bytes.toBytes("qual"), 579 HConstants.LATEST_TIMESTAMP, Bytes.toBytes(value + key), tags); 580 writer.append(kv); 581 } else { 582 kv = new KeyValue(Bytes.toBytes(key), Bytes.toBytes("family"), Bytes.toBytes("qual"), 583 Bytes.toBytes(value + key)); 584 writer.append(kv); 585 } 586 } 587 return (start + n); 588 } 589 590 private void readAllRecords(HFileScanner scanner) throws IOException { 591 readAndCheckbytes(scanner, 0, 100); 592 } 593 594 // read the records and check 595 private int readAndCheckbytes(HFileScanner scanner, int start, int n) throws IOException { 596 String value = "value"; 597 int i = start; 598 for (; i < (start + n); i++) { 599 ByteBuffer key = ByteBuffer.wrap(((KeyValue) scanner.getKey()).getKey()); 600 ByteBuffer val = scanner.getValue(); 601 String keyStr = String.format(localFormatter, Integer.valueOf(i)); 602 String valStr = value + keyStr; 603 KeyValue kv = new KeyValue(Bytes.toBytes(keyStr), Bytes.toBytes("family"), 604 Bytes.toBytes("qual"), Bytes.toBytes(valStr)); 605 byte[] keyBytes = 606 new KeyValue.KeyOnlyKeyValue(Bytes.toBytes(key), 0, Bytes.toBytes(key).length).getKey(); 607 assertTrue("bytes for keys do not match " + keyStr + " " + Bytes.toString(Bytes.toBytes(key)), 608 Arrays.equals(kv.getKey(), keyBytes)); 609 byte[] valBytes = Bytes.toBytes(val); 610 assertTrue("bytes for vals do not match " + valStr + " " + Bytes.toString(valBytes), 611 Arrays.equals(Bytes.toBytes(valStr), valBytes)); 612 if (!scanner.next()) { 613 break; 614 } 615 } 616 assertEquals(i, start + n - 1); 617 return (start + n); 618 } 619 620 private byte[] getSomeKey(int rowId) { 621 KeyValue kv = new KeyValue(Bytes.toBytes(String.format(localFormatter, Integer.valueOf(rowId))), 622 Bytes.toBytes("family"), Bytes.toBytes("qual"), HConstants.LATEST_TIMESTAMP, Type.Put); 623 return kv.getKey(); 624 } 625 626 private void writeRecords(Writer writer, boolean useTags) throws IOException { 627 writeSomeRecords(writer, 0, 100, useTags); 628 writer.close(); 629 } 630 631 private FSDataOutputStream createFSOutput(Path name) throws IOException { 632 // if (fs.exists(name)) fs.delete(name, true); 633 FSDataOutputStream fout = fs.create(name); 634 return fout; 635 } 636 637 /** 638 * test none codecs 639 */ 640 void basicWithSomeCodec(String codec, boolean useTags) throws IOException { 641 if (useTags) { 642 conf.setInt("hfile.format.version", 3); 643 } 644 Path ncHFile = new Path(ROOT_DIR, "basic.hfile." + codec.toString() + useTags); 645 FSDataOutputStream fout = createFSOutput(ncHFile); 646 HFileContext meta = new HFileContextBuilder().withBlockSize(minBlockSize) 647 .withCompression(HFileWriterImpl.compressionByName(codec)).build(); 648 Writer writer = 649 HFile.getWriterFactory(conf, cacheConf).withOutputStream(fout).withFileContext(meta).create(); 650 LOG.info(Objects.toString(writer)); 651 writeRecords(writer, useTags); 652 fout.close(); 653 FSDataInputStream fin = fs.open(ncHFile); 654 ReaderContext context = new ReaderContextBuilder().withFileSystemAndPath(fs, ncHFile).build(); 655 Reader reader = createReaderFromStream(context, cacheConf, conf); 656 System.out.println(cacheConf.toString()); 657 // Load up the index. 658 // Get a scanner that caches and that does not use pread. 659 HFileScanner scanner = reader.getScanner(conf, true, false); 660 // Align scanner at start of the file. 661 scanner.seekTo(); 662 readAllRecords(scanner); 663 int seekTo = scanner.seekTo(KeyValueUtil.createKeyValueFromKey(getSomeKey(50))); 664 System.out.println(seekTo); 665 assertTrue("location lookup failed", 666 scanner.seekTo(KeyValueUtil.createKeyValueFromKey(getSomeKey(50))) == 0); 667 // read the key and see if it matches 668 ByteBuffer readKey = ByteBuffer.wrap(((KeyValue) scanner.getKey()).getKey()); 669 assertTrue("seeked key does not match", Arrays.equals(getSomeKey(50), Bytes.toBytes(readKey))); 670 671 scanner.seekTo(KeyValueUtil.createKeyValueFromKey(getSomeKey(0))); 672 ByteBuffer val1 = scanner.getValue(); 673 scanner.seekTo(KeyValueUtil.createKeyValueFromKey(getSomeKey(0))); 674 ByteBuffer val2 = scanner.getValue(); 675 assertTrue(Arrays.equals(Bytes.toBytes(val1), Bytes.toBytes(val2))); 676 677 reader.close(); 678 fin.close(); 679 fs.delete(ncHFile, true); 680 } 681 682 @Test 683 public void testTFileFeatures() throws IOException { 684 testHFilefeaturesInternals(false); 685 testHFilefeaturesInternals(true); 686 } 687 688 protected void testHFilefeaturesInternals(boolean useTags) throws IOException { 689 basicWithSomeCodec("none", useTags); 690 basicWithSomeCodec("gz", useTags); 691 } 692 693 private void writeNumMetablocks(Writer writer, int n) { 694 for (int i = 0; i < n; i++) { 695 writer.appendMetaBlock("HFileMeta" + i, new Writable() { 696 private int val; 697 698 public Writable setVal(int val) { 699 this.val = val; 700 return this; 701 } 702 703 @Override 704 public void write(DataOutput out) throws IOException { 705 out.write(Bytes.toBytes("something to test" + val)); 706 } 707 708 @Override 709 public void readFields(DataInput in) throws IOException { 710 } 711 }.setVal(i)); 712 } 713 } 714 715 private void someTestingWithMetaBlock(Writer writer) { 716 writeNumMetablocks(writer, 10); 717 } 718 719 private void readNumMetablocks(Reader reader, int n) throws IOException { 720 for (int i = 0; i < n; i++) { 721 ByteBuff actual = reader.getMetaBlock("HFileMeta" + i, false).getBufferWithoutHeader(); 722 ByteBuffer expected = ByteBuffer.wrap(Bytes.toBytes("something to test" + i)); 723 assertEquals("failed to match metadata", Bytes.toStringBinary(expected), Bytes.toStringBinary( 724 actual.array(), actual.arrayOffset() + actual.position(), actual.capacity())); 725 } 726 } 727 728 private void someReadingWithMetaBlock(Reader reader) throws IOException { 729 readNumMetablocks(reader, 10); 730 } 731 732 private void metablocks(final String compress) throws Exception { 733 Path mFile = new Path(ROOT_DIR, "meta.hfile"); 734 FSDataOutputStream fout = createFSOutput(mFile); 735 HFileContext meta = 736 new HFileContextBuilder().withCompression(HFileWriterImpl.compressionByName(compress)) 737 .withBlockSize(minBlockSize).build(); 738 Writer writer = 739 HFile.getWriterFactory(conf, cacheConf).withOutputStream(fout).withFileContext(meta).create(); 740 someTestingWithMetaBlock(writer); 741 writer.close(); 742 fout.close(); 743 ReaderContext context = new ReaderContextBuilder().withFileSystemAndPath(fs, mFile).build(); 744 Reader reader = createReaderFromStream(context, cacheConf, conf); 745 // No data -- this should return false. 746 assertFalse(reader.getScanner(conf, false, false).seekTo()); 747 someReadingWithMetaBlock(reader); 748 fs.delete(mFile, true); 749 reader.close(); 750 } 751 752 // test meta blocks for hfiles 753 @Test 754 public void testMetaBlocks() throws Exception { 755 metablocks("none"); 756 metablocks("gz"); 757 } 758 759 @Test 760 public void testNullMetaBlocks() throws Exception { 761 for (Compression.Algorithm compressAlgo : HBaseCommonTestingUtil.COMPRESSION_ALGORITHMS) { 762 Path mFile = new Path(ROOT_DIR, "nometa_" + compressAlgo + ".hfile"); 763 FSDataOutputStream fout = createFSOutput(mFile); 764 HFileContext meta = 765 new HFileContextBuilder().withCompression(compressAlgo).withBlockSize(minBlockSize).build(); 766 Writer writer = HFile.getWriterFactory(conf, cacheConf).withOutputStream(fout) 767 .withFileContext(meta).create(); 768 KeyValue kv = 769 new KeyValue(Bytes.toBytes("foo"), Bytes.toBytes("f1"), null, Bytes.toBytes("value")); 770 writer.append(kv); 771 writer.close(); 772 fout.close(); 773 Reader reader = HFile.createReader(fs, mFile, cacheConf, true, conf); 774 assertNull(reader.getMetaBlock("non-existant", false)); 775 } 776 } 777 778 /** 779 * Make sure the ordinals for our compression algorithms do not change on us. 780 */ 781 @Test 782 public void testCompressionOrdinance() { 783 assertTrue(Compression.Algorithm.LZO.ordinal() == 0); 784 assertTrue(Compression.Algorithm.GZ.ordinal() == 1); 785 assertTrue(Compression.Algorithm.NONE.ordinal() == 2); 786 assertTrue(Compression.Algorithm.SNAPPY.ordinal() == 3); 787 assertTrue(Compression.Algorithm.LZ4.ordinal() == 4); 788 } 789 790 @Test 791 public void testShortMidpointSameQual() { 792 Cell left = 793 ExtendedCellBuilderFactory.create(CellBuilderType.DEEP_COPY).setRow(Bytes.toBytes("a")) 794 .setFamily(Bytes.toBytes("a")).setQualifier(Bytes.toBytes("a")).setTimestamp(11) 795 .setType(Type.Maximum.getCode()).setValue(HConstants.EMPTY_BYTE_ARRAY).build(); 796 Cell right = 797 ExtendedCellBuilderFactory.create(CellBuilderType.DEEP_COPY).setRow(Bytes.toBytes("a")) 798 .setFamily(Bytes.toBytes("a")).setQualifier(Bytes.toBytes("a")).setTimestamp(9) 799 .setType(Type.Maximum.getCode()).setValue(HConstants.EMPTY_BYTE_ARRAY).build(); 800 Cell mid = HFileWriterImpl.getMidpoint(CellComparatorImpl.COMPARATOR, left, right); 801 assertTrue( 802 PrivateCellUtil.compareKeyIgnoresMvcc(CellComparatorImpl.COMPARATOR, left, mid) <= 0); 803 assertTrue( 804 PrivateCellUtil.compareKeyIgnoresMvcc(CellComparatorImpl.COMPARATOR, mid, right) == 0); 805 } 806 807 private Cell getCell(byte[] row, byte[] family, byte[] qualifier) { 808 return ExtendedCellBuilderFactory.create(CellBuilderType.DEEP_COPY).setRow(row) 809 .setFamily(family).setQualifier(qualifier).setTimestamp(HConstants.LATEST_TIMESTAMP) 810 .setType(KeyValue.Type.Maximum.getCode()).setValue(HConstants.EMPTY_BYTE_ARRAY).build(); 811 } 812 813 @Test 814 public void testGetShortMidpoint() { 815 Cell left = getCell(Bytes.toBytes("a"), Bytes.toBytes("a"), Bytes.toBytes("a")); 816 Cell right = getCell(Bytes.toBytes("a"), Bytes.toBytes("a"), Bytes.toBytes("a")); 817 Cell mid = HFileWriterImpl.getMidpoint(CellComparatorImpl.COMPARATOR, left, right); 818 assertTrue( 819 PrivateCellUtil.compareKeyIgnoresMvcc(CellComparatorImpl.COMPARATOR, left, mid) <= 0); 820 assertTrue( 821 PrivateCellUtil.compareKeyIgnoresMvcc(CellComparatorImpl.COMPARATOR, mid, right) <= 0); 822 left = getCell(Bytes.toBytes("a"), Bytes.toBytes("a"), Bytes.toBytes("a")); 823 right = getCell(Bytes.toBytes("b"), Bytes.toBytes("a"), Bytes.toBytes("a")); 824 mid = HFileWriterImpl.getMidpoint(CellComparatorImpl.COMPARATOR, left, right); 825 assertTrue(PrivateCellUtil.compareKeyIgnoresMvcc(CellComparatorImpl.COMPARATOR, left, mid) < 0); 826 assertTrue( 827 PrivateCellUtil.compareKeyIgnoresMvcc(CellComparatorImpl.COMPARATOR, mid, right) <= 0); 828 left = getCell(Bytes.toBytes("g"), Bytes.toBytes("a"), Bytes.toBytes("a")); 829 right = getCell(Bytes.toBytes("i"), Bytes.toBytes("a"), Bytes.toBytes("a")); 830 mid = HFileWriterImpl.getMidpoint(CellComparatorImpl.COMPARATOR, left, right); 831 assertTrue(PrivateCellUtil.compareKeyIgnoresMvcc(CellComparatorImpl.COMPARATOR, left, mid) < 0); 832 assertTrue( 833 PrivateCellUtil.compareKeyIgnoresMvcc(CellComparatorImpl.COMPARATOR, mid, right) <= 0); 834 left = getCell(Bytes.toBytes("a"), Bytes.toBytes("a"), Bytes.toBytes("a")); 835 right = getCell(Bytes.toBytes("bbbbbbb"), Bytes.toBytes("a"), Bytes.toBytes("a")); 836 mid = HFileWriterImpl.getMidpoint(CellComparatorImpl.COMPARATOR, left, right); 837 assertTrue(PrivateCellUtil.compareKeyIgnoresMvcc(CellComparatorImpl.COMPARATOR, left, mid) < 0); 838 assertTrue( 839 PrivateCellUtil.compareKeyIgnoresMvcc(CellComparatorImpl.COMPARATOR, mid, right) < 0); 840 assertEquals(1, mid.getRowLength()); 841 left = getCell(Bytes.toBytes("a"), Bytes.toBytes("a"), Bytes.toBytes("a")); 842 right = getCell(Bytes.toBytes("b"), Bytes.toBytes("a"), Bytes.toBytes("a")); 843 mid = HFileWriterImpl.getMidpoint(CellComparatorImpl.COMPARATOR, left, right); 844 assertTrue(PrivateCellUtil.compareKeyIgnoresMvcc(CellComparatorImpl.COMPARATOR, left, mid) < 0); 845 assertTrue( 846 PrivateCellUtil.compareKeyIgnoresMvcc(CellComparatorImpl.COMPARATOR, mid, right) <= 0); 847 left = getCell(Bytes.toBytes("a"), Bytes.toBytes("a"), Bytes.toBytes("a")); 848 right = getCell(Bytes.toBytes("a"), Bytes.toBytes("aaaaaaaa"), Bytes.toBytes("b")); 849 mid = HFileWriterImpl.getMidpoint(CellComparatorImpl.COMPARATOR, left, right); 850 assertTrue(PrivateCellUtil.compareKeyIgnoresMvcc(CellComparatorImpl.COMPARATOR, left, mid) < 0); 851 assertTrue( 852 PrivateCellUtil.compareKeyIgnoresMvcc(CellComparatorImpl.COMPARATOR, mid, right) < 0); 853 assertEquals(2, mid.getFamilyLength()); 854 left = getCell(Bytes.toBytes("a"), Bytes.toBytes("a"), Bytes.toBytes("a")); 855 right = getCell(Bytes.toBytes("a"), Bytes.toBytes("a"), Bytes.toBytes("aaaaaaaaa")); 856 mid = HFileWriterImpl.getMidpoint(CellComparatorImpl.COMPARATOR, left, right); 857 assertTrue(PrivateCellUtil.compareKeyIgnoresMvcc(CellComparatorImpl.COMPARATOR, left, mid) < 0); 858 assertTrue( 859 PrivateCellUtil.compareKeyIgnoresMvcc(CellComparatorImpl.COMPARATOR, mid, right) < 0); 860 assertEquals(2, mid.getQualifierLength()); 861 left = getCell(Bytes.toBytes("a"), Bytes.toBytes("a"), Bytes.toBytes("a")); 862 right = getCell(Bytes.toBytes("a"), Bytes.toBytes("a"), Bytes.toBytes("b")); 863 mid = HFileWriterImpl.getMidpoint(CellComparatorImpl.COMPARATOR, left, right); 864 assertTrue(PrivateCellUtil.compareKeyIgnoresMvcc(CellComparatorImpl.COMPARATOR, left, mid) < 0); 865 assertTrue( 866 PrivateCellUtil.compareKeyIgnoresMvcc(CellComparatorImpl.COMPARATOR, mid, right) <= 0); 867 assertEquals(1, mid.getQualifierLength()); 868 869 // Verify boundary conditions 870 left = getCell(Bytes.toBytes("a"), Bytes.toBytes("a"), new byte[] { 0x00, (byte) 0xFE }); 871 right = getCell(Bytes.toBytes("a"), Bytes.toBytes("a"), new byte[] { 0x00, (byte) 0xFF }); 872 mid = HFileWriterImpl.getMidpoint(CellComparatorImpl.COMPARATOR, left, right); 873 assertTrue(PrivateCellUtil.compareKeyIgnoresMvcc(CellComparatorImpl.COMPARATOR, left, mid) < 0); 874 assertTrue( 875 PrivateCellUtil.compareKeyIgnoresMvcc(CellComparatorImpl.COMPARATOR, mid, right) == 0); 876 left = getCell(Bytes.toBytes("a"), Bytes.toBytes("a"), new byte[] { 0x00, 0x12 }); 877 right = getCell(Bytes.toBytes("a"), Bytes.toBytes("a"), new byte[] { 0x00, 0x12, 0x00 }); 878 mid = HFileWriterImpl.getMidpoint(CellComparatorImpl.COMPARATOR, left, right); 879 assertTrue(PrivateCellUtil.compareKeyIgnoresMvcc(CellComparatorImpl.COMPARATOR, left, mid) < 0); 880 assertTrue( 881 PrivateCellUtil.compareKeyIgnoresMvcc(CellComparatorImpl.COMPARATOR, mid, right) == 0); 882 883 // Assert that if meta comparator, it returns the right cell -- i.e. no 884 // optimization done. 885 left = getCell(Bytes.toBytes("g"), Bytes.toBytes("a"), Bytes.toBytes("a")); 886 right = getCell(Bytes.toBytes("i"), Bytes.toBytes("a"), Bytes.toBytes("a")); 887 mid = HFileWriterImpl.getMidpoint(MetaCellComparator.META_COMPARATOR, left, right); 888 assertTrue(PrivateCellUtil.compareKeyIgnoresMvcc(CellComparatorImpl.COMPARATOR, left, mid) < 0); 889 assertTrue( 890 PrivateCellUtil.compareKeyIgnoresMvcc(CellComparatorImpl.COMPARATOR, mid, right) == 0); 891 byte[] family = Bytes.toBytes("family"); 892 byte[] qualA = Bytes.toBytes("qfA"); 893 byte[] qualB = Bytes.toBytes("qfB"); 894 final CellComparatorImpl keyComparator = CellComparatorImpl.COMPARATOR; 895 // verify that faked shorter rowkey could be generated 896 long ts = 5; 897 KeyValue kv1 = new KeyValue(Bytes.toBytes("the quick brown fox"), family, qualA, ts, Type.Put); 898 KeyValue kv2 = new KeyValue(Bytes.toBytes("the who test text"), family, qualA, ts, Type.Put); 899 Cell newKey = HFileWriterImpl.getMidpoint(keyComparator, kv1, kv2); 900 assertTrue(keyComparator.compare(kv1, newKey) < 0); 901 assertTrue((keyComparator.compare(kv2, newKey)) > 0); 902 byte[] expectedArray = Bytes.toBytes("the r"); 903 Bytes.equals(newKey.getRowArray(), newKey.getRowOffset(), newKey.getRowLength(), expectedArray, 904 0, expectedArray.length); 905 906 // verify: same with "row + family + qualifier", return rightKey directly 907 kv1 = new KeyValue(Bytes.toBytes("ilovehbase"), family, qualA, 5, Type.Put); 908 kv2 = new KeyValue(Bytes.toBytes("ilovehbase"), family, qualA, 0, Type.Put); 909 assertTrue(keyComparator.compare(kv1, kv2) < 0); 910 newKey = HFileWriterImpl.getMidpoint(keyComparator, kv1, kv2); 911 assertTrue(keyComparator.compare(kv1, newKey) < 0); 912 assertTrue((keyComparator.compare(kv2, newKey)) == 0); 913 kv1 = new KeyValue(Bytes.toBytes("ilovehbase"), family, qualA, -5, Type.Put); 914 kv2 = new KeyValue(Bytes.toBytes("ilovehbase"), family, qualA, -10, Type.Put); 915 assertTrue(keyComparator.compare(kv1, kv2) < 0); 916 newKey = HFileWriterImpl.getMidpoint(keyComparator, kv1, kv2); 917 assertTrue(keyComparator.compare(kv1, newKey) < 0); 918 assertTrue((keyComparator.compare(kv2, newKey)) == 0); 919 920 // verify: same with row, different with qualifier 921 kv1 = new KeyValue(Bytes.toBytes("ilovehbase"), family, qualA, 5, Type.Put); 922 kv2 = new KeyValue(Bytes.toBytes("ilovehbase"), family, qualB, 5, Type.Put); 923 assertTrue(keyComparator.compare(kv1, kv2) < 0); 924 newKey = HFileWriterImpl.getMidpoint(keyComparator, kv1, kv2); 925 assertTrue(keyComparator.compare(kv1, newKey) < 0); 926 assertTrue((keyComparator.compare(kv2, newKey)) > 0); 927 assertTrue(Arrays.equals(CellUtil.cloneFamily(newKey), family)); 928 assertTrue(Arrays.equals(CellUtil.cloneQualifier(newKey), qualB)); 929 assertTrue(newKey.getTimestamp() == HConstants.LATEST_TIMESTAMP); 930 assertTrue(newKey.getTypeByte() == Type.Maximum.getCode()); 931 932 // verify metaKeyComparator's getShortMidpointKey output 933 final CellComparatorImpl metaKeyComparator = MetaCellComparator.META_COMPARATOR; 934 kv1 = new KeyValue(Bytes.toBytes("ilovehbase123"), family, qualA, 5, Type.Put); 935 kv2 = new KeyValue(Bytes.toBytes("ilovehbase234"), family, qualA, 0, Type.Put); 936 newKey = HFileWriterImpl.getMidpoint(metaKeyComparator, kv1, kv2); 937 assertTrue(metaKeyComparator.compare(kv1, newKey) < 0); 938 assertTrue((metaKeyComparator.compare(kv2, newKey) == 0)); 939 940 // verify common fix scenario 941 kv1 = new KeyValue(Bytes.toBytes("ilovehbase"), family, qualA, ts, Type.Put); 942 kv2 = new KeyValue(Bytes.toBytes("ilovehbaseandhdfs"), family, qualA, ts, Type.Put); 943 assertTrue(keyComparator.compare(kv1, kv2) < 0); 944 newKey = HFileWriterImpl.getMidpoint(keyComparator, kv1, kv2); 945 assertTrue(keyComparator.compare(kv1, newKey) < 0); 946 assertTrue((keyComparator.compare(kv2, newKey)) > 0); 947 expectedArray = Bytes.toBytes("ilovehbasea"); 948 Bytes.equals(newKey.getRowArray(), newKey.getRowOffset(), newKey.getRowLength(), expectedArray, 949 0, expectedArray.length); 950 // verify only 1 offset scenario 951 kv1 = new KeyValue(Bytes.toBytes("100abcdefg"), family, qualA, ts, Type.Put); 952 kv2 = new KeyValue(Bytes.toBytes("101abcdefg"), family, qualA, ts, Type.Put); 953 assertTrue(keyComparator.compare(kv1, kv2) < 0); 954 newKey = HFileWriterImpl.getMidpoint(keyComparator, kv1, kv2); 955 assertTrue(keyComparator.compare(kv1, newKey) < 0); 956 assertTrue((keyComparator.compare(kv2, newKey)) > 0); 957 expectedArray = Bytes.toBytes("101"); 958 Bytes.equals(newKey.getRowArray(), newKey.getRowOffset(), newKey.getRowLength(), expectedArray, 959 0, expectedArray.length); 960 } 961 962 @Test 963 public void testDBEShipped() throws IOException { 964 for (DataBlockEncoding encoding : DataBlockEncoding.values()) { 965 DataBlockEncoder encoder = encoding.getEncoder(); 966 if (encoder == null) { 967 continue; 968 } 969 Path f = new Path(ROOT_DIR, testName.getMethodName() + "_" + encoding); 970 HFileContext context = 971 new HFileContextBuilder().withIncludesTags(false).withDataBlockEncoding(encoding).build(); 972 HFileWriterImpl writer = (HFileWriterImpl) HFile.getWriterFactory(conf, cacheConf) 973 .withPath(fs, f).withFileContext(context).create(); 974 975 KeyValue kv = new KeyValue(Bytes.toBytes("testkey1"), Bytes.toBytes("family"), 976 Bytes.toBytes("qual"), Bytes.toBytes("testvalue")); 977 KeyValue kv2 = new KeyValue(Bytes.toBytes("testkey2"), Bytes.toBytes("family"), 978 Bytes.toBytes("qual"), Bytes.toBytes("testvalue")); 979 KeyValue kv3 = new KeyValue(Bytes.toBytes("testkey3"), Bytes.toBytes("family"), 980 Bytes.toBytes("qual"), Bytes.toBytes("testvalue")); 981 982 ByteBuffer buffer = ByteBuffer.wrap(kv.getBuffer()); 983 ByteBuffer buffer2 = ByteBuffer.wrap(kv2.getBuffer()); 984 ByteBuffer buffer3 = ByteBuffer.wrap(kv3.getBuffer()); 985 986 writer.append(new ByteBufferKeyValue(buffer, 0, buffer.remaining())); 987 writer.beforeShipped(); 988 989 // pollute first cell's backing ByteBuffer 990 ByteBufferUtils.copyFromBufferToBuffer(buffer3, buffer); 991 992 // write another cell, if DBE not Shipped, test will fail 993 writer.append(new ByteBufferKeyValue(buffer2, 0, buffer2.remaining())); 994 writer.close(); 995 } 996 } 997 998 /** 999 * Test case for CombinedBlockCache with TinyLfu as L1 cache 1000 */ 1001 @Test 1002 public void testReaderWithTinyLfuCombinedBlockCache() throws Exception { 1003 testReaderCombinedCache("TinyLfu"); 1004 } 1005 1006 /** 1007 * Test case for CombinedBlockCache with AdaptiveLRU as L1 cache 1008 */ 1009 @Test 1010 public void testReaderWithAdaptiveLruCombinedBlockCache() throws Exception { 1011 testReaderCombinedCache("AdaptiveLRU"); 1012 } 1013 1014 /** 1015 * Test case for CombinedBlockCache with AdaptiveLRU as L1 cache 1016 */ 1017 @Test 1018 public void testReaderWithLruCombinedBlockCache() throws Exception { 1019 testReaderCombinedCache("LRU"); 1020 } 1021 1022 private void testReaderCombinedCache(final String l1CachePolicy) throws Exception { 1023 int bufCount = 1024; 1024 int blockSize = 64 * 1024; 1025 ByteBuffAllocator alloc = initAllocator(true, bufCount, blockSize, 0); 1026 fillByteBuffAllocator(alloc, bufCount); 1027 Path storeFilePath = writeStoreFile(); 1028 // Open the file reader with CombinedBlockCache 1029 BlockCache combined = initCombinedBlockCache(l1CachePolicy); 1030 conf.setBoolean(EVICT_BLOCKS_ON_CLOSE_KEY, true); 1031 CacheConfig cacheConfig = new CacheConfig(conf, null, combined, alloc); 1032 HFile.Reader reader = HFile.createReader(fs, storeFilePath, cacheConfig, true, conf); 1033 long offset = 0; 1034 Cacheable cachedBlock = null; 1035 while (offset < reader.getTrailer().getLoadOnOpenDataOffset()) { 1036 BlockCacheKey key = new BlockCacheKey(storeFilePath.getName(), offset); 1037 HFileBlock block = reader.readBlock(offset, -1, true, true, false, true, null, null); 1038 offset += block.getOnDiskSizeWithHeader(); 1039 // Read the cached block. 1040 cachedBlock = combined.getBlock(key, false, false, true); 1041 try { 1042 Assert.assertNotNull(cachedBlock); 1043 Assert.assertTrue(cachedBlock instanceof HFileBlock); 1044 HFileBlock hfb = (HFileBlock) cachedBlock; 1045 // Data block will be cached in BucketCache, so it should be an off-heap block. 1046 if (hfb.getBlockType().isData()) { 1047 Assert.assertTrue(hfb.isSharedMem()); 1048 } else if (!l1CachePolicy.equals("TinyLfu")) { 1049 Assert.assertFalse(hfb.isSharedMem()); 1050 } 1051 } finally { 1052 cachedBlock.release(); 1053 } 1054 block.release(); // return back the ByteBuffer back to allocator. 1055 } 1056 reader.close(); 1057 combined.shutdown(); 1058 if (cachedBlock != null) { 1059 Assert.assertEquals(0, cachedBlock.refCnt()); 1060 } 1061 Assert.assertEquals(bufCount, alloc.getFreeBufferCount()); 1062 alloc.clean(); 1063 } 1064 1065 @Test 1066 public void testHFileContextBuilderWithIndexEncoding() throws IOException { 1067 HFileContext context = 1068 new HFileContextBuilder().withIndexBlockEncoding(IndexBlockEncoding.PREFIX_TREE).build(); 1069 HFileContext newContext = new HFileContextBuilder(context).build(); 1070 assertTrue(newContext.getIndexBlockEncoding() == IndexBlockEncoding.PREFIX_TREE); 1071 } 1072}