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