View Javadoc

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   /**
224    * @return the current Heap
225    */
226   public PriorityQueue<KeyValueScanner> getHeap() {
227     return this.heap;
228   }
229 }