This is an automated email from the ASF dual-hosted git repository.
taklwu pushed a commit to branch HBASE-30018
in repository https://gitbox.apache.org/repos/asf/hbase.git
The following commit(s) were added to refs/heads/HBASE-30018 by this push:
new dfe290e1bd5 HBASE-30019 Introduce CacheEngine and CacheTopology
abstractions (#8155)
dfe290e1bd5 is described below
commit dfe290e1bd5137775337e42ba61b3db11275f053
Author: Vladimir Rodionov <[email protected]>
AuthorDate: Wed Apr 29 12:56:27 2026 -0700
HBASE-30019 Introduce CacheEngine and CacheTopology abstractions (#8155)
Signed-off-by: Tak Lon (Stephen) Wu <[email protected]>
Signed-off-by: Wellington Chevreuil <[email protected]>
---
.../hadoop/hbase/io/hfile/cache/CacheEngine.java | 292 +++++++++++++++++++++
.../hbase/io/hfile/cache/CacheEngineView.java | 89 +++++++
.../hadoop/hbase/io/hfile/cache/CacheTier.java | 46 ++++
.../hadoop/hbase/io/hfile/cache/CacheTopology.java | 201 ++++++++++++++
.../hbase/io/hfile/cache/CacheTopologyType.java | 42 +++
.../hbase/io/hfile/cache/CacheTopologyView.java | 87 ++++++
.../hbase/io/hfile/cache/SingleEngineTopology.java | 93 +++++++
.../io/hfile/cache/TieredExclusiveTopology.java | 126 +++++++++
.../io/hfile/cache/TieredInclusiveTopology.java | 113 ++++++++
9 files changed, 1089 insertions(+)
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
new file mode 100644
index 00000000000..8d6629de3e2
--- /dev/null
+++
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/CacheEngine.java
@@ -0,0 +1,292 @@
+/*
+ * 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.Optional;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.io.hfile.BlockCacheKey;
+import org.apache.hadoop.hbase.io.hfile.BlockType;
+import org.apache.hadoop.hbase.io.hfile.CacheStats;
+import org.apache.hadoop.hbase.io.hfile.Cacheable;
+import org.apache.hadoop.hbase.io.hfile.HFileBlock;
+import org.apache.yetus.audience.InterfaceAudience;
+
+/**
+ * Storage abstraction for a concrete HBase block cache backend.
+ * <p>
+ * A {@code CacheEngine} represents the storage layer only. It is responsible
for storing,
+ * retrieving, invalidating, and reporting statistics for cached blocks. It
does not perform tier
+ * orchestration, admission control, placement decisions, or
promotion/demotion across cache levels.
+ * </p>
+ * <p>
+ * This interface is intentionally aligned with the storage-oriented subset of
the current
+ * {@code BlockCache} contract so that existing implementations such as
LruBlockCache and
+ * BucketCache can be migrated incrementally with minimal behavioral risk.
+ * </p>
+ * <p>
+ * Responsibilities of a {@code CacheEngine} include:
+ * </p>
+ * <ul>
+ * <li>block lookup</li>
+ * <li>block insertion</li>
+ * <li>targeted invalidation / eviction</li>
+ * <li>capacity and occupancy reporting</li>
+ * <li>engine-local statistics</li>
+ * <li>optional implementation-specific fit/capability checks</li>
+ * </ul>
+ * <p>
+ * Non-responsibilities include:
+ * </p>
+ * <ul>
+ * <li>L1/L2 topology orchestration</li>
+ * <li>admission policy</li>
+ * <li>tier placement decisions</li>
+ * <li>promotion or demotion across tiers</li>
+ * </ul>
+ */
[email protected]
+public interface CacheEngine {
+
+ /**
+ * Returns a human-readable name for this cache engine instance.
+ * <p>
+ * The name is intended for logging, metrics, diagnostics, and configuration
reporting. It should
+ * be stable for the lifetime of the engine instance.
+ * </p>
+ * @return engine name
+ */
+ String getName();
+
+ /**
+ * Adds a block to the cache.
+ * @param cacheKey block cache key
+ * @param buf block contents
+ * @param inMemory whether the block should be treated as in-memory
+ */
+ void cacheBlock(BlockCacheKey cacheKey, Cacheable buf, boolean inMemory);
+
+ /**
+ * Adds a block to the cache, optionally waiting for asynchronous cache
backends.
+ * <p>
+ * This is primarily useful for implementations such as BucketCache that may
buffer writes
+ * asynchronously.
+ * </p>
+ * @param cacheKey block cache key
+ * @param buf block contents
+ * @param inMemory whether the block should be treated as in-memory
+ * @param waitWhenCache whether to wait for the cache operation to be
accepted/flushed
+ */
+ default void cacheBlock(BlockCacheKey cacheKey, Cacheable buf, boolean
inMemory,
+ boolean waitWhenCache) {
+ cacheBlock(cacheKey, buf, inMemory);
+ }
+
+ /**
+ * Adds a block to the cache, defaulting to non in-memory treatment.
+ * @param cacheKey block cache key
+ * @param buf block contents
+ */
+ void cacheBlock(BlockCacheKey cacheKey, Cacheable buf);
+
+ /**
+ * Fetches a block from the cache.
+ * @param cacheKey block to fetch
+ * @param caching whether caching is enabled for the request;
used for metrics
+ * @param repeat whether this is a repeated lookup for the same
block; used to avoid
+ * double-counting misses
+ * @param updateCacheMetrics whether cache metrics should be updated
+ * @return cached block, or {@code null} if not present
+ */
+ Cacheable getBlock(BlockCacheKey cacheKey, boolean caching, boolean repeat,
+ boolean updateCacheMetrics);
+
+ /**
+ * Fetches a block from the cache with an optional block type hint.
+ * <p>
+ * Implementations may ignore the block type if it is not needed.
+ * </p>
+ * @param cacheKey block to fetch
+ * @param caching whether caching is enabled for the request;
used for metrics
+ * @param repeat whether this is a repeated lookup for the same
block; used to avoid
+ * double-counting misses
+ * @param updateCacheMetrics whether cache metrics should be updated
+ * @param blockType optional block type hint
+ * @return cached block, or {@code null} if not present
+ */
+ default Cacheable getBlock(BlockCacheKey cacheKey, boolean caching, boolean
repeat,
+ boolean updateCacheMetrics, BlockType blockType) {
+ return getBlock(cacheKey, caching, repeat, updateCacheMetrics);
+ }
+
+ /**
+ * Evicts a single block from the cache.
+ * @param cacheKey block to evict
+ * @return {@code true} if the block existed and was evicted, {@code false}
otherwise
+ */
+ boolean evictBlock(BlockCacheKey cacheKey);
+
+ /**
+ * Evicts all cached blocks for the given HFile.
+ * @param hfileName HFile name
+ * @return number of blocks evicted
+ */
+ int evictBlocksByHfileName(String hfileName);
+
+ /**
+ * Evicts all cached blocks for the given HFile within the specified offset
range.
+ * <p>
+ * This is useful for targeted invalidation during file lifecycle events
where only a subset of
+ * blocks should be removed.
+ * </p>
+ * @param hfileName HFile name
+ * @param initOffset inclusive start offset
+ * @param endOffset inclusive end offset
+ * @return number of blocks evicted
+ */
+ default int evictBlocksRangeByHfileName(String hfileName, long initOffset,
long endOffset) {
+ return 0;
+ }
+
+ /**
+ * Evicts all cached blocks associated with the specified region.
+ * <p>
+ * This is a new API intended to support region-scoped invalidation in a
storage-oriented way,
+ * without requiring higher-level code to enumerate files first.
+ * </p>
+ * @param regionName region name
+ * @return number of blocks evicted
+ */
+ default int evictBlocksByRegionName(String regionName) {
+ return 0;
+ }
+
+ /**
+ * Returns engine statistics.
+ * @return cache statistics
+ */
+ CacheStats getStats();
+
+ /**
+ * Shuts down this cache engine and releases any owned resources.
+ */
+ void shutdown();
+
+ /**
+ * Returns the maximum configured cache size, in bytes.
+ * @return maximum cache size
+ */
+ long getMaxSize();
+
+ /**
+ * Returns the amount of free space available in the cache, in bytes.
+ * @return free size
+ */
+ long getFreeSize();
+
+ /**
+ * Returns the currently occupied cache size, in bytes.
+ * @return occupied size
+ */
+ long size();
+
+ /**
+ * Returns the currently occupied size of data blocks, in bytes.
+ * @return occupied data-block size
+ */
+ long getCurrentDataSize();
+
+ /**
+ * Returns the total number of cached blocks.
+ * @return total block count
+ */
+ long getBlockCount();
+
+ /**
+ * Returns the number of cached data blocks.
+ * @return data block count
+ */
+ long getDataBlockCount();
+
+ /**
+ * Checks whether the given block can fit into this cache engine.
+ * <p>
+ * This method is optional because not all engines expose a meaningful fit
check.
+ * </p>
+ * @param block block to check
+ * @return empty if unsupported; otherwise whether the block fits
+ */
+ default Optional<Boolean> blockFitsIntoTheCache(HFileBlock block) {
+ return Optional.empty();
+ }
+
+ /**
+ * Checks whether the block represented by the given key is already cached.
+ * <p>
+ * This method is optional because not all engines can answer it efficiently.
+ * </p>
+ * @param key block cache key
+ * @return empty if unsupported; otherwise whether the block is cached
+ */
+ default Optional<Boolean> isAlreadyCached(BlockCacheKey key) {
+ return Optional.empty();
+ }
+
+ /**
+ * Returns the size of the cached block represented by the given key.
+ * <p>
+ * This method is optional because not all engines expose per-block size
cheaply.
+ * </p>
+ * @param key block cache key
+ * @return empty if unsupported or not present; otherwise cached block size
+ */
+ default Optional<Integer> getBlockSize(BlockCacheKey key) {
+ return Optional.empty();
+ }
+
+ /**
+ * Returns whether this cache engine is enabled.
+ * @return {@code true} if enabled, {@code false} otherwise
+ */
+ default boolean isCacheEnabled() {
+ return true;
+ }
+
+ /**
+ * Waits for asynchronous engine initialization to complete.
+ * <p>
+ * Some cache backends may perform initialization asynchronously. Engines
that do not require this
+ * may simply return {@code true} immediately.
+ * </p>
+ * @param timeout maximum time to wait
+ * @return {@code true} if the cache is enabled, {@code false} otherwise
+ */
+ default boolean waitForCacheInitialization(long timeout) {
+ return true;
+ }
+
+ /**
+ * Refreshes this engine's configuration.
+ * <p>
+ * The default implementation is a no-op.
+ * </p>
+ * @param config new configuration
+ */
+ default void onConfigurationChange(Configuration config) {
+ // noop
+ }
+}
diff --git
a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/CacheEngineView.java
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/CacheEngineView.java
new file mode 100644
index 00000000000..b2362d5f561
--- /dev/null
+++
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/CacheEngineView.java
@@ -0,0 +1,89 @@
+/*
+ * 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 org.apache.hadoop.hbase.io.hfile.Cacheable;
+import org.apache.hadoop.hbase.io.hfile.HFileBlock;
+import org.apache.yetus.audience.InterfaceAudience;
+
+/**
+ * Default read-only view over a {@link CacheEngine}.
+ */
[email protected]
+public final class CacheEngineView {
+
+ private final CacheEngine engine;
+
+ /** Create a new view over the given cache engine. */
+
+ CacheEngineView(CacheEngine engine) {
+ this.engine = engine;
+ }
+
+ /* Getters for cache engine properties. */
+ /**
+ * Returns the name of the cache engine.
+ * @return the name of the cache engine
+ */
+ public String getName() {
+ return engine.getName();
+ }
+
+ /**
+ * Returns the maximum size of the cache in bytes.
+ * @return the maximum size of the cache in bytes
+ */
+ public long getMaxSize() {
+ return engine.getMaxSize();
+ }
+
+ /**
+ * Returns the current size of the cache in bytes.
+ * @return the current size of the cache in bytes
+ */
+ public long size() {
+ return engine.size();
+ }
+
+ /**
+ * Returns the free size of the cache in bytes.
+ * @return the free size of the cache in bytes
+ */
+ public long getFreeSize() {
+ return engine.getFreeSize();
+ }
+
+ /**
+ * Returns the number of blocks currently stored in the cache.
+ * @return the number of blocks currently stored in the cache
+ */
+ public long getBlockCount() {
+ return engine.getBlockCount();
+ }
+
+ /**
+ * Is engine able to store this block.
+ * @return true - if yes, false - otherwise
+ */
+ public boolean canStore(Cacheable block) {
+ if (block instanceof HFileBlock) {
+ return engine.blockFitsIntoTheCache((HFileBlock) block).orElse(true);
+ }
+ return true;
+ }
+}
diff --git
a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/CacheTier.java
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/CacheTier.java
new file mode 100644
index 00000000000..69f1d51a49a
--- /dev/null
+++
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/CacheTier.java
@@ -0,0 +1,46 @@
+/*
+ * 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 org.apache.yetus.audience.InterfaceAudience;
+
+/**
+ * Logical cache tier used by topology and policy code.
+ * <p>
+ * This enum describes the role an engine plays inside a topology. It is
intentionally separate from
+ * {@link CacheEngineType}, which describes the implementation family of an
engine.
+ * </p>
+ */
[email protected]
+public enum CacheTier {
+
+ /**
+ * Single-tier topology engine.
+ */
+ SINGLE,
+
+ /**
+ * First-level cache, usually smaller and optimized for low latency.
+ */
+ L1,
+
+ /**
+ * Second-level cache, usually larger and optimized for capacity.
+ */
+ L2
+}
diff --git
a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/CacheTopology.java
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/CacheTopology.java
new file mode 100644
index 00000000000..daf7a053ea4
--- /dev/null
+++
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/CacheTopology.java
@@ -0,0 +1,201 @@
+/*
+ * 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.List;
+import java.util.Optional;
+import org.apache.hadoop.hbase.io.hfile.BlockCacheKey;
+import org.apache.hadoop.hbase.io.hfile.CacheStats;
+import org.apache.hadoop.hbase.io.hfile.Cacheable;
+import org.apache.yetus.audience.InterfaceAudience;
+
+/**
+ * Describes orchestration of one or more cache engines.
+ * <p>
+ * {@code CacheTopology} is responsible for the structural relationship
between cache engines, for
+ * example a single-engine cache, a tiered exclusive L1/L2 cache, or a tiered
inclusive L1/L2 cache.
+ * </p>
+ * <p>
+ * This abstraction does not own storage. Storage belongs to {@link
CacheEngine}. This abstraction
+ * also does not decide admission or write placement. Admission, placement,
representation, and
+ * promotion decisions belong to the policy layer.
+ * </p>
+ * <p>
+ * Responsibilities of a cache topology include:
+ * </p>
+ * <ul>
+ * <li>exposing participating cache engines</li>
+ * <li>mapping engines to logical tiers such as L1 and L2</li>
+ * <li>providing aggregate cache statistics</li>
+ * <li>performing topology-specific promotion when requested by policy</li>
+ * <li>optionally performing topology-specific demotion</li>
+ * <li>coordinating shutdown of participating engines</li>
+ * </ul>
+ * <p>
+ * Non-responsibilities include:
+ * </p>
+ * <ul>
+ * <li>block storage</li>
+ * <li>local eviction algorithms</li>
+ * <li>cache admission control</li>
+ * <li>write routing / target tier selection</li>
+ * <li>HFile read/write path integration</li>
+ * </ul>
+ */
[email protected]
+public interface CacheTopology {
+
+ /**
+ * Returns a human-readable topology name.
+ * <p>
+ * The name is intended for logging, metrics, diagnostics, and configuration
reporting. It should
+ * be stable for the lifetime of this topology instance.
+ * </p>
+ * @return topology name
+ */
+ String getName();
+
+ /**
+ * Returns the topology type.
+ * <p>
+ * The type identifies the topology family, such as single-engine, tiered
exclusive, or tiered
+ * inclusive.
+ * </p>
+ * @return topology type
+ */
+ CacheTopologyType getType();
+
+ /**
+ * Returns the cache engines participating in this topology.
+ * <p>
+ * The returned list is primarily intended for diagnostics, metrics, and
topology inspection.
+ * Callers should not use it to bypass topology and policy logic for normal
cache operations.
+ * </p>
+ * @return participating cache engines
+ */
+ List<CacheEngine> getEngines();
+
+ /**
+ * Returns the logical cache tiers defined by this topology.
+ * <p>
+ * This method describes the structure of the topology in terms of {@link
CacheTier} identifiers
+ * (for example, {@code SINGLE}, {@code L1}, {@code L2}). The returned list
defines which tiers
+ * are present and can be used for lookup, placement, and promotion
decisions.
+ * </p>
+ * <p>
+ * The ordering of tiers is significant and should reflect lookup priority
(for example,
+ * {@code L1} before {@code L2}).
+ * </p>
+ * <p>
+ * This method must be explicitly implemented by each topology. Callers must
not infer tier
+ * structure from {@link #getEngines()} or other properties.
+ * </p>
+ * @return ordered list of tiers defined by this topology
+ */
+ List<CacheTier> getTiers();
+
+ /**
+ * Returns the cache engine associated with the given logical tier, if one
exists.
+ * <p>
+ * For example, a tiered topology may expose an L1 and L2 engine. A
single-engine topology may
+ * return an engine for {@link CacheTier#SINGLE} and an empty result for
L1/L2.
+ * </p>
+ * @param tier logical cache tier
+ * @return cache engine for the tier, or empty if this topology does not
define that tier
+ */
+ Optional<CacheEngine> getEngine(CacheTier tier);
+
+ /**
+ * Returns aggregate topology-level cache statistics.
+ * <p>
+ * For a single-engine topology, this may simply return the underlying
engine statistics. For a
+ * multi-engine topology, this should represent an aggregate view suitable
for compatibility with
+ * existing HBase block cache metrics.
+ * </p>
+ * @return aggregate cache statistics
+ */
+ CacheStats getStats();
+
+ /**
+ * Promotes a cached block from one engine to another.
+ * <p>
+ * This method performs the topology-specific mechanics of promotion. The
decision whether a block
+ * should be promoted belongs to the placement/admission policy layer. For
example, a policy may
+ * decide that an index block found in L2 should be promoted to L1, and then
call this method to
+ * perform the promotion.
+ * </p>
+ * <p>
+ * Implementations may either copy or move the block depending on topology
semantics. For example:
+ * </p>
+ * <ul>
+ * <li>inclusive may copy the block into the target tier while retaining it
in source</li>
+ * <li>exclusive may move the block into the target tier and remove it from
source</li>
+ * </ul>
+ * @param cacheKey block cache key
+ * @param block cached block to promote
+ * @param sourceEngine engine where the block was found
+ * @param targetEngine engine where the block should be promoted
+ * @return {@code true} if promotion was performed, {@code false} otherwise
+ */
+ boolean promote(BlockCacheKey cacheKey, Cacheable block, CacheEngine
sourceEngine,
+ CacheEngine targetEngine);
+
+ /**
+ * Demotes a cached block from one engine to another.
+ * <p>
+ * Demotion is optional because not all topologies or cache engines can
support it efficiently. In
+ * many implementations, eviction does not expose the evicted block in a
form that can be cheaply
+ * demoted. The default implementation therefore performs no action.
+ * </p>
+ * <p>
+ * The decision whether demotion should happen belongs to the policy layer
or to a
+ * topology-specific eviction callback mechanism. This method only provides
a standard hook for
+ * topologies that support demotion.
+ * </p>
+ * @param cacheKey block cache key
+ * @param block cached block to demote
+ * @param sourceEngine engine from which the block is being demoted
+ * @param targetEngine engine where the block should be demoted
+ * @return {@code true} if demotion was performed, {@code false} otherwise
+ */
+ default boolean demote(BlockCacheKey cacheKey, Cacheable block, CacheEngine
sourceEngine,
+ CacheEngine targetEngine) {
+ return false;
+ }
+
+ /**
+ * Shuts down this topology and any cache engines owned by it.
+ * <p>
+ * If the topology does not own the life cycle of its engines, the
implementation should document
+ * that behavior. The default expectation is that shutting down a topology
shuts down
+ * participating engines.
+ * </p>
+ */
+ void shutdown();
+
+ /**
+ * Returns a read-only view of this topology.
+ * <p>
+ * The returned view is intended for policy implementations, diagnostics,
and metrics. It should
+ * expose topology and engine state without allowing callers to mutate cache
contents or bypass
+ * topology behavior.
+ * </p>
+ * @return read-only topology view
+ */
+ CacheTopologyView getView();
+}
diff --git
a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/CacheTopologyType.java
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/CacheTopologyType.java
new file mode 100644
index 00000000000..50ff46d5bde
--- /dev/null
+++
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/CacheTopologyType.java
@@ -0,0 +1,42 @@
+/*
+ * 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 org.apache.yetus.audience.InterfaceAudience;
+
+/**
+ * Identifies the structural form of a cache topology.
+ */
[email protected]
+public enum CacheTopologyType {
+
+ /**
+ * A topology with a single cache engine.
+ */
+ SINGLE,
+
+ /**
+ * A tiered topology where a block normally resides in only one tier at a
time.
+ */
+ TIERED_EXCLUSIVE,
+
+ /**
+ * A tiered topology where a block present in an upper tier may also remain
in a lower tier.
+ */
+ TIERED_INCLUSIVE
+}
diff --git
a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/CacheTopologyView.java
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/CacheTopologyView.java
new file mode 100644
index 00000000000..98c21afd072
--- /dev/null
+++
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/CacheTopologyView.java
@@ -0,0 +1,87 @@
+/*
+ * 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.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import org.apache.yetus.audience.InterfaceAudience;
+
+/**
+ * Default immutable-style read-only view over a {@link CacheTopology}.
+ * <p>
+ * This view delegates to the topology and exposes only read-only engine views.
+ * </p>
+ */
[email protected]
+public final class CacheTopologyView {
+
+ private final CacheTopology topology;
+
+ /**
+ * Constructs a CacheTopologyView for the given CacheTopology.
+ */
+ CacheTopologyView(CacheTopology topology) {
+ this.topology = topology;
+ }
+
+ /**
+ * Delegating getters for topology properties and read-only engine views.
+ */
+
+ public String getName() {
+ return topology.getName();
+ }
+
+ /**
+ * Returns the cache topology type, which can be SINGLE, TIERED_EXCLUSIVE,
or TIERED_INCLUSIVE.
+ * @return the cache topology type
+ */
+ public CacheTopologyType getType() {
+ return topology.getType();
+ }
+
+ /**
+ * Returns the list of cache tiers in this topology. For a single-tier
topology, it returns a list
+ * containing only CacheTier.SINGLE. For a multi-tier topology, it returns a
list containing
+ * CacheTier.L1 and CacheTier.L2.
+ * @return the list of cache tiers in this topology
+ */
+ public List<CacheTier> getTiers() {
+ return topology.getTiers();
+ }
+
+ /**
+ * Returns an Optional containing a CacheEngineView for the specified cache
tier if it exists in
+ * the topology, or an empty Optional if the tier is not present.
+ * @param tier the cache tier for which to retrieve the engine view
+ * @return an Optional containing the CacheEngineView for the specified
tier, or empty if not
+ * present
+ */
+ public Optional<CacheEngineView> getEngine(CacheTier tier) {
+ return topology.getEngine(tier).map(CacheEngineView::new);
+ }
+
+ /**
+ * Returns a list of CacheEngineView objects representing all cache engines
in this topology.
+ * @return a list of CacheEngineView objects for all engines in this topology
+ */
+ public List<CacheEngineView> getEngines() {
+ return
topology.getEngines().stream().map(CacheEngineView::new).collect(Collectors.toList());
+ }
+}
diff --git
a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/SingleEngineTopology.java
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/SingleEngineTopology.java
new file mode 100644
index 00000000000..c94deabdae5
--- /dev/null
+++
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/SingleEngineTopology.java
@@ -0,0 +1,93 @@
+/*
+ * 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.Collections;
+import java.util.List;
+import java.util.Optional;
+import org.apache.hadoop.hbase.io.hfile.BlockCacheKey;
+import org.apache.hadoop.hbase.io.hfile.CacheStats;
+import org.apache.hadoop.hbase.io.hfile.Cacheable;
+import org.apache.yetus.audience.InterfaceAudience;
+
+/**
+ * Single-engine cache topology.
+ * <p>
+ * This topology wraps a single {@link CacheEngine}. It is primarily useful as
a baseline topology
+ * and as a simple bridge for cache configurations that do not use L1/L2
tiering.
+ * </p>
+ */
[email protected]
+public class SingleEngineTopology implements CacheTopology {
+
+ private final String name;
+ private final CacheEngine engine;
+ private final CacheTopologyView view;
+
+ public SingleEngineTopology(String name, CacheEngine engine) {
+ this.name = name;
+ this.engine = engine;
+ this.view = new CacheTopologyView(this);
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public CacheTopologyType getType() {
+ return CacheTopologyType.SINGLE;
+ }
+
+ @Override
+ public List<CacheEngine> getEngines() {
+ return Collections.singletonList(engine);
+ }
+
+ @Override
+ public Optional<CacheEngine> getEngine(CacheTier tier) {
+ return tier == CacheTier.SINGLE ? Optional.of(engine) : Optional.empty();
+ }
+
+ @Override
+ public CacheTopologyView getView() {
+ return view;
+ }
+
+ @Override
+ public CacheStats getStats() {
+ return engine.getStats();
+ }
+
+ @Override
+ public boolean promote(BlockCacheKey cacheKey, Cacheable block, CacheEngine
sourceEngine,
+ CacheEngine targetEngine) {
+ return false;
+ }
+
+ @Override
+ public void shutdown() {
+ engine.shutdown();
+ }
+
+ @Override
+ public List<CacheTier> getTiers() {
+ return Collections.singletonList(CacheTier.SINGLE);
+ }
+}
diff --git
a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/TieredExclusiveTopology.java
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/TieredExclusiveTopology.java
new file mode 100644
index 00000000000..d20d5c8c34e
--- /dev/null
+++
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/TieredExclusiveTopology.java
@@ -0,0 +1,126 @@
+/*
+ * 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.Arrays;
+import java.util.List;
+import java.util.Optional;
+import org.apache.hadoop.hbase.io.hfile.BlockCacheKey;
+import org.apache.hadoop.hbase.io.hfile.CacheStats;
+import org.apache.hadoop.hbase.io.hfile.Cacheable;
+import org.apache.yetus.audience.InterfaceAudience;
+
+/**
+ * Tiered exclusive cache topology.
+ * <p>
+ * In an exclusive topology, a block should normally reside in only one tier
at a time. Promotion
+ * from L2 to L1 is therefore modeled as a move: insert into L1 and evict from
L2.
+ * </p>
+ * <p>
+ * This class is introduced as a topology foundation. Production wiring and
policy-driven routing
+ * are handled in later migration phases.
+ * </p>
+ */
[email protected]
+public class TieredExclusiveTopology implements CacheTopology {
+
+ private final String name;
+ private final CacheEngine l1;
+ private final CacheEngine l2;
+ private final CacheTopologyView view;
+
+ public TieredExclusiveTopology(String name, CacheEngine l1, CacheEngine l2) {
+ this.name = name;
+ this.l1 = l1;
+ this.l2 = l2;
+ this.view = new CacheTopologyView(this);
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public CacheTopologyType getType() {
+ return CacheTopologyType.TIERED_EXCLUSIVE;
+ }
+
+ @Override
+ public List<CacheEngine> getEngines() {
+ return Arrays.asList(l1, l2);
+ }
+
+ @Override
+ public List<CacheTier> getTiers() {
+ return Arrays.asList(CacheTier.L1, CacheTier.L2);
+ }
+
+ @Override
+ public Optional<CacheEngine> getEngine(CacheTier tier) {
+ switch (tier) {
+ case L1:
+ return Optional.of(l1);
+ case L2:
+ return Optional.of(l2);
+ default:
+ return Optional.empty();
+ }
+ }
+
+ @Override
+ public CacheTopologyView getView() {
+ return view;
+ }
+
+ @Override
+ public CacheStats getStats() {
+ // TODO: replace with aggregate topology stats in follow-up metrics ticket.
+ return l1.getStats();
+ }
+
+ @Override
+ public boolean promote(BlockCacheKey cacheKey, Cacheable block, CacheEngine
sourceEngine,
+ CacheEngine targetEngine) {
+ if (sourceEngine == null || targetEngine == null || block == null) {
+ return false;
+ }
+
+ targetEngine.cacheBlock(cacheKey, block);
+ sourceEngine.evictBlock(cacheKey);
+ return true;
+ }
+
+ @Override
+ public boolean demote(BlockCacheKey cacheKey, Cacheable block, CacheEngine
sourceEngine,
+ CacheEngine targetEngine) {
+ if (sourceEngine == null || targetEngine == null || block == null) {
+ return false;
+ }
+
+ targetEngine.cacheBlock(cacheKey, block);
+ sourceEngine.evictBlock(cacheKey);
+ return true;
+ }
+
+ @Override
+ public void shutdown() {
+ l1.shutdown();
+ l2.shutdown();
+ }
+}
diff --git
a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/TieredInclusiveTopology.java
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/TieredInclusiveTopology.java
new file mode 100644
index 00000000000..984e7ca98ca
--- /dev/null
+++
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/TieredInclusiveTopology.java
@@ -0,0 +1,113 @@
+/*
+ * 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.Arrays;
+import java.util.List;
+import java.util.Optional;
+import org.apache.hadoop.hbase.io.hfile.BlockCacheKey;
+import org.apache.hadoop.hbase.io.hfile.CacheStats;
+import org.apache.hadoop.hbase.io.hfile.Cacheable;
+import org.apache.yetus.audience.InterfaceAudience;
+
+/**
+ * Tiered inclusive cache topology.
+ * <p>
+ * In an inclusive topology, a block promoted from L2 to L1 may remain in L2.
Promotion is therefore
+ * modeled as a copy rather than a move.
+ * </p>
+ * <p>
+ * This class is introduced as a topology foundation. Production wiring and
policy-driven routing
+ * are handled in later migration phases.
+ * </p>
+ */
[email protected]
+public class TieredInclusiveTopology implements CacheTopology {
+
+ private final String name;
+ private final CacheEngine l1;
+ private final CacheEngine l2;
+ private final CacheTopologyView view;
+
+ public TieredInclusiveTopology(String name, CacheEngine l1, CacheEngine l2) {
+ this.name = name;
+ this.l1 = l1;
+ this.l2 = l2;
+ this.view = new CacheTopologyView(this);
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public CacheTopologyType getType() {
+ return CacheTopologyType.TIERED_INCLUSIVE;
+ }
+
+ @Override
+ public List<CacheEngine> getEngines() {
+ return Arrays.asList(l1, l2);
+ }
+
+ @Override
+ public List<CacheTier> getTiers() {
+ return Arrays.asList(CacheTier.L1, CacheTier.L2);
+ }
+
+ @Override
+ public Optional<CacheEngine> getEngine(CacheTier tier) {
+ switch (tier) {
+ case L1:
+ return Optional.of(l1);
+ case L2:
+ return Optional.of(l2);
+ default:
+ return Optional.empty();
+ }
+ }
+
+ @Override
+ public CacheTopologyView getView() {
+ return view;
+ }
+
+ @Override
+ public CacheStats getStats() {
+ // TODO: replace with aggregate topology stats in follow-up metrics ticket.
+ return l1.getStats();
+ }
+
+ @Override
+ public boolean promote(BlockCacheKey cacheKey, Cacheable block, CacheEngine
sourceEngine,
+ CacheEngine targetEngine) {
+ if (targetEngine == null || block == null) {
+ return false;
+ }
+
+ targetEngine.cacheBlock(cacheKey, block);
+ return true;
+ }
+
+ @Override
+ public void shutdown() {
+ l1.shutdown();
+ l2.shutdown();
+ }
+}