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.regionserver;
22
23 import org.apache.hadoop.hbase.KeyValue;
24 import org.apache.hadoop.hbase.KeyValue.KVComparator;
25
26 import java.io.IOException;
27 import java.util.Comparator;
28 import java.util.List;
29 import java.util.PriorityQueue;
30
31 /**
32 * Implements a heap merge across any number of KeyValueScanners.
33 * <p>
34 * Implements KeyValueScanner itself.
35 * <p>
36 * This class is used at the Region level to merge across Stores
37 * and at the Store level to merge across the memstore and StoreFiles.
38 * <p>
39 * In the Region case, we also need InternalScanner.next(List), so this class
40 * also implements InternalScanner. WARNING: As is, if you try to use this
41 * as an InternalScanner at the Store level, you will get runtime exceptions.
42 */
43 public class KeyValueHeap implements KeyValueScanner, InternalScanner {
44 private PriorityQueue<KeyValueScanner> heap = null;
45 private KeyValueScanner current = null;
46 private KVScannerComparator comparator;
47
48 /**
49 * Constructor. This KeyValueHeap will handle closing of passed in
50 * KeyValueScanners.
51 * @param scanners
52 * @param comparator
53 */
54 public KeyValueHeap(List<? extends KeyValueScanner> scanners,
55 KVComparator comparator) {
56 this.comparator = new KVScannerComparator(comparator);
57 if (!scanners.isEmpty()) {
58 this.heap = new PriorityQueue<KeyValueScanner>(scanners.size(),
59 this.comparator);
60 for (KeyValueScanner scanner : scanners) {
61 if (scanner.peek() != null) {
62 this.heap.add(scanner);
63 } else {
64 scanner.close();
65 }
66 }
67 this.current = heap.poll();
68 }
69 }
70
71 public KeyValue peek() {
72 if (this.current == null) {
73 return null;
74 }
75 return this.current.peek();
76 }
77
78 public KeyValue next() throws IOException {
79 if(this.current == null) {
80 return null;
81 }
82 KeyValue kvReturn = this.current.next();
83 KeyValue kvNext = this.current.peek();
84 if (kvNext == null) {
85 this.current.close();
86 this.current = this.heap.poll();
87 } else {
88 KeyValueScanner topScanner = this.heap.peek();
89 if (topScanner == null ||
90 this.comparator.compare(kvNext, topScanner.peek()) > 0) {
91 this.heap.add(this.current);
92 this.current = this.heap.poll();
93 }
94 }
95 return kvReturn;
96 }
97
98 /**
99 * Gets the next row of keys from the top-most scanner.
100 * <p>
101 * This method takes care of updating the heap.
102 * <p>
103 * This can ONLY be called when you are using Scanners that implement
104 * InternalScanner as well as KeyValueScanner (a {@link StoreScanner}).
105 * @param result
106 * @param limit
107 * @return true if there are more keys, false if all scanners are done
108 */
109 public boolean next(List<KeyValue> result, int limit) throws IOException {
110 if (this.current == null) {
111 return false;
112 }
113 InternalScanner currentAsInternal = (InternalScanner)this.current;
114 boolean mayContainsMoreRows = currentAsInternal.next(result, limit);
115 KeyValue pee = this.current.peek();
116 /*
117 * By definition, any InternalScanner must return false only when it has no
118 * further rows to be fetched. So, we can close a scanner if it returns
119 * false. All existing implementations seem to be fine with this. It is much
120 * more efficient to close scanners which are not needed than keep them in
121 * the heap. This is also required for certain optimizations.
122 */
123 if (pee == null || !mayContainsMoreRows) {
124 this.current.close();
125 } else {
126 this.heap.add(this.current);
127 }
128 this.current = this.heap.poll();
129 return (this.current != null);
130 }
131
132 /**
133 * Gets the next row of keys from the top-most scanner.
134 * <p>
135 * This method takes care of updating the heap.
136 * <p>
137 * This can ONLY be called when you are using Scanners that implement
138 * InternalScanner as well as KeyValueScanner (a {@link StoreScanner}).
139 * @param result
140 * @return true if there are more keys, false if all scanners are done
141 */
142 public boolean next(List<KeyValue> result) throws IOException {
143 return next(result, -1);
144 }
145
146 private static class KVScannerComparator implements Comparator<KeyValueScanner> {
147 private KVComparator kvComparator;
148 /**
149 * Constructor
150 * @param kvComparator
151 */
152 public KVScannerComparator(KVComparator kvComparator) {
153 this.kvComparator = kvComparator;
154 }
155 public int compare(KeyValueScanner left, KeyValueScanner right) {
156 return compare(left.peek(), right.peek());
157 }
158 /**
159 * Compares two KeyValue
160 * @param left
161 * @param right
162 * @return less than 0 if left is smaller, 0 if equal etc..
163 */
164 public int compare(KeyValue left, KeyValue right) {
165 return this.kvComparator.compare(left, right);
166 }
167 /**
168 * @return KVComparator
169 */
170 public KVComparator getComparator() {
171 return this.kvComparator;
172 }
173 }
174
175 public void close() {
176 if (this.current != null) {
177 this.current.close();
178 }
179 if (this.heap != null) {
180 KeyValueScanner scanner;
181 while ((scanner = this.heap.poll()) != null) {
182 scanner.close();
183 }
184 }
185 }
186
187 /**
188 * Seeks all scanners at or below the specified seek key. If we earlied-out
189 * of a row, we may end up skipping values that were never reached yet.
190 * Rather than iterating down, we want to give the opportunity to re-seek.
191 * <p>
192 * As individual scanners may run past their ends, those scanners are
193 * automatically closed and removed from the heap.
194 * @param seekKey KeyValue to seek at or after
195 * @return true if KeyValues exist at or after specified key, false if not
196 * @throws IOException
197 */
198 public boolean seek(KeyValue seekKey) throws IOException {
199 if (this.current == null) {
200 return false;
201 }
202 this.heap.add(this.current);
203 this.current = null;
204
205 KeyValueScanner scanner;
206 while((scanner = this.heap.poll()) != null) {
207 KeyValue topKey = scanner.peek();
208 if(comparator.getComparator().compare(seekKey, topKey) <= 0) { // Correct?
209 // Top KeyValue is at-or-after Seek KeyValue
210 this.current = scanner;
211 return true;
212 }
213 if(!scanner.seek(seekKey)) {
214 scanner.close();
215 } else {
216 this.heap.add(scanner);
217 }
218 }
219 // Heap is returning empty, scanner is done
220 return false;
221 }
222
223 public boolean reseek(KeyValue seekKey) throws IOException {
224 //This function is very identical to the seek(KeyValue) function except that
225 //scanner.seek(seekKey) is changed to scanner.reseek(seekKey)
226 if (this.current == null) {
227 return false;
228 }
229 this.heap.add(this.current);
230 this.current = null;
231
232 KeyValueScanner scanner;
233 while ((scanner = this.heap.poll()) != null) {
234 KeyValue topKey = scanner.peek();
235 if (comparator.getComparator().compare(seekKey, topKey) <= 0) {
236 // Top KeyValue is at-or-after Seek KeyValue
237 this.current = scanner;
238 return true;
239 }
240 if (!scanner.reseek(seekKey)) {
241 scanner.close();
242 } else {
243 this.heap.add(scanner);
244 }
245 }
246 // Heap is returning empty, scanner is done
247 return false;
248 }
249
250 /**
251 * @return the current Heap
252 */
253 public PriorityQueue<KeyValueScanner> getHeap() {
254 return this.heap;
255 }
256 }