1   /*
2    * Copyright 2009 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.regionserver;
22  
23  import java.io.IOException;
24  import java.util.ArrayList;
25  import java.util.Collections;
26  import java.util.Iterator;
27  import java.util.List;
28  
29  import org.apache.hadoop.hbase.HBaseTestCase;
30  import org.apache.hadoop.hbase.HConstants;
31  import org.apache.hadoop.hbase.KeyValue;
32  import org.apache.hadoop.hbase.util.Bytes;
33  
34  
35  public class TestKeyValueHeap extends HBaseTestCase {
36    private static final boolean PRINT = false;
37  
38    List<KeyValueScanner> scanners = new ArrayList<KeyValueScanner>();
39  
40    private byte[] row1;
41    private byte[] fam1;
42    private byte[] col1;
43    private byte[] data;
44  
45    private byte[] row2;
46    private byte[] fam2;
47    private byte[] col2;
48  
49    private byte[] col3;
50    private byte[] col4;
51    private byte[] col5;
52  
53    public void setUp() throws Exception {
54      super.setUp();
55      data = Bytes.toBytes("data");
56      row1 = Bytes.toBytes("row1");
57      fam1 = Bytes.toBytes("fam1");
58      col1 = Bytes.toBytes("col1");
59      row2 = Bytes.toBytes("row2");
60      fam2 = Bytes.toBytes("fam2");
61      col2 = Bytes.toBytes("col2");
62      col3 = Bytes.toBytes("col3");
63      col4 = Bytes.toBytes("col4");
64      col5 = Bytes.toBytes("col5");
65    }
66  
67    public void testSorted() throws IOException{
68      //Cases that need to be checked are:
69      //1. The "smallest" KeyValue is in the same scanners as current
70      //2. Current scanner gets empty
71  
72      List<KeyValue> l1 = new ArrayList<KeyValue>();
73      l1.add(new KeyValue(row1, fam1, col5, data));
74      l1.add(new KeyValue(row2, fam1, col1, data));
75      l1.add(new KeyValue(row2, fam1, col2, data));
76      scanners.add(new Scanner(l1));
77  
78      List<KeyValue> l2 = new ArrayList<KeyValue>();
79      l2.add(new KeyValue(row1, fam1, col1, data));
80      l2.add(new KeyValue(row1, fam1, col2, data));
81      scanners.add(new Scanner(l2));
82  
83      List<KeyValue> l3 = new ArrayList<KeyValue>();
84      l3.add(new KeyValue(row1, fam1, col3, data));
85      l3.add(new KeyValue(row1, fam1, col4, data));
86      l3.add(new KeyValue(row1, fam2, col1, data));
87      l3.add(new KeyValue(row1, fam2, col2, data));
88      l3.add(new KeyValue(row2, fam1, col3, data));
89      scanners.add(new Scanner(l3));
90  
91      List<KeyValue> expected = new ArrayList<KeyValue>();
92      expected.add(new KeyValue(row1, fam1, col1, data));
93      expected.add(new KeyValue(row1, fam1, col2, data));
94      expected.add(new KeyValue(row1, fam1, col3, data));
95      expected.add(new KeyValue(row1, fam1, col4, data));
96      expected.add(new KeyValue(row1, fam1, col5, data));
97      expected.add(new KeyValue(row1, fam2, col1, data));
98      expected.add(new KeyValue(row1, fam2, col2, data));
99      expected.add(new KeyValue(row2, fam1, col1, data));
100     expected.add(new KeyValue(row2, fam1, col2, data));
101     expected.add(new KeyValue(row2, fam1, col3, data));
102 
103     //Creating KeyValueHeap
104     KeyValueHeap kvh =
105       new KeyValueHeap(scanners, KeyValue.COMPARATOR);
106 
107     List<KeyValue> actual = new ArrayList<KeyValue>();
108     while(kvh.peek() != null){
109       actual.add(kvh.next());
110     }
111 
112     assertEquals(expected.size(), actual.size());
113     for(int i=0; i<expected.size(); i++){
114       assertEquals(expected.get(i), actual.get(i));
115       if(PRINT){
116         System.out.println("expected " +expected.get(i)+
117             "\nactual   " +actual.get(i) +"\n");
118       }
119     }
120 
121     //Check if result is sorted according to Comparator
122     for(int i=0; i<actual.size()-1; i++){
123       int ret = KeyValue.COMPARATOR.compare(actual.get(i), actual.get(i+1));
124       assertTrue(ret < 0);
125     }
126 
127   }
128 
129   public void testSeek() throws IOException {
130     //Cases:
131     //1. Seek KeyValue that is not in scanner
132     //2. Check that smallest that is returned from a seek is correct
133 
134     List<KeyValue> l1 = new ArrayList<KeyValue>();
135     l1.add(new KeyValue(row1, fam1, col5, data));
136     l1.add(new KeyValue(row2, fam1, col1, data));
137     l1.add(new KeyValue(row2, fam1, col2, data));
138     scanners.add(new Scanner(l1));
139 
140     List<KeyValue> l2 = new ArrayList<KeyValue>();
141     l2.add(new KeyValue(row1, fam1, col1, data));
142     l2.add(new KeyValue(row1, fam1, col2, data));
143     scanners.add(new Scanner(l2));
144 
145     List<KeyValue> l3 = new ArrayList<KeyValue>();
146     l3.add(new KeyValue(row1, fam1, col3, data));
147     l3.add(new KeyValue(row1, fam1, col4, data));
148     l3.add(new KeyValue(row1, fam2, col1, data));
149     l3.add(new KeyValue(row1, fam2, col2, data));
150     l3.add(new KeyValue(row2, fam1, col3, data));
151     scanners.add(new Scanner(l3));
152 
153     List<KeyValue> expected = new ArrayList<KeyValue>();
154     expected.add(new KeyValue(row2, fam1, col1, data));
155 
156     //Creating KeyValueHeap
157     KeyValueHeap kvh =
158       new KeyValueHeap(scanners, KeyValue.COMPARATOR);
159 
160     KeyValue seekKv = new KeyValue(row2, fam1, null, null);
161     kvh.seek(seekKv);
162 
163     List<KeyValue> actual = new ArrayList<KeyValue>();
164     actual.add(kvh.peek());
165 
166     assertEquals(expected.size(), actual.size());
167     for(int i=0; i<expected.size(); i++){
168       assertEquals(expected.get(i), actual.get(i));
169       if(PRINT){
170         System.out.println("expected " +expected.get(i)+
171             "\nactual   " +actual.get(i) +"\n");
172       }
173     }
174 
175   }
176 
177   public void testScannerLeak() throws IOException {
178     // Test for unclosed scanners (HBASE-1927)
179 
180     List<KeyValue> l1 = new ArrayList<KeyValue>();
181     l1.add(new KeyValue(row1, fam1, col5, data));
182     l1.add(new KeyValue(row2, fam1, col1, data));
183     l1.add(new KeyValue(row2, fam1, col2, data));
184     scanners.add(new Scanner(l1));
185 
186     List<KeyValue> l2 = new ArrayList<KeyValue>();
187     l2.add(new KeyValue(row1, fam1, col1, data));
188     l2.add(new KeyValue(row1, fam1, col2, data));
189     scanners.add(new Scanner(l2));
190 
191     List<KeyValue> l3 = new ArrayList<KeyValue>();
192     l3.add(new KeyValue(row1, fam1, col3, data));
193     l3.add(new KeyValue(row1, fam1, col4, data));
194     l3.add(new KeyValue(row1, fam2, col1, data));
195     l3.add(new KeyValue(row1, fam2, col2, data));
196     l3.add(new KeyValue(row2, fam1, col3, data));
197     scanners.add(new Scanner(l3));
198 
199     List<KeyValue> l4 = new ArrayList<KeyValue>();
200     scanners.add(new Scanner(l4));
201 
202     //Creating KeyValueHeap
203     KeyValueHeap kvh = new KeyValueHeap(scanners, KeyValue.COMPARATOR);
204 
205     while(kvh.next() != null);
206 
207     for(KeyValueScanner scanner : scanners) {
208       assertTrue(((Scanner)scanner).isClosed());
209     }
210   }
211 
212   private static class Scanner implements KeyValueScanner {
213     private Iterator<KeyValue> iter;
214     private KeyValue current;
215     private boolean closed = false;
216 
217     public Scanner(List<KeyValue> list) {
218       Collections.sort(list, KeyValue.COMPARATOR);
219       iter = list.iterator();
220       if(iter.hasNext()){
221         current = iter.next();
222       }
223     }
224 
225     public KeyValue peek() {
226       return current;
227     }
228 
229     public KeyValue next() {
230       KeyValue oldCurrent = current;
231       if(iter.hasNext()){
232         current = iter.next();
233       } else {
234         current = null;
235       }
236       return oldCurrent;
237     }
238 
239     public void close(){
240       closed = true;
241     }
242 
243     public boolean isClosed() {
244       return closed;
245     }
246 
247     public boolean seek(KeyValue seekKv) {
248       while(iter.hasNext()){
249         KeyValue next = iter.next();
250         int ret = KeyValue.COMPARATOR.compare(next, seekKv);
251         if(ret >= 0){
252           current = next;
253           return true;
254         }
255       }
256       return false;
257     }
258   }
259 
260 }