You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by ns...@apache.org on 2011/10/11 21:12:48 UTC

svn commit: r1182029 - in /hbase/branches/0.89/src: main/java/org/apache/hadoop/hbase/regionserver/ main/java/org/apache/hadoop/hbase/regionserver/compactions/ test/java/org/apache/hadoop/hbase/regionserver/

Author: nspiegelberg
Date: Tue Oct 11 19:12:47 2011
New Revision: 1182029

URL: http://svn.apache.org/viewvc?rev=1182029&view=rev
Log:
HBASE-4463 Run more aggressive compactions during off peak hours

Summary:
Increases the compact selection ratio from 1.3 to 5 at off-peak hours. This
will help utilize the available iops and bandwidth to decrease average num of
files per store. Only one such aggressive compaction is queued per store at any
point.

Test Plan: Started running the unit tests.

Reviewers: kannan, nspiegelberg, mbautin

Reviewed By: kannan

CC: hbase-eng@lists, hbase@lists, nspiegelberg, kannan, kranganathan

Differential Revision: 331843

Revert Plan: OK

Added:
    hbase/branches/0.89/src/main/java/org/apache/hadoop/hbase/regionserver/compactions/CompactSelection.java
Modified:
    hbase/branches/0.89/src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java
    hbase/branches/0.89/src/main/java/org/apache/hadoop/hbase/regionserver/Store.java
    hbase/branches/0.89/src/main/java/org/apache/hadoop/hbase/regionserver/compactions/CompactionRequest.java
    hbase/branches/0.89/src/test/java/org/apache/hadoop/hbase/regionserver/TestCompactSelection.java

Modified: hbase/branches/0.89/src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.89/src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java?rev=1182029&r1=1182028&r2=1182029&view=diff
==============================================================================
--- hbase/branches/0.89/src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java (original)
+++ hbase/branches/0.89/src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java Tue Oct 11 19:12:47 2011
@@ -1065,7 +1065,7 @@ public class HRegion implements HeapSize
           }
         }
         LOG.info("Starting compaction on " + cr.getStore() + " in region "
-            + this);
+            + this + (cr.getFiles().isOffPeakCompaction()?" as an off-peak compaction":""));
         doRegionCompactionPrep();
         boolean completed = false;
         try {

Modified: hbase/branches/0.89/src/main/java/org/apache/hadoop/hbase/regionserver/Store.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.89/src/main/java/org/apache/hadoop/hbase/regionserver/Store.java?rev=1182029&r1=1182028&r2=1182029&view=diff
==============================================================================
--- hbase/branches/0.89/src/main/java/org/apache/hadoop/hbase/regionserver/Store.java (original)
+++ hbase/branches/0.89/src/main/java/org/apache/hadoop/hbase/regionserver/Store.java Tue Oct 11 19:12:47 2011
@@ -39,7 +39,6 @@ import org.apache.hadoop.fs.FileStatus;
 import org.apache.hadoop.fs.FileSystem;
 import org.apache.hadoop.fs.FileUtil;
 import org.apache.hadoop.fs.Path;
-import org.apache.hadoop.util.StringUtils;
 import org.apache.hadoop.hbase.HColumnDescriptor;
 import org.apache.hadoop.hbase.HConstants;
 import org.apache.hadoop.hbase.HRegionInfo;
@@ -52,6 +51,7 @@ import org.apache.hadoop.hbase.io.hfile.
 import org.apache.hadoop.hbase.io.hfile.HFileScanner;
 import org.apache.hadoop.hbase.monitoring.MonitoredTask;
 import org.apache.hadoop.hbase.monitoring.TaskMonitor;
+import org.apache.hadoop.hbase.regionserver.compactions.CompactSelection;
 import org.apache.hadoop.hbase.regionserver.compactions.CompactionRequest;
 import org.apache.hadoop.hbase.util.Bytes;
 import org.apache.hadoop.hbase.util.ClassSize;
@@ -103,9 +103,6 @@ public class Store implements HeapSize {
   private final int maxFilesToCompact;
   private final long minCompactSize;
   private final long maxCompactSize;
-  // compactRatio: double on purpose!  Float.MAX < Long.MAX < Double.MAX
-  // With float, java will downcast your long to float for comparisons (bad)
-  private double compactRatio;
   private long lastCompactSize = 0;
   volatile boolean forceMajor = false;
   /* how many bytes to write between status checks */
@@ -208,7 +205,6 @@ public class Store implements HeapSize {
       this.region.memstoreFlushSize);
     this.maxCompactSize
       = conf.getLong("hbase.hstore.compaction.max.size", Long.MAX_VALUE);
-    this.compactRatio = conf.getFloat("hbase.hstore.compaction.ratio", 1.2F);
 
     if (Store.closeCheckInterval == 0) {
       Store.closeCheckInterval = conf.getInt(
@@ -881,7 +877,7 @@ public class Store implements HeapSize {
           Preconditions.checkArgument(idx != -1);
           candidates.subList(0, idx + 1).clear();
         }
-        List<StoreFile> filesToCompact = compactSelection(candidates);
+        CompactSelection filesToCompact = compactSelection(candidates);
 
         // no files to compact
         if (filesToCompact.isEmpty()) {
@@ -919,6 +915,7 @@ public class Store implements HeapSize {
   }
 
   public void finishRequest(CompactionRequest cr) {
+    cr.finishRequest();
     synchronized (filesCompacting) {
       filesCompacting.removeAll(cr.getFiles());
     }
@@ -943,7 +940,7 @@ public class Store implements HeapSize {
    * @return subset copy of candidate list that meets compaction criteria
    * @throws IOException
    */
-  List<StoreFile> compactSelection(List<StoreFile> candidates)
+  CompactSelection compactSelection(List<StoreFile> candidates)
       throws IOException {
     // ASSUMPTION!!! filesCompacting is locked when calling this function
 
@@ -958,7 +955,7 @@ public class Store implements HeapSize {
      *    | |  | |  | |  | | | | | |
      *    | |  | |  | |  | | | | | |
      */
-    List<StoreFile> filesToCompact = new ArrayList<StoreFile>(candidates);
+    CompactSelection filesToCompact = new CompactSelection(conf, candidates);
 
     boolean forcemajor = this.forceMajor && filesCompacting.isEmpty();
     if (!forcemajor) {
@@ -968,11 +965,12 @@ public class Store implements HeapSize {
       while (pos < filesToCompact.size() &&
              filesToCompact.get(pos).getReader().length() > maxCompactSize &&
              !filesToCompact.get(pos).isReference()) ++pos;
-      filesToCompact.subList(0, pos).clear();
+      filesToCompact.clearSubList(0, pos);
     }
 
     if (filesToCompact.isEmpty()) {
       LOG.debug(this.storeNameStr + ": no store files to compact");
+      filesToCompact.emptyFileList();
       return filesToCompact;
     }
 
@@ -983,7 +981,7 @@ public class Store implements HeapSize {
     if (!majorcompaction && !hasReferences(filesToCompact)) {
       // we're doing a minor compaction, let's see what files are applicable
       int start = 0;
-      double r = this.compactRatio;
+      double r = filesToCompact.getCompactSelectionRatio();
 
       // exclude bulk import files from minor compactions, if configured
       if (conf.getBoolean("hbase.hstore.compaction.exclude.bulk", false)) {
@@ -998,7 +996,8 @@ public class Store implements HeapSize {
 
       // skip selection algorithm if we don't have enough files
       if (filesToCompact.size() < this.minFilesToCompact) {
-        return Collections.emptyList();
+        filesToCompact.emptyFileList();
+        return filesToCompact;
       }
 
       /* TODO: add sorting + unit test back in when HBASE-2856 is fixed
@@ -1041,7 +1040,7 @@ public class Store implements HeapSize {
       int end = Math.min(countOfFiles, start + this.maxFilesToCompact);
       long totalSize = fileSizes[start]
                      + ((start+1 < countOfFiles) ? sumSize[start+1] : 0);
-      filesToCompact = filesToCompact.subList(start, end);
+      filesToCompact = filesToCompact.getSubList(start, end);
 
       // if we don't have enough files to compact, just wait
       if (filesToCompact.size() < this.minFilesToCompact) {
@@ -1051,13 +1050,14 @@ public class Store implements HeapSize {
             + StringUtils.humanReadableInt(totalSize)
             + " have met compaction criteria.");
         }
-        return Collections.emptyList();
+        filesToCompact.emptyFileList();
+        return filesToCompact;
       }
     } else {
       // all files included in this compaction, up to max
       if (filesToCompact.size() > this.maxFilesToCompact) {
         int pastMax = filesToCompact.size() - this.maxFilesToCompact;
-        filesToCompact.subList(0, pastMax).clear();
+        filesToCompact.clearSubList(0, pastMax);
       }
     }
     return filesToCompact;
@@ -1743,7 +1743,7 @@ public class Store implements HeapSize {
   }
 
   public static final long FIXED_OVERHEAD = ClassSize.align(
-      ClassSize.OBJECT + (14 * ClassSize.REFERENCE) + (1 * Bytes.SIZEOF_DOUBLE) +
+      ClassSize.OBJECT + (14 * ClassSize.REFERENCE) +
       (7 * Bytes.SIZEOF_LONG) + (6 * Bytes.SIZEOF_INT) + (3 * Bytes.SIZEOF_BOOLEAN));
 
   public static final long DEEP_OVERHEAD = ClassSize.align(FIXED_OVERHEAD +

Added: hbase/branches/0.89/src/main/java/org/apache/hadoop/hbase/regionserver/compactions/CompactSelection.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.89/src/main/java/org/apache/hadoop/hbase/regionserver/compactions/CompactSelection.java?rev=1182029&view=auto
==============================================================================
--- hbase/branches/0.89/src/main/java/org/apache/hadoop/hbase/regionserver/compactions/CompactSelection.java (added)
+++ hbase/branches/0.89/src/main/java/org/apache/hadoop/hbase/regionserver/compactions/CompactSelection.java Tue Oct 11 19:12:47 2011
@@ -0,0 +1,175 @@
+/**
+ * Copyright 2011 The Apache Software Foundation
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.hadoop.hbase.regionserver.compactions;
+
+import java.util.AbstractList;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.regionserver.StoreFile;
+
+public class CompactSelection extends AbstractList<StoreFile> {
+  private static final long serialVersionUID = 1L;
+  static final Log LOG = LogFactory.getLog(CompactSelection.class);
+  // the actual list - this is needed to handle methods like "sublist"
+  // correctly
+  List<StoreFile> filesToCompact = new ArrayList<StoreFile>();
+  // number of off peak compactions either in the compaction queue or
+  // happening now
+  public static Integer numOutstandingOffPeakCompactions = 0;
+  // HBase conf object
+  Configuration conf;
+  // was this compaction promoted to an off-peak
+  boolean isOffPeakCompaction = false;
+  // compactRatio: double on purpose!  Float.MAX < Long.MAX < Double.MAX
+  // With float, java will downcast your long to float for comparisons (bad)
+  private double compactRatio;
+  // compaction ratio off-peak
+  private double compactRatioOffPeak;
+  // offpeak start time
+  private int offPeakStartHour = -1;
+  // off peak end time
+  private int offPeakEndHour = -1;
+
+  public CompactSelection(Configuration conf, List<StoreFile> filesToCompact) {
+    this.filesToCompact = filesToCompact;
+    this.conf = conf;
+    this.compactRatio = conf.getFloat("hbase.hstore.compaction.ratio", 1.2F);
+    this.compactRatioOffPeak = conf.getFloat("hbase.hstore.compaction.ratio.offpeak", 5.0F);
+
+    // Peak time is from [offPeakStartHour, offPeakEndHour). Valid numbers are [0, 23]
+    this.offPeakStartHour = conf.getInt("hbase.offpeak.start.hour", -1);
+    this.offPeakEndHour = conf.getInt("hbase.offpeak.end.hour", -1);
+    if (!isValidHour(this.offPeakStartHour) || !isValidHour(this.offPeakEndHour)) {
+      if (!(this.offPeakStartHour == -1 && this.offPeakEndHour == -1)) {
+        LOG.warn("Invalid start/end hour for peak hour : start = " +
+            this.offPeakStartHour + " end = " + this.offPeakEndHour +
+            ". Valid numbers are [0-23]");
+      }
+      this.offPeakStartHour = this.offPeakEndHour = -1;
+    }
+  }
+
+  /**
+   * If the current hour falls in the off peak times and there are no
+   * outstanding off peak compactions, the current compaction is
+   * promoted to an off peak compaction. Currently only one off peak
+   * compaction is present in the compaction queue.
+   *
+   * @param currentHour
+   * @return
+   */
+  public double getCompactSelectionRatio() {
+    double r = this.compactRatio;
+    synchronized(numOutstandingOffPeakCompactions) {
+      if (isOffPeakHour() && numOutstandingOffPeakCompactions == 0) {
+        r = this.compactRatioOffPeak;
+        numOutstandingOffPeakCompactions++;
+        isOffPeakCompaction = true;
+      }
+    }
+    if(isOffPeakCompaction) {
+      LOG.info("Running an off-peak compaction, selection ratio = " +
+          compactRatioOffPeak + ", numOutstandingOffPeakCompactions is now " +
+          numOutstandingOffPeakCompactions);
+    }
+    return r;
+  }
+
+  /**
+   * The current compaction finished, so reset the off peak compactions count
+   * if this was an off peak compaction.
+   */
+  public void finishRequest() {
+    if (isOffPeakCompaction) {
+      synchronized(numOutstandingOffPeakCompactions) {
+        numOutstandingOffPeakCompactions--;
+        isOffPeakCompaction = false;
+      }
+      LOG.info("Compaction done, numOutstandingOffPeakCompactions is now " +
+          numOutstandingOffPeakCompactions);
+    }
+  }
+
+  /**
+   * Removes all files from the current compaction list, and resets off peak
+   * compactions is set.
+   */
+  public void emptyFileList() {
+    filesToCompact.clear();
+    if (isOffPeakCompaction) {
+      synchronized(numOutstandingOffPeakCompactions) {
+        // reset the off peak count
+        numOutstandingOffPeakCompactions--;
+        isOffPeakCompaction = false;
+      }
+      LOG.info("Nothing to compact, numOutstandingOffPeakCompactions is now " +
+          numOutstandingOffPeakCompactions);
+    }
+  }
+
+  public boolean isOffPeakCompaction() {
+    return this.isOffPeakCompaction;
+  }
+
+  private boolean isOffPeakHour() {
+    int currentHour = (new GregorianCalendar()).get(Calendar.HOUR_OF_DAY);
+    // If offpeak time checking is disabled just return false.
+    if (this.offPeakStartHour == this.offPeakEndHour) {
+      return false;
+    }
+    if (this.offPeakStartHour < this.offPeakEndHour) {
+      return (currentHour >= this.offPeakStartHour && currentHour < this.offPeakEndHour);
+    }
+    return (currentHour >= this.offPeakStartHour || currentHour < this.offPeakEndHour);
+  }
+
+  public CompactSelection subList(int start, int end) {
+    throw new UnsupportedOperationException();
+  }
+
+  public CompactSelection getSubList(int start, int end) {
+    filesToCompact = filesToCompact.subList(start, end);
+    return this;
+  }
+
+  public void clearSubList(int start, int end) {
+    filesToCompact.subList(start, end).clear();
+  }
+
+  private boolean isValidHour(int hour) {
+    return (hour >= 0 && hour <= 23);
+  }
+
+  @Override
+  public StoreFile get(int index) {
+    return filesToCompact.get(index);
+  }
+
+  @Override
+  public int size() {
+    return filesToCompact.size();
+  }
+}

Modified: hbase/branches/0.89/src/main/java/org/apache/hadoop/hbase/regionserver/compactions/CompactionRequest.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.89/src/main/java/org/apache/hadoop/hbase/regionserver/compactions/CompactionRequest.java?rev=1182029&r1=1182028&r2=1182029&view=diff
==============================================================================
--- hbase/branches/0.89/src/main/java/org/apache/hadoop/hbase/regionserver/compactions/CompactionRequest.java (original)
+++ hbase/branches/0.89/src/main/java/org/apache/hadoop/hbase/regionserver/compactions/CompactionRequest.java Tue Oct 11 19:12:47 2011
@@ -1,3 +1,22 @@
+/**
+ * Copyright 2011 The Apache Software Foundation
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 package org.apache.hadoop.hbase.regionserver.compactions;
 
 import java.io.IOException;
@@ -30,7 +49,7 @@ public class CompactionRequest implement
     static final Log LOG = LogFactory.getLog(CompactionRequest.class);
     private final HRegion r;
     private final Store s;
-    private final List<StoreFile> files;
+    private final CompactSelection files;
     private final long totalSize;
     private final boolean isMajor;
     private int p;
@@ -38,7 +57,7 @@ public class CompactionRequest implement
     private HRegionServer server = null;
 
     public CompactionRequest(HRegion r, Store s,
-        List<StoreFile> files, boolean isMajor, int p) {
+        CompactSelection files, boolean isMajor, int p) {
       Preconditions.checkNotNull(r);
       Preconditions.checkNotNull(files);
 
@@ -55,6 +74,10 @@ public class CompactionRequest implement
       this.date = new Date();
     }
 
+    public void finishRequest() {
+      this.files.finishRequest();
+    }
+
     /**
      * This function will define where in the priority queue the request will
      * end up.  Those with the highest priorities will be first.  When the
@@ -99,7 +122,7 @@ public class CompactionRequest implement
     }
 
     /** Gets the StoreFiles for the request */
-    public List<StoreFile> getFiles() {
+    public CompactSelection getFiles() {
       return files;
     }
 

Modified: hbase/branches/0.89/src/test/java/org/apache/hadoop/hbase/regionserver/TestCompactSelection.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.89/src/test/java/org/apache/hadoop/hbase/regionserver/TestCompactSelection.java?rev=1182029&r1=1182028&r2=1182029&view=diff
==============================================================================
--- hbase/branches/0.89/src/test/java/org/apache/hadoop/hbase/regionserver/TestCompactSelection.java (original)
+++ hbase/branches/0.89/src/test/java/org/apache/hadoop/hbase/regionserver/TestCompactSelection.java Tue Oct 11 19:12:47 2011
@@ -22,6 +22,8 @@ package org.apache.hadoop.hbase.regionse
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
 import java.util.List;
 
 import junit.framework.TestCase;
@@ -36,9 +38,7 @@ import org.apache.hadoop.hbase.HColumnDe
 import org.apache.hadoop.hbase.HConstants;
 import org.apache.hadoop.hbase.HRegionInfo;
 import org.apache.hadoop.hbase.HTableDescriptor;
-import org.apache.hadoop.hbase.regionserver.StoreFile.Reader;
 import org.apache.hadoop.hbase.regionserver.wal.HLog;
-import org.apache.hadoop.hbase.regionserver.wal.TestWALReplay;
 import org.apache.hadoop.hbase.util.Bytes;
 
 import com.google.common.collect.Lists;
@@ -228,4 +228,41 @@ public class TestCompactSelection extend
     // empty case (because all files are too big)
     compactEquals(sfCreate(tooBig, tooBig) /* empty */);
   }
+
+  public void testOffPeakCompactionRatio() throws IOException {
+    /*
+     * NOTE: these tests are specific to describe the implementation of the
+     * current compaction algorithm.  Developed to ensure that refactoring
+     * doesn't implicitly alter this.
+     */
+    long tooBig = maxSize + 1;
+
+    Calendar calendar = new GregorianCalendar();
+    int hourOfDay = calendar.get(Calendar.HOUR_OF_DAY);
+    LOG.debug("Hour of day = " + hourOfDay);
+    int hourPlusOne = ((hourOfDay+1)%24);
+    int hourMinusOne = ((hourOfDay-1)%24);
+    int hourMinusTwo = ((hourOfDay-2)%24);
+
+    // check compact selection without peak hour setting
+    LOG.debug("Testing compact selection without off-peak settings...");
+    compactEquals(sfCreate(999,50,12,12,1), 12, 12, 1);
+
+    // set an off-peak compaction threshold
+    this.conf.setFloat("hbase.hstore.compaction.ratio.offpeak", 5.0F);
+
+    // set peak hour to current time and check compact selection
+    this.conf.setLong("hbase.offpeak.start.hour", hourMinusOne);
+    this.conf.setLong("hbase.offpeak.end.hour", hourPlusOne);
+    LOG.debug("Testing compact selection with off-peak settings (" +
+        hourMinusOne + ", " + hourPlusOne + ")");
+    compactEquals(sfCreate(999,50,12,12, 1), 50, 12, 12, 1);
+
+    // set peak hour outside current selection and check compact selection
+    this.conf.setLong("hbase.offpeak.start.hour", hourMinusTwo);
+    this.conf.setLong("hbase.offpeak.end.hour", hourMinusOne);
+    LOG.debug("Testing compact selection with off-peak settings (" +
+        hourMinusTwo + ", " + hourMinusOne + ")");
+    compactEquals(sfCreate(999,50,12,12, 1), 12, 12, 1);
+  }
 }