You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by mb...@apache.org on 2012/07/13 01:16:46 UTC

svn commit: r1361000 - in /hbase/branches/0.89-fb/src: main/java/org/apache/hadoop/hbase/io/hfile/ test/java/org/apache/hadoop/hbase/client/ test/java/org/apache/hadoop/hbase/io/hfile/ test/java/org/apache/hadoop/hbase/regionserver/

Author: mbautin
Date: Thu Jul 12 23:16:46 2012
New Revision: 1361000

URL: http://svn.apache.org/viewvc?rev=1361000&view=rev
Log:
[jira] [HBASE-6366] [89-fb] Delay eviction on close

Author: michalgr

Summary:
Delay eviction on close.

Since now evictBlocksByHfileName does not evict blocks immediately. It adds name of the file to list of recently closed files. During next eviction blocks corresponding to files on list are evicted and the list is cleared.

Test Plan: I prepared unit test. It runs ok.

Reviewers: liyintang, kranganathan

Reviewed By: liyintang

CC: JIRA

Differential Revision: https://reviews.facebook.net/D4071

Added:
    hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/io/hfile/CacheTestHelper.java
Modified:
    hbase/branches/0.89-fb/src/main/java/org/apache/hadoop/hbase/io/hfile/LruBlockCache.java
    hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/client/TestFromClientSide.java
    hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/io/hfile/TestLruBlockCache.java
    hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/regionserver/TestStoreFile.java

Modified: hbase/branches/0.89-fb/src/main/java/org/apache/hadoop/hbase/io/hfile/LruBlockCache.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.89-fb/src/main/java/org/apache/hadoop/hbase/io/hfile/LruBlockCache.java?rev=1361000&r1=1360999&r2=1361000&view=diff
==============================================================================
--- hbase/branches/0.89-fb/src/main/java/org/apache/hadoop/hbase/io/hfile/LruBlockCache.java (original)
+++ hbase/branches/0.89-fb/src/main/java/org/apache/hadoop/hbase/io/hfile/LruBlockCache.java Thu Jul 12 23:16:46 2012
@@ -25,9 +25,12 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.EnumMap;
 import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.PriorityQueue;
+import java.util.Set;
 import java.util.SortedSet;
 import java.util.TreeSet;
 import java.util.concurrent.ConcurrentHashMap;
@@ -124,6 +127,9 @@ public class LruBlockCache implements Bl
   /** Eviction thread */
   private final EvictionThread evictionThread;
 
+  /** Synchronized list of closed files **/
+  private final List<String> recentlyClosedFiles; 
+  
   /** Statistics thread schedule pool (for heavy debugging, could remove) */
   private final ScheduledExecutorService scheduleThreadPool =
     Executors.newScheduledThreadPool(1,
@@ -270,6 +276,8 @@ public class LruBlockCache implements Bl
     }
     this.scheduleThreadPool.scheduleAtFixedRate(new StatisticsThread(this),
         statThreadPeriod, statThreadPeriod, TimeUnit.SECONDS);
+    
+    this.recentlyClosedFiles = Collections.synchronizedList(new LinkedList<String>());
   }
 
   public void setMaxSize(long maxSize) {
@@ -367,27 +375,51 @@ public class LruBlockCache implements Bl
   }
 
   /**
-   * Evicts all blocks for a specific HFile. This is an
-   * expensive operation implemented as a linear-time search through all blocks
- * in the cache. Ideally this should be a search in a log-access-time map.
+   * Adds specific HFile to a list of recently closed files. Next time the eviction happens
+   * all blocks of the File will be evicted.
    *
    * <p>
    * This is used for evict-on-close to remove all blocks of a specific HFile.
    *
-   * @return the number of blocks evicted
+   * @return 0
    */
   @Override
   public int evictBlocksByHfileName(String hfileName) {
     int numEvicted = 0;
-    for (BlockCacheKey key : map.keySet()) {
-      if (key.getHfileName().equals(hfileName)) {
-        if (evictBlock(key))
-          ++numEvicted;
-      }
-    }
+    recentlyClosedFiles.add(hfileName);
     return numEvicted;
   }
 
+  /**
+   * Evicts blocks specified by file names in recentlyClosedFiles list and clears the list.
+   * 
+   * @return number of freed bytes
+   */
+  private long doDelayedEviction(){
+    Set<String> closedRecently = new HashSet<String>();
+    
+    LOG.debug("Delayed eviction started.");
+    
+    synchronized(recentlyClosedFiles){
+      for (String fname : recentlyClosedFiles){
+        closedRecently.add(fname);
+      }
+      recentlyClosedFiles.clear();
+    }
+    
+    long bytesFreed = 0;
+    for (CachedBlock block : map.values()){
+      if (closedRecently.contains(block.getCacheKey().getHfileName())){
+          bytesFreed += evictBlock(block);
+      }
+    }
+    
+    float freedMB = ((float)bytesFreed)/((float)(1024*1024));
+    LOG.debug("Delayed eviction finished. Freed " + Float.toString(freedMB) + " MB");
+    
+    return bytesFreed;
+  }
+  
   protected long evictBlock(CachedBlock block) {
     map.remove(block.getCacheKey());
     updateSizeMetrics(block, true);
@@ -395,7 +427,7 @@ public class LruBlockCache implements Bl
     stats.evicted(block);
     return block.heapSize();
   }
-
+  
   /**
    * Multi-threaded call to run the eviction process.
    */
@@ -420,6 +452,12 @@ public class LruBlockCache implements Bl
 
       long bytesToFree = size.get() - minSize();
 
+
+      if(bytesToFree <= 0) return;
+
+      doDelayedEviction();
+
+      bytesToFree = size.get() - minSize();
       LOG.debug("Block cache LRU eviction started.  Attempting to free " +
           bytesToFree + " bytes");
 
@@ -806,7 +844,7 @@ public class LruBlockCache implements Bl
   }
 
   public final static long CACHE_FIXED_OVERHEAD = ClassSize.align(
-      (3 * Bytes.SIZEOF_LONG) + (8 * ClassSize.REFERENCE) +
+      (3 * Bytes.SIZEOF_LONG) + (9 * ClassSize.REFERENCE) +
       (5 * Bytes.SIZEOF_FLOAT) + Bytes.SIZEOF_BOOLEAN
       + ClassSize.OBJECT);
 

Modified: hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/client/TestFromClientSide.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/client/TestFromClientSide.java?rev=1361000&r1=1360999&r2=1361000&view=diff
==============================================================================
--- hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/client/TestFromClientSide.java (original)
+++ hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/client/TestFromClientSide.java Thu Jul 12 23:16:46 2012
@@ -69,6 +69,7 @@ import org.apache.hadoop.hbase.filter.Si
 import org.apache.hadoop.hbase.filter.WhileMatchFilter;
 import org.apache.hadoop.hbase.io.hfile.BlockCache;
 import org.apache.hadoop.hbase.io.hfile.CacheConfig;
+import org.apache.hadoop.hbase.io.hfile.CacheTestHelper;
 import org.apache.hadoop.hbase.ipc.HRegionInterface;
 import org.apache.hadoop.hbase.regionserver.HRegion;
 import org.apache.hadoop.hbase.regionserver.HRegionServer;
@@ -4163,7 +4164,8 @@ public class TestFromClientSide {
     cacheConf.setCacheDataOnWrite(true);
     cacheConf.setEvictOnClose(true);
     BlockCache cache = cacheConf.getBlockCache();
-
+    // Get rid of blocks marked to be evicted.
+    CacheTestHelper.forceDelayedEviction(cache);
     // establish baseline stats
     long startBlockCount = cache.getBlockCount();
     long startBlockHits = cache.getStats().getHitCount();
@@ -4180,6 +4182,7 @@ public class TestFromClientSide {
     // flush the data
     System.out.println("Flushing cache");
     region.flushcache();
+    CacheTestHelper.forceDelayedEviction(cache);
     // expect one more block in cache, no change in hits/misses
     long expectedBlockCount = startBlockCount + 1;
     long expectedBlockHits = startBlockHits;
@@ -4207,6 +4210,7 @@ public class TestFromClientSide {
     // flush, one new block
     System.out.println("Flushing cache");
     region.flushcache();
+    CacheTestHelper.forceDelayedEviction(cache);
     assertEquals(++expectedBlockCount, cache.getBlockCount());
     assertEquals(expectedBlockHits, cache.getStats().getHitCount());
     assertEquals(expectedBlockMiss, cache.getStats().getMissCount());
@@ -4218,6 +4222,7 @@ public class TestFromClientSide {
     waitForStoreFileCount(store, 1, 10000); // wait 10 seconds max
     assertEquals(1, store.getNumberOfStoreFiles());
     expectedBlockCount -= 2; // evicted two blocks, cached none
+    CacheTestHelper.forceDelayedEviction(cache);
     assertEquals(expectedBlockCount, cache.getBlockCount());
     expectedBlockHits += 2;
     assertEquals(expectedBlockMiss, cache.getStats().getMissCount());

Added: hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/io/hfile/CacheTestHelper.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/io/hfile/CacheTestHelper.java?rev=1361000&view=auto
==============================================================================
--- hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/io/hfile/CacheTestHelper.java (added)
+++ hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/io/hfile/CacheTestHelper.java Thu Jul 12 23:16:46 2012
@@ -0,0 +1,16 @@
+package org.apache.hadoop.hbase.io.hfile;
+
+import java.lang.reflect.Method;
+
+public class CacheTestHelper {
+  public static void forceDelayedEviction(BlockCache bc) throws Exception{
+    if (bc instanceof LruBlockCache){
+      LruBlockCache lbc = (LruBlockCache)bc;
+      Method doDelayedEvictionMethod = LruBlockCache.class.getDeclaredMethod("doDelayedEviction");
+      doDelayedEvictionMethod.setAccessible(true);
+      doDelayedEvictionMethod.invoke(lbc);
+    }
+  }
+  
+
+}

Modified: hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/io/hfile/TestLruBlockCache.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/io/hfile/TestLruBlockCache.java?rev=1361000&r1=1360999&r2=1361000&view=diff
==============================================================================
--- hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/io/hfile/TestLruBlockCache.java (original)
+++ hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/io/hfile/TestLruBlockCache.java Thu Jul 12 23:16:46 2012
@@ -374,6 +374,28 @@ public class TestLruBlockCache {
 
 
   }
+  
+  @Test
+  public void testDelayedFileEviction() throws Exception {
+    long maxSize = 100000;
+    long blockSize = calculateBlockSizeDefault(maxSize, 10);
+    String fileName = "testFile";
+    LruBlockCache cache = new LruBlockCache(maxSize,blockSize,false);
+
+    CachedItem [] blocks = generateFileBlocks(fileName, 5, 10000);
+    // Add and get the multi blocks
+    for (CachedItem block : blocks) {
+      cache.cacheBlock(block.cacheKey, block);
+      assertEquals(cache.getBlock(block.cacheKey, true), block);
+    }
+
+    cache.evictBlocksByHfileName(fileName);
+    
+    CacheTestHelper.forceDelayedEviction(cache);
+    
+    for (CachedItem block : blocks)
+      assertEquals(null, cache.getBlock(block.cacheKey, true));
+  }
 
   // test scan resistance
   @Test
@@ -507,6 +529,14 @@ public class TestLruBlockCache {
     }
     return blocks;
   }
+  
+  private CachedItem [] generateFileBlocks(String fileName, int numBlocks, int size) {
+    CachedItem [] blocks = new CachedItem[numBlocks];
+    for(int i=0;i<numBlocks;i++) {
+      blocks[i] = new CachedItem(fileName, size, size * i);
+    }
+    return blocks;
+  }
 
   private CachedItem [] generateFixedBlocks(int numBlocks, long size, String pfx) {
     return generateFixedBlocks(numBlocks, (int)size, pfx);
@@ -554,6 +584,11 @@ public class TestLruBlockCache {
       this.cacheKey = new BlockCacheKey(blockName, 0);
       this.size = size;
     }
+    
+    CachedItem(String blockName, int size, int offset) {
+      this.cacheKey = new BlockCacheKey(blockName, offset);
+      this.size = size;
+    }
 
     /** The size of this item reported to the block cache layer */
     @Override

Modified: hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/regionserver/TestStoreFile.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/regionserver/TestStoreFile.java?rev=1361000&r1=1360999&r2=1361000&view=diff
==============================================================================
--- hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/regionserver/TestStoreFile.java (original)
+++ hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/regionserver/TestStoreFile.java Thu Jul 12 23:16:46 2012
@@ -43,6 +43,7 @@ import org.apache.hadoop.hbase.io.Refere
 import org.apache.hadoop.hbase.io.encoding.DataBlockEncoding;
 import org.apache.hadoop.hbase.io.hfile.BlockCache;
 import org.apache.hadoop.hbase.io.hfile.CacheConfig;
+import org.apache.hadoop.hbase.io.hfile.CacheTestHelper;
 import org.apache.hadoop.hbase.io.hfile.HFile;
 import org.apache.hadoop.hbase.io.hfile.HFileDataBlockEncoder;
 import org.apache.hadoop.hbase.io.hfile.HFileDataBlockEncoderImpl;
@@ -740,6 +741,10 @@ public class TestStoreFile extends HBase
     // Grab the block cache and get the initial hit/miss counts
     BlockCache bc = new CacheConfig(conf).getBlockCache();
     assertNotNull(bc);
+
+    // Do delayed eviction. Test relies on that.
+    CacheTestHelper.forceDelayedEviction(bc);
+
     CacheStats cs = bc.getStats();
     long startHit = cs.getHitCount();
     long startMiss = cs.getMissCount();
@@ -766,6 +771,7 @@ public class TestStoreFile extends HBase
     startMiss += 3;
     scanner.close();
     reader.close(cacheConf.shouldEvictOnClose());
+    CacheTestHelper.forceDelayedEviction(bc);
 
     // Now write a StoreFile with three blocks, with cache on write on
     conf.setBoolean(CacheConfig.CACHE_BLOCKS_ON_WRITE_KEY, true);
@@ -786,6 +792,7 @@ public class TestStoreFile extends HBase
     startHit += 3;
     scanner.close();
     reader.close(cacheConf.shouldEvictOnClose());
+    CacheTestHelper.forceDelayedEviction(bc);
 
     // Let's read back the two files to ensure the blocks exactly match
     hsf = new StoreFile(this.fs, pathCowOff, conf, cacheConf,
@@ -823,6 +830,7 @@ public class TestStoreFile extends HBase
     readerOne.close(cacheConf.shouldEvictOnClose());
     scannerTwo.close();
     readerTwo.close(cacheConf.shouldEvictOnClose());
+    CacheTestHelper.forceDelayedEviction(bc);
 
     // Let's close the first file with evict on close turned on
     conf.setBoolean("hbase.rs.evictblocksonclose", true);
@@ -831,6 +839,7 @@ public class TestStoreFile extends HBase
         StoreFile.BloomType.NONE, NoOpDataBlockEncoder.INSTANCE);
     reader = hsf.createReader();
     reader.close(cacheConf.shouldEvictOnClose());
+    CacheTestHelper.forceDelayedEviction(bc);
 
     // We should have 3 new evictions
     assertEquals(startHit, cs.getHitCount());
@@ -845,6 +854,7 @@ public class TestStoreFile extends HBase
         StoreFile.BloomType.NONE, NoOpDataBlockEncoder.INSTANCE);
     reader = hsf.createReader();
     reader.close(cacheConf.shouldEvictOnClose());
+    CacheTestHelper.forceDelayedEviction(bc);
 
     // We expect no changes
     assertEquals(startHit, cs.getHitCount());