1   /*
2    * Copyright 2010 The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  
21  package org.apache.hadoop.hbase.io;
22  
23  import org.apache.hadoop.fs.FileSystem;
24  import org.apache.hadoop.fs.Path;
25  import org.apache.hadoop.hbase.HBaseTestingUtility;
26  import org.apache.hadoop.hbase.KeyValue;
27  import org.apache.hadoop.hbase.io.hfile.HFile;
28  import org.apache.hadoop.hbase.io.hfile.HFileScanner;
29  import org.apache.hadoop.hbase.util.Bytes;
30  import org.junit.Test;
31  
32  import java.io.IOException;
33  import java.util.ArrayList;
34  import java.util.List;
35  
36  import static org.junit.Assert.assertTrue;
37  
38  
39  public class TestHalfStoreFileReader {
40  
41    /**
42     * Test the scanner and reseek of a half hfile scanner. The scanner API
43     * demands that seekTo and reseekTo() only return < 0 if the key lies
44     * before the start of the file (with no position on the scanner). Returning
45     * 0 if perfect match (rare), and return > 1 if we got an imperfect match.
46     *
47     * The latter case being the most common, we should generally be returning 1,
48     * and if we do, there may or may not be a 'next' in the scanner/file.
49     *
50     * A bug in the half file scanner was returning -1 at the end of the bottom
51     * half, and that was causing the infrastructure above to go null causing NPEs
52     * and other problems.  This test reproduces that failure, and also tests
53     * both the bottom and top of the file while we are at it.
54     *
55     * @throws IOException
56     */
57    @Test
58    public void testHalfScanAndReseek() throws IOException {
59      HBaseTestingUtility test_util = new HBaseTestingUtility();
60      String root_dir = HBaseTestingUtility.getTestDir("TestHalfStoreFile").toString();
61      Path p = new Path(root_dir, "test");
62  
63      FileSystem fs = FileSystem.get(test_util.getConfiguration());
64  
65      HFile.Writer w = new HFile.Writer(fs, p, 1024, "none", KeyValue.KEY_COMPARATOR);
66  
67      // write some things.
68      List<KeyValue> items = genSomeKeys();
69      for (KeyValue kv : items) {
70        w.append(kv);
71      }
72      w.close();
73  
74      HFile.Reader r = new HFile.Reader(fs, p, null, false);
75      r.loadFileInfo();
76      byte [] midkey = r.midkey();
77      KeyValue midKV = KeyValue.createKeyValueFromKey(midkey);
78      midkey = midKV.getRow();
79  
80      //System.out.println("midkey: " + midKV + " or: " + Bytes.toStringBinary(midkey));
81  
82      Reference bottom = new Reference(midkey, Reference.Range.bottom);
83      doTestOfScanAndReseek(p, fs, bottom);
84  
85      Reference top = new Reference(midkey, Reference.Range.top);
86      doTestOfScanAndReseek(p, fs, top);
87    }
88  
89    private void doTestOfScanAndReseek(Path p, FileSystem fs, Reference bottom)
90        throws IOException {
91      final HalfStoreFileReader halfreader =
92          new HalfStoreFileReader(fs, p, null, bottom);
93      halfreader.loadFileInfo();
94      final HFileScanner scanner = halfreader.getScanner(false, false);
95  
96      scanner.seekTo();
97      KeyValue curr;
98      do {
99        curr = scanner.getKeyValue();
100       KeyValue reseekKv =
101           getLastOnCol(curr);
102       int ret = scanner.reseekTo(reseekKv.getKey());
103       assertTrue("reseek to returned: " + ret, ret > 0);
104       //System.out.println(curr + ": " + ret);
105     } while (scanner.next());
106 
107     int ret = scanner.reseekTo(getLastOnCol(curr).getKey());
108     //System.out.println("Last reseek: " + ret);
109     assertTrue( ret > 0 );
110   }
111 
112   private KeyValue getLastOnCol(KeyValue curr) {
113     return KeyValue.createLastOnRow(
114         curr.getBuffer(), curr.getRowOffset(), curr.getRowLength(),
115         curr.getBuffer(), curr.getFamilyOffset(), curr.getFamilyLength(),
116         curr.getBuffer(), curr.getQualifierOffset(), curr.getQualifierLength());
117   }
118 
119   static final int SIZE = 1000;
120 
121   static byte[] _b(String s) {
122     return Bytes.toBytes(s);
123   }
124 
125   List<KeyValue> genSomeKeys() {
126     List<KeyValue> ret = new ArrayList<KeyValue>(SIZE);
127     for (int i = 0 ; i < SIZE; i++) {
128       KeyValue kv =
129           new KeyValue(
130               _b(String.format("row_%04d", i)),
131               _b("family"),
132               _b("qualifier"),
133               1000, // timestamp
134               _b("value"));
135       ret.add(kv);
136     }
137     return ret;
138   }
139 
140 
141 }