Skip to content

Commit 2028e56

Browse files
Andrew Purtellapurtell
authored andcommitted
HBASE-22114 Port HBASE-15560 (TinyLFU-based BlockCache) to branch-1
HBASE-15560 W-TinyLFU based BlockCache (Ben Manes)
1 parent 0fe1c9c commit 2028e56

File tree

15 files changed

+1341
-79
lines changed

15 files changed

+1341
-79
lines changed

hbase-common/src/main/java/org/apache/hadoop/hbase/io/util/HeapMemorySizeUtil.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ public static float getL2BlockCacheHeapPercent(Configuration conf) {
180180
* @return the number of bytes to use for LRU, negative if disabled.
181181
* @throws IllegalArgumentException if HFILE_BLOCK_CACHE_SIZE_KEY is > 1.0
182182
*/
183-
public static long getLruCacheSize(final Configuration conf) {
183+
public static long getFirstLevelCacheSize(final Configuration conf, final long xmx) {
184184
float cachePercentage = conf.getFloat(HConstants.HFILE_BLOCK_CACHE_SIZE_KEY,
185185
HConstants.HFILE_BLOCK_CACHE_SIZE_DEFAULT);
186186
if (cachePercentage <= 0.0001f) {

hbase-common/src/main/resources/hbase-default.xml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -834,6 +834,15 @@ possible configurations would overwhelm and obscure the important.
834834
<description>
835835
The default thread pool size if parallel-seeking feature enabled.</description>
836836
</property>
837+
<property>
838+
<name>hfile.block.cache.policy</name>
839+
<value>LRU</value>
840+
<description>The eviction policy for the L1 block cache (LRU or TinyLFU). If you want to
841+
use TinyLFU you must build with JDK8+, run with JRE8+, and have both the
842+
hbase-tinylfu-blockcache module and its dependency the caffiene library installed into
843+
the classpath.
844+
</description>
845+
</property>
837846
<property>
838847
<name>hfile.block.cache.size</name>
839848
<value>0.4</value>

hbase-it/pom.xml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,21 @@
362362
</dependencies>
363363

364364
<profiles>
365+
366+
<profile>
367+
<id>build-with-jdk8</id>
368+
<activation>
369+
<jdk>1.8</jdk>
370+
</activation>
371+
<dependencies>
372+
<dependency>
373+
<groupId>org.apache.hbase</groupId>
374+
<artifactId>hbase-tinylfu-blockcache</artifactId>
375+
<version>${project.version}</version>
376+
</dependency>
377+
</dependencies>
378+
</profile>
379+
365380
<profile>
366381
<id>rsgroup</id>
367382
<activation>

hbase-resource-bundle/src/main/resources/supplemental-models.xml

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,20 @@ under the License.
342342
</licenses>
343343
</project>
344344
</supplement>
345+
<supplement>
346+
<project>
347+
<groupId>com.github.ben-manes.caffeine</groupId>
348+
<artifactId>caffeine</artifactId>
349+
350+
<licenses>
351+
<license>
352+
<name>Apache License, Version 2.0</name>
353+
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
354+
<distribution>repo</distribution>
355+
</license>
356+
</licenses>
357+
</project>
358+
</supplement>
345359
<supplement>
346360
<project>
347361
<groupId>com.lmax</groupId>
@@ -2181,7 +2195,7 @@ Mozilla Public License Version 2.0
21812195
means any form of the work other than Source Code Form.
21822196

21832197
1.7. "Larger Work"
2184-
means a work that combines Covered Software with other material, in
2198+
means a work that combines Covered Software with other material, in
21852199
a separate file or files, that is not Covered Software.
21862200

21872201
1.8. "License"
@@ -2612,4 +2626,17 @@ Copyright (c) 2007-2011 The JRuby project
26122626
</licenses>
26132627
</project>
26142628
</supplement>
2629+
<supplement>
2630+
<project>
2631+
<groupId>com.google.errorprone</groupId>
2632+
<artifactId>error_prone_annotations</artifactId>
2633+
<licenses>
2634+
<license>
2635+
<name>Apache License, Version 2.0</name>
2636+
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
2637+
<distribution>repo</distribution>
2638+
</license>
2639+
</licenses>
2640+
</project>
2641+
</supplement>
26152642
</supplementalDataModels>

hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/CacheConfig.java

Lines changed: 78 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
2222

2323
import java.io.IOException;
2424
import java.lang.management.ManagementFactory;
25-
import java.lang.management.MemoryUsage;
25+
import java.util.concurrent.Executor;
26+
import java.util.concurrent.ForkJoinPool;
2627

2728
import org.apache.commons.logging.Log;
2829
import org.apache.commons.logging.LogFactory;
@@ -43,6 +44,12 @@
4344
public class CacheConfig {
4445
private static final Log LOG = LogFactory.getLog(CacheConfig.class.getName());
4546

47+
/**
48+
* Configuration key to cache block policy (Lru, TinyLfu).
49+
*/
50+
public static final String HFILE_BLOCK_CACHE_POLICY_KEY = "hfile.block.cache.policy";
51+
public static final String HFILE_BLOCK_CACHE_POLICY_DEFAULT = "LRU";
52+
4653
/**
4754
* Configuration key to cache data blocks on write. There are separate
4855
* switches for bloom blocks and non-root index blocks.
@@ -90,19 +97,19 @@ public class CacheConfig {
9097
* is an in-memory map that needs to be persisted across restarts. Where to store this
9198
* in-memory state is what you supply here: e.g. <code>/tmp/bucketcache.map</code>.
9299
*/
93-
public static final String BUCKET_CACHE_PERSISTENT_PATH_KEY =
100+
public static final String BUCKET_CACHE_PERSISTENT_PATH_KEY =
94101
"hbase.bucketcache.persistent.path";
95102

96103
/**
97104
* If the bucket cache is used in league with the lru on-heap block cache (meta blocks such
98105
* as indices and blooms are kept in the lru blockcache and the data blocks in the
99106
* bucket cache).
100107
*/
101-
public static final String BUCKET_CACHE_COMBINED_KEY =
108+
public static final String BUCKET_CACHE_COMBINED_KEY =
102109
"hbase.bucketcache.combinedcache.enabled";
103110

104111
public static final String BUCKET_CACHE_WRITER_THREADS_KEY = "hbase.bucketcache.writer.threads";
105-
public static final String BUCKET_CACHE_WRITER_QUEUE_KEY =
112+
public static final String BUCKET_CACHE_WRITER_QUEUE_KEY =
106113
"hbase.bucketcache.writer.queuelength";
107114

108115
/**
@@ -175,6 +182,7 @@ private static enum ExternalBlockCaches {
175182
memcached("org.apache.hadoop.hbase.io.hfile.MemcachedBlockCache");
176183
// TODO(eclark): Consider more. Redis, etc.
177184
Class<? extends BlockCache> clazz;
185+
@SuppressWarnings("unchecked")
178186
ExternalBlockCaches(String clazzName) {
179187
try {
180188
clazz = (Class<? extends BlockCache>) Class.forName(clazzName);
@@ -507,7 +515,9 @@ public boolean shouldCacheDataCompressed() {
507515
* @return true if this {@link BlockCategory} should be compressed in blockcache, false otherwise
508516
*/
509517
public boolean shouldCacheCompressed(BlockCategory category) {
510-
if (!isBlockCacheEnabled()) return false;
518+
if (!isBlockCacheEnabled()) {
519+
return false;
520+
}
511521
switch (category) {
512522
case DATA:
513523
return this.cacheDataCompressed;
@@ -609,27 +619,61 @@ public String toString() {
609619
*/
610620
// Clear this if in tests you'd make more than one block cache instance.
611621
static BlockCache GLOBAL_BLOCK_CACHE_INSTANCE;
612-
private static LruBlockCache GLOBAL_L1_CACHE_INSTANCE = null;
613-
private static BlockCache GLOBAL_L2_CACHE_INSTANCE = null;
622+
private static FirstLevelBlockCache GLOBAL_L1_CACHE_INSTANCE;
623+
private static BlockCache GLOBAL_L2_CACHE_INSTANCE;
624+
private static ForkJoinPool GLOBAL_FORKJOIN_POOL;
614625

615626
/** Boolean whether we have disabled the block cache entirely. */
616627
static boolean blockCacheDisabled = false;
617628

618629
/**
619-
* @param c Configuration to use.
620-
* @return An L1 instance. Currently an instance of LruBlockCache.
630+
* @param c Configuration to use
631+
* @return An L1 instance
632+
*/
633+
public static FirstLevelBlockCache getL1(final Configuration c) {
634+
long xmx = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getMax();
635+
long l1CacheSize = HeapMemorySizeUtil.getFirstLevelCacheSize(c, xmx);
636+
return getL1(l1CacheSize, c);
637+
}
638+
639+
/**
640+
* @param c Configuration to use
641+
* @param xmx Max heap memory
642+
* @return An L1 instance
621643
*/
622-
private static synchronized LruBlockCache getL1(final Configuration c) {
644+
645+
private synchronized static FirstLevelBlockCache getL1(long cacheSize, Configuration c) {
623646
if (GLOBAL_L1_CACHE_INSTANCE != null) return GLOBAL_L1_CACHE_INSTANCE;
624-
final long lruCacheSize = HeapMemorySizeUtil.getLruCacheSize(c);
625-
if (lruCacheSize < 0) {
626-
blockCacheDisabled = true;
647+
if (cacheSize < 0) {
648+
return null;
627649
}
628-
if (blockCacheDisabled) return null;
650+
String policy = c.get(HFILE_BLOCK_CACHE_POLICY_KEY, HFILE_BLOCK_CACHE_POLICY_DEFAULT);
629651
int blockSize = c.getInt(BLOCKCACHE_BLOCKSIZE_KEY, HConstants.DEFAULT_BLOCKSIZE);
630-
LOG.info("Allocating LruBlockCache size=" +
631-
StringUtils.byteDesc(lruCacheSize) + ", blockSize=" + StringUtils.byteDesc(blockSize));
632-
GLOBAL_L1_CACHE_INSTANCE = new LruBlockCache(lruCacheSize, blockSize, true, c);
652+
LOG.info("Allocating BlockCache size=" +
653+
StringUtils.byteDesc(cacheSize) + ", blockSize=" + StringUtils.byteDesc(blockSize));
654+
if (policy.equalsIgnoreCase("LRU")) {
655+
GLOBAL_L1_CACHE_INSTANCE = new LruBlockCache(cacheSize, blockSize, true, c);
656+
} else if (policy.equalsIgnoreCase("TinyLFU")) {
657+
if (GLOBAL_FORKJOIN_POOL == null) {
658+
GLOBAL_FORKJOIN_POOL = new ForkJoinPool();
659+
}
660+
Class<?> tinyLFUClass;
661+
try {
662+
tinyLFUClass = Class.forName("org.apache.hadoop.hbase.io.hfile.TinyLfuBlockCache");
663+
GLOBAL_L1_CACHE_INSTANCE = (FirstLevelBlockCache)
664+
tinyLFUClass.getDeclaredConstructor(long.class, long.class, Executor.class,
665+
Configuration.class)
666+
.newInstance(cacheSize, blockSize, GLOBAL_FORKJOIN_POOL, c);
667+
} catch (Exception e) {
668+
throw new RuntimeException(
669+
"Unable to instantiate the TinyLfuBlockCache block cache policy." +
670+
"If you want to use TinyLFU you must build with JDK8+, run with JRE8+, and have both " +
671+
"the hbase-tinylfu-blockcache module and its dependency the caffiene library " +
672+
"installed into the classpath.", e);
673+
}
674+
} else {
675+
throw new IllegalArgumentException("Unknown block cache policy " + policy);
676+
}
633677
return GLOBAL_L1_CACHE_INSTANCE;
634678
}
635679

@@ -669,7 +713,7 @@ public CacheStats getL2Stats() {
669713
}
670714

671715
private static BlockCache getExternalBlockcache(Configuration c) {
672-
Class klass = null;
716+
Class<?> klass = null;
673717

674718
// Get the class, from the config. s
675719
try {
@@ -697,7 +741,9 @@ private static BlockCache getExternalBlockcache(Configuration c) {
697741
private static BlockCache getBucketCache(Configuration c) {
698742
// Check for L2. ioengine name must be non-null.
699743
String bucketCacheIOEngineName = c.get(BUCKET_CACHE_IOENGINE_KEY, null);
700-
if (bucketCacheIOEngineName == null || bucketCacheIOEngineName.length() <= 0) return null;
744+
if (bucketCacheIOEngineName == null || bucketCacheIOEngineName.length() <= 0) {
745+
return null;
746+
}
701747

702748
int blockSize = c.getInt(BLOCKCACHE_BLOCKSIZE_KEY, HConstants.DEFAULT_BLOCKSIZE);
703749
final long bucketCacheSize = HeapMemorySizeUtil.getBucketCacheSize(c);
@@ -755,33 +801,35 @@ private static BlockCache getBucketCache(Configuration c) {
755801
* @return The block cache or <code>null</code>.
756802
*/
757803
public static synchronized BlockCache instantiateBlockCache(Configuration conf) {
758-
if (GLOBAL_BLOCK_CACHE_INSTANCE != null) return GLOBAL_BLOCK_CACHE_INSTANCE;
759-
if (blockCacheDisabled) return null;
804+
if (GLOBAL_BLOCK_CACHE_INSTANCE != null) {
805+
return GLOBAL_BLOCK_CACHE_INSTANCE;
806+
}
807+
if (blockCacheDisabled) {
808+
return null;
809+
}
760810
if (conf.get(DEPRECATED_BLOCKCACHE_BLOCKSIZE_KEY) != null) {
761811
LOG.warn("The config key " + DEPRECATED_BLOCKCACHE_BLOCKSIZE_KEY +
762812
" is deprecated now, instead please use " + BLOCKCACHE_BLOCKSIZE_KEY +". "
763813
+ "In future release we will remove the deprecated config.");
764814
}
765-
LruBlockCache l1 = getL1(conf);
766-
// blockCacheDisabled is set as a side-effect of getL1Internal(), so check it again after the call.
767-
if (blockCacheDisabled) return null;
815+
FirstLevelBlockCache l1 = getL1(conf);
768816
BlockCache l2 = getL2(conf);
769817
if (l2 == null) {
770818
GLOBAL_BLOCK_CACHE_INSTANCE = l1;
771819
} else {
772820
boolean useExternal = conf.getBoolean(EXTERNAL_BLOCKCACHE_KEY, EXTERNAL_BLOCKCACHE_DEFAULT);
773-
boolean combinedWithLru = conf.getBoolean(BUCKET_CACHE_COMBINED_KEY,
821+
boolean combinedWithL1 = conf.getBoolean(BUCKET_CACHE_COMBINED_KEY,
774822
DEFAULT_BUCKET_CACHE_COMBINED);
775823
if (useExternal) {
776824
GLOBAL_BLOCK_CACHE_INSTANCE = new InclusiveCombinedBlockCache(l1, l2);
777825
} else {
778-
if (combinedWithLru) {
826+
if (combinedWithL1) {
779827
GLOBAL_BLOCK_CACHE_INSTANCE = new CombinedBlockCache(l1, l2);
780828
} else {
781-
// L1 and L2 are not 'combined'. They are connected via the LruBlockCache victimhandler
782-
// mechanism. It is a little ugly but works according to the following: when the
783-
// background eviction thread runs, blocks evicted from L1 will go to L2 AND when we get
784-
// a block from the L1 cache, if not in L1, we will search L2.
829+
// L1 and L2 are not 'combined'. They are connected via the FirstLevelBlockCache
830+
// victimhandler mechanism. It is a little ugly but works according to the following:
831+
// when the background eviction thread runs, blocks evicted from L1 will go to L2 AND when
832+
// we get a block from the L1 cache, if not in L1, we will search L2.
785833
GLOBAL_BLOCK_CACHE_INSTANCE = l1;
786834
}
787835
}

0 commit comments

Comments
 (0)