diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/BlockCacheFactory.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/BlockCacheFactory.java
index debfc09442a1..c3dd8947ce87 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/BlockCacheFactory.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/BlockCacheFactory.java
@@ -27,6 +27,9 @@
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HConstants;
import org.apache.hadoop.hbase.io.hfile.bucket.BucketCache;
+import org.apache.hadoop.hbase.io.hfile.cache.CacheEngine;
+import org.apache.hadoop.hbase.io.hfile.cache.CacheEngines;
+import org.apache.hadoop.hbase.io.hfile.cache.LruCacheEngine;
import org.apache.hadoop.hbase.io.util.MemorySizeUtil;
import org.apache.hadoop.hbase.regionserver.HRegion;
import org.apache.hadoop.hbase.util.ReflectionUtils;
@@ -71,8 +74,8 @@ public final class BlockCacheFactory {
*/
public static final String BLOCKCACHE_BLOCKSIZE_KEY = "hbase.blockcache.minblocksize";
- private static final String EXTERNAL_BLOCKCACHE_KEY = "hbase.blockcache.use.external";
- private static final boolean EXTERNAL_BLOCKCACHE_DEFAULT = false;
+ public static final String EXTERNAL_BLOCKCACHE_KEY = "hbase.blockcache.use.external";
+ public static final boolean EXTERNAL_BLOCKCACHE_DEFAULT = false;
private static final String EXTERNAL_BLOCKCACHE_CLASS_KEY = "hbase.blockcache.external.class";
@@ -135,6 +138,62 @@ public static BlockCache createBlockCache(Configuration conf) {
return createBlockCache(conf, null);
}
+ /**
+ * Creates the configured first-level cache as a cache engine.
+ *
+ * Cache implementations that have been migrated to {@link CacheEngine} are instantiated directly.
+ * Legacy {@link FirstLevelBlockCache} implementations are adapted until their migration is
+ * complete.
+ * @param c cache configuration
+ * @return first-level cache engine, or {@code null} when the on-heap cache is disabled
+ */
+ public static CacheEngine createFirstLevelCacheEngine(final Configuration c) {
+ final long cacheSize = MemorySizeUtil.getOnHeapCacheSize(c);
+ if (cacheSize < 0) {
+ return null;
+ }
+
+ String policy = c.get(BLOCKCACHE_POLICY_KEY, BLOCKCACHE_POLICY_DEFAULT);
+ int blockSize = c.getInt(BLOCKCACHE_BLOCKSIZE_KEY, HConstants.DEFAULT_BLOCKSIZE);
+ LOG.info("Allocating CacheEngine size=" + StringUtils.byteDesc(cacheSize) + ", blockSize="
+ + StringUtils.byteDesc(blockSize));
+
+ if (policy.equalsIgnoreCase("LRU")) {
+ return new LruCacheEngine(cacheSize, blockSize, true, c);
+ } else if (policy.equalsIgnoreCase("IndexOnlyLRU")) {
+ return CacheEngines.fromBlockCache(new IndexOnlyLruBlockCache(cacheSize, blockSize, true, c));
+ } else if (policy.equalsIgnoreCase("TinyLFU")) {
+ return CacheEngines
+ .fromBlockCache(new TinyLfuBlockCache(cacheSize, blockSize, ForkJoinPool.commonPool(), c));
+ } else if (policy.equalsIgnoreCase("AdaptiveLRU")) {
+ return CacheEngines.fromBlockCache(new LruAdaptiveBlockCache(cacheSize, blockSize, true, c));
+ } else {
+ throw new IllegalArgumentException("Unknown policy: " + policy);
+ }
+ }
+
+ /**
+ * Creates the configured external second-level cache as a cache engine.
+ * @param c cache configuration
+ * @return external cache engine, or {@code null} when no external cache can be created
+ */
+ public static CacheEngine createExternalCacheEngine(Configuration c) {
+ BlockCache blockCache = createExternalBlockcache(c);
+ return blockCache == null ? null : CacheEngines.fromBlockCache(blockCache);
+ }
+
+ /**
+ * Creates the configured bucket cache as a cache engine.
+ * @param c cache configuration
+ * @param onlineRegions currently online regions
+ * @return bucket cache engine, or {@code null} when BucketCache is disabled
+ */
+ public static CacheEngine createBucketCacheEngine(Configuration c,
+ Map onlineRegions) {
+ BucketCache bucketCache = createBucketCache(c, onlineRegions);
+ return bucketCache == null ? null : CacheEngines.fromBlockCache(bucketCache);
+ }
+
private static FirstLevelBlockCache createFirstLevelCache(final Configuration c) {
final long cacheSize = MemorySizeUtil.getOnHeapCacheSize(c);
if (cacheSize < 0) {
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/BlockCacheUtil.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/BlockCacheUtil.java
index f48da92f515d..d9f4c39afdb8 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/BlockCacheUtil.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/BlockCacheUtil.java
@@ -30,6 +30,7 @@
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.ConcurrentSkipListSet;
import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.io.hfile.cache.CacheEngine;
import org.apache.hadoop.hbase.metrics.impl.FastLongHistogram;
import org.apache.hadoop.hbase.nio.ByteBuff;
import org.apache.hadoop.hbase.regionserver.HRegion;
@@ -248,6 +249,51 @@ public static boolean shouldReplaceExistingCacheBlock(BlockCache blockCache,
}
}
+ /**
+ * Because of region splitting, it is possible that the split key is located in the middle of a
+ * block. As a result, both daughter regions may load the same block from their parent HFile.
+ *
+ * When using positional reads, HBase does not force the read to include the complete next-block
+ * header. Therefore, when two threads try to cache the same block, one thread may have read the
+ * complete next-block header while the other did not. If the already cached block does not
+ * contain {@code nextBlockOnDiskSize} but the new block does, replacing the existing block with
+ * the new one improves subsequent read performance. See HBASE-20447.
+ *
+ * @param cacheEngine cache engine to check
+ * @param cacheKey block cache key
+ * @param newBlock new block being considered for insertion
+ * @return {@code true} if the existing cached block should be replaced by {@code newBlock};
+ * {@code false} if the existing cached block should be retained
+ */
+ public static boolean shouldReplaceExistingCacheBlock(CacheEngine cacheEngine,
+ BlockCacheKey cacheKey, Cacheable newBlock) {
+ // NOTICE: getBlock retains the existingBlock before returning it.
+ Cacheable existingBlock = cacheEngine.getBlock(cacheKey, false, false, false);
+ if (existingBlock == null) {
+ return true;
+ }
+
+ try {
+ int comparison = BlockCacheUtil.validateBlockAddition(existingBlock, newBlock, cacheKey);
+ if (comparison < 0) {
+ LOG.warn("Cached block contents differ by nextBlockOnDiskSize, the new block has "
+ + "nextBlockOnDiskSize set. Caching new block.");
+ return true;
+ } else if (comparison > 0) {
+ LOG.warn("Cached block contents differ by nextBlockOnDiskSize, the existing block has "
+ + "nextBlockOnDiskSize set. Keeping cached block.");
+ return false;
+ } else {
+ LOG.debug("Caching an already cached block: {}. This is harmless and can happen in rare "
+ + "cases (see HBASE-8547)", cacheKey);
+ return false;
+ }
+ } finally {
+ // Release the reference retained by CacheEngine#getBlock.
+ existingBlock.release();
+ }
+ }
+
public static Set listAllFilesNames(Map onlineRegions) {
Set files = new HashSet<>();
onlineRegions.values().forEach(r -> {
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/CacheConfig.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/CacheConfig.java
index 9ab39b00c90d..0950f58d29b8 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/CacheConfig.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/CacheConfig.java
@@ -320,6 +320,7 @@ private void initFromConf(Configuration conf, ColumnFamilyDescriptor family) {
/**
* Constructs a cache configuration copied from the specified configuration.
+ * @param cacheConf cache configuration to copy
*/
public CacheConfig(CacheConfig cacheConf) {
this.cacheDataOnRead = cacheConf.cacheDataOnRead;
@@ -337,9 +338,7 @@ public CacheConfig(CacheConfig cacheConf) {
this.blockCache = cacheConf.blockCache;
this.byteBuffAllocator = cacheConf.byteBuffAllocator;
this.heapUsageThreshold = cacheConf.heapUsageThreshold;
- this.cacheAccessService = blockCache != null
- ? CacheAccessServices.fromBlockCache(blockCache)
- : CacheAccessServices.disabled();
+ this.cacheAccessService = cacheConf.cacheAccessService;
}
private CacheConfig() {
@@ -551,8 +550,12 @@ public boolean shouldLockOnCacheMiss(BlockType blockType) {
}
/**
- * Returns the block cache.
- * @return the block cache, or null if caching is completely disabled
+ * Returns the legacy block cache when this configuration exposes one.
+ *
+ * Native cache-engine configurations do not necessarily expose a {@link BlockCache}. Callers
+ * should use {@link #getCacheAccessService()} for cache access and diagnostics.
+ *
+ * @return legacy block cache when available
*/
public Optional getBlockCache() {
return Optional.ofNullable(this.blockCache);
@@ -561,10 +564,9 @@ public Optional getBlockCache() {
/**
* Returns the cache access service used by HFile read/write path callers.
*
- * This service is the migration-facing cache abstraction. For now it is backed by the existing
- * {@link BlockCache} when block cache is configured, or by a disabled no-op implementation when
- * block cache is unavailable. This keeps cache construction unchanged while allowing callers such
- * as {@code HFileReaderImpl} to depend on {@link CacheAccessService}.
+ * The cache access service is the primary cache abstraction exposed by this configuration. It may
+ * be backed by native {@link CacheEngine} implementations, legacy {@link BlockCache}
+ * implementations adapted as cache engines, or a combination of both.
*
* @return cache access service
*/
@@ -583,10 +585,10 @@ private static boolean isCombinedBlockCacheCompatible(CacheAccessService cacheAc
if (!(cacheAccessService instanceof TopologyBackedCacheAccessService)) {
return false;
}
-
TopologyBackedCacheAccessService service =
(TopologyBackedCacheAccessService) cacheAccessService;
- return service.getTopology().getType() == CacheTopologyType.TIERED_EXCLUSIVE;
+ CacheTopology topology = service.getTopology();
+ return topology.getTiers().contains(CacheTier.L1) && topology.getTiers().contains(CacheTier.L2);
}
public ByteBuffAllocator getByteBuffAllocator() {
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/HFileBlock.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/HFileBlock.java
index 087c619a7e99..47632910e838 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/HFileBlock.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/HFileBlock.java
@@ -2084,7 +2084,7 @@ public boolean equals(Object comparison) {
return true;
}
- DataBlockEncoding getDataBlockEncoding() {
+ public DataBlockEncoding getDataBlockEncoding() {
if (blockType == BlockType.ENCODED_DATA) {
return DataBlockEncoding.getEncodingById(getDataBlockEncodingId());
}
@@ -2211,7 +2211,7 @@ private static HFileBlock shallowClone(HFileBlock blk, ByteBuff newBuf) {
return createBuilder(blk, newBuf).build();
}
- static HFileBlock deepCloneOnHeap(HFileBlock blk) {
+ public static HFileBlock deepCloneOnHeap(HFileBlock blk) {
ByteBuff deepCloned = ByteBuff
.wrap(ByteBuffer.wrap(blk.bufWithoutChecksum.toBytes(0, blk.bufWithoutChecksum.limit())));
return createBuilder(blk, deepCloned).build();
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/AggregateCacheStats.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/AggregateCacheStats.java
new file mode 100644
index 000000000000..5dbdb022a494
--- /dev/null
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/AggregateCacheStats.java
@@ -0,0 +1,386 @@
+/*
+ * 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.io.hfile.cache;
+
+import java.util.Objects;
+import java.util.function.ToLongFunction;
+import org.apache.hadoop.hbase.io.hfile.CacheStats;
+import org.apache.yetus.audience.InterfaceAudience;
+
+/**
+ * Aggregate cache statistics backed by multiple cache-engine statistics instances.
+ */
+@InterfaceAudience.Private
+final class AggregateCacheStats extends CacheStats {
+
+ private final CacheStats[] delegates;
+
+ /**
+ * Creates aggregate cache statistics.
+ * @param name aggregate statistics name
+ * @param delegates cache statistics to aggregate
+ */
+ AggregateCacheStats(String name, CacheStats... delegates) {
+ super(name);
+ Objects.requireNonNull(delegates, "delegates must not be null");
+
+ int count = 0;
+ for (CacheStats delegate : delegates) {
+ if (delegate != null) {
+ count++;
+ }
+ }
+
+ this.delegates = new CacheStats[count];
+ int index = 0;
+ for (CacheStats delegate : delegates) {
+ if (delegate != null) {
+ this.delegates[index++] = delegate;
+ }
+ }
+ }
+
+ /**
+ * Sums a value across all underlying cache statistics.
+ * @param extractor value extractor
+ * @return aggregate value
+ */
+ private long sum(ToLongFunction extractor) {
+ long result = 0L;
+ for (CacheStats delegate : delegates) {
+ result += extractor.applyAsLong(delegate);
+ }
+ return result;
+ }
+
+ /**
+ * Returns the aggregate data-block miss count.
+ * @return aggregate data-block miss count
+ */
+ @Override
+ public long getDataMissCount() {
+ return sum(CacheStats::getDataMissCount);
+ }
+
+ /**
+ * Returns the aggregate leaf-index miss count.
+ * @return aggregate leaf-index miss count
+ */
+ @Override
+ public long getLeafIndexMissCount() {
+ return sum(CacheStats::getLeafIndexMissCount);
+ }
+
+ /**
+ * Returns the aggregate bloom-chunk miss count.
+ * @return aggregate bloom-chunk miss count
+ */
+ @Override
+ public long getBloomChunkMissCount() {
+ return sum(CacheStats::getBloomChunkMissCount);
+ }
+
+ /**
+ * Returns the aggregate metadata miss count.
+ * @return aggregate metadata miss count
+ */
+ @Override
+ public long getMetaMissCount() {
+ return sum(CacheStats::getMetaMissCount);
+ }
+
+ /**
+ * Returns the aggregate root-index miss count.
+ * @return aggregate root-index miss count
+ */
+ @Override
+ public long getRootIndexMissCount() {
+ return sum(CacheStats::getRootIndexMissCount);
+ }
+
+ /**
+ * Returns the aggregate intermediate-index miss count.
+ * @return aggregate intermediate-index miss count
+ */
+ @Override
+ public long getIntermediateIndexMissCount() {
+ return sum(CacheStats::getIntermediateIndexMissCount);
+ }
+
+ /**
+ * Returns the aggregate file-info miss count.
+ * @return aggregate file-info miss count
+ */
+ @Override
+ public long getFileInfoMissCount() {
+ return sum(CacheStats::getFileInfoMissCount);
+ }
+
+ /**
+ * Returns the aggregate general-bloom metadata miss count.
+ * @return aggregate general-bloom metadata miss count
+ */
+ @Override
+ public long getGeneralBloomMetaMissCount() {
+ return sum(CacheStats::getGeneralBloomMetaMissCount);
+ }
+
+ /**
+ * Returns the aggregate delete-family bloom miss count.
+ * @return aggregate delete-family bloom miss count
+ */
+ @Override
+ public long getDeleteFamilyBloomMissCount() {
+ return sum(CacheStats::getDeleteFamilyBloomMissCount);
+ }
+
+ /**
+ * Returns the aggregate trailer miss count.
+ * @return aggregate trailer miss count
+ */
+ @Override
+ public long getTrailerMissCount() {
+ return sum(CacheStats::getTrailerMissCount);
+ }
+
+ /**
+ * Returns the aggregate data-block hit count.
+ * @return aggregate data-block hit count
+ */
+ @Override
+ public long getDataHitCount() {
+ return sum(CacheStats::getDataHitCount);
+ }
+
+ /**
+ * Returns the aggregate leaf-index hit count.
+ * @return aggregate leaf-index hit count
+ */
+ @Override
+ public long getLeafIndexHitCount() {
+ return sum(CacheStats::getLeafIndexHitCount);
+ }
+
+ /**
+ * Returns the aggregate bloom-chunk hit count.
+ * @return aggregate bloom-chunk hit count
+ */
+ @Override
+ public long getBloomChunkHitCount() {
+ return sum(CacheStats::getBloomChunkHitCount);
+ }
+
+ /**
+ * Returns the aggregate metadata hit count.
+ * @return aggregate metadata hit count
+ */
+ @Override
+ public long getMetaHitCount() {
+ return sum(CacheStats::getMetaHitCount);
+ }
+
+ /**
+ * Returns the aggregate root-index hit count.
+ * @return aggregate root-index hit count
+ */
+ @Override
+ public long getRootIndexHitCount() {
+ return sum(CacheStats::getRootIndexHitCount);
+ }
+
+ /**
+ * Returns the aggregate intermediate-index hit count.
+ * @return aggregate intermediate-index hit count
+ */
+ @Override
+ public long getIntermediateIndexHitCount() {
+ return sum(CacheStats::getIntermediateIndexHitCount);
+ }
+
+ /**
+ * Returns the aggregate file-info hit count.
+ * @return aggregate file-info hit count
+ */
+ @Override
+ public long getFileInfoHitCount() {
+ return sum(CacheStats::getFileInfoHitCount);
+ }
+
+ /**
+ * Returns the aggregate general-bloom metadata hit count.
+ * @return aggregate general-bloom metadata hit count
+ */
+ @Override
+ public long getGeneralBloomMetaHitCount() {
+ return sum(CacheStats::getGeneralBloomMetaHitCount);
+ }
+
+ /**
+ * Returns the aggregate delete-family bloom hit count.
+ * @return aggregate delete-family bloom hit count
+ */
+ @Override
+ public long getDeleteFamilyBloomHitCount() {
+ return sum(CacheStats::getDeleteFamilyBloomHitCount);
+ }
+
+ /**
+ * Returns the aggregate trailer hit count.
+ * @return aggregate trailer hit count
+ */
+ @Override
+ public long getTrailerHitCount() {
+ return sum(CacheStats::getTrailerHitCount);
+ }
+
+ /**
+ * Returns the aggregate miss count.
+ * @return aggregate miss count
+ */
+ @Override
+ public long getMissCount() {
+ return sum(CacheStats::getMissCount);
+ }
+
+ /**
+ * Returns the aggregate primary-replica miss count.
+ * @return aggregate primary-replica miss count
+ */
+ @Override
+ public long getPrimaryMissCount() {
+ return sum(CacheStats::getPrimaryMissCount);
+ }
+
+ /**
+ * Returns the aggregate caching-request miss count.
+ * @return aggregate caching-request miss count
+ */
+ @Override
+ public long getMissCachingCount() {
+ return sum(CacheStats::getMissCachingCount);
+ }
+
+ /**
+ * Returns the aggregate hit count.
+ * @return aggregate hit count
+ */
+ @Override
+ public long getHitCount() {
+ return sum(CacheStats::getHitCount);
+ }
+
+ /**
+ * Returns the aggregate primary-replica hit count.
+ * @return aggregate primary-replica hit count
+ */
+ @Override
+ public long getPrimaryHitCount() {
+ return sum(CacheStats::getPrimaryHitCount);
+ }
+
+ /**
+ * Returns the aggregate caching-request hit count.
+ * @return aggregate caching-request hit count
+ */
+ @Override
+ public long getHitCachingCount() {
+ return sum(CacheStats::getHitCachingCount);
+ }
+
+ /**
+ * Returns the aggregate eviction count.
+ * @return aggregate eviction count
+ */
+ @Override
+ public long getEvictionCount() {
+ return sum(CacheStats::getEvictionCount);
+ }
+
+ /**
+ * Returns the aggregate number of evicted blocks.
+ * @return aggregate evicted-block count
+ */
+ @Override
+ public long getEvictedCount() {
+ return sum(CacheStats::getEvictedCount);
+ }
+
+ /**
+ * Returns the aggregate number of evicted primary-replica blocks.
+ * @return aggregate primary evicted-block count
+ */
+ @Override
+ public long getPrimaryEvictedCount() {
+ return sum(CacheStats::getPrimaryEvictedCount);
+ }
+
+ /**
+ * Returns the aggregate failed-insert count.
+ * @return aggregate failed-insert count
+ */
+ @Override
+ public long getFailedInserts() {
+ return sum(CacheStats::getFailedInserts);
+ }
+
+ /**
+ * Rolls the metrics period for all underlying cache statistics.
+ */
+ @Override
+ public void rollMetricsPeriod() {
+ for (CacheStats delegate : delegates) {
+ delegate.rollMetricsPeriod();
+ }
+ }
+
+ /**
+ * Returns the aggregate hit count over the configured rolling window.
+ * @return aggregate rolling hit count
+ */
+ @Override
+ public long getSumHitCountsPastNPeriods() {
+ return sum(CacheStats::getSumHitCountsPastNPeriods);
+ }
+
+ /**
+ * Returns the aggregate request count over the configured rolling window.
+ * @return aggregate rolling request count
+ */
+ @Override
+ public long getSumRequestCountsPastNPeriods() {
+ return sum(CacheStats::getSumRequestCountsPastNPeriods);
+ }
+
+ /**
+ * Returns the aggregate caching-hit count over the configured rolling window.
+ * @return aggregate rolling caching-hit count
+ */
+ @Override
+ public long getSumHitCachingCountsPastNPeriods() {
+ return sum(CacheStats::getSumHitCachingCountsPastNPeriods);
+ }
+
+ /**
+ * Returns the aggregate caching-request count over the configured rolling window.
+ * @return aggregate rolling caching-request count
+ */
+ @Override
+ public long getSumRequestCachingCountsPastNPeriods() {
+ return sum(CacheStats::getSumRequestCachingCountsPastNPeriods);
+ }
+}
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/CacheAccessServices.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/CacheAccessServices.java
index 344cfc19b501..050ab87f3dde 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/CacheAccessServices.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/CacheAccessServices.java
@@ -17,6 +17,7 @@
*/
package org.apache.hadoop.hbase.io.hfile.cache;
+import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import org.apache.hadoop.conf.Configuration;
@@ -25,20 +26,15 @@
import org.apache.hadoop.hbase.io.hfile.CachedBlock;
import org.apache.hadoop.hbase.io.hfile.CombinedBlockCache;
import org.apache.hadoop.hbase.io.hfile.InclusiveCombinedBlockCache;
+import org.apache.hadoop.hbase.regionserver.HRegion;
import org.apache.yetus.audience.InterfaceAudience;
/**
* Utility methods for creating {@link CacheAccessService} instances.
*
- * This class keeps service construction centralized without introducing a full factory or plugin
- * loader in the initial {@code CacheAccessService} ticket. The first supported construction modes
- * are a legacy {@link BlockCache}-backed service, a topology-backed service, and a disabled no-op
- * service.
- *
- *
- * A later integration step can move construction into {@code BlockCacheFactory} once HBase runtime
- * wiring starts returning {@link CacheAccessService} instead of, or alongside, raw
- * {@link BlockCache}.
+ * This class is the construction entry point for cache access services. Native {@link CacheEngine}
+ * implementations are used directly, while legacy {@link BlockCache} implementations are adapted to
+ * cache engines during migration.
*
*/
@InterfaceAudience.Private
@@ -81,33 +77,59 @@ public static CacheAccessService fromBlockCache(BlockCache blockCache) {
DefaultHBaseCachePlacementAdmissionPolicy.INSTANCE);
}
+ /**
+ * Creates a {@link CacheAccessService} from the block cache configuration.
+ * @param conf cache configuration
+ * @return configured cache access service, or a disabled service when block caching is disabled
+ * @throws NullPointerException if {@code conf} is {@code null}
+ */
+ public static CacheAccessService fromConfiguration(Configuration conf) {
+ return fromConfiguration(conf, null);
+ }
+
/**
* Creates a {@link CacheAccessService} from the block cache configuration.
*
- * This method is a compatibility factory for tests and transitional code paths that want to
- * obtain a {@link CacheAccessService} directly from {@link Configuration}, while still using the
- * existing {@link BlockCacheFactory} and legacy {@link BlockCache} implementations underneath.
- *
- *
- * The method delegates block-cache construction to
- * {@link BlockCacheFactory#createBlockCache(Configuration)}. If the legacy factory creates a
- * {@link BlockCache}, the returned service is backed by that cache through
- * {@link TopologyBackedCacheAccessService}. If the legacy factory does not create a cache, this
- * method returns the disabled/no-op cache access service.
- *
- *
- * This method does not introduce new cache-engine or topology-based runtime wiring. It is
- * intended only as a bridge while existing HBase tests and integration paths migrate from direct
- * {@link BlockCache} usage to {@link CacheAccessService}.
+ * Cache implementations that implement {@link CacheEngine} natively are used directly. Legacy
+ * {@link BlockCache} implementations are adapted to {@link CacheEngine} until their migration is
+ * complete.
*
- * @param conf configuration used by {@link BlockCacheFactory}
- * @return cache access service created from the configured legacy block cache, or disabled when
- * no block cache is configured
+ * @param conf cache configuration
+ * @param onlineRegions currently online regions, or {@code null} when unavailable
+ * @return configured cache access service, or a disabled service when block caching is disabled
* @throws NullPointerException if {@code conf} is {@code null}
*/
- public static CacheAccessService fromConfiguration(Configuration conf) {
+ public static CacheAccessService fromConfiguration(Configuration conf,
+ Map onlineRegions) {
Objects.requireNonNull(conf, "conf must not be null");
- return fromBlockCache(BlockCacheFactory.createBlockCache(conf));
+
+ CacheEngine l1 = BlockCacheFactory.createFirstLevelCacheEngine(conf);
+ if (l1 == null) {
+ return disabled();
+ }
+
+ CachePlacementAdmissionPolicy policy = DefaultHBaseCachePlacementAdmissionPolicy.INSTANCE;
+
+ boolean useExternal = conf.getBoolean(BlockCacheFactory.EXTERNAL_BLOCKCACHE_KEY,
+ BlockCacheFactory.EXTERNAL_BLOCKCACHE_DEFAULT);
+
+ if (useExternal) {
+ CacheEngine l2 = BlockCacheFactory.createExternalCacheEngine(conf);
+ if (l2 == null) {
+ return TopologyBackedCacheAccessServices.fromSingleCacheEngine("single", l1, policy);
+ }
+
+ return TopologyBackedCacheAccessServices.fromTieredInclusiveCacheEngines("inclusive", l1, l2,
+ policy);
+ }
+
+ CacheEngine l2 = BlockCacheFactory.createBucketCacheEngine(conf, onlineRegions);
+ if (l2 == null) {
+ return TopologyBackedCacheAccessServices.fromSingleCacheEngine("single", l1, policy);
+ }
+
+ return TopologyBackedCacheAccessServices.fromTieredExclusiveCacheEngines("combined", l1, l2,
+ policy);
}
/**
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/CacheEngine.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/CacheEngine.java
index 04020725b570..55dc85b37163 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/CacheEngine.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/CacheEngine.java
@@ -344,4 +344,17 @@ default Optional