diff --git a/src/test/java/org/apache/hadoop/hbase/io/TestHalfStoreFileReader.java b/src/test/java/org/apache/hadoop/hbase/io/TestHalfStoreFileReader.java new file mode 100644 index 00000000000..47f4c391057 --- /dev/null +++ b/src/test/java/org/apache/hadoop/hbase/io/TestHalfStoreFileReader.java @@ -0,0 +1,141 @@ +/* + * Copyright 2010 The Apache Software Foundation + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.hbase.io; + +import org.apache.hadoop.fs.FileSystem; +import org.apache.hadoop.fs.Path; +import org.apache.hadoop.hbase.HBaseTestingUtility; +import org.apache.hadoop.hbase.KeyValue; +import org.apache.hadoop.hbase.io.hfile.HFile; +import org.apache.hadoop.hbase.io.hfile.HFileScanner; +import org.apache.hadoop.hbase.util.Bytes; +import org.junit.Test; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.assertTrue; + + +public class TestHalfStoreFileReader { + + /** + * 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. + * + * @throws IOException + */ + @Test + public void testHalfScanAndReseek() throws IOException { + HBaseTestingUtility test_util = new HBaseTestingUtility(); + String root_dir = HBaseTestingUtility.getTestDir("TestHalfStoreFile").toString(); + Path p = new Path(root_dir, "test"); + + FileSystem fs = FileSystem.get(test_util.getConfiguration()); + + HFile.Writer w = new HFile.Writer(fs, p, 1024, "none", KeyValue.KEY_COMPARATOR); + + // write some things. + List items = genSomeKeys(); + for (KeyValue kv : items) { + w.append(kv); + } + w.close(); + + HFile.Reader r = new HFile.Reader(fs, p, null, false); + r.loadFileInfo(); + byte [] midkey = r.midkey(); + KeyValue midKV = KeyValue.createKeyValueFromKey(midkey); + midkey = midKV.getRow(); + + //System.out.println("midkey: " + midKV + " or: " + Bytes.toStringBinary(midkey)); + + Reference bottom = new Reference(midkey, Reference.Range.bottom); + doTestOfScanAndReseek(p, fs, bottom); + + Reference top = new Reference(midkey, Reference.Range.top); + doTestOfScanAndReseek(p, fs, top); + } + + private void doTestOfScanAndReseek(Path p, FileSystem fs, Reference bottom) + throws IOException { + final HalfStoreFileReader halfreader = + new HalfStoreFileReader(fs, p, null, bottom); + halfreader.loadFileInfo(); + final HFileScanner scanner = halfreader.getScanner(false, false); + + scanner.seekTo(); + KeyValue curr; + do { + curr = scanner.getKeyValue(); + KeyValue reseekKv = + getLastOnCol(curr); + int ret = scanner.reseekTo(reseekKv.getKey()); + assertTrue("reseek to returned: " + ret, ret > 0); + //System.out.println(curr + ": " + ret); + } while (scanner.next()); + + int ret = scanner.reseekTo(getLastOnCol(curr).getKey()); + //System.out.println("Last reseek: " + ret); + assertTrue( ret > 0 ); + } + + private KeyValue getLastOnCol(KeyValue curr) { + return KeyValue.createLastOnRow( + curr.getBuffer(), curr.getRowOffset(), curr.getRowLength(), + curr.getBuffer(), curr.getFamilyOffset(), curr.getFamilyLength(), + curr.getBuffer(), curr.getQualifierOffset(), curr.getQualifierLength()); + } + + static final int SIZE = 1000; + + static byte[] _b(String s) { + return Bytes.toBytes(s); + } + + List genSomeKeys() { + List ret = new ArrayList(SIZE); + for (int i = 0 ; i < SIZE; i++) { + KeyValue kv = + new KeyValue( + _b(String.format("row_%04d", i)), + _b("family"), + _b("qualifier"), + 1000, // timestamp + _b("value")); + ret.add(kv); + } + return ret; + } + + +}