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 static org.junit.Assert.assertTrue;
24  
25  import java.io.IOException;
26  import java.util.ArrayList;
27  import java.util.List;
28  
29  import org.apache.hadoop.conf.Configuration;
30  import org.apache.hadoop.fs.FileSystem;
31  import org.apache.hadoop.fs.Path;
32  import org.apache.hadoop.hbase.HBaseTestingUtility;
33  import org.apache.hadoop.hbase.KeyValue;
34  import org.apache.hadoop.hbase.io.hfile.CacheConfig;
35  import org.apache.hadoop.hbase.io.hfile.HFile;
36  import org.apache.hadoop.hbase.io.hfile.HFileScanner;
37  import org.apache.hadoop.hbase.util.Bytes;
38  import org.junit.Test;
39  
40  
41  public class TestHalfStoreFileReader {
42  
43    /**
44     * Test the scanner and reseek of a half hfile scanner. The scanner API
45     * demands that seekTo and reseekTo() only return < 0 if the key lies
46     * before the start of the file (with no position on the scanner). Returning
47     * 0 if perfect match (rare), and return > 1 if we got an imperfect match.
48     *
49     * The latter case being the most common, we should generally be returning 1,
50     * and if we do, there may or may not be a 'next' in the scanner/file.
51     *
52     * A bug in the half file scanner was returning -1 at the end of the bottom
53     * half, and that was causing the infrastructure above to go null causing NPEs
54     * and other problems.  This test reproduces that failure, and also tests
55     * both the bottom and top of the file while we are at it.
56     *
57     * @throws IOException
58     */
59    @Test
60    public void testHalfScanAndReseek() throws IOException {
61      HBaseTestingUtility test_util = new HBaseTestingUtility();
62      String root_dir = test_util.getDataTestDir("TestHalfStoreFile").toString();
63      Path p = new Path(root_dir, "test");
64  
65      Configuration conf = test_util.getConfiguration();
66      FileSystem fs = FileSystem.get(conf);
67      CacheConfig cacheConf = new CacheConfig(conf);
68  
69      HFile.Writer w =
70        HFile.getWriterFactory(conf, cacheConf).createWriter(fs, p, 1024,
71          "none", KeyValue.KEY_COMPARATOR);
72  
73      // write some things.
74      List<KeyValue> items = genSomeKeys();
75      for (KeyValue kv : items) {
76        w.append(kv);
77      }
78      w.close();
79  
80      HFile.Reader r = HFile.createReader(fs, p, cacheConf);
81      r.loadFileInfo();
82      byte [] midkey = r.midkey();
83      KeyValue midKV = KeyValue.createKeyValueFromKey(midkey);
84      midkey = midKV.getRow();
85  
86      //System.out.println("midkey: " + midKV + " or: " + Bytes.toStringBinary(midkey));
87  
88      Reference bottom = new Reference(midkey, Reference.Range.bottom);
89      doTestOfScanAndReseek(p, fs, bottom, cacheConf);
90  
91      Reference top = new Reference(midkey, Reference.Range.top);
92      doTestOfScanAndReseek(p, fs, top, cacheConf);
93    }
94  
95    private void doTestOfScanAndReseek(Path p, FileSystem fs, Reference bottom,
96        CacheConfig cacheConf)
97        throws IOException {
98      final HalfStoreFileReader halfreader =
99          new HalfStoreFileReader(fs, p, cacheConf, bottom);
100     halfreader.loadFileInfo();
101     final HFileScanner scanner = halfreader.getScanner(false, false);
102 
103     scanner.seekTo();
104     KeyValue curr;
105     do {
106       curr = scanner.getKeyValue();
107       KeyValue reseekKv =
108           getLastOnCol(curr);
109       int ret = scanner.reseekTo(reseekKv.getKey());
110       assertTrue("reseek to returned: " + ret, ret > 0);
111       //System.out.println(curr + ": " + ret);
112     } while (scanner.next());
113 
114     int ret = scanner.reseekTo(getLastOnCol(curr).getKey());
115     //System.out.println("Last reseek: " + ret);
116     assertTrue( ret > 0 );
117   }
118 
119   private KeyValue getLastOnCol(KeyValue curr) {
120     return KeyValue.createLastOnRow(
121         curr.getBuffer(), curr.getRowOffset(), curr.getRowLength(),
122         curr.getBuffer(), curr.getFamilyOffset(), curr.getFamilyLength(),
123         curr.getBuffer(), curr.getQualifierOffset(), curr.getQualifierLength());
124   }
125 
126   static final int SIZE = 1000;
127 
128   static byte[] _b(String s) {
129     return Bytes.toBytes(s);
130   }
131 
132   List<KeyValue> genSomeKeys() {
133     List<KeyValue> ret = new ArrayList<KeyValue>(SIZE);
134     for (int i = 0 ; i < SIZE; i++) {
135       KeyValue kv =
136           new KeyValue(
137               _b(String.format("row_%04d", i)),
138               _b("family"),
139               _b("qualifier"),
140               1000, // timestamp
141               _b("value"));
142       ret.add(kv);
143     }
144     return ret;
145   }
146 
147 
148 }