View Javadoc

1   /*
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  
20  package org.apache.hadoop.hbase.regionserver;
21  
22  import java.io.IOException;
23  import java.util.ArrayList;
24  import java.util.Arrays;
25  import java.util.List;
26  
27  import org.apache.hadoop.hbase.Cell;
28  import org.apache.hadoop.hbase.HBaseTestCase;
29  import org.apache.hadoop.hbase.KeyValue;
30  import org.apache.hadoop.hbase.testclassification.SmallTests;
31  import org.apache.hadoop.hbase.util.Bytes;
32  import org.apache.hadoop.hbase.util.CollectionBackedScanner;
33  import org.junit.Test;
34  import org.junit.experimental.categories.Category;
35  
36  @Category(SmallTests.class)
37  public class TestKeyValueHeap extends HBaseTestCase {
38    private byte[] row1 = Bytes.toBytes("row1");
39    private byte[] fam1 = Bytes.toBytes("fam1");
40    private byte[] col1 = Bytes.toBytes("col1");
41    private byte[] data = Bytes.toBytes("data");
42  
43    private byte[] row2 = Bytes.toBytes("row2");
44    private byte[] fam2 = Bytes.toBytes("fam2");
45    private byte[] col2 = Bytes.toBytes("col2");
46  
47    private byte[] col3 = Bytes.toBytes("col3");
48    private byte[] col4 = Bytes.toBytes("col4");
49    private byte[] col5 = Bytes.toBytes("col5");
50  
51    // Variable name encoding. kv<row#><fam#><col#>
52    Cell kv111 = new KeyValue(row1, fam1, col1, data);
53    Cell kv112 = new KeyValue(row1, fam1, col2, data);
54    Cell kv113 = new KeyValue(row1, fam1, col3, data);
55    Cell kv114 = new KeyValue(row1, fam1, col4, data);
56    Cell kv115 = new KeyValue(row1, fam1, col5, data);
57    Cell kv121 = new KeyValue(row1, fam2, col1, data);
58    Cell kv122 = new KeyValue(row1, fam2, col2, data);
59    Cell kv211 = new KeyValue(row2, fam1, col1, data);
60    Cell kv212 = new KeyValue(row2, fam1, col2, data);
61    Cell kv213 = new KeyValue(row2, fam1, col3, data);
62  
63    TestScanner s1 = new TestScanner(Arrays.asList(kv115, kv211, kv212));
64    TestScanner s2 = new TestScanner(Arrays.asList(kv111, kv112));
65    TestScanner s3 = new TestScanner(Arrays.asList(kv113, kv114, kv121, kv122, kv213));
66  
67    List<KeyValueScanner> scanners = new ArrayList<KeyValueScanner>(Arrays.asList(s1, s2, s3));
68  
69    /*
70     * Uses {@code scanners} to build a KeyValueHeap, iterates over it and asserts that returned
71     * Cells are same as {@code expected}.
72     * @return List of Cells returned from scanners.
73     */
74    public List<Cell> assertCells(List<Cell> expected, List<KeyValueScanner> scanners)
75        throws IOException {
76      //Creating KeyValueHeap
77      KeyValueHeap kvh = new KeyValueHeap(scanners, KeyValue.COMPARATOR);
78  
79      List<Cell> actual = new ArrayList<>();
80      while(kvh.peek() != null){
81        actual.add(kvh.next());
82      }
83  
84      assertEquals(expected, actual);
85      return actual;
86    }
87  
88    public void setUp() throws Exception {
89      super.setUp();
90    }
91  
92    public void testSorted() throws IOException{
93      //Cases that need to be checked are:
94      //1. The "smallest" Cell is in the same scanners as current
95      //2. Current scanner gets empty
96  
97      List<Cell> expected = Arrays.asList(
98          kv111, kv112, kv113, kv114, kv115, kv121, kv122, kv211, kv212, kv213);
99  
100     List<Cell> actual = assertCells(expected, scanners);
101 
102     //Check if result is sorted according to Comparator
103     for(int i=0; i<actual.size()-1; i++){
104       int ret = KeyValue.COMPARATOR.compare(actual.get(i), actual.get(i+1));
105       assertTrue(ret < 0);
106     }
107   }
108 
109   public void testSeek() throws IOException {
110     //Cases:
111     //1. Seek Cell that is not in scanner
112     //2. Check that smallest that is returned from a seek is correct
113 
114     List<Cell> expected = Arrays.asList(kv211);
115 
116     //Creating KeyValueHeap
117     KeyValueHeap kvh =
118       new KeyValueHeap(scanners, KeyValue.COMPARATOR);
119 
120     Cell seekKv = new KeyValue(row2, fam1, null, null);
121     kvh.seek(seekKv);
122 
123     List<Cell> actual = Arrays.asList(kvh.peek());
124 
125     assertEquals("Expected = " + Arrays.toString(expected.toArray())
126         + "\n Actual = " + Arrays.toString(actual.toArray()), expected, actual);
127   }
128 
129   public void testScannerLeak() throws IOException {
130     // Test for unclosed scanners (HBASE-1927)
131 
132     TestScanner s4 = new TestScanner(new ArrayList<Cell>());
133     scanners.add(s4);
134 
135     //Creating KeyValueHeap
136     KeyValueHeap kvh = new KeyValueHeap(scanners, KeyValue.COMPARATOR);
137 
138     while(kvh.next() != null);
139 
140     for(KeyValueScanner scanner : scanners) {
141       assertTrue(((TestScanner)scanner).isClosed());
142     }
143   }
144 
145   public void testScannerException() throws IOException {
146     // Test for NPE issue when exception happens in scanners (HBASE-13835)
147 
148     TestScanner s1 = new SeekTestScanner(Arrays.asList(kv115, kv211, kv212));
149     TestScanner s2 = new SeekTestScanner(Arrays.asList(kv111, kv112));
150     TestScanner s3 = new SeekTestScanner(Arrays.asList(kv113, kv114, kv121, kv122, kv213));
151     TestScanner s4 = new SeekTestScanner(new ArrayList<Cell>());
152 
153     List<KeyValueScanner> scanners = new ArrayList<KeyValueScanner>(Arrays.asList(s1, s2, s3, s4));
154 
155     // Creating KeyValueHeap
156     KeyValueHeap kvh = new KeyValueHeap(scanners, KeyValue.COMPARATOR);
157 
158     try {
159       for (KeyValueScanner scanner : scanners) {
160         ((SeekTestScanner) scanner).setRealSeekDone(false);
161       }
162       while (kvh.next() != null);
163       // The pollRealKV should throw IOE.
164       assertTrue(false);
165     } catch (IOException ioe) {
166       kvh.close();
167     }
168 
169     // It implies there is no NPE thrown from kvh.close() if getting here
170     for (KeyValueScanner scanner : scanners) {
171       // Verify that close is called and only called once for each scanner
172       assertTrue(((SeekTestScanner) scanner).isClosed());
173       assertEquals(((SeekTestScanner) scanner).getClosedNum(), 1);
174     }
175   }
176 
177   @Test
178   public void testPriorityId() throws IOException {
179     Cell kv113A = new KeyValue(row1, fam1, col3, Bytes.toBytes("aaa"));
180     Cell kv113B = new KeyValue(row1, fam1, col3, Bytes.toBytes("bbb"));
181     {
182       TestScanner scan1 = new TestScanner(Arrays.asList(kv111, kv112, kv113A), 1);
183       TestScanner scan2 = new TestScanner(Arrays.asList(kv113B), 2);
184       List<Cell> expected = Arrays.asList(kv111, kv112, kv113B, kv113A);
185       assertCells(expected, new ArrayList<KeyValueScanner>(Arrays.asList(scan1, scan2)));
186     }
187     {
188       TestScanner scan1 = new TestScanner(Arrays.asList(kv111, kv112, kv113A), 2);
189       TestScanner scan2 = new TestScanner(Arrays.asList(kv113B), 1);
190       List<Cell> expected = Arrays.asList(kv111, kv112, kv113A, kv113B);
191       assertCells(expected, new ArrayList<KeyValueScanner>(Arrays.asList(scan1, scan2)));
192     }
193   }
194 
195   private static class TestScanner extends CollectionBackedScanner {
196     private boolean closed = false;
197     private long scannerOrder = 0;
198 
199     public TestScanner(List<Cell> list) {
200       super(list);
201     }
202 
203     public TestScanner(List<Cell> list, long scannerOrder) {
204       this(list);
205       this.scannerOrder = scannerOrder;
206     }
207 
208     @Override
209     public long getScannerOrder() {
210       return scannerOrder;
211     }
212 
213     @Override
214     public void close(){
215       closed = true;
216     }
217 
218     public boolean isClosed() {
219       return closed;
220     }
221   }
222 
223   private static class SeekTestScanner extends TestScanner {
224     private int closedNum = 0;
225     private boolean realSeekDone = true;
226 
227     public SeekTestScanner(List<Cell> list) {
228       super(list);
229     }
230 
231     @Override
232     public void close() {
233       super.close();
234       closedNum++;
235     }
236 
237     public int getClosedNum() {
238       return closedNum;
239     }
240 
241     @Override
242     public boolean realSeekDone() {
243       return realSeekDone;
244     }
245 
246     public void setRealSeekDone(boolean done) {
247       realSeekDone = done;
248     }
249 
250     @Override
251     public void enforceSeek() throws IOException {
252       throw new IOException("enforceSeek must not be called on a " + "non-lazy scanner");
253     }
254   }
255 }