@Test
  public void testHalfScanAndReseek() throws Exception {
    ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.PARANOID);
    Configuration conf = TEST_UTIL.getConfiguration();
    FileSystem fs = FileSystem.get(conf);
    String root_dir = TEST_UTIL.getDataTestDir().toString();
    Path parentPath = new Path(new Path(root_dir, "parent"), "CF");
    fs.mkdirs(parentPath);
    String tableName = Paths.get(root_dir).getFileName().toString();
    RegionInfo splitAHri = RegionInfoBuilder.newBuilder(TableName.valueOf(tableName)).build();
    Thread.currentThread().sleep(1000);
    RegionInfo splitBHri = RegionInfoBuilder.newBuilder(TableName.valueOf(tableName)).build();
    Path splitAPath = new Path(new Path(root_dir, splitAHri.getRegionNameAsString()), "CF");
    Path splitBPath = new Path(new Path(root_dir, splitBHri.getRegionNameAsString()), "CF");
    Path filePath = StoreFileWriter.getUniqueFile(fs, parentPath);
    String ioEngineName = "file:" + TEST_UTIL.getDataTestDir() + "/bucketNoRecycler.cache";
    BucketCache bucketCache = new BucketCache(ioEngineName, 32 * 1024 * 1024, 1024,
      new int[] { 4 * 1024, 8 * 1024, 64 * 1024, 96 * 1024 }, 1, 1, null);
    conf.setBoolean(CACHE_BLOCKS_ON_WRITE_KEY, true);
    conf.setInt(BUFFER_SIZE_KEY, 1024);
    ByteBuffAllocator allocator = ByteBuffAllocator.create(conf, true);

    final AtomicInteger counter = new AtomicInteger();
    RefCnt.detector.setLeakListener(new ResourceLeakDetector.LeakListener() {
      @Override
      public void onLeak(String s, String s1) {
        counter.incrementAndGet();
      }
    });

    ColumnFamilyDescriptorBuilder cfBuilder =
      ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes("CF"));
    CacheConfig cacheConf = new CacheConfig(conf, cfBuilder.build(), bucketCache, allocator);

    HFileContext meta = new HFileContextBuilder().withBlockSize(1024).build();
    HFile.Writer w =
      HFile.getWriterFactory(conf, cacheConf).withPath(fs, filePath).withFileContext(meta).create();

    // write some things.
    List<KeyValue> items = genSomeKeys();
    for (KeyValue kv : items) {
      w.append(kv);
    }
    w.close();

    HFile.Reader r = HFile.createReader(fs, filePath, cacheConf, true, conf);
    Cell midKV = r.midKey().get();
    byte[] midkey = CellUtil.cloneRow(midKV);

    Path splitFileA = new Path(splitAPath, filePath.getName() + ".parent");
    Path splitFileB = new Path(splitBPath, filePath.getName() + ".parent");

    HRegionFileSystem splitAregionFS =
      HRegionFileSystem.create(conf, fs, new Path(root_dir), splitAHri);
    StoreContext splitAStoreContext =
      StoreContext.getBuilder().withColumnFamilyDescriptor(ColumnFamilyDescriptorBuilder.of("CF"))
        .withFamilyStoreDirectoryPath(splitAPath).withRegionFileSystem(splitAregionFS).build();
    StoreFileTracker splitAsft = StoreFileTrackerFactory.create(conf, false, splitAStoreContext);
    Reference bottom = new Reference(midkey, Reference.Range.bottom);
    splitAsft.createReference(bottom, splitFileA);
    doTestOfScanAndReseek(splitFileA, fs, bottom, cacheConf);

    HRegionFileSystem splitBregionFS =
      HRegionFileSystem.create(conf, fs, new Path(root_dir), splitBHri);
    StoreContext splitBStoreContext =
      StoreContext.getBuilder().withColumnFamilyDescriptor(ColumnFamilyDescriptorBuilder.of("CF"))
        .withFamilyStoreDirectoryPath(splitBPath).withRegionFileSystem(splitBregionFS).build();
    StoreFileTracker splitBsft = StoreFileTrackerFactory.create(conf, false, splitBStoreContext);
    Reference top = new Reference(midkey, Reference.Range.top);
    splitBsft.createReference(top, splitFileB);
    doTestOfScanAndReseek(splitFileB, fs, top, cacheConf);

    r.close();

    assertEquals(0, counter.get());
  }
Test the scanner and reseek of a half hfile scanner. The scanner API demands that seekTo and
reseekTo() only return < 0 if the key lies before the start of the file (with no position on
the scanner). Returning 0 if perfect match (rare), and return > 1 if we got an imperfect match.
The latter case being the most common, we should generally be returning 1, and if we do, there
may or may not be a 'next' in the scanner/file. A bug in the half file scanner was returning -1
at the end of the bottom half, and that was causing the infrastructure above to go null causing
NPEs and other problems. This test reproduces that failure, and also tests both the bottom and
top of the file while we are at it.
hbase-server/src/test/java/org/apache/hadoop/hbase/io/TestHalfStoreFileReader.java