Skip to content

Commit a263f0b

Browse files
apurtellAndrew Purtell
authored andcommitted
HBASE-22114 Port HBASE-15560 (TinyLFU-based BlockCache) to branch-1
HBASE-15560 W-TinyLFU based BlockCache (Ben Manes)
1 parent 16b91c8 commit a263f0b

File tree

15 files changed

+1351
-79
lines changed

15 files changed

+1351
-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
@@ -806,6 +806,15 @@ possible configurations would overwhelm and obscure the important.
806806
<description>
807807
The default thread pool size if parallel-seeking feature enabled.</description>
808808
</property>
809+
<property>
810+
<name>hfile.block.cache.policy</name>
811+
<value>LRU</value>
812+
<description>The eviction policy for the L1 block cache (LRU or TinyLFU). If you want to
813+
use TinyLFU you must build with JDK8+, run with JRE8+, and have both the
814+
hbase-tinylfu-blockcache module and its dependency the caffiene library installed into
815+
the classpath.
816+
</description>
817+
</property>
809818
<property>
810819
<name>hfile.block.cache.size</name>
811820
<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;
@@ -45,6 +46,12 @@
4546
public class CacheConfig {
4647
private static final Log LOG = LogFactory.getLog(CacheConfig.class.getName());
4748

49+
/**
50+
* Configuration key to cache block policy (Lru, TinyLfu).
51+
*/
52+
public static final String HFILE_BLOCK_CACHE_POLICY_KEY = "hfile.block.cache.policy";
53+
public static final String HFILE_BLOCK_CACHE_POLICY_DEFAULT = "LRU";
54+
4855
/**
4956
* Configuration key to cache data blocks on write. There are separate
5057
* switches for bloom blocks and non-root index blocks.
@@ -92,19 +99,19 @@ public class CacheConfig {
9299
* is an in-memory map that needs to be persisted across restarts. Where to store this
93100
* in-memory state is what you supply here: e.g. <code>/tmp/bucketcache.map</code>.
94101
*/
95-
public static final String BUCKET_CACHE_PERSISTENT_PATH_KEY =
102+
public static final String BUCKET_CACHE_PERSISTENT_PATH_KEY =
96103
"hbase.bucketcache.persistent.path";
97104

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

106113
public static final String BUCKET_CACHE_WRITER_THREADS_KEY = "hbase.bucketcache.writer.threads";
107-
public static final String BUCKET_CACHE_WRITER_QUEUE_KEY =
114+
public static final String BUCKET_CACHE_WRITER_QUEUE_KEY =
108115
"hbase.bucketcache.writer.queuelength";
109116

110117
/**
@@ -177,6 +184,7 @@ private static enum ExternalBlockCaches {
177184
memcached("org.apache.hadoop.hbase.io.hfile.MemcachedBlockCache");
178185
// TODO(eclark): Consider more. Redis, etc.
179186
Class<? extends BlockCache> clazz;
187+
@SuppressWarnings("unchecked")
180188
ExternalBlockCaches(String clazzName) {
181189
try {
182190
clazz = (Class<? extends BlockCache>) Class.forName(clazzName);
@@ -511,7 +519,9 @@ public boolean shouldCacheDataCompressed() {
511519
* @return true if this {@link BlockCategory} should be compressed in blockcache, false otherwise
512520
*/
513521
public boolean shouldCacheCompressed(BlockCategory category) {
514-
if (!isBlockCacheEnabled()) return false;
522+
if (!isBlockCacheEnabled()) {
523+
return false;
524+
}
515525
switch (category) {
516526
case DATA:
517527
return this.cacheDataCompressed;
@@ -614,28 +624,62 @@ public String toString() {
614624
// Clear this if in tests you'd make more than one block cache instance.
615625
@VisibleForTesting
616626
static BlockCache GLOBAL_BLOCK_CACHE_INSTANCE;
617-
private static LruBlockCache GLOBAL_L1_CACHE_INSTANCE = null;
618-
private static BlockCache GLOBAL_L2_CACHE_INSTANCE = null;
627+
private static FirstLevelBlockCache GLOBAL_L1_CACHE_INSTANCE;
628+
private static BlockCache GLOBAL_L2_CACHE_INSTANCE;
629+
private static ForkJoinPool GLOBAL_FORKJOIN_POOL;
619630

620631
/** Boolean whether we have disabled the block cache entirely. */
621632
@VisibleForTesting
622633
static boolean blockCacheDisabled = false;
623634

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

@@ -676,7 +720,7 @@ public CacheStats getL2Stats() {
676720
}
677721

678722
private static BlockCache getExternalBlockcache(Configuration c) {
679-
Class klass = null;
723+
Class<?> klass = null;
680724

681725
// Get the class, from the config. s
682726
try {
@@ -704,7 +748,9 @@ private static BlockCache getExternalBlockcache(Configuration c) {
704748
private static BlockCache getBucketCache(Configuration c) {
705749
// Check for L2. ioengine name must be non-null.
706750
String bucketCacheIOEngineName = c.get(BUCKET_CACHE_IOENGINE_KEY, null);
707-
if (bucketCacheIOEngineName == null || bucketCacheIOEngineName.length() <= 0) return null;
751+
if (bucketCacheIOEngineName == null || bucketCacheIOEngineName.length() <= 0) {
752+
return null;
753+
}
708754

709755
int blockSize = c.getInt(BLOCKCACHE_BLOCKSIZE_KEY, HConstants.DEFAULT_BLOCKSIZE);
710756
final long bucketCacheSize = HeapMemorySizeUtil.getBucketCacheSize(c);
@@ -762,33 +808,35 @@ private static BlockCache getBucketCache(Configuration c) {
762808
* @return The block cache or <code>null</code>.
763809
*/
764810
public static synchronized BlockCache instantiateBlockCache(Configuration conf) {
765-
if (GLOBAL_BLOCK_CACHE_INSTANCE != null) return GLOBAL_BLOCK_CACHE_INSTANCE;
766-
if (blockCacheDisabled) return null;
811+
if (GLOBAL_BLOCK_CACHE_INSTANCE != null) {
812+
return GLOBAL_BLOCK_CACHE_INSTANCE;
813+
}
814+
if (blockCacheDisabled) {
815+
return null;
816+
}
767817
if (conf.get(DEPRECATED_BLOCKCACHE_BLOCKSIZE_KEY) != null) {
768818
LOG.warn("The config key " + DEPRECATED_BLOCKCACHE_BLOCKSIZE_KEY +
769819
" is deprecated now, instead please use " + BLOCKCACHE_BLOCKSIZE_KEY +". "
770820
+ "In future release we will remove the deprecated config.");
771821
}
772-
LruBlockCache l1 = getL1(conf);
773-
// blockCacheDisabled is set as a side-effect of getL1Internal(), so check it again after the call.
774-
if (blockCacheDisabled) return null;
822+
FirstLevelBlockCache l1 = getL1(conf);
775823
BlockCache l2 = getL2(conf);
776824
if (l2 == null) {
777825
GLOBAL_BLOCK_CACHE_INSTANCE = l1;
778826
} else {
779827
boolean useExternal = conf.getBoolean(EXTERNAL_BLOCKCACHE_KEY, EXTERNAL_BLOCKCACHE_DEFAULT);
780-
boolean combinedWithLru = conf.getBoolean(BUCKET_CACHE_COMBINED_KEY,
828+
boolean combinedWithL1 = conf.getBoolean(BUCKET_CACHE_COMBINED_KEY,
781829
DEFAULT_BUCKET_CACHE_COMBINED);
782830
if (useExternal) {
783831
GLOBAL_BLOCK_CACHE_INSTANCE = new InclusiveCombinedBlockCache(l1, l2);
784832
} else {
785-
if (combinedWithLru) {
833+
if (combinedWithL1) {
786834
GLOBAL_BLOCK_CACHE_INSTANCE = new CombinedBlockCache(l1, l2);
787835
} else {
788-
// L1 and L2 are not 'combined'. They are connected via the LruBlockCache victimhandler
789-
// mechanism. It is a little ugly but works according to the following: when the
790-
// background eviction thread runs, blocks evicted from L1 will go to L2 AND when we get
791-
// a block from the L1 cache, if not in L1, we will search L2.
836+
// L1 and L2 are not 'combined'. They are connected via the FirstLevelBlockCache
837+
// victimhandler mechanism. It is a little ugly but works according to the following:
838+
// when the background eviction thread runs, blocks evicted from L1 will go to L2 AND when
839+
// we get a block from the L1 cache, if not in L1, we will search L2.
792840
GLOBAL_BLOCK_CACHE_INSTANCE = l1;
793841
}
794842
}

0 commit comments

Comments
 (0)