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