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();
+  }
+}


Reply via email to