You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by ec...@apache.org on 2016/01/19 21:24:32 UTC

hbase git commit: HBASE-15102 Fix HeapMemoryTuner overtuning memstore

Repository: hbase
Updated Branches:
  refs/heads/branch-1 7d7a2d871 -> a61f3ecc8


HBASE-15102 Fix HeapMemoryTuner overtuning memstore

HeapMemoryTuner often over tunes memstore without looking at
the lower limit of the previous memstore size and causing a
situation in which memstore used size suddenly exceeds the
total memstore size.

Signed-off-by: Elliott Clark <ec...@apache.org>


Project: http://git-wip-us.apache.org/repos/asf/hbase/repo
Commit: http://git-wip-us.apache.org/repos/asf/hbase/commit/a61f3ecc
Tree: http://git-wip-us.apache.org/repos/asf/hbase/tree/a61f3ecc
Diff: http://git-wip-us.apache.org/repos/asf/hbase/diff/a61f3ecc

Branch: refs/heads/branch-1
Commit: a61f3ecc8b3dad256b7ec5258e1ec4aa811cb606
Parents: 7d7a2d8
Author: Ashu Pachauri <as...@gmail.com>
Authored: Wed Jan 13 13:49:43 2016 -0800
Committer: Elliott Clark <ec...@apache.org>
Committed: Tue Jan 19 12:23:59 2016 -0800

----------------------------------------------------------------------
 .../regionserver/DefaultHeapMemoryTuner.java    | 218 +++++++++++--------
 .../regionserver/TestHeapMemoryManager.java     |  23 +-
 2 files changed, 143 insertions(+), 98 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/hbase/blob/a61f3ecc/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/DefaultHeapMemoryTuner.java
----------------------------------------------------------------------
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/DefaultHeapMemoryTuner.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/DefaultHeapMemoryTuner.java
index b6e81dd..1d237d0 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/DefaultHeapMemoryTuner.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/DefaultHeapMemoryTuner.java
@@ -124,30 +124,113 @@ class DefaultHeapMemoryTuner implements HeapMemoryTuner {
 
   @Override
   public TunerResult tune(TunerContext context) {
-    long blockedFlushCount = context.getBlockedFlushCount();
-    long unblockedFlushCount = context.getUnblockedFlushCount();
-    long evictCount = context.getEvictCount();
-    long cacheMissCount = context.getCacheMissCount();
-    long totalFlushCount = blockedFlushCount+unblockedFlushCount;
-    rollingStatsForCacheMisses.insertDataValue(cacheMissCount);
-    rollingStatsForFlushes.insertDataValue(totalFlushCount);
-    rollingStatsForEvictions.insertDataValue(evictCount);
-    StepDirection newTuneDirection = StepDirection.NEUTRAL;
+    float curMemstoreSize = context.getCurMemStoreSize();
+    float curBlockCacheSize = context.getCurBlockCacheSize();
+    addToRollingStats(context);
+
     if (ignoreInitialPeriods < numPeriodsToIgnore) {
       // Ignoring the first few tuner periods
       ignoreInitialPeriods++;
       rollingStatsForTunerSteps.insertDataValue(0);
       return NO_OP_TUNER_RESULT;
     }
-    String tunerLog = "";
+    StepDirection newTuneDirection = getTuneDirection(context);
+
+    float newMemstoreSize;
+    float newBlockCacheSize;
+
+    // Adjusting step size for tuning to get to steady state or restart from steady state.
+    // Even if the step size was 4% and 32 GB memory size, we will be shifting 1 GB back and forth
+    // per tuner operation and it can affect the performance of cluster so we keep on decreasing
+    // step size until everything settles.
+    if (prevTuneDirection == StepDirection.NEUTRAL
+        && newTuneDirection != StepDirection.NEUTRAL
+        && rollingStatsForTunerSteps.getDeviation() < TUNER_STEP_EPS) {
+      // Restarting the tuning from steady state and setting step size to maximum.
+      // The deviation cannot be that low if last period was neutral and some recent periods were
+      // not neutral.
+      step = maximumStepSize;
+    } else if ((newTuneDirection == StepDirection.INCREASE_MEMSTORE_SIZE
+        && decayingTunerStepSizeSum < 0) ||
+        (newTuneDirection == StepDirection.INCREASE_BLOCK_CACHE_SIZE
+        && decayingTunerStepSizeSum > 0)) {
+      // Current step is opposite of past tuner actions so decrease the step size to reach steady
+      // state.
+      step = step/2.00f;
+    }
+    if (step < minimumStepSize) {
+      // If step size is too small then we do nothing.
+      LOG.debug("Tuner step size is too low; we will not perform any tuning this time.");
+      step = 0.0f;
+      newTuneDirection = StepDirection.NEUTRAL;
+    }
+    // Increase / decrease the memstore / block cahce sizes depending on new tuner step.
+    float globalMemstoreLowerMark = HeapMemorySizeUtil.getGlobalMemStoreLowerMark(conf,
+        curMemstoreSize);
+    // We don't want to exert immediate pressure on memstore. So, we decrease its size gracefully;
+    // we set a minimum bar in the middle of the total memstore size and the lower limit.
+    float minMemstoreSize = ((globalMemstoreLowerMark + 1) * curMemstoreSize) / 2.00f;
+
+    switch (newTuneDirection) {
+    case INCREASE_BLOCK_CACHE_SIZE:
+        if (curMemstoreSize - step < minMemstoreSize) {
+          step = curMemstoreSize - minMemstoreSize;
+        }
+        newMemstoreSize = curMemstoreSize - step;
+        newBlockCacheSize = curBlockCacheSize + step;
+        rollingStatsForTunerSteps.insertDataValue(-(int)(step*100000));
+        decayingTunerStepSizeSum = (decayingTunerStepSizeSum - step)/2.00f;
+        break;
+    case INCREASE_MEMSTORE_SIZE:
+        newBlockCacheSize = curBlockCacheSize - step;
+        newMemstoreSize = curMemstoreSize + step;
+        rollingStatsForTunerSteps.insertDataValue((int)(step*100000));
+        decayingTunerStepSizeSum = (decayingTunerStepSizeSum + step)/2.00f;
+        break;
+    default:
+        prevTuneDirection = StepDirection.NEUTRAL;
+        rollingStatsForTunerSteps.insertDataValue(0);
+        decayingTunerStepSizeSum = (decayingTunerStepSizeSum)/2.00f;
+        return NO_OP_TUNER_RESULT;
+    }
+    // Check we are within max/min bounds.
+    if (newMemstoreSize > globalMemStorePercentMaxRange) {
+      newMemstoreSize = globalMemStorePercentMaxRange;
+    } else if (newMemstoreSize < globalMemStorePercentMinRange) {
+      newMemstoreSize = globalMemStorePercentMinRange;
+    }
+    if (newBlockCacheSize > blockCachePercentMaxRange) {
+      newBlockCacheSize = blockCachePercentMaxRange;
+    } else if (newBlockCacheSize < blockCachePercentMinRange) {
+      newBlockCacheSize = blockCachePercentMinRange;
+    }
+    TUNER_RESULT.setBlockCacheSize(newBlockCacheSize);
+    TUNER_RESULT.setMemstoreSize(newMemstoreSize);
+    prevTuneDirection = newTuneDirection;
+    return TUNER_RESULT;
+  }
+
+  /**
+   * Determine best direction of tuning base on given context.
+   * @param context The tuner context.
+   * @return tuning direction.
+   */
+  private StepDirection getTuneDirection(TunerContext context) {
+    StepDirection newTuneDirection = StepDirection.NEUTRAL;
+    long blockedFlushCount = context.getBlockedFlushCount();
+    long unblockedFlushCount = context.getUnblockedFlushCount();
+    long evictCount = context.getEvictCount();
+    long cacheMissCount = context.getCacheMissCount();
+    long totalFlushCount = blockedFlushCount+unblockedFlushCount;
+    float curMemstoreSize = context.getCurMemStoreSize();
+    float curBlockCacheSize = context.getCurBlockCacheSize();
+    StringBuilder tunerLog = new StringBuilder();
     // We can consider memstore or block cache to be sufficient if
     // we are using only a minor fraction of what have been already provided to it.
     boolean earlyMemstoreSufficientCheck = totalFlushCount == 0
-            || context.getCurMemStoreUsed() < context.getCurMemStoreSize()*sufficientMemoryLevel;
+        || context.getCurMemStoreUsed() < curMemstoreSize * sufficientMemoryLevel;
     boolean earlyBlockCacheSufficientCheck = evictCount == 0 ||
-            context.getCurBlockCacheUsed() < context.getCurBlockCacheSize()*sufficientMemoryLevel;
-    float newMemstoreSize;
-    float newBlockCacheSize;
+        context.getCurBlockCacheUsed() < curBlockCacheSize * sufficientMemoryLevel;
     if (earlyMemstoreSufficientCheck && earlyBlockCacheSufficientCheck) {
       // Both memstore and block cache memory seems to be sufficient. No operation required.
       newTuneDirection = StepDirection.NEUTRAL;
@@ -168,15 +251,15 @@ class DefaultHeapMemoryTuner implements HeapMemoryTuner {
       case INCREASE_BLOCK_CACHE_SIZE:
         if ((double)evictCount > rollingStatsForEvictions.getMean() ||
             (double)totalFlushCount > rollingStatsForFlushes.getMean() +
-            rollingStatsForFlushes.getDeviation()/2.00) {
+                rollingStatsForFlushes.getDeviation()/2.00) {
           // Reverting previous step as it was not useful.
           // Tuning failed to decrease evictions or tuning resulted in large number of flushes.
           newTuneDirection = StepDirection.INCREASE_MEMSTORE_SIZE;
-          tunerLog += "Reverting previous tuning.";
+          tunerLog.append("We will revert previous tuning");
           if ((double)evictCount > rollingStatsForEvictions.getMean()) {
-            tunerLog += " As could not decrease evctions sufficiently.";
+            tunerLog.append(" because we could not decrease evictions sufficiently.");
           } else {
-            tunerLog += " As number of flushes rose significantly.";
+            tunerLog.append(" because the number of flushes rose significantly.");
           }
           isReverting = true;
         }
@@ -184,15 +267,15 @@ class DefaultHeapMemoryTuner implements HeapMemoryTuner {
       case INCREASE_MEMSTORE_SIZE:
         if ((double)totalFlushCount > rollingStatsForFlushes.getMean() ||
             (double)evictCount > rollingStatsForEvictions.getMean() +
-            rollingStatsForEvictions.getDeviation()/2.00) {
+                rollingStatsForEvictions.getDeviation()/2.00) {
           // Reverting previous step as it was not useful.
           // Tuning failed to decrease flushes or tuning resulted in large number of evictions.
           newTuneDirection = StepDirection.INCREASE_BLOCK_CACHE_SIZE;
-          tunerLog += "Reverting previous tuning.";
+          tunerLog.append("We will revert previous tuning");
           if ((double)totalFlushCount > rollingStatsForFlushes.getMean()) {
-            tunerLog += " As could not decrease flushes sufficiently.";
+            tunerLog.append(" because we could not decrease flushes sufficiently.");
           } else {
-            tunerLog += " As number of evictions rose significantly.";
+            tunerLog.append(" because number of evictions rose significantly.");
           }
           isReverting = true;
         }
@@ -215,97 +298,52 @@ class DefaultHeapMemoryTuner implements HeapMemoryTuner {
         if ((double)cacheMissCount < rollingStatsForCacheMisses.getMean() -
             rollingStatsForCacheMisses.getDeviation()*0.80 &&
             (double)totalFlushCount < rollingStatsForFlushes.getMean() -
-            rollingStatsForFlushes.getDeviation()*0.80) {
+                rollingStatsForFlushes.getDeviation()*0.80) {
           // Everything is fine no tuning required
           newTuneDirection = StepDirection.NEUTRAL;
         } else if ((double)cacheMissCount > rollingStatsForCacheMisses.getMean() +
             rollingStatsForCacheMisses.getDeviation()*0.80 &&
             (double)totalFlushCount < rollingStatsForFlushes.getMean() -
-            rollingStatsForFlushes.getDeviation()*0.80) {
+                rollingStatsForFlushes.getDeviation()*0.80) {
           // more misses , increasing cache size
           newTuneDirection = StepDirection.INCREASE_BLOCK_CACHE_SIZE;
-          tunerLog +=
-              "Increasing block cache size as observed increase in number of cache misses.";
+          tunerLog.append(
+              "Going to increase block cache size due to increase in number of cache misses.");
         } else if ((double)cacheMissCount < rollingStatsForCacheMisses.getMean() -
             rollingStatsForCacheMisses.getDeviation()*0.80 &&
             (double)totalFlushCount > rollingStatsForFlushes.getMean() +
-            rollingStatsForFlushes.getDeviation()*0.80) {
+                rollingStatsForFlushes.getDeviation()*0.80) {
           // more flushes , increasing memstore size
           newTuneDirection = StepDirection.INCREASE_MEMSTORE_SIZE;
-          tunerLog += "Increasing memstore size as observed increase in number of flushes.";
+          tunerLog.append("Going to increase memstore size due to increase in number of flushes.");
         } else if (blockedFlushCount > 0 && prevTuneDirection == StepDirection.NEUTRAL) {
           // we do not want blocked flushes
           newTuneDirection = StepDirection.INCREASE_MEMSTORE_SIZE;
-          tunerLog += "Increasing memstore size as observed "
-                      + blockedFlushCount + " blocked flushes.";
+          tunerLog.append("Going to increase memstore size due to"
+              + blockedFlushCount + " blocked flushes.");
         } else {
           // Default. Not enough facts to do tuning.
+          tunerLog.append("Going to do nothing because we "
+              + "could not determine best tuning direction");
           newTuneDirection = StepDirection.NEUTRAL;
         }
       }
     }
-    // Adjusting step size for tuning to get to steady state or restart from steady state.
-    // Even if the step size was 4% and 32 GB memory size, we will be shifting 1 GB back and forth
-    // per tuner operation and it can affect the performance of cluster so we keep on decreasing
-    // step size until everything settles.
-    if (prevTuneDirection == StepDirection.NEUTRAL
-        && newTuneDirection != StepDirection.NEUTRAL
-        && rollingStatsForTunerSteps.getDeviation() < TUNER_STEP_EPS) {
-      // Restarting the tuning from steady state and setting step size to maximum.
-      // The deviation cannot be that low if last period was neutral and some recent periods were
-      // not neutral.
-      step = maximumStepSize;
-    } else if ((newTuneDirection == StepDirection.INCREASE_MEMSTORE_SIZE
-        && decayingTunerStepSizeSum < 0) ||
-        (newTuneDirection == StepDirection.INCREASE_BLOCK_CACHE_SIZE
-        && decayingTunerStepSizeSum > 0)) {
-      // Current step is opposite of past tuner actions so decrease the step size to reach steady
-      // state.
-      step = step/2.00f;
-    }
-    if (step < minimumStepSize) {
-      // If step size is too small then we do nothing.
-      step = 0.0f;
-      newTuneDirection = StepDirection.NEUTRAL;
-    }
-    // Increase / decrease the memstore / block cahce sizes depending on new tuner step.
-    switch (newTuneDirection) {
-    case INCREASE_BLOCK_CACHE_SIZE:
-        newBlockCacheSize = context.getCurBlockCacheSize() + step;
-        newMemstoreSize = context.getCurMemStoreSize() - step;
-        rollingStatsForTunerSteps.insertDataValue(-(int)(step*100000));
-        decayingTunerStepSizeSum = (decayingTunerStepSizeSum - step)/2.00f;
-        break;
-    case INCREASE_MEMSTORE_SIZE:
-        newBlockCacheSize = context.getCurBlockCacheSize() - step;
-        newMemstoreSize = context.getCurMemStoreSize() + step;
-        rollingStatsForTunerSteps.insertDataValue((int)(step*100000));
-        decayingTunerStepSizeSum = (decayingTunerStepSizeSum + step)/2.00f;
-        break;
-    default:
-        prevTuneDirection = StepDirection.NEUTRAL;
-        rollingStatsForTunerSteps.insertDataValue(0);
-        decayingTunerStepSizeSum = (decayingTunerStepSizeSum)/2.00f;
-        return NO_OP_TUNER_RESULT;
-    }
-    // Check we are within max/min bounds.
-    if (newMemstoreSize > globalMemStorePercentMaxRange) {
-      newMemstoreSize = globalMemStorePercentMaxRange;
-    } else if (newMemstoreSize < globalMemStorePercentMinRange) {
-      newMemstoreSize = globalMemStorePercentMinRange;
-    }
-    if (newBlockCacheSize > blockCachePercentMaxRange) {
-      newBlockCacheSize = blockCachePercentMaxRange;
-    } else if (newBlockCacheSize < blockCachePercentMinRange) {
-      newBlockCacheSize = blockCachePercentMinRange;
-    }
-    TUNER_RESULT.setBlockCacheSize(newBlockCacheSize);
-    TUNER_RESULT.setMemstoreSize(newMemstoreSize);
     if (LOG.isDebugEnabled()) {
-      LOG.debug(tunerLog);
+      LOG.debug(tunerLog.toString());
     }
-    prevTuneDirection = newTuneDirection;
-    return TUNER_RESULT;
+    return newTuneDirection;
+  }
+
+  /**
+   * Add the given context to the rolling tuner stats.
+   * @param context The tuner context.
+   */
+  private void addToRollingStats(TunerContext context) {
+    rollingStatsForCacheMisses.insertDataValue(context.getCacheMissCount());
+    rollingStatsForFlushes.insertDataValue(context.getBlockedFlushCount() +
+        context.getUnblockedFlushCount());
+    rollingStatsForEvictions.insertDataValue(context.getEvictCount());
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/hbase/blob/a61f3ecc/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestHeapMemoryManager.java
----------------------------------------------------------------------
diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestHeapMemoryManager.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestHeapMemoryManager.java
index 99acf0f..0e72d0d 100644
--- a/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestHeapMemoryManager.java
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestHeapMemoryManager.java
@@ -227,6 +227,7 @@ public class TestHeapMemoryManager {
     blockCache.setTestBlockSize((long) (maxHeapSize * 0.4 * 0.8));
     regionServerAccounting.setTestMemstoreSize(0);
     Configuration conf = HBaseConfiguration.create();
+    conf.setFloat(HeapMemorySizeUtil.MEMSTORE_SIZE_LOWER_LIMIT_KEY, 0.7f);
     conf.setFloat(HeapMemoryManager.MEMSTORE_SIZE_MAX_RANGE_KEY, 0.75f);
     conf.setFloat(HeapMemoryManager.MEMSTORE_SIZE_MIN_RANGE_KEY, 0.10f);
     conf.setFloat(HeapMemoryManager.BLOCK_CACHE_SIZE_MAX_RANGE_KEY, 0.7f);
@@ -238,6 +239,11 @@ public class TestHeapMemoryManager {
         new RegionServerStub(conf), new RegionServerAccountingStub());
     long oldMemstoreHeapSize = memStoreFlusher.memstoreSize;
     long oldBlockCacheSize = blockCache.maxSize;
+    long oldMemstoreLowerMarkSize = 7 * oldMemstoreHeapSize / 10;
+    long maxTuneSize = oldMemstoreHeapSize -  (oldMemstoreLowerMarkSize + oldMemstoreHeapSize) / 2;
+    float maxStepValue = (maxTuneSize * 1.0f) / oldMemstoreHeapSize;
+    maxStepValue = maxStepValue > DefaultHeapMemoryTuner.DEFAULT_MAX_STEP_VALUE ?
+        DefaultHeapMemoryTuner.DEFAULT_MAX_STEP_VALUE:maxStepValue;
     final ChoreService choreService = new ChoreService("TEST_SERVER_NAME");
     heapMemoryManager.start(choreService);
     blockCache.evictBlock(null);
@@ -245,20 +251,21 @@ public class TestHeapMemoryManager {
     blockCache.evictBlock(null);
     // Allow the tuner to run once and do necessary memory up
     waitForTune(memStoreFlusher, memStoreFlusher.memstoreSize);
-    assertHeapSpaceDelta(-(DefaultHeapMemoryTuner.DEFAULT_MAX_STEP_VALUE), oldMemstoreHeapSize,
-        memStoreFlusher.memstoreSize);
-    assertHeapSpaceDelta(DefaultHeapMemoryTuner.DEFAULT_MAX_STEP_VALUE, oldBlockCacheSize,
-        blockCache.maxSize);
+    assertHeapSpaceDelta(-maxStepValue, oldMemstoreHeapSize, memStoreFlusher.memstoreSize);
+    assertHeapSpaceDelta(maxStepValue, oldBlockCacheSize, blockCache.maxSize);
     oldMemstoreHeapSize = memStoreFlusher.memstoreSize;
     oldBlockCacheSize = blockCache.maxSize;
+    oldMemstoreLowerMarkSize = 7 * oldMemstoreHeapSize / 10;
+    maxTuneSize = oldMemstoreHeapSize -  (oldMemstoreLowerMarkSize + oldMemstoreHeapSize) / 2;
+    maxStepValue = (maxTuneSize * 1.0f) / oldMemstoreHeapSize;
+    maxStepValue = maxStepValue > DefaultHeapMemoryTuner.DEFAULT_MAX_STEP_VALUE ?
+        DefaultHeapMemoryTuner.DEFAULT_MAX_STEP_VALUE:maxStepValue;
     // Do some more evictions before the next run of HeapMemoryTuner
     blockCache.evictBlock(null);
     // Allow the tuner to run once and do necessary memory up
     waitForTune(memStoreFlusher, memStoreFlusher.memstoreSize);
-    assertHeapSpaceDelta(-(DefaultHeapMemoryTuner.DEFAULT_MAX_STEP_VALUE), oldMemstoreHeapSize,
-        memStoreFlusher.memstoreSize);
-    assertHeapSpaceDelta(DefaultHeapMemoryTuner.DEFAULT_MAX_STEP_VALUE, oldBlockCacheSize,
-        blockCache.maxSize);
+    assertHeapSpaceDelta(-maxStepValue, oldMemstoreHeapSize, memStoreFlusher.memstoreSize);
+    assertHeapSpaceDelta(maxStepValue, oldBlockCacheSize, blockCache.maxSize);
   }
 
   @Test