001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.regionserver.compactions;
019
020import static org.apache.hadoop.hbase.regionserver.StripeStoreFileManager.OPEN_KEY;
021
022import java.io.IOException;
023import java.util.ArrayList;
024import java.util.Collection;
025import java.util.List;
026import org.apache.hadoop.conf.Configuration;
027import org.apache.hadoop.fs.Path;
028import org.apache.hadoop.hbase.CellComparator;
029import org.apache.hadoop.hbase.regionserver.HStoreFile;
030import org.apache.hadoop.hbase.regionserver.StoreConfigInformation;
031import org.apache.hadoop.hbase.regionserver.StoreUtils;
032import org.apache.hadoop.hbase.regionserver.StripeStoreConfig;
033import org.apache.hadoop.hbase.regionserver.StripeStoreFlusher;
034import org.apache.hadoop.hbase.regionserver.throttle.ThroughputController;
035import org.apache.hadoop.hbase.security.User;
036import org.apache.hadoop.hbase.util.Bytes;
037import org.apache.hadoop.hbase.util.ConcatenatedLists;
038import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
039import org.apache.hadoop.hbase.util.Pair;
040import org.apache.yetus.audience.InterfaceAudience;
041import org.slf4j.Logger;
042import org.slf4j.LoggerFactory;
043
044import org.apache.hbase.thirdparty.com.google.common.collect.ImmutableList;
045
046/**
047 * Stripe store implementation of compaction policy.
048 */
049@InterfaceAudience.Private
050public class StripeCompactionPolicy extends CompactionPolicy {
051  private final static Logger LOG = LoggerFactory.getLogger(StripeCompactionPolicy.class);
052  // Policy used to compact individual stripes.
053  private ExploringCompactionPolicy stripePolicy = null;
054
055  private StripeStoreConfig config;
056
057  public StripeCompactionPolicy(Configuration conf, StoreConfigInformation storeConfigInfo,
058    StripeStoreConfig config) {
059    super(conf, storeConfigInfo);
060    this.config = config;
061    stripePolicy = new ExploringCompactionPolicy(conf, storeConfigInfo);
062  }
063
064  public List<HStoreFile> preSelectFilesForCoprocessor(StripeInformationProvider si,
065    List<HStoreFile> filesCompacting) {
066    // We sincerely hope nobody is messing with us with their coprocessors.
067    // If they do, they are very likely to shoot themselves in the foot.
068    // We'll just exclude all the filesCompacting from the list.
069    ArrayList<HStoreFile> candidateFiles = new ArrayList<>(si.getStorefiles());
070    candidateFiles.removeAll(filesCompacting);
071    return candidateFiles;
072  }
073
074  public StripeCompactionRequest createEmptyRequest(StripeInformationProvider si,
075    CompactionRequestImpl request) {
076    // Treat as L0-ish compaction with fixed set of files, and hope for the best.
077    if (si.getStripeCount() > 0) {
078      return new BoundaryStripeCompactionRequest(request, si.getStripeBoundaries());
079    }
080    Pair<Long, Integer> targetKvsAndCount =
081      estimateTargetKvs(request.getFiles(), this.config.getInitialCount());
082    return new SplitStripeCompactionRequest(request, OPEN_KEY, OPEN_KEY,
083      targetKvsAndCount.getSecond(), targetKvsAndCount.getFirst());
084  }
085
086  public StripeStoreFlusher.StripeFlushRequest selectFlush(CellComparator comparator,
087    StripeInformationProvider si, int kvCount) {
088    if (this.config.isUsingL0Flush()) {
089      // L0 is used, return dumb request.
090      return new StripeStoreFlusher.StripeFlushRequest(comparator);
091    }
092    if (si.getStripeCount() == 0) {
093      // No stripes - start with the requisite count, derive KVs per stripe.
094      int initialCount = this.config.getInitialCount();
095      return new StripeStoreFlusher.SizeStripeFlushRequest(comparator, initialCount,
096        kvCount / initialCount);
097    }
098    // There are stripes - do according to the boundaries.
099    return new StripeStoreFlusher.BoundaryStripeFlushRequest(comparator, si.getStripeBoundaries());
100  }
101
102  public StripeCompactionRequest selectCompaction(StripeInformationProvider si,
103    List<HStoreFile> filesCompacting, boolean isOffpeak) throws IOException {
104    // TODO: first cut - no parallel compactions. To have more fine grained control we
105    // probably need structure more sophisticated than a list.
106    if (!filesCompacting.isEmpty()) {
107      LOG.debug("Not selecting compaction: " + filesCompacting.size() + " files compacting");
108      return null;
109    }
110
111    // We are going to do variations of compaction in strict order of preference.
112    // A better/more advanced approach is to use a heuristic to see which one is "more
113    // necessary" at current time.
114
115    // This can happen due to region split. We can skip it later; for now preserve
116    // compact-all-things behavior.
117    Collection<HStoreFile> allFiles = si.getStorefiles();
118    if (StoreUtils.hasReferences(allFiles)) {
119      LOG.debug("There are references in the store; compacting all files");
120      long targetKvs = estimateTargetKvs(allFiles, config.getInitialCount()).getFirst();
121      SplitStripeCompactionRequest request =
122        new SplitStripeCompactionRequest(allFiles, OPEN_KEY, OPEN_KEY, targetKvs);
123      request.setMajorRangeFull();
124      request.getRequest().setAfterSplit(true);
125      return request;
126    }
127
128    int stripeCount = si.getStripeCount();
129    List<HStoreFile> l0Files = si.getLevel0Files();
130
131    // See if we need to make new stripes.
132    boolean shouldCompactL0 =
133      this.config.getLevel0MinFiles() <= l0Files.size() || allL0FilesExpired(si);
134    if (stripeCount == 0) {
135      if (!shouldCompactL0) {
136        return null; // nothing to do.
137      }
138      return selectL0OnlyCompaction(si);
139    }
140
141    boolean canDropDeletesNoL0 = l0Files.isEmpty();
142    if (shouldCompactL0) {
143      if (!canDropDeletesNoL0) {
144        // If we need to compact L0, see if we can add something to it, and drop deletes.
145        StripeCompactionRequest result =
146          selectSingleStripeCompaction(si, !shouldSelectL0Files(si), canDropDeletesNoL0, isOffpeak);
147        if (result != null) {
148          return result;
149        }
150      }
151      LOG.debug("Selecting L0 compaction with " + l0Files.size() + " files");
152      return selectL0OnlyCompaction(si);
153    }
154
155    // Try to delete fully expired stripes
156    StripeCompactionRequest result = selectExpiredMergeCompaction(si, canDropDeletesNoL0);
157    if (result != null) {
158      return result;
159    }
160
161    // Ok, nothing special here, let's see if we need to do a common compaction.
162    // This will also split the stripes that are too big if needed.
163    return selectSingleStripeCompaction(si, false, canDropDeletesNoL0, isOffpeak);
164  }
165
166  public boolean needsCompactions(StripeInformationProvider si, List<HStoreFile> filesCompacting) {
167    // Approximation on whether we need compaction.
168    return filesCompacting.isEmpty() && (StoreUtils.hasReferences(si.getStorefiles())
169      || (si.getLevel0Files().size() >= this.config.getLevel0MinFiles())
170      || needsSingleStripeCompaction(si) || hasExpiredStripes(si) || allL0FilesExpired(si));
171  }
172
173  @Override
174  public boolean shouldPerformMajorCompaction(Collection<HStoreFile> filesToCompact)
175    throws IOException {
176    return false; // there's never a major compaction!
177  }
178
179  @Override
180  public boolean throttleCompaction(long compactionSize) {
181    return compactionSize > comConf.getThrottlePoint();
182  }
183
184  /**
185   * @param si StoreFileManager.
186   * @return Whether any stripe potentially needs compaction.
187   */
188  protected boolean needsSingleStripeCompaction(StripeInformationProvider si) {
189    int minFiles = this.config.getStripeCompactMinFiles();
190    for (List<HStoreFile> stripe : si.getStripes()) {
191      if (stripe.size() >= minFiles) return true;
192    }
193    return false;
194  }
195
196  protected StripeCompactionRequest selectSingleStripeCompaction(StripeInformationProvider si,
197    boolean includeL0, boolean canDropDeletesWithoutL0, boolean isOffpeak) throws IOException {
198    ArrayList<ImmutableList<HStoreFile>> stripes = si.getStripes();
199
200    int bqIndex = -1;
201    List<HStoreFile> bqSelection = null;
202    int stripeCount = stripes.size();
203    long bqTotalSize = -1;
204    for (int i = 0; i < stripeCount; ++i) {
205      // If we want to compact L0 to drop deletes, we only want whole-stripe compactions.
206      // So, pass includeL0 as 2nd parameter to indicate that.
207      List<HStoreFile> selection = selectSimpleCompaction(stripes.get(i),
208        !canDropDeletesWithoutL0 && includeL0, isOffpeak, false);
209      if (selection.isEmpty()) continue;
210      long size = 0;
211      for (HStoreFile sf : selection) {
212        size += sf.getReader().length();
213      }
214      if (
215        bqSelection == null || selection.size() > bqSelection.size()
216          || (selection.size() == bqSelection.size() && size < bqTotalSize)
217      ) {
218        bqSelection = selection;
219        bqIndex = i;
220        bqTotalSize = size;
221      }
222    }
223    if (bqSelection == null) {
224      LOG.debug("No good compaction is possible in any stripe");
225      return null;
226    }
227    List<HStoreFile> filesToCompact = new ArrayList<>(bqSelection);
228    // See if we can, and need to, split this stripe.
229    int targetCount = 1;
230    long targetKvs = Long.MAX_VALUE;
231    boolean hasAllFiles = filesToCompact.size() == stripes.get(bqIndex).size();
232    String splitString = "";
233    if (hasAllFiles && bqTotalSize >= config.getSplitSize()) {
234      if (includeL0) {
235        // We want to avoid the scenario where we compact a stripe w/L0 and then split it.
236        // So, if we might split, don't compact the stripe with L0.
237        return null;
238      }
239      Pair<Long, Integer> kvsAndCount = estimateTargetKvs(filesToCompact, config.getSplitCount());
240      targetKvs = kvsAndCount.getFirst();
241      targetCount = kvsAndCount.getSecond();
242      splitString = "; the stripe will be split into at most " + targetCount + " stripes with "
243        + targetKvs + " target KVs";
244    }
245
246    LOG.debug("Found compaction in a stripe with end key [" + Bytes.toString(si.getEndRow(bqIndex))
247      + "], with " + filesToCompact.size() + " files of total size " + bqTotalSize + splitString);
248
249    // See if we can drop deletes.
250    StripeCompactionRequest req;
251    if (includeL0) {
252      assert hasAllFiles;
253      List<HStoreFile> l0Files = si.getLevel0Files();
254      LOG.debug("Adding " + l0Files.size() + " files to compaction to be able to drop deletes");
255      ConcatenatedLists<HStoreFile> sfs = new ConcatenatedLists<>();
256      sfs.addSublist(filesToCompact);
257      sfs.addSublist(l0Files);
258      req = new BoundaryStripeCompactionRequest(sfs, si.getStripeBoundaries());
259    } else {
260      req = new SplitStripeCompactionRequest(filesToCompact, si.getStartRow(bqIndex),
261        si.getEndRow(bqIndex), targetCount, targetKvs);
262    }
263    if (hasAllFiles && (canDropDeletesWithoutL0 || includeL0)) {
264      req.setMajorRange(si.getStartRow(bqIndex), si.getEndRow(bqIndex));
265    }
266    req.getRequest().setOffPeak(isOffpeak);
267    return req;
268  }
269
270  /**
271   * Selects the compaction of a single stripe using default policy.
272   * @param sfs          Files.
273   * @param allFilesOnly Whether a compaction of all-or-none files is needed.
274   * @return The resulting selection.
275   */
276  private List<HStoreFile> selectSimpleCompaction(List<HStoreFile> sfs, boolean allFilesOnly,
277    boolean isOffpeak, boolean forceCompact) {
278    int minFilesLocal =
279      Math.max(allFilesOnly ? sfs.size() : 0, this.config.getStripeCompactMinFiles());
280    int maxFilesLocal = Math.max(this.config.getStripeCompactMaxFiles(), minFilesLocal);
281    List<HStoreFile> selected =
282      stripePolicy.applyCompactionPolicy(sfs, false, isOffpeak, minFilesLocal, maxFilesLocal);
283    if (forceCompact && (selected == null || selected.isEmpty()) && !sfs.isEmpty()) {
284      return stripePolicy.selectCompactFiles(sfs, maxFilesLocal, isOffpeak);
285    }
286    return selected;
287  }
288
289  private boolean shouldSelectL0Files(StripeInformationProvider si) {
290    return si.getLevel0Files().size() > this.config.getStripeCompactMaxFiles()
291      || getTotalFileSize(si.getLevel0Files()) > comConf.getMaxCompactSize();
292  }
293
294  private StripeCompactionRequest selectL0OnlyCompaction(StripeInformationProvider si) {
295    List<HStoreFile> l0Files = si.getLevel0Files();
296    List<HStoreFile> selectedFiles = l0Files;
297    if (shouldSelectL0Files(si)) {
298      selectedFiles = selectSimpleCompaction(l0Files, false, false, true);
299      assert !selectedFiles.isEmpty() : "Selected L0 files should not be empty";
300    }
301    StripeCompactionRequest request;
302    if (si.getStripeCount() == 0) {
303      Pair<Long, Integer> estimate = estimateTargetKvs(selectedFiles, config.getInitialCount());
304      long targetKvs = estimate.getFirst();
305      int targetCount = estimate.getSecond();
306      request =
307        new SplitStripeCompactionRequest(selectedFiles, OPEN_KEY, OPEN_KEY, targetCount, targetKvs);
308      if (selectedFiles.size() == l0Files.size()) {
309        ((SplitStripeCompactionRequest) request).setMajorRangeFull(); // L0 only, can drop deletes.
310      }
311      LOG.debug("Creating {} initial stripes with {} kvs each via L0 compaction of {}/{} files",
312        targetCount, targetKvs, selectedFiles.size(), l0Files.size());
313    } else {
314      request = new BoundaryStripeCompactionRequest(selectedFiles, si.getStripeBoundaries());
315      LOG.debug("Boundary L0 compaction of {}/{} files", selectedFiles.size(), l0Files.size());
316    }
317    return request;
318  }
319
320  private StripeCompactionRequest selectExpiredMergeCompaction(StripeInformationProvider si,
321    boolean canDropDeletesNoL0) {
322    long cfTtl = this.storeConfigInfo.getStoreFileTtl();
323    if (cfTtl == Long.MAX_VALUE) {
324      return null; // minversion might be set, cannot delete old files
325    }
326    long timestampCutoff = EnvironmentEdgeManager.currentTime() - cfTtl;
327    // Merge the longest sequence of stripes where all files have expired, if any.
328    int start = -1, bestStart = -1, length = 0, bestLength = 0;
329    ArrayList<ImmutableList<HStoreFile>> stripes = si.getStripes();
330    OUTER: for (int i = 0; i < stripes.size(); ++i) {
331      for (HStoreFile storeFile : stripes.get(i)) {
332        if (storeFile.getReader().getMaxTimestamp() < timestampCutoff) continue;
333        // Found non-expired file, this stripe has to stay.
334        if (length > bestLength) {
335          bestStart = start;
336          bestLength = length;
337        }
338        start = -1;
339        length = 0;
340        continue OUTER;
341      }
342      if (start == -1) {
343        start = i;
344      }
345      ++length;
346    }
347    if (length > bestLength) {
348      bestStart = start;
349      bestLength = length;
350    }
351    if (bestLength == 0) return null;
352    if (bestLength == 1) {
353      // This is currently inefficient. If only one stripe expired, we will rewrite some
354      // entire stripe just to delete some expired files because we rely on metadata and it
355      // cannot simply be updated in an old file. When we either determine stripe dynamically
356      // or move metadata to manifest, we can just drop the "expired stripes".
357      if (bestStart == (stripes.size() - 1)) return null;
358      ++bestLength;
359    }
360    LOG.debug("Merging " + bestLength + " stripes to delete expired store files");
361    int endIndex = bestStart + bestLength - 1;
362    ConcatenatedLists<HStoreFile> sfs = new ConcatenatedLists<>();
363    sfs.addAllSublists(stripes.subList(bestStart, endIndex + 1));
364    SplitStripeCompactionRequest result = new SplitStripeCompactionRequest(sfs,
365      si.getStartRow(bestStart), si.getEndRow(endIndex), 1, Long.MAX_VALUE);
366    if (canDropDeletesNoL0) {
367      result.setMajorRangeFull();
368    }
369    return result;
370  }
371
372  protected boolean hasExpiredStripes(StripeInformationProvider si) {
373    // Find if exists a stripe where all files have expired, if any.
374    ArrayList<ImmutableList<HStoreFile>> stripes = si.getStripes();
375    for (ImmutableList<HStoreFile> stripe : stripes) {
376      if (allFilesExpired(stripe)) {
377        return true;
378      }
379    }
380    return false;
381  }
382
383  protected boolean allL0FilesExpired(StripeInformationProvider si) {
384    return allFilesExpired(si.getLevel0Files());
385  }
386
387  private boolean allFilesExpired(final List<HStoreFile> storeFiles) {
388    if (storeFiles == null || storeFiles.isEmpty()) {
389      return false;
390    }
391    long cfTtl = this.storeConfigInfo.getStoreFileTtl();
392    if (cfTtl == Long.MAX_VALUE) {
393      return false; // minversion might be set, cannot delete old files
394    }
395    long timestampCutoff = EnvironmentEdgeManager.currentTime() - cfTtl;
396    for (HStoreFile storeFile : storeFiles) {
397      // Check store file is not empty and has not expired
398      if (
399        storeFile.getReader().getMaxTimestamp() >= timestampCutoff
400          && storeFile.getReader().getEntries() != 0
401      ) {
402        return false;
403      }
404    }
405    return true;
406  }
407
408  private static long getTotalKvCount(final Collection<HStoreFile> candidates) {
409    long totalSize = 0;
410    for (HStoreFile storeFile : candidates) {
411      totalSize += storeFile.getReader().getEntries();
412    }
413    return totalSize;
414  }
415
416  public static long getTotalFileSize(final Collection<HStoreFile> candidates) {
417    long totalSize = 0;
418    for (HStoreFile storeFile : candidates) {
419      totalSize += storeFile.getReader().length();
420    }
421    return totalSize;
422  }
423
424  private Pair<Long, Integer> estimateTargetKvs(Collection<HStoreFile> files, double splitCount) {
425    // If the size is larger than what we target, we don't want to split into proportionally
426    // larger parts and then have to split again very soon. So, we will increase the multiplier
427    // by one until we get small enough parts. E.g. 5Gb stripe that should have been split into
428    // 2 parts when it was 3Gb will be split into 3x1.67Gb parts, rather than 2x2.5Gb parts.
429    long totalSize = getTotalFileSize(files);
430    long targetPartSize = config.getSplitPartSize();
431    assert targetPartSize > 0 && splitCount > 0;
432    double ratio = totalSize / (splitCount * targetPartSize); // ratio of real to desired size
433    while (ratio > 1.0) {
434      // Ratio of real to desired size if we increase the multiplier.
435      double newRatio = totalSize / ((splitCount + 1.0) * targetPartSize);
436      if ((1.0 / newRatio) >= ratio) break; // New ratio is < 1.0, but further than the last one.
437      ratio = newRatio;
438      splitCount += 1.0;
439    }
440    long kvCount = (long) (getTotalKvCount(files) / splitCount);
441    return new Pair<>(kvCount, (int) Math.ceil(splitCount));
442  }
443
444  /** Stripe compaction request wrapper. */
445  public abstract static class StripeCompactionRequest {
446    protected CompactionRequestImpl request;
447    protected byte[] majorRangeFromRow = null, majorRangeToRow = null;
448
449    public List<Path> execute(StripeCompactor compactor, ThroughputController throughputController)
450      throws IOException {
451      return execute(compactor, throughputController, null);
452    }
453
454    /**
455     * Executes the request against compactor (essentially, just calls correct overload of compact
456     * method), to simulate more dynamic dispatch.
457     * @param compactor Compactor.
458     * @return result of compact(...)
459     */
460    public abstract List<Path> execute(StripeCompactor compactor,
461      ThroughputController throughputController, User user) throws IOException;
462
463    public StripeCompactionRequest(CompactionRequestImpl request) {
464      this.request = request;
465    }
466
467    /**
468     * Sets compaction "major range". Major range is the key range for which all the files are
469     * included, so they can be treated like major-compacted files.
470     * @param startRow Left boundary, inclusive.
471     * @param endRow   Right boundary, exclusive.
472     */
473    public void setMajorRange(byte[] startRow, byte[] endRow) {
474      this.majorRangeFromRow = startRow;
475      this.majorRangeToRow = endRow;
476    }
477
478    public CompactionRequestImpl getRequest() {
479      return this.request;
480    }
481
482    public void setRequest(CompactionRequestImpl request) {
483      assert request != null;
484      this.request = request;
485      this.majorRangeFromRow = this.majorRangeToRow = null;
486    }
487  }
488
489  /**
490   * Request for stripe compactor that will cause it to split the source files into several separate
491   * files at the provided boundaries.
492   */
493  private static class BoundaryStripeCompactionRequest extends StripeCompactionRequest {
494    private final List<byte[]> targetBoundaries;
495
496    /**
497     * @param request          Original request.
498     * @param targetBoundaries New files should be written with these boundaries.
499     */
500    public BoundaryStripeCompactionRequest(CompactionRequestImpl request,
501      List<byte[]> targetBoundaries) {
502      super(request);
503      this.targetBoundaries = targetBoundaries;
504    }
505
506    public BoundaryStripeCompactionRequest(Collection<HStoreFile> files,
507      List<byte[]> targetBoundaries) {
508      this(new CompactionRequestImpl(files), targetBoundaries);
509    }
510
511    @Override
512    public List<Path> execute(StripeCompactor compactor, ThroughputController throughputController,
513      User user) throws IOException {
514      return compactor.compact(this.request, this.targetBoundaries, this.majorRangeFromRow,
515        this.majorRangeToRow, throughputController, user);
516    }
517  }
518
519  /**
520   * Request for stripe compactor that will cause it to split the source files into several separate
521   * files into based on key-value count, as well as file count limit. Most of the files will be
522   * roughly the same size. The last file may be smaller or larger depending on the interplay of the
523   * amount of data and maximum number of files allowed.
524   */
525  private static class SplitStripeCompactionRequest extends StripeCompactionRequest {
526    private final byte[] startRow, endRow;
527    private final int targetCount;
528    private final long targetKvs;
529
530    /**
531     * @param request     Original request.
532     * @param startRow    Left boundary of the range to compact, inclusive.
533     * @param endRow      Right boundary of the range to compact, exclusive.
534     * @param targetCount The maximum number of stripe to compact into.
535     * @param targetKvs   The KV count of each segment. If targetKvs*targetCount is less than total
536     *                    number of kvs, all the overflow data goes into the last stripe.
537     */
538    public SplitStripeCompactionRequest(CompactionRequestImpl request, byte[] startRow,
539      byte[] endRow, int targetCount, long targetKvs) {
540      super(request);
541      this.startRow = startRow;
542      this.endRow = endRow;
543      this.targetCount = targetCount;
544      this.targetKvs = targetKvs;
545    }
546
547    public SplitStripeCompactionRequest(Collection<HStoreFile> files, byte[] startRow,
548      byte[] endRow, long targetKvs) {
549      this(files, startRow, endRow, Integer.MAX_VALUE, targetKvs);
550    }
551
552    public SplitStripeCompactionRequest(Collection<HStoreFile> files, byte[] startRow,
553      byte[] endRow, int targetCount, long targetKvs) {
554      this(new CompactionRequestImpl(files), startRow, endRow, targetCount, targetKvs);
555    }
556
557    @Override
558    public List<Path> execute(StripeCompactor compactor, ThroughputController throughputController,
559      User user) throws IOException {
560      return compactor.compact(this.request, this.targetCount, this.targetKvs, this.startRow,
561        this.endRow, this.majorRangeFromRow, this.majorRangeToRow, throughputController, user);
562    }
563
564    /**
565     * Set major range of the compaction to the entire compaction range. See
566     * {@link #setMajorRange(byte[], byte[])}.
567     */
568    public void setMajorRangeFull() {
569      setMajorRange(this.startRow, this.endRow);
570    }
571  }
572
573  /** The information about stripes that the policy needs to do its stuff */
574  public static interface StripeInformationProvider {
575    public Collection<HStoreFile> getStorefiles();
576
577    /**
578     * Gets the start row for a given stripe.
579     * @param stripeIndex Stripe index.
580     * @return Start row. May be an open key.
581     */
582    public byte[] getStartRow(int stripeIndex);
583
584    /**
585     * Gets the end row for a given stripe.
586     * @param stripeIndex Stripe index.
587     * @return End row. May be an open key.
588     */
589    public byte[] getEndRow(int stripeIndex);
590
591    /**
592     * @return Level 0 files.
593     */
594    public List<HStoreFile> getLevel0Files();
595
596    /**
597     * @return All stripe boundaries; including the open ones on both ends.
598     */
599    public List<byte[]> getStripeBoundaries();
600
601    /**
602     * @return The stripes.
603     */
604    public ArrayList<ImmutableList<HStoreFile>> getStripes();
605
606    /**
607     * @return Stripe count.
608     */
609    public int getStripeCount();
610  }
611}