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  package org.apache.hadoop.hbase.regionserver.compactions;
20  
21  import com.google.common.base.Function;
22  import com.google.common.base.Joiner;
23  import com.google.common.base.Preconditions;
24  import com.google.common.base.Predicate;
25  import com.google.common.collect.Collections2;
26  
27  import java.util.ArrayList;
28  import java.util.Collection;
29  
30  import org.apache.hadoop.hbase.classification.InterfaceAudience;
31  import org.apache.hadoop.hbase.classification.InterfaceStability;
32  import org.apache.hadoop.hbase.regionserver.Store;
33  import org.apache.hadoop.hbase.regionserver.StoreFile;
34  import org.apache.hadoop.hbase.regionserver.StoreFile.Reader;
35  import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
36  import org.apache.hadoop.util.StringUtils;
37  
38  /**
39   * This class holds all logical details necessary to run a compaction.
40   */
41  @InterfaceAudience.LimitedPrivate({ "coprocessor" })
42  @InterfaceStability.Evolving
43  public class CompactionRequest implements Comparable<CompactionRequest> {
44  
45    // was this compaction promoted to an off-peak
46    private boolean isOffPeak = false;
47    private enum DisplayCompactionType { MINOR, ALL_FILES, MAJOR }
48    private DisplayCompactionType isMajor = DisplayCompactionType.MINOR;
49    private int priority = Store.NO_PRIORITY;
50    private Collection<StoreFile> filesToCompact;
51    private boolean isAfterSplit = false;
52  
53    // CompactRequest object creation time.
54    private long selectionTime;
55    // System time used to compare objects in FIFO order. TODO: maybe use selectionTime?
56    private Long timeInNanos;
57    private String regionName = "";
58    private String storeName = "";
59    private long totalSize = -1L;
60  
61    /**
62     * This ctor should be used by coprocessors that want to subclass CompactionRequest.
63     */
64    public CompactionRequest() {
65      this.selectionTime = EnvironmentEdgeManager.currentTime();
66      this.timeInNanos = System.nanoTime();
67    }
68  
69    public CompactionRequest(Collection<StoreFile> files) {
70      this();
71      Preconditions.checkNotNull(files);
72      this.filesToCompact = files;
73      recalculateSize();
74    }
75  
76    public void updateFiles(Collection<StoreFile> files) {
77      this.filesToCompact = files;
78    }
79  
80    /**
81     * Called before compaction is executed by CompactSplitThread; for use by coproc subclasses.
82     */
83    public void beforeExecute() {}
84  
85    /**
86     * Called after compaction is executed by CompactSplitThread; for use by coproc subclasses.
87     */
88    public void afterExecute() {}
89  
90    /**
91     * Combines the request with other request. Coprocessors subclassing CR may override
92     * this if they want to do clever things based on CompactionPolicy selection that
93     * is passed to this method via "other". The default implementation just does a copy.
94     * @param other Request to combine with.
95     * @return The result (may be "this" or "other").
96     */
97    public CompactionRequest combineWith(CompactionRequest other) {
98      this.filesToCompact = new ArrayList<StoreFile>(other.getFiles());
99      this.isOffPeak = other.isOffPeak;
100     this.isMajor = other.isMajor;
101     this.priority = other.priority;
102     this.selectionTime = other.selectionTime;
103     this.timeInNanos = other.timeInNanos;
104     this.regionName = other.regionName;
105     this.storeName = other.storeName;
106     this.totalSize = other.totalSize;
107     recalculateSize();
108     return this;
109   }
110 
111   /**
112    * This function will define where in the priority queue the request will
113    * end up.  Those with the highest priorities will be first.  When the
114    * priorities are the same it will first compare priority then date
115    * to maintain a FIFO functionality.
116    *
117    * <p>Note: The enqueue timestamp is accurate to the nanosecond. if two
118    * requests have same timestamp then this function will break the tie
119    * arbitrarily with hashCode() comparing.
120    */
121   @Override
122   public int compareTo(CompactionRequest request) {
123     //NOTE: The head of the priority queue is the least element
124     if (this.equals(request)) {
125       return 0; //they are the same request
126     }
127     int compareVal;
128 
129     compareVal = priority - request.priority; //compare priority
130     if (compareVal != 0) {
131       return compareVal;
132     }
133 
134     compareVal = timeInNanos.compareTo(request.timeInNanos);
135     if (compareVal != 0) {
136       return compareVal;
137     }
138 
139     // break the tie based on hash code
140     return this.hashCode() - request.hashCode();
141   }
142 
143   public boolean isAfterSplit() {
144     return isAfterSplit;
145   }
146 
147   public void setAfterSplit(boolean afterSplit) {
148     isAfterSplit = afterSplit;
149   }
150 
151   @Override
152   public int hashCode() {
153     final int prime = 31;
154     int result = 1;
155     result = prime * result + ((filesToCompact == null) ? 0 : filesToCompact.hashCode());
156     result = prime * result + ((isMajor == null) ? 0 : isMajor.hashCode());
157     result = prime * result + (isOffPeak ? 1231 : 1237);
158     result = prime * result + priority;
159     result = prime * result + ((regionName == null) ? 0 : regionName.hashCode());
160     result = prime * result + (int) (selectionTime ^ (selectionTime >>> 32));
161     result = prime * result + ((storeName == null) ? 0 : storeName.hashCode());
162     result = prime * result + ((timeInNanos == null) ? 0 : timeInNanos.hashCode());
163     result = prime * result + (int) (totalSize ^ (totalSize >>> 32));
164     result = prime * result + (isAfterSplit ? 1231 : 1237);
165     return result;
166   }
167 
168   @Override
169   public boolean equals(Object obj) {
170     if (this == obj) {
171       return true;
172     }
173     if (obj == null) {
174       return false;
175     }
176     if (getClass() != obj.getClass()) {
177       return false;
178     }
179     CompactionRequest other = (CompactionRequest) obj;
180     if (filesToCompact == null) {
181       if (other.filesToCompact != null) {
182         return false;
183       }
184     } else if (!filesToCompact.equals(other.filesToCompact)) {
185       return false;
186     }
187     if (isMajor != other.isMajor) {
188       return false;
189     }
190     if (isOffPeak != other.isOffPeak) {
191       return false;
192     }
193     if (priority != other.priority) {
194       return false;
195     }
196     if (regionName == null) {
197       if (other.regionName != null) {
198         return false;
199       }
200     } else if (!regionName.equals(other.regionName)) {
201       return false;
202     }
203     if (selectionTime != other.selectionTime) {
204       return false;
205     }
206     if (storeName == null) {
207       if (other.storeName != null) {
208         return false;
209       }
210     } else if (!storeName.equals(other.storeName)) {
211       return false;
212     }
213     if (timeInNanos == null) {
214       if (other.timeInNanos != null) {
215         return false;
216       }
217     } else if (!timeInNanos.equals(other.timeInNanos)) {
218       return false;
219     }
220     if (totalSize != other.totalSize) {
221       return false;
222     }
223     if (isAfterSplit != other.isAfterSplit) {
224       return false;
225     }
226     return true;
227   }
228 
229   public Collection<StoreFile> getFiles() {
230     return this.filesToCompact;
231   }
232 
233   /**
234    * Sets the region/store name, for logging.
235    */
236   public void setDescription(String regionName, String storeName) {
237     this.regionName = regionName;
238     this.storeName = storeName;
239   }
240 
241   /** Gets the total size of all StoreFiles in compaction */
242   public long getSize() {
243     return totalSize;
244   }
245 
246   public boolean isAllFiles() {
247     return this.isMajor == DisplayCompactionType.MAJOR
248         || this.isMajor == DisplayCompactionType.ALL_FILES;
249   }
250 
251   public boolean isMajor() {
252     return this.isMajor == DisplayCompactionType.MAJOR;
253   }
254 
255   /** Gets the priority for the request */
256   public int getPriority() {
257     return priority;
258   }
259 
260   /** Sets the priority for the request */
261   public void setPriority(int p) {
262     this.priority = p;
263   }
264 
265   public boolean isOffPeak() {
266     return this.isOffPeak;
267   }
268 
269   public void setOffPeak(boolean value) {
270     this.isOffPeak = value;
271   }
272 
273   public long getSelectionTime() {
274     return this.selectionTime;
275   }
276 
277   /**
278    * Specify if this compaction should be a major compaction based on the state of the store
279    * @param isMajor <tt>true</tt> if the system determines that this compaction should be a major
280    *          compaction
281    */
282   public void setIsMajor(boolean isMajor, boolean isAllFiles) {
283     assert isAllFiles || !isMajor;
284     this.isMajor = !isAllFiles ? DisplayCompactionType.MINOR
285         : (isMajor ? DisplayCompactionType.MAJOR : DisplayCompactionType.ALL_FILES);
286   }
287 
288   @Override
289   public String toString() {
290     String fsList = Joiner.on(", ").join(
291         Collections2.transform(Collections2.filter(
292             this.getFiles(),
293             new Predicate<StoreFile>() {
294               @Override
295               public boolean apply(StoreFile sf) {
296                 return sf.getReader() != null;
297               }
298           }), new Function<StoreFile, String>() {
299             @Override
300             public String apply(StoreFile sf) {
301               return StringUtils.humanReadableInt(
302                 (sf.getReader() == null) ? 0 : sf.getReader().length());
303             }
304           }));
305 
306     return "regionName=" + regionName + ", storeName=" + storeName +
307       ", fileCount=" + this.getFiles().size() +
308       ", fileSize=" + StringUtils.humanReadableInt(totalSize) +
309         ((fsList.isEmpty()) ? "" : " (" + fsList + ")") +
310       ", priority=" + priority + ", time=" + timeInNanos;
311   }
312 
313   /**
314    * Recalculate the size of the compaction based on current files.
315    * @param files files that should be included in the compaction
316    */
317   private void recalculateSize() {
318     long sz = 0;
319     for (StoreFile sf : this.filesToCompact) {
320       Reader r = sf.getReader();
321       sz += r == null ? 0 : r.length();
322     }
323     this.totalSize = sz;
324   }
325 }
326