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 09c627ae837 HBASE-30020 Introduce cache placement and admission policy
API (#8184)
09c627ae837 is described below
commit 09c627ae8377b89459ef8f77a33332439a4174ed
Author: Vladimir Rodionov <[email protected]>
AuthorDate: Thu May 7 12:12:00 2026 -0700
HBASE-30020 Introduce cache placement and admission policy API (#8184)
Signed-off-by: Peter Somogyi <[email protected]>
Signed-off-by: Tak Lon (Stephen) Wu <[email protected]>
Signed-off-by: Wellington Chevreuil <[email protected]>
---
.../hbase/io/hfile/cache/AdmissionDecision.java | 89 ++++++++++
.../hbase/io/hfile/cache/AdmissionPriority.java | 42 +++++
.../hfile/cache/CachePlacementAdmissionPolicy.java | 88 ++++++++++
.../hbase/io/hfile/cache/CacheRequestContext.java | 187 +++++++++++++++++++++
.../hbase/io/hfile/cache/CacheWriteContext.java | 166 ++++++++++++++++++
.../hbase/io/hfile/cache/CacheWriteSource.java | 57 +++++++
.../DefaultHBaseCachePlacementAdmissionPolicy.java | 124 ++++++++++++++
.../hbase/io/hfile/cache/PromotionAction.java | 37 ++++
.../hbase/io/hfile/cache/PromotionDecision.java | 122 ++++++++++++++
.../io/hfile/cache/RepresentationDecision.java | 52 ++++++
.../hadoop/hbase/io/hfile/cache/TierDecision.java | 115 +++++++++++++
11 files changed, 1079 insertions(+)
diff --git
a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/AdmissionDecision.java
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/AdmissionDecision.java
new file mode 100644
index 00000000000..8e173c616f7
--- /dev/null
+++
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/AdmissionDecision.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.yetus.audience.InterfaceAudience;
+
+/**
+ * Result of cache admission evaluation.
+ */
[email protected]
+public final class AdmissionDecision {
+
+ private static final AdmissionDecision ADMIT = new AdmissionDecision(true,
"ADMIT");
+
+ private static final AdmissionDecision REJECT = new AdmissionDecision(false,
"REJECT");
+
+ private final boolean admit;
+ private final String reason;
+
+ private AdmissionDecision(boolean admit, String reason) {
+ this.admit = admit;
+ this.reason = reason;
+ }
+
+ /**
+ * Returns a normal-priority admission decision.
+ * @return admit decision
+ */
+ public static AdmissionDecision admit() {
+ return ADMIT;
+ }
+
+ /**
+ * Returns an admission decision with a reason.
+ * @param reason reason text
+ * @return admit decision
+ */
+ public static AdmissionDecision admit(String reason) {
+ return new AdmissionDecision(true, reason);
+ }
+
+ /**
+ * Returns a rejection decision. * @return reject decision
+ */
+ public static AdmissionDecision reject() {
+ return REJECT;
+ }
+
+ /**
+ * Returns a rejection decision with a reason.
+ * @param reason rejection reason
+ * @return reject decision
+ */
+ public static AdmissionDecision reject(String reason) {
+ return new AdmissionDecision(false, reason);
+ }
+
+ /**
+ * Returns whether the block should be admitted.
+ * @return true when admitted
+ */
+ public boolean isAdmitted() {
+ return admit;
+ }
+
+ /**
+ * Returns the decision reason.
+ * @return decision reason
+ */
+ public String getReason() {
+ return reason;
+ }
+
+}
diff --git
a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/AdmissionPriority.java
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/AdmissionPriority.java
new file mode 100644
index 00000000000..3157d98061e
--- /dev/null
+++
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/AdmissionPriority.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;
+
+/**
+ * Priority hint associated with an admitted cache block.
+ */
[email protected]
+public enum AdmissionPriority {
+
+ /**
+ * Low-priority admission, suitable for opportunistic insertions such as
prefetch.
+ */
+ LOW,
+
+ /**
+ * Normal-priority admission.
+ */
+ NORMAL,
+
+ /**
+ * High-priority admission, suitable for metadata-heavy or latency-sensitive
blocks.
+ */
+ HIGH
+}
diff --git
a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/CachePlacementAdmissionPolicy.java
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/CachePlacementAdmissionPolicy.java
new file mode 100644
index 00000000000..7ce89fdceeb
--- /dev/null
+++
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/CachePlacementAdmissionPolicy.java
@@ -0,0 +1,88 @@
+/*
+ * 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.BlockCacheKey;
+import org.apache.hadoop.hbase.io.hfile.Cacheable;
+import org.apache.yetus.audience.InterfaceAudience;
+
+/**
+ * Policy interface for cache admission, tier placement, representation, and
promotion.
+ * <p>
+ * This policy decides what should happen for a cache operation. It does not
execute cache
+ * operations directly. Execution belongs to {@link CacheTopology} and storage
belongs to
+ * {@link CacheEngine}.
+ * </p>
+ * <p>
+ * The policy may consult {@link CacheTopologyView} to inspect available tiers
and engine capacity,
+ * but it must not mutate cache state.
+ * </p>
+ */
[email protected]
+public interface CachePlacementAdmissionPolicy {
+
+ /**
+ * Decides whether a block should be admitted into the cache.
+ * @param cacheKey block cache key
+ * @param block block to cache
+ * @param context write context describing insertion source and hints
+ * @param priority admission priority hint
+ * @param topologyView read-only topology view
+ * @return admission decision
+ */
+ AdmissionDecision shouldAdmit(BlockCacheKey cacheKey, Cacheable block,
CacheWriteContext context,
+ AdmissionPriority priority, CacheTopologyView topologyView);
+
+ /**
+ * Selects the target tier for an admitted block.
+ * @param cacheKey block cache key
+ * @param block block to cache
+ * @param context write context describing insertion source and hints
+ * @param topologyView read-only topology view
+ * @return tier decision
+ */
+ TierDecision selectTier(BlockCacheKey cacheKey, Cacheable block,
CacheWriteContext context,
+ CacheTopologyView topologyView);
+
+ /**
+ * Selects the cached representation for an admitted block.
+ * <p>
+ * The initial compatibility policy should preserve the representation
currently produced by
+ * existing HBase code paths.
+ * </p>
+ * @param cacheKey block cache key
+ * @param block block to cache
+ * @param context write context describing insertion source and hints
+ * @param topologyView read-only topology view
+ * @return representation decision
+ */
+ RepresentationDecision selectRepresentation(BlockCacheKey cacheKey,
Cacheable block,
+ CacheWriteContext context, CacheTopologyView topologyView);
+
+ /**
+ * Decides whether a cache hit should trigger promotion to another tier.
+ * @param cacheKey block cache key
+ * @param block cached block
+ * @param sourceTier tier where the block was found
+ * @param context read request context
+ * @param topologyView read-only topology view
+ * @return promotion decision
+ */
+ PromotionDecision shouldPromote(BlockCacheKey cacheKey, Cacheable block,
CacheTier sourceTier,
+ CacheRequestContext context, CacheTopologyView topologyView);
+}
diff --git
a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/CacheRequestContext.java
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/CacheRequestContext.java
new file mode 100644
index 00000000000..17aee5265fd
--- /dev/null
+++
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/CacheRequestContext.java
@@ -0,0 +1,187 @@
+/*
+ * 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.BlockType;
+import org.apache.yetus.audience.InterfaceAudience;
+
+/**
+ * Context for cache lookup.
+ * <p>
+ * This object carries read-path cache lookup intent. It mirrors the existing
low-allocation
+ * BlockCache request parameters while allowing future call sites to pass
structured context.
+ * </p>
+ */
[email protected]
+public final class CacheRequestContext {
+
+ private final boolean caching;
+ private final boolean repeat;
+ private final boolean updateCacheMetrics;
+ private final BlockType blockType;
+ private final boolean compaction;
+ private final boolean prefetch;
+
+ private CacheRequestContext(Builder builder) {
+ this.caching = builder.caching;
+ this.repeat = builder.repeat;
+ this.updateCacheMetrics = builder.updateCacheMetrics;
+ this.blockType = builder.blockType;
+ this.compaction = builder.compaction;
+ this.prefetch = builder.prefetch;
+ }
+
+ /**
+ * Creates a new builder.
+ */
+ public static Builder newBuilder() {
+ return new Builder();
+ }
+
+ /**
+ * Returns whether caching is enabled for this request.
+ * @return true when caching is enabled
+ */
+ public boolean isCaching() {
+ return caching;
+ }
+
+ /**
+ * Returns whether this is a repeated lookup for the same block.
+ * @return true when this is a repeated lookup
+ */
+ public boolean isRepeat() {
+ return repeat;
+ }
+
+ /**
+ * Returns whether cache metrics should be updated.
+ * @return true when metrics should be updated
+ */
+ public boolean isUpdateCacheMetrics() {
+ return updateCacheMetrics;
+ }
+
+ /**
+ * Returns the expected block type, if known.
+ * @return block type, or null if unknown
+ */
+ public BlockType getBlockType() {
+ return blockType;
+ }
+
+ /**
+ * Returns whether this request is associated with compaction.
+ * @return true when compaction-related
+ */
+ public boolean isCompaction() {
+ return compaction;
+ }
+
+ /**
+ * Returns whether this request is associated with prefetch.
+ * @return true when prefetch-related
+ */
+ public boolean isPrefetch() {
+ return prefetch;
+ }
+
+ /**
+ * Builder for {@link CacheRequestContext}.
+ */
+ public static final class Builder {
+
+ private boolean caching;
+ private boolean repeat;
+ private boolean updateCacheMetrics = true;
+ private BlockType blockType;
+ private boolean compaction;
+ private boolean prefetch;
+
+ private Builder() {
+ }
+
+ /**
+ * Sets whether caching is enabled.
+ * @param caching true when caching is enabled
+ * @return this builder
+ */
+ public Builder setCaching(boolean caching) {
+ this.caching = caching;
+ return this;
+ }
+
+ /**
+ * Sets repeated-lookup status.
+ * @param repeat true when this is a repeated lookup
+ * @return this builder
+ */
+ public Builder setRepeat(boolean repeat) {
+ this.repeat = repeat;
+ return this;
+ }
+
+ /**
+ * Sets whether cache metrics should be updated.
+ * @param updateCacheMetrics true when metrics should be updated
+ * @return this builder
+ */
+ public Builder setUpdateCacheMetrics(boolean updateCacheMetrics) {
+ this.updateCacheMetrics = updateCacheMetrics;
+ return this;
+ }
+
+ /**
+ * Sets the expected block type.
+ * @param blockType expected block type
+ * @return this builder
+ */
+ public Builder setBlockType(BlockType blockType) {
+ this.blockType = blockType;
+ return this;
+ }
+
+ /**
+ * Sets compaction request status.
+ * @param compaction true when compaction-related
+ * @return this builder
+ */
+ public Builder setCompaction(boolean compaction) {
+ this.compaction = compaction;
+ return this;
+ }
+
+ /**
+ * Sets prefetch request status.
+ * @param prefetch true when prefetch-related
+ * @return this builder
+ */
+ public Builder setPrefetch(boolean prefetch) {
+ this.prefetch = prefetch;
+ return this;
+ }
+
+ /**
+ * Builds the context.
+ * @return cache request context
+ */
+ public CacheRequestContext build() {
+ return new CacheRequestContext(this);
+ }
+ }
+}
diff --git
a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/CacheWriteContext.java
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/CacheWriteContext.java
new file mode 100644
index 00000000000..e9b2f729f5b
--- /dev/null
+++
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/CacheWriteContext.java
@@ -0,0 +1,166 @@
+/*
+ * 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.BlockType.BlockCategory;
+import org.apache.yetus.audience.InterfaceAudience;
+
+/**
+ * Context for cache insertion.
+ * <p>
+ * This object carries insertion intent from read-miss population, write path
population, prefetch,
+ * compaction, or promotion. It is intentionally small and immutable.
+ * </p>
+ */
[email protected]
+public final class CacheWriteContext {
+
+ private final boolean inMemory;
+ private final boolean waitWhenCache;
+ private final boolean cacheCompressed;
+ private final BlockCategory blockCategory;
+ private final CacheWriteSource source;
+
+ private CacheWriteContext(Builder builder) {
+ this.inMemory = builder.inMemory;
+ this.waitWhenCache = builder.waitWhenCache;
+ this.cacheCompressed = builder.cacheCompressed;
+ this.blockCategory = builder.blockCategory;
+ this.source = builder.source;
+ }
+
+ /**
+ * Creates a new builder.
+ */
+ public static Builder newBuilder() {
+ return new Builder();
+ }
+
+ /**
+ * Returns whether this block should receive in-memory treatment.
+ * @return true when in-memory treatment is requested
+ */
+ public boolean isInMemory() {
+ return inMemory;
+ }
+
+ /**
+ * Returns whether insertion should wait for asynchronous cache acceptance.
+ * @return true when wait is requested
+ */
+ public boolean isWaitWhenCache() {
+ return waitWhenCache;
+ }
+
+ /**
+ * Returns whether compressed caching is requested.
+ * @return true when compressed caching is requested
+ */
+ public boolean isCacheCompressed() {
+ return cacheCompressed;
+ }
+
+ /**
+ * Returns the block category, if known.
+ * @return block category, or null if unknown
+ */
+ public BlockCategory getBlockCategory() {
+ return blockCategory;
+ }
+
+ /**
+ * Returns the source of this cache write.
+ * @return cache write source
+ */
+ public CacheWriteSource getSource() {
+ return source;
+ }
+
+ /**
+ * Builder for {@link CacheWriteContext}.
+ */
+ public static final class Builder {
+
+ private boolean inMemory;
+ private boolean waitWhenCache;
+ private boolean cacheCompressed;
+ private BlockCategory blockCategory;
+ private CacheWriteSource source = CacheWriteSource.READ_MISS;
+
+ private Builder() {
+ }
+
+ /**
+ * Sets in-memory treatment.
+ * @param inMemory true when in-memory treatment is requested
+ * @return this builder
+ */
+ public Builder setInMemory(boolean inMemory) {
+ this.inMemory = inMemory;
+ return this;
+ }
+
+ /**
+ * Sets wait-on-cache behavior.
+ * @param waitWhenCache true when insertion should wait
+ * @return this builder
+ */
+ public Builder setWaitWhenCache(boolean waitWhenCache) {
+ this.waitWhenCache = waitWhenCache;
+ return this;
+ }
+
+ /**
+ * Sets compressed cache preference.
+ * @param cacheCompressed true when compressed caching is requested
+ * @return this builder
+ */
+ public Builder setCacheCompressed(boolean cacheCompressed) {
+ this.cacheCompressed = cacheCompressed;
+ return this;
+ }
+
+ /**
+ * Sets the block category.
+ * @param blockCategory block category
+ * @return this builder
+ */
+ public Builder setBlockCategory(BlockCategory blockCategory) {
+ this.blockCategory = blockCategory;
+ return this;
+ }
+
+ /**
+ * Sets the cache write source.
+ * @param source cache write source
+ * @return this builder
+ */
+ public Builder setSource(CacheWriteSource source) {
+ this.source = source;
+ return this;
+ }
+
+ /**
+ * Builds the context.
+ * @return cache write context
+ */
+ public CacheWriteContext build() {
+ return new CacheWriteContext(this);
+ }
+ }
+}
diff --git
a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/CacheWriteSource.java
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/CacheWriteSource.java
new file mode 100644
index 00000000000..2c98d9d7c94
--- /dev/null
+++
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/CacheWriteSource.java
@@ -0,0 +1,57 @@
+/*
+ * 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;
+
+/**
+ * Source of a cache insertion request.
+ * <p>
+ * This value describes where a cache population request originated. It does
not replace existing
+ * {@code CacheConfig} decisions. Callers should invoke cache population only
after existing HBase
+ * logic has determined that the block is eligible for caching.
+ * </p>
+ */
[email protected]
+public enum CacheWriteSource {
+
+ /**
+ * Cache population after a read miss.
+ */
+ READ_MISS,
+
+ /**
+ * Cache population during flush output generation.
+ */
+ FLUSH,
+
+ /**
+ * Cache population during compaction output generation.
+ */
+ COMPACTION,
+
+ /**
+ * Cache population by prefetch.
+ */
+ PREFETCH,
+
+ /**
+ * Cache population caused by promotion from another tier.
+ */
+ PROMOTION
+}
diff --git
a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/DefaultHBaseCachePlacementAdmissionPolicy.java
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/DefaultHBaseCachePlacementAdmissionPolicy.java
new file mode 100644
index 00000000000..039c620a33d
--- /dev/null
+++
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/DefaultHBaseCachePlacementAdmissionPolicy.java
@@ -0,0 +1,124 @@
+/*
+ * 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 org.apache.hadoop.hbase.io.hfile.BlockCacheKey;
+import org.apache.hadoop.hbase.io.hfile.BlockType;
+import org.apache.hadoop.hbase.io.hfile.Cacheable;
+import org.apache.hadoop.hbase.io.hfile.HFileBlock;
+import org.apache.yetus.audience.InterfaceAudience;
+
+/**
+ * Default compatibility policy that preserves current HBase block cache
behavior.
+ * <p>
+ * This policy is intentionally conservative. It admits cache insertion
requests by default, assumes
+ * existing {@code CacheConfig} logic has already decided whether cache
population should be
+ * attempted, preserves the current HBase block representation, and maps
metadata-like blocks to L1
+ * and data blocks to L2 when a tiered topology is available.
+ * </p>
+ */
[email protected]
+public class DefaultHBaseCachePlacementAdmissionPolicy implements
CachePlacementAdmissionPolicy {
+
+ @Override
+ public AdmissionDecision shouldAdmit(BlockCacheKey cacheKey, Cacheable block,
+ CacheWriteContext context, AdmissionPriority priority, CacheTopologyView
topologyView) {
+ Objects.requireNonNull(cacheKey, "cacheKey must not be null");
+ Objects.requireNonNull(block, "block must not be null");
+ Objects.requireNonNull(context, "context must not be null");
+ Objects.requireNonNull(priority, "priority must not be null");
+ Objects.requireNonNull(topologyView, "topologyView must not be null");
+
+ return AdmissionDecision.admit();
+ }
+
+ @Override
+ public TierDecision selectTier(BlockCacheKey cacheKey, Cacheable block,
CacheWriteContext context,
+ CacheTopologyView topologyView) {
+ Objects.requireNonNull(cacheKey, "cacheKey must not be null");
+ Objects.requireNonNull(block, "block must not be null");
+ Objects.requireNonNull(context, "context must not be null");
+ Objects.requireNonNull(topologyView, "topologyView must not be null");
+ /*
+ * Default compatibility placement prefers metadata/index/bloom blocks in
L1 and data blocks in
+ * L2 when both tiers are available, but falls back to any available tier
rather than rejecting
+ * placement.
+ */
+ if (topologyView.getType() == CacheTopologyType.SINGLE) {
+ return TierDecision.single(CacheTier.SINGLE);
+ }
+
+ if (isMetaOrIndexBlock(block)) {
+ if (topologyView.getEngine(CacheTier.L1).isPresent()) {
+ return TierDecision.single(CacheTier.L1);
+ }
+ if (topologyView.getEngine(CacheTier.L2).isPresent()) {
+ return TierDecision.single(CacheTier.L2);
+ }
+ return TierDecision.none();
+ }
+
+ if (topologyView.getEngine(CacheTier.L2).isPresent()) {
+ return TierDecision.single(CacheTier.L2);
+ }
+ if (topologyView.getEngine(CacheTier.L1).isPresent()) {
+ return TierDecision.single(CacheTier.L1);
+ }
+
+ return TierDecision.none();
+ }
+
+ @Override
+ public RepresentationDecision selectRepresentation(BlockCacheKey cacheKey,
Cacheable block,
+ CacheWriteContext context, CacheTopologyView topologyView) {
+ Objects.requireNonNull(cacheKey, "cacheKey must not be null");
+ Objects.requireNonNull(block, "block must not be null");
+ Objects.requireNonNull(context, "context must not be null");
+ Objects.requireNonNull(topologyView, "topologyView must not be null");
+
+ return RepresentationDecision.CURRENT_HBASE_DEFAULT;
+ }
+
+ @Override
+ public PromotionDecision shouldPromote(BlockCacheKey cacheKey, Cacheable
block,
+ CacheTier sourceTier, CacheRequestContext context, CacheTopologyView
topologyView) {
+ Objects.requireNonNull(cacheKey, "cacheKey must not be null");
+ Objects.requireNonNull(block, "block must not be null");
+ Objects.requireNonNull(sourceTier, "sourceTier must not be null");
+ Objects.requireNonNull(context, "context must not be null");
+ Objects.requireNonNull(topologyView, "topologyView must not be null");
+ /*
+ * DefaultHBaseCachePlacementAdmissionPolicy should preserve existing
placement behavior. If
+ * meta/index/bloom blocks belong in L1, they should be inserted into L1
when cached. If data
+ * blocks belong in L2, promoting them to L1 on hit would change current
behavior and may
+ * pollute L1. Therefore the compatibility policy should not promote by
default.
+ */
+ return PromotionDecision.none();
+
+ }
+
+ private static boolean isMetaOrIndexBlock(Cacheable block) {
+ if (!(block instanceof HFileBlock)) {
+ return false;
+ }
+
+ BlockType blockType = ((HFileBlock) block).getBlockType();
+ return blockType != null && blockType.getCategory() !=
BlockType.BlockCategory.DATA;
+ }
+}
diff --git
a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/PromotionAction.java
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/PromotionAction.java
new file mode 100644
index 00000000000..94feb91f3d3
--- /dev/null
+++
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/PromotionAction.java
@@ -0,0 +1,37 @@
+/*
+ * 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;
+
+/**
+ * Promotion action selected by cache placement policy.
+ */
[email protected]
+public enum PromotionAction {
+
+ /**
+ * Do not promote the block.
+ */
+ NONE,
+
+ /**
+ * Promote the block to another tier.
+ */
+ PROMOTE
+}
diff --git
a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/PromotionDecision.java
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/PromotionDecision.java
new file mode 100644
index 00000000000..87e4cf1584b
--- /dev/null
+++
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/PromotionDecision.java
@@ -0,0 +1,122 @@
+/*
+ * 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 org.apache.yetus.audience.InterfaceAudience;
+
+/**
+ * Decision describing whether and how a cache hit should trigger promotion.
+ * <p>
+ * This decision is produced by {@link CachePlacementAdmissionPolicy} and
executed by
+ * {@link CacheTopology}. The policy decides whether promotion should happen
and which tier should
+ * receive the promoted block. The topology decides how promotion is performed
for its semantics,
+ * for example copy for inclusive topology or move for exclusive topology.
+ * </p>
+ * <p>
+ * This class is immutable. The no-promotion decision is represented by {@link
#none()}. Promotion
+ * decisions must include a non-null target tier.
+ * </p>
+ */
[email protected]
+public final class PromotionDecision {
+
+ private static final PromotionDecision NONE =
+ new PromotionDecision(PromotionAction.NONE, null, false);
+
+ private final PromotionAction action;
+ private final CacheTier targetTier;
+ private final boolean asynchronous;
+
+ private PromotionDecision(PromotionAction action, CacheTier targetTier,
boolean asynchronous) {
+ this.action = Objects.requireNonNull(action, "action must not be null");
+ this.targetTier = targetTier;
+ this.asynchronous = asynchronous;
+ }
+
+ /**
+ * Returns a decision that performs no promotion.
+ * @return no-promotion decision
+ */
+ public static PromotionDecision none() {
+ return NONE;
+ }
+
+ /**
+ * Returns a decision to promote the block to the specified target tier.
+ * <p>
+ * The target tier must not be null. The actual promotion mechanics are
topology-specific. For
+ * example, an inclusive topology may copy the block into the target tier
while an exclusive
+ * topology may move the block.
+ * </p>
+ * @param targetTier target tier for promotion
+ * @param asynchronous whether promotion may be performed asynchronously
+ * @return promotion decision
+ */
+ public static PromotionDecision promoteTo(CacheTier targetTier, boolean
asynchronous) {
+ Objects.requireNonNull(targetTier, "targetTier must not be null");
+ return new PromotionDecision(PromotionAction.PROMOTE, targetTier,
asynchronous);
+ }
+
+ /**
+ * Returns the promotion action.
+ * @return promotion action
+ */
+ public PromotionAction getAction() {
+ return action;
+ }
+
+ /**
+ * Returns whether this decision requests promotion.
+ * @return true when promotion is requested
+ */
+ public boolean shouldPromote() {
+ return action == PromotionAction.PROMOTE;
+ }
+
+ /**
+ * Returns whether this decision has a target tier.
+ * @return true when target tier is available
+ */
+ public boolean hasTargetTier() {
+ return targetTier != null;
+ }
+
+ /**
+ * Returns the target tier for promotion.
+ * <p>
+ * This method is valid only when {@link #shouldPromote()} returns true.
+ * </p>
+ * @return target tier
+ * @throws IllegalStateException if this decision does not request promotion
+ */
+ public CacheTier getTargetTier() {
+ if (targetTier == null) {
+ throw new IllegalStateException("Promotion target tier is not
available");
+ }
+ return targetTier;
+ }
+
+ /**
+ * Returns whether promotion may be executed asynchronously.
+ * @return true when asynchronous promotion is allowed
+ */
+ public boolean isAsynchronous() {
+ return asynchronous;
+ }
+}
diff --git
a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/RepresentationDecision.java
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/RepresentationDecision.java
new file mode 100644
index 00000000000..e30ba8e7172
--- /dev/null
+++
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/RepresentationDecision.java
@@ -0,0 +1,52 @@
+/*
+ * 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;
+
+/**
+ * Decision describing the cached representation for an admitted block.
+ */
[email protected]
+public enum RepresentationDecision {
+
+ /**
+ * Preserve the representation already produced by current HBase code paths.
+ */
+
+ CURRENT_HBASE_DEFAULT,
+
+ /**
+ * Prefer packed or serialized representation.
+ */
+
+ PACKED,
+
+ /**
+ * Prefer unpacked or ready-to-use representation.
+ */
+
+ UNPACKED,
+
+ /**
+ * Let the target cache engine choose its preferred representation.
+ */
+
+ ENGINE_DEFAULT
+
+}
diff --git
a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/TierDecision.java
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/TierDecision.java
new file mode 100644
index 00000000000..fb0434b0ff7
--- /dev/null
+++
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/TierDecision.java
@@ -0,0 +1,115 @@
+/*
+ * 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.Objects;
+import org.apache.yetus.audience.InterfaceAudience;
+
+/**
+ * Decision describing the target cache tiers for a cache insertion.
+ * <p>
+ * This decision explicitly lists the {@link CacheTier}s that should receive
the block. It replaces
+ * implicit or topology-derived placement rules with a clear, ordered set of
target tiers.
+ * </p>
+ * <p>
+ * The order of tiers is significant and should reflect placement priority.
For example, a policy
+ * may return {@code [L1, L2]} to indicate that the block should be stored in
L1 first and also
+ * written to L2 (e.g. for inclusive topologies).
+ * </p>
+ * <p>
+ * An empty decision indicates that the block should not be stored in any tier.
+ * </p>
+ * <p>
+ * This class is intentionally simple and immutable. It does not encode
topology-specific behavior
+ * (e.g. inclusive vs exclusive). The {@link CacheTopology} is responsible for
interpreting and
+ * executing this decision.
+ * </p>
+ */
[email protected]
+public final class TierDecision {
+
+ private static final TierDecision NONE = new TierDecision(List.of());
+
+ private final List<CacheTier> tiers;
+
+ private TierDecision(List<CacheTier> tiers) {
+ this.tiers = tiers;
+ }
+
+ /**
+ * Returns a decision that does not place the block into any cache tier.
+ * @return empty tier decision
+ */
+ public static TierDecision none() {
+ return NONE;
+ }
+
+ /**
+ * Returns a decision targeting a single cache tier.
+ * @param tier target tier
+ * @return tier decision for a single tier
+ */
+ public static TierDecision single(CacheTier tier) {
+ Objects.requireNonNull(tier, "tier must not be null");
+ return new TierDecision(List.of(tier));
+ }
+
+ /**
+ * Returns a decision targeting multiple cache tiers.
+ * <p>
+ * The provided list must not be null, must not contain null elements, and
will be copied to
+ * preserve immutability.
+ * </p>
+ * @param tiers ordered list of target tiers
+ * @return tier decision for multiple tiers, or {@link #none()} if empty
+ */
+ public static TierDecision multiple(List<CacheTier> tiers) {
+ Objects.requireNonNull(tiers, "tiers must not be null");
+
+ if (tiers.isEmpty()) {
+ return NONE;
+ }
+
+ for (CacheTier tier : tiers) {
+ Objects.requireNonNull(tier, "tier in tiers must not be null");
+ }
+
+ return new TierDecision(List.copyOf(tiers));
+ }
+
+ /**
+ * Returns the ordered list of target tiers.
+ * <p>
+ * The returned list is immutable. The order reflects placement priority and
may be interpreted by
+ * the topology when executing the decision.
+ * </p>
+ * @return ordered list of target tiers
+ */
+ public List<CacheTier> getTiers() {
+ return tiers;
+ }
+
+ /**
+ * Returns whether this decision contains no target tiers.
+ * @return true when no tiers are selected
+ */
+ public boolean isEmpty() {
+ return tiers.isEmpty();
+ }
+}